Branch data Line data Source code
1 : : // $Id: ConnCompressor.cc 7008 2010-03-25 02:42:20Z vern $
2 : :
3 : : #include <arpa/inet.h>
4 : :
5 : : #include "ConnCompressor.h"
6 : : #include "Event.h"
7 : : #include "net_util.h"
8 : :
9 : : // The basic model of the compressor is to wait for an answer before
10 : : // instantiating full connection state. Until we see a reply, only a minimal
11 : : // amount of state is stored. This has some consequences:
12 : : //
13 : : // - We try to mimic TCP.cc as close as possible, but this works only to a
14 : : // certain degree; e.g., we don't consider any of the wait-a-bit-after-
15 : : // the-connection-has-been-closed timers. That means we will get differences
16 : : // in connection semantics if the compressor is turned on. On the other
17 : : // hand, these differences will occur only for not well-established
18 : : // sessions, and experience shows that for these kinds of connections
19 : : // semantics are ill-defined in any case.
20 : : //
21 : : // - If an originator sends multiple different packets before we see a reply,
22 : : // we lose the information about additional packets (more precisely, we
23 : : // merge the packet headers into one). In particular, we lose any payload.
24 : : // This is a major problem if we see only one direction of a connection.
25 : : // When analyzing only SYN/FIN/RSTs this leads to differences if we miss
26 : : // the SYN/ACK.
27 : : //
28 : : // To avoid losing payload, there is the option cc_instantiate_on_data:
29 : : // if enabled and the originator sends a non-control packet after the
30 : : // initial packet, we instantiate full connection state.
31 : : //
32 : : // - We lose some of the information contained in initial packets (e.g., most
33 : : // IP/TCP options and any payload). If you depend on them, you don't
34 : : // want to use the compressor.
35 : : //
36 : : // Optionally, the compressor can take care only of initial SYNs and
37 : : // instantiate full connection state for all other connection setups.
38 : : // To enable, set cc_handle_only_syns to true.
39 : : //
40 : : // - The compressor may handle refused connections (i.e., initial packets
41 : : // followed by RST from responder) itself. Again, this leads to differences
42 : : // from default TCP processing and is therefore turned off by default.
43 : : // To enable, set cc_handle_resets to true.
44 : : //
45 : : // - We don't match signatures on connections which are completely handled
46 : : // by the compressor. Matching would require significant additional state
47 : : // w/o being very helpful.
48 : : //
49 : : // - Trace rewriting doesn't work if the compressor is turned on (this is
50 : : // not a conceptual problem, but simply not implemented).
51 : :
52 : :
53 : : #ifdef DEBUG
54 : 2401 : static inline const char* fmt_conn_id(const ConnCompressor::PendingConn* c)
55 : : {
56 [ + + ]: 2401 : if ( c->ip1_is_src )
57 : : return fmt_conn_id(c->key.ip1, c->key.port1,
58 : 885 : c->key.ip2, c->key.port2);
59 : : else
60 : : return fmt_conn_id(c->key.ip2, c->key.port2,
61 : 2401 : c->key.ip1, c->key.port1);
62 : : }
63 : :
64 : 17466 : static inline const char* fmt_conn_id(const Connection* c)
65 : : {
66 : : return fmt_conn_id(c->OrigAddr(), c->OrigPort(),
67 : 17466 : c->RespAddr(), c->RespPort());
68 : : }
69 : :
70 : 414 : static inline const char* fmt_conn_id(const IP_Hdr* ip)
71 : : {
72 : 414 : const struct tcphdr* tp = (const struct tcphdr*) ip->Payload();
73 : : return fmt_conn_id(ip->SrcAddr(), tp->th_sport,
74 : 414 : ip->DstAddr(), tp->th_dport);
75 : : }
76 : : #endif
77 : :
78 : 1 : ConnCompressor::ConnCompressor()
79 : : {
80 : 1 : first_block = last_block = 0;
81 : 1 : first_non_expired = 0;
82 : 1 : conn_val = 0;
83 : :
84 : 1 : sizes.connections = sizes.connections_total = 0;
85 : 1 : sizes.pending_valid = sizes.pending_total = sizes.pending_in_mem = 0;
86 : 1 : sizes.hash_table_size = 0;
87 : 1 : sizes.memory = 0;
88 : 1 : }
89 : :
90 : 1 : ConnCompressor::~ConnCompressor()
91 : : {
92 : : Block* next;
93 [ - + ][ # # ]: 1 : for ( Block* b = first_block; b; b = next )
94 : : {
95 : 0 : next = b->next;
96 : 0 : delete b;
97 : : }
98 : 1 : }
99 : :
100 : : Connection* ConnCompressor::NextPacket(double t, HashKey* key, const IP_Hdr* ip,
101 : 18556 : const struct pcap_pkthdr* hdr, const u_char* const pkt)
102 : : {
103 : : // Expire old stuff.
104 : 18556 : DoExpire(t);
105 : :
106 : : // Most sanity checks on header sizes are already done ...
107 : 18556 : const struct tcphdr* tp = (const struct tcphdr*) ip->Payload();
108 : :
109 : : // ... except this one.
110 : 18556 : uint32 tcp_hdr_len = tp->th_off * 4;
111 [ - + ]: 18556 : if ( tcp_hdr_len > uint32(ip->TotalLen() - ip->HdrLen()) )
112 : : {
113 : 0 : sessions->Weird("truncated_header", hdr, pkt);
114 [ # # ]: 0 : delete key;
115 : 0 : return 0;
116 : : }
117 : :
118 : 18556 : bool external = current_iosrc->GetCurrentTag();
119 : 18556 : ConnData* c = conns.Lookup(key);
120 : :
121 : 18556 : Unref(conn_val);
122 : 18556 : conn_val = 0;
123 : :
124 : : // Do we already have a Connection object?
125 [ + + + + ]: 18556 : if ( c && IsConnPtr(c) )
[ + + ]
126 : : {
127 : 16528 : Connection* conn = MakeConnPtr(c);
128 : 16528 : int consistent = 1;
129 : :
130 [ - + ]: 16528 : if ( external )
131 : : {
132 : : // External, and we already have a full connection.
133 : : // That means we use the same logic as in NetSessions
134 : : // to compare the tags.
135 : 0 : consistent = sessions->CheckConnectionTag(conn);
136 [ # # ]: 0 : if ( consistent < 0 )
137 : : {
138 [ # # ]: 0 : delete key;
139 : 0 : return 0;
140 : : }
141 : : }
142 : :
143 [ + - ][ + + ]: 16528 : if ( ! consistent || conn->IsReuse(t, ip->Payload()) )
[ + + ]
144 : : {
145 [ + - ]: 14 : if ( consistent )
146 : : {
147 : 14 : DBG_LOG(DBG_COMPRESSOR, "%s reuse", fmt_conn_id(conn));
148 : 14 : conn->Event(connection_reused, 0);
149 : : }
150 : :
151 : 14 : sessions->Remove(conn);
152 : 14 : --sizes.connections;
153 : :
154 : 14 : return Instantiate(t, key, ip);
155 : : }
156 : :
157 : 16514 : DBG_LOG(DBG_COMPRESSOR, "%s pass through", fmt_conn_id(conn));
158 [ + - ]: 16514 : delete key;
159 : 16514 : return conn;
160 : : }
161 : :
162 [ + + ]: 2028 : PendingConn* pending = c ? MakePendingConnPtr(c) : 0;
163 : :
164 [ + + ][ - + ]: 2028 : if ( c && external )
165 : : {
166 : : // External, but previous packets were not, i.e., they used
167 : : // the global timer queue. We finish the old connection
168 : : // and instantiate a full one now.
169 : 0 : DBG_LOG(DBG_TM, "got packet with tag %s for already"
170 : : "known cc connection, instantiating full conn",
171 : : current_iosrc->GetCurrentTag()->c_str());
172 : :
173 : : Event(pending, 0, connection_attempt,
174 : 0 : TCP_ENDPOINT_INACTIVE, 0, TCP_ENDPOINT_INACTIVE);
175 : : Event(pending, 0, connection_state_remove,
176 : 0 : TCP_ENDPOINT_INACTIVE, 0, TCP_ENDPOINT_INACTIVE);
177 : 0 : Remove(key);
178 : :
179 : 0 : return Instantiate(t, key, ip);
180 : : }
181 : :
182 [ + + ][ - + ]: 2028 : if ( c && pending->invalid &&
[ # # ]
183 : : network_time < pending->time + tcp_session_timer )
184 : : {
185 : : // The old connection has terminated sooner than
186 : : // tcp_session_timer. We assume this packet to be
187 : : // a latecomer, and ignore it.
188 : 0 : DBG_LOG(DBG_COMPRESSOR, "%s ignored", fmt_conn_id(pending));
189 : 0 : sessions->DumpPacket(hdr, pkt);
190 [ # # ]: 0 : delete key;
191 : 0 : return 0;
192 : : }
193 : :
194 : : // Simulate tcp_{reset,close}_delay for initial FINs/RSTs
195 [ + + ][ + - ]: 2028 : if ( c && ! pending->invalid &&
[ - + ][ # # ]
[ - + ][ # # ]
196 : : ((pending->FIN && pending->time + tcp_close_delay < t ) ||
197 : : (pending->RST && pending->time + tcp_reset_delay < t )) )
198 : : {
199 : 0 : DBG_LOG(DBG_COMPRESSOR, "%s closed", fmt_conn_id(pending));
200 : : int orig_state =
201 [ # # ]: 0 : pending->FIN ? TCP_ENDPOINT_CLOSED : TCP_ENDPOINT_RESET;
202 : :
203 : : Event(pending, 0, connection_partial_close, orig_state,
204 : : ip->PayloadLen() - (tp->th_off * 4),
205 : 0 : TCP_ENDPOINT_INACTIVE);
206 : : Event(pending, 0, connection_state_remove, orig_state,
207 : : ip->PayloadLen() - (tp->th_off * 4),
208 : 0 : TCP_ENDPOINT_INACTIVE);
209 : :
210 : 0 : Remove(key);
211 : :
212 : 0 : Connection* tc = FirstFromOrig(t, key, ip, tp);
213 [ # # ]: 0 : if ( ! tc )
214 : : {
215 [ # # ]: 0 : delete key;
216 : 0 : sessions->DumpPacket(hdr, pkt);
217 : : }
218 : :
219 : 0 : return tc;
220 : : }
221 : :
222 : : Connection* tc;
223 : :
224 [ + + ][ - + ]: 3035 : if ( ! c || pending->invalid )
225 : : {
226 : : // First packet of a connection.
227 [ - + ]: 1007 : if ( c )
228 : 0 : Remove(key);
229 : :
230 [ - + ]: 1007 : if ( external )
231 : : // External, we directly instantiate a full connection.
232 : 0 : tc = Instantiate(t, key, ip);
233 : : else
234 : 1007 : tc = FirstFromOrig(t, key, ip, tp);
235 : : }
236 : :
237 [ + + ][ + - ]: 1021 : else if ( addr_eq(ip->SrcAddr(), SrcAddr(pending)) &&
[ + + ]
238 : : tp->th_sport == SrcPort(pending) )
239 : : // Another packet from originator.
240 : 497 : tc = NextFromOrig(pending, t, key, tp);
241 : :
242 : : else
243 : : // A reply.
244 : 524 : tc = Response(pending, t, key, ip, tp);
245 : :
246 [ + + ]: 2028 : if ( ! tc )
247 : : {
248 [ + - ]: 1104 : delete key;
249 : 1104 : sessions->DumpPacket(hdr, pkt);
250 : : }
251 : :
252 : 18556 : return tc;
253 : : }
254 : :
255 : : static int parse_tcp_options(unsigned int opt, unsigned int optlen,
256 : : const u_char* option, TCP_Analyzer* analyzer,
257 : 5910 : bool is_orig, void* cookie)
258 : : {
259 : 5910 : ConnCompressor::PendingConn* c = (ConnCompressor::PendingConn*) cookie;
260 : :
261 : : // We're only interested in window_scale.
262 [ + + ]: 5910 : if ( opt == 3 )
263 : 654 : c->window_scale = option[2];
264 : :
265 : 5910 : return 0;
266 : : }
267 : :
268 : : Connection* ConnCompressor::FirstFromOrig(double t, HashKey* key,
269 : 1007 : const IP_Hdr* ip, const tcphdr* tp)
270 : : {
271 [ + - ][ + + ]: 1007 : if ( cc_handle_only_syns && ! (tp->th_flags & TH_SYN) )
272 : 400 : return Instantiate(t, key, ip);
273 : :
274 : : // The first packet of a connection.
275 : 607 : PendingConn* pending = MakeNewState(t);
276 : 607 : PktHdrToPendingConn(t, key, ip, tp, pending);
277 : :
278 : 607 : DBG_LOG(DBG_COMPRESSOR, "%s our", fmt_conn_id(pending));
279 : :
280 : : // The created DictEntry will point directly into our PendingConn.
281 : : // So, we have to be careful when we delete it.
282 : : conns.Dictionary::Insert(&pending->key, sizeof(pending->key),
283 : 607 : pending->hash, MakeMapPtr(pending), 0);
284 : :
285 : : // Mimic some of TCP_Analyzer's weirds for SYNs.
286 : : // To be completely precise, we'd need to check this at a few
287 : : // more locations in NextFromOrig() and Reply(). However, that
288 : : // does not really seem worth it, as this is the standard case.
289 [ + - ]: 607 : if ( tp->th_flags & TH_SYN )
290 : : {
291 [ - + ]: 607 : if ( tp->th_flags & TH_RST )
292 : 0 : Weird(pending, t, "TCP_christmas");
293 : :
294 [ - + ]: 607 : if ( tp->th_flags & TH_URG )
295 : 0 : Weird(pending, t, "baroque_SYN");
296 : :
297 : 607 : int len = ip->TotalLen() - ip->HdrLen() - tp->th_off * 4;
298 : :
299 [ - + ]: 607 : if ( len > 0 )
300 : : // T/TCP definitely complicates this.
301 : 0 : Weird(pending, t, "SYN_with_data");
302 : : }
303 : :
304 [ - + ]: 607 : if ( tp->th_flags & TH_FIN )
305 : : {
306 [ # # ]: 0 : if ( ! (tp->th_flags & TH_SYN) )
307 : 0 : Weird(pending, t, "spontaneous_FIN");
308 : : }
309 : :
310 [ - + ]: 607 : if ( tp->th_flags & TH_RST )
311 : : {
312 [ # # ]: 0 : if ( ! (tp->th_flags & TH_SYN) )
313 : 0 : Weird(pending, t, "spontaneous_RST");
314 : : }
315 : :
316 : 607 : ++sizes.pending_valid;
317 : 607 : ++sizes.pending_total;
318 : 607 : ++sizes.pending_in_mem;
319 : :
320 : : Event(pending, 0, new_connection,
321 : 607 : TCP_ENDPOINT_INACTIVE, 0, TCP_ENDPOINT_INACTIVE);
322 : :
323 [ - + ]: 607 : if ( current_iosrc->GetCurrentTag() )
324 : : {
325 : : Val* tag =
326 : 0 : new StringVal(current_iosrc->GetCurrentTag()->c_str());
327 : : Event(pending, 0, connection_external,
328 : 0 : TCP_ENDPOINT_INACTIVE, 0, TCP_ENDPOINT_INACTIVE, tag);
329 : : }
330 : :
331 : 1007 : return 0;
332 : : }
333 : :
334 : : Connection* ConnCompressor::NextFromOrig(PendingConn* pending, double t,
335 : 497 : HashKey* key, const tcphdr* tp)
336 : : {
337 : : // Another packet from the same host without seeing an answer so far.
338 : 497 : DBG_LOG(DBG_COMPRESSOR, "%s same again", fmt_conn_id(pending));
339 : :
340 : : // New window scale overrides old - not great, this is a (subtle)
341 : : // evasion opportunity.
342 [ - + ]: 497 : if ( TCP_Analyzer::ParseTCPOptions(tp, parse_tcp_options, 0, 0,
343 : : pending) < 0 )
344 : 0 : Weird(pending, t, "corrupt_tcp_options");
345 : :
346 [ + + ]: 497 : if ( tp->th_flags & TH_SYN )
347 : : // New seq overrides old.
348 : 69 : pending->seq = tp->th_seq;
349 : :
350 : : // Mimic TCP_Endpoint::Size()
351 : 497 : int size = ntohl(tp->th_seq) - ntohl(pending->seq);
352 [ + + ]: 497 : if ( size != 0 )
353 : 426 : --size;
354 : :
355 [ + + ][ + - ]: 497 : if ( size != 0 && (pending->FIN || (tp->th_flags & TH_FIN)) )
[ + + ]
356 : 11 : --size;
357 : :
358 [ + + ]: 497 : if ( size < 0 )
359 : : // We only care about the size for broken connections.
360 : : // Surely for those it's more likely that the sequence
361 : : // numbers are confused than that they really transferred
362 : : // > 2 GB of data. Plus, for 64-bit ints these sign-extend
363 : : // up to truly huge, non-sensical unsigned values.
364 : 6 : size = 0;
365 : :
366 [ + - ]: 497 : if ( pending->SYN )
367 : : {
368 : : // We're in state SYN_SENT or SYN_ACK_SENT.
369 [ + + ]: 497 : if ( tp->th_flags & TH_RST)
370 : : {
371 : : Event(pending, t, connection_reset,
372 : 11 : TCP_ENDPOINT_RESET, size, TCP_ENDPOINT_INACTIVE);
373 : : Event(pending, t, connection_state_remove,
374 : 11 : TCP_ENDPOINT_RESET, size, TCP_ENDPOINT_INACTIVE);
375 : :
376 : 11 : Invalidate(key);
377 : 11 : return 0;
378 : : }
379 : :
380 [ + + ]: 486 : else if ( tp->th_flags & TH_FIN)
381 : : {
382 : : Event(pending, t, connection_partial_close,
383 : 11 : TCP_ENDPOINT_CLOSED, size, TCP_ENDPOINT_INACTIVE);
384 : : Event(pending, t, connection_state_remove,
385 : 11 : TCP_ENDPOINT_CLOSED, size, TCP_ENDPOINT_INACTIVE);
386 : 11 : Invalidate(key);
387 : 11 : return 0;
388 : : }
389 : :
390 [ + + ]: 475 : else if ( tp->th_flags & TH_SYN )
391 : : {
392 [ + + ][ - + ]: 69 : if ( (tp->th_flags & TH_ACK) && ! pending->ACK )
393 : 0 : Weird(pending, t, "repeated_SYN_with_ack");
394 : : else
395 : : {
396 : : // We adjust the start-time. Unfortunately
397 : : // this means that we have to create a new
398 : : // PendingConn as all of them need to be
399 : : // monotonically increasing in time. This
400 : : // leads to some inconsistencies with TCP.cc,
401 : : // as by doing this we basically restart our
402 : : // attempt_timer.
403 : :
404 : 69 : pending = MoveState(t, pending);
405 : :
406 : : // Removing is necessary because the key
407 : : // will be destroyed at some point.
408 : : conns.Remove(&pending->key, sizeof(pending->key),
409 : 69 : pending->hash, true);
410 : : conns.Dictionary::Insert(&pending->key,
411 : : sizeof(pending->key), pending->hash,
412 : 69 : MakeMapPtr(pending), 0);
413 : : }
414 : : }
415 : :
416 : : else
417 : : {
418 : : // A data packet without seeing a SYN/ACK first. As
419 : : // long as we stick with the principle of instantiating
420 : : // state only when we see a reply, we have to throw
421 : : // this data away. Optionally we may instantiate a
422 : : // real connection now.
423 : :
424 [ - + ]: 406 : if ( cc_instantiate_on_data )
425 : 475 : return Instantiate(key, pending);
426 : : // else
427 : : // Weird(pending, t, "data_without_SYN_ACK");
428 : : }
429 : : }
430 : :
431 : : else
432 : : { // We're in state INACTIVE.
433 [ # # ]: 0 : if ( tp->th_flags & TH_RST)
434 : : {
435 : : Event(pending, t, connection_reset,
436 : 0 : TCP_ENDPOINT_RESET, size, TCP_ENDPOINT_INACTIVE);
437 : : Event(pending, t, connection_state_remove,
438 : 0 : TCP_ENDPOINT_RESET, size, TCP_ENDPOINT_INACTIVE);
439 : :
440 : 0 : Invalidate(key);
441 : 0 : return 0;
442 : : }
443 : :
444 [ # # ]: 0 : else if ( tp->th_flags & TH_FIN)
445 : : {
446 : : Event(pending, t, connection_half_finished,
447 : 0 : TCP_ENDPOINT_CLOSED, size, TCP_ENDPOINT_INACTIVE);
448 : : Event(pending, t, connection_state_remove,
449 : 0 : TCP_ENDPOINT_CLOSED, size, TCP_ENDPOINT_INACTIVE);
450 : :
451 : 0 : Invalidate(key);
452 : 0 : return 0;
453 : : }
454 : :
455 [ # # ]: 0 : else if ( tp->th_flags & TH_SYN )
456 : : {
457 : : if ( ! tp->th_flags & TH_ACK )
458 : : {
459 : : Weird(pending, t, "SYN_after_partial");
460 : : pending->SYN = 1;
461 : : }
462 : : }
463 : :
464 : : else
465 : : // Another data packet. See discussion above.
466 [ # # ]: 0 : if ( cc_instantiate_on_data )
467 : 0 : return Instantiate(key, pending);
468 : :
469 : : // else
470 : : // Weird(pending, t, "data_without_SYN_ACK");
471 : : }
472 : :
473 : 497 : return 0;
474 : : }
475 : :
476 : : Connection* ConnCompressor::Response(PendingConn* pending, double t,
477 : : HashKey* key, const IP_Hdr* ip,
478 : 524 : const tcphdr* tp)
479 : : {
480 : : // The packet comes from the former responder. That means we are
481 : : // seeing a reply, so we are going to create a "real" connection now.
482 : 524 : DBG_LOG(DBG_COMPRESSOR, "%s response", fmt_conn_id(pending));
483 : :
484 : : // Optional: if it's a RST after SYN, we directly generate a
485 : : // connection_rejected and throw the state away.
486 [ - + # # ]: 524 : if ( cc_handle_resets && (tp->th_flags & TH_RST) && pending->SYN )
[ # # ]
487 : : {
488 : : // See discussion of size in DoExpire().
489 : 0 : DBG_LOG(DBG_COMPRESSOR, "%s reset", fmt_conn_id(pending));
490 : :
491 : : Event(pending, t, connection_reset,
492 : 0 : TCP_ENDPOINT_SYN_SENT, 0, TCP_ENDPOINT_RESET);
493 : : Event(pending, t, connection_state_remove,
494 : 0 : TCP_ENDPOINT_SYN_SENT, 0, TCP_ENDPOINT_RESET);
495 : :
496 : 0 : Invalidate(key);
497 : 0 : return 0;
498 : : }
499 : :
500 : : // If a connection's initial packet is a RST, Bro's standard TCP
501 : : // processing considers the connection done right away. We simulate
502 : : // this by instantiating a second connection in this case. The
503 : : // first one will time out eventually.
504 [ - + ][ # # ]: 524 : if ( pending->RST && ! pending->SYN )
505 : : {
506 : : int orig_state =
507 [ # # ]: 0 : pending->RST ? TCP_ENDPOINT_RESET : TCP_ENDPOINT_CLOSED;
508 : : Event(pending, 0, connection_attempt,
509 : 0 : orig_state, 0, TCP_ENDPOINT_INACTIVE);
510 : : Event(pending, 0, connection_state_remove,
511 : 0 : orig_state, 0, TCP_ENDPOINT_INACTIVE);
512 : :
513 : : // Override with current packet.
514 : 0 : PktHdrToPendingConn(t, key, ip, tp, pending);
515 : 0 : return 0;
516 : : }
517 : :
518 : 524 : return Instantiate(key, pending);
519 : : }
520 : :
521 : 524 : Connection* ConnCompressor::Instantiate(HashKey* key, PendingConn* pending)
522 : : {
523 : : // Instantantiate a Connection.
524 : : ConnID conn_id;
525 : 524 : conn_id.src_addr = SrcAddr(pending);
526 : 524 : conn_id.dst_addr = DstAddr(pending);
527 : 524 : conn_id.src_port = SrcPort(pending);
528 : 524 : conn_id.dst_port = DstPort(pending);
529 : :
530 : 524 : pending->invalid = 1;
531 : 524 : --sizes.pending_valid;
532 : 524 : --sizes.pending_total;
533 : :
534 : : // Fake the first packet.
535 : 524 : const IP_Hdr* faked_pkt = PendingConnToPacket(pending);
536 : : Connection* new_conn = sessions->NewConn(key, pending->time, &conn_id,
537 : 524 : faked_pkt->Payload(), IPPROTO_TCP);
538 : :
539 [ - + ]: 524 : if ( ! new_conn )
540 : : {
541 : : // This connection is not to be analyzed (e.g., it may be
542 : : // a partial one).
543 : 0 : DBG_LOG(DBG_COMPRESSOR, "%s nop", fmt_conn_id(pending));
544 : 0 : return 0;
545 : : }
546 : :
547 : 524 : DBG_LOG(DBG_COMPRESSOR, "%s instantiated", fmt_conn_id(pending));
548 : :
549 : 524 : ++sizes.connections;
550 : 524 : ++sizes.connections_total;
551 : :
552 [ - + ]: 524 : if ( new_packet )
553 : : new_conn->Event(new_packet, 0,
554 : 0 : sessions->BuildHeader(faked_pkt->IP4_Hdr()));
555 : :
556 : : // NewConn() may have swapped originator and responder.
557 : : int is_orig = addr_eq(conn_id.src_addr, new_conn->OrigAddr()) &&
558 [ + + ][ + - ]: 524 : conn_id.src_port == new_conn->OrigPort();
559 : :
560 : : // Pass the faked packet to the connection.
561 : 524 : const u_char* payload = faked_pkt->Payload();
562 : :
563 : : int dummy_record_packet, dummy_record_content;
564 : : new_conn->NextPacket(pending->time, is_orig,
565 : : faked_pkt, faked_pkt->PayloadLen(),
566 : : faked_pkt->PayloadLen(), payload,
567 : 524 : dummy_record_packet, dummy_record_content, 0, 0, 0);
568 : :
569 : : // Removing necessary because the key will be destroyed at some point.
570 : 524 : conns.Remove(&pending->key, sizeof(pending->key), pending->hash, true);
571 : 524 : conns.Insert(key, MakeMapPtr(new_conn));
572 : :
573 : 524 : return new_conn;
574 : : }
575 : :
576 : : Connection* ConnCompressor::Instantiate(double t, HashKey* key,
577 : 414 : const IP_Hdr* ip)
578 : : {
579 : 414 : const struct tcphdr* tp = (const struct tcphdr*) ip->Payload();
580 : :
581 : : ConnID conn_id;
582 : 414 : conn_id.src_addr = ip->SrcAddr();
583 : 414 : conn_id.dst_addr = ip->DstAddr();
584 : 414 : conn_id.src_port = tp->th_sport;
585 : 414 : conn_id.dst_port = tp->th_dport;
586 : :
587 : : Connection* new_conn =
588 : 414 : sessions->NewConn(key, t, &conn_id, ip->Payload(), IPPROTO_TCP);
589 : :
590 [ - + ]: 414 : if ( ! new_conn )
591 : : {
592 : : // This connection is not to be analyzed (e.g., it may be
593 : : // a partial one).
594 : 0 : DBG_LOG(DBG_COMPRESSOR, "%s nop", fmt_conn_id(ip));
595 : 0 : return 0;
596 : : }
597 : :
598 : 414 : DBG_LOG(DBG_COMPRESSOR, "%s instantiated", fmt_conn_id(ip));
599 : :
600 : 414 : conns.Insert(key, MakeMapPtr(new_conn));
601 : 414 : ++sizes.connections;
602 : 414 : ++sizes.connections_total;
603 : :
604 [ + - ]: 414 : if ( new_connection )
605 : 414 : new_conn->Event(new_connection, 0);
606 : :
607 [ - + ]: 414 : if ( current_iosrc->GetCurrentTag() )
608 : : {
609 : : Val* tag =
610 : 0 : new StringVal(current_iosrc->GetCurrentTag()->c_str());
611 : 0 : new_conn->Event(connection_external, 0, tag);
612 : : }
613 : :
614 : 414 : return new_conn;
615 : : }
616 : :
617 : : void ConnCompressor::PktHdrToPendingConn(double time, const HashKey* key,
618 : 607 : const IP_Hdr* ip, const struct tcphdr* tp, PendingConn* c)
619 : : {
620 : 607 : memcpy(&c->key, key->Key(), key->Size());
621 : :
622 : 607 : c->hash = key->Hash();
623 : : c->ip1_is_src = addr_eq(c->key.ip1, ip->SrcAddr()) &&
624 [ + + + - ]: 607 : c->key.port1 == tp->th_sport;
625 : 607 : c->time = time;
626 : 607 : c->window = tp->th_win;
627 : 607 : c->seq = tp->th_seq;
628 : 607 : c->ack = tp->th_ack;
629 : 607 : c->window_scale = 0;
630 : 607 : c->SYN = (tp->th_flags & TH_SYN) != 0;
631 : 607 : c->FIN = (tp->th_flags & TH_FIN) != 0;
632 : 607 : c->RST = (tp->th_flags & TH_RST) != 0;
633 : 607 : c->ACK = (tp->th_flags & TH_ACK) != 0;
634 : 607 : c->invalid = 0;
635 : :
636 [ - + ]: 607 : if ( TCP_Analyzer::ParseTCPOptions(tp, parse_tcp_options, 0, 0, c) < 0 )
637 : 0 : sessions->Weird("corrupt_tcp_options", ip);
638 : 607 : }
639 : :
640 : : // Fakes an empty TCP packet based on the information in PendingConn.
641 : 524 : const IP_Hdr* ConnCompressor::PendingConnToPacket(const PendingConn* c)
642 : : {
643 : : static ip* ip = 0;
644 : : static tcphdr* tp = 0;
645 : : static IP_Hdr* ip_hdr = 0;
646 : :
647 [ + + ]: 524 : if ( ! ip )
648 : : { // Initialize. ### Note, only handles IPv4 for now.
649 : 1 : int packet_length = sizeof(*ip) + sizeof(*tp);
650 : 1 : ip = (struct ip*) new char[packet_length];
651 : 1 : tp = (struct tcphdr*) (((char*) ip) + sizeof(*ip));
652 : 1 : ip_hdr = new IP_Hdr(ip);
653 : :
654 : : // Constant fields.
655 : 1 : ip->ip_v = 4;
656 : 1 : ip->ip_hl = sizeof(*ip) / 4; // no options
657 : 1 : ip->ip_tos = 0;
658 : 1 : ip->ip_len = htons(packet_length);
659 : 1 : ip->ip_id = 0;
660 : 1 : ip->ip_off = 0;
661 : 1 : ip->ip_ttl = 255;
662 : 1 : ip->ip_p = IPPROTO_TCP;
663 : 1 : ip->ip_sum = 0; // is not going to be checked
664 : :
665 : 1 : tp->th_off = sizeof(*tp) / 4; // no options for now
666 : 1 : tp->th_urp = 0;
667 : : }
668 : :
669 : : // Note, do *not* use copy_addr() here. This is because we're
670 : : // copying to an IPv4 header, which has room for exactly and
671 : : // only an IPv4 address.
672 : : #ifdef BROv6
673 : : if ( ! is_v4_addr(c->key.ip1) || ! is_v4_addr(c->key.ip2) )
674 : : internal_error("IPv6 snuck into connection compressor");
675 : : #endif
676 : : *(uint32*) &ip->ip_src =
677 [ + + ]: 524 : to_v4_addr(c->ip1_is_src ? c->key.ip1 : c->key.ip2);
678 : : *(uint32*) &ip->ip_dst =
679 [ + + ]: 524 : to_v4_addr(c->ip1_is_src ? c->key.ip2 : c->key.ip1);
680 : :
681 [ + + ]: 524 : if ( c->ip1_is_src )
682 : : {
683 : 112 : tp->th_sport = c->key.port1;
684 : 112 : tp->th_dport = c->key.port2;
685 : : }
686 : : else
687 : : {
688 : 412 : tp->th_sport = c->key.port2;
689 : 412 : tp->th_dport = c->key.port1;
690 : : }
691 : :
692 : 524 : tp->th_win = c->window;
693 : 524 : tp->th_seq = c->seq;
694 : 524 : tp->th_ack = c->ack;
695 : 524 : tp->th_flags = MakeFlags(c);
696 : 524 : tp->th_sum = 0;
697 : 524 : tp->th_sum = 0xffff - tcp_checksum(ip, tp, 0);
698 : :
699 : : // FIXME: Add TCP options.
700 : 524 : return ip_hdr;
701 : : }
702 : :
703 : 1214 : uint8 ConnCompressor::MakeFlags(const PendingConn* c) const
704 : : {
705 : 1214 : uint8 tcp_flags = 0;
706 [ + - ]: 1214 : if ( c->SYN )
707 : 1214 : tcp_flags |= TH_SYN;
708 [ - + ]: 1214 : if ( c->FIN )
709 : 0 : tcp_flags |= TH_FIN;
710 [ - + ]: 1214 : if ( c->RST )
711 : 0 : tcp_flags |= TH_RST;
712 [ + + ]: 1214 : if ( c->ACK )
713 : 232 : tcp_flags |= TH_ACK;
714 : :
715 : 1214 : return tcp_flags;
716 : : }
717 : :
718 : : ConnCompressor::PendingConn* ConnCompressor::MoveState(double time,
719 : 69 : PendingConn* c)
720 : : {
721 : 69 : PendingConn* nc = MakeNewState(time);
722 : 69 : memcpy(nc, c, sizeof(PendingConn));
723 : 69 : c->invalid = 1;
724 : 69 : nc->time = time;
725 : 69 : ++sizes.pending_in_mem;
726 : 69 : return nc;
727 : : }
728 : :
729 : 676 : ConnCompressor::PendingConn* ConnCompressor::MakeNewState(double t)
730 : : {
731 : : // See if there is enough space in the current block.
732 [ + + ][ + + ]: 676 : if ( last_block &&
733 : : int(sizeof(PendingConn)) <= BLOCK_SIZE - last_block->bytes_used )
734 : : {
735 : 606 : PendingConn* c = (PendingConn*) &last_block->data[last_block->bytes_used];
736 : 606 : last_block->bytes_used += sizeof(PendingConn);
737 : 606 : c->is_pending = true;
738 : 606 : return c;
739 : : }
740 : :
741 : : // Get new block.
742 : 70 : Block* b = new Block;
743 : 70 : b->time = t;
744 : 70 : b->bytes_used = sizeof(PendingConn);
745 : 70 : b->next = 0;
746 : 70 : b->prev = last_block;
747 : :
748 [ + + ]: 70 : if ( last_block )
749 : 1 : last_block->next = b;
750 : : else
751 : 69 : first_block = b;
752 : :
753 : 70 : last_block = b;
754 : :
755 : 70 : sizes.memory += padded_sizeof(*b);
756 : 70 : PendingConn* c = (PendingConn*) &b->data;
757 : 70 : c->is_pending = true;
758 : 676 : return c;
759 : : }
760 : :
761 : 18557 : void ConnCompressor::DoExpire(double t)
762 : : {
763 [ + + ]: 34282 : while ( first_block )
764 : : {
765 : 15725 : Block* b = first_block;
766 : :
767 : : unsigned char* p =
768 [ + + ]: 15725 : first_non_expired ? first_non_expired : b->data;
769 : :
770 [ + + ]: 16401 : while ( p < b->data + b->bytes_used )
771 : : {
772 : 16331 : Unref(conn_val);
773 : 16331 : conn_val = 0;
774 : :
775 : 16331 : PendingConn* c = (PendingConn*) p;
776 [ + + + + ]: 16331 : if ( t && (c->time + tcp_SYN_timeout > t) )
777 : : {
778 : : // All following entries are still
779 : : // recent enough.
780 : 15655 : first_non_expired = p;
781 : 15655 : return;
782 : : }
783 : :
784 [ + + ]: 676 : if ( ! c->invalid )
785 : : {
786 : : // Expired.
787 : 61 : DBG_LOG(DBG_COMPRESSOR, "%s expire", fmt_conn_id(c));
788 : :
789 : 61 : HashKey key(&c->key, sizeof(c->key), c->hash, true);
790 : :
791 : 61 : ConnData* cd = conns.Lookup(&key);
792 [ + - + - ]: 61 : if ( cd && ! IsConnPtr(cd) )
[ + - ]
793 : : conns.Remove(&c->key, sizeof(c->key),
794 : 61 : c->hash, true);
795 : :
796 : 61 : int orig_state = TCP_ENDPOINT_INACTIVE;
797 : :
798 [ - + ]: 61 : if ( c->FIN )
799 : 0 : orig_state = TCP_ENDPOINT_CLOSED;
800 [ - + ]: 61 : if ( c->RST )
801 : 0 : orig_state = TCP_ENDPOINT_RESET;
802 [ + - ]: 61 : if ( c->SYN )
803 : 61 : orig_state = TCP_ENDPOINT_SYN_SENT;
804 : :
805 : : // We're not able to get the correct size
806 : : // here (with "correct" meaning value that
807 : : // standard connection processing reports).
808 : : // We could if would also store last_seq, but
809 : : // doesn't seem worth it.
810 : :
811 : : Event(c, 0, connection_attempt,
812 : 61 : orig_state, 0, TCP_ENDPOINT_INACTIVE);
813 : : Event(c, 0, connection_state_remove,
814 : 61 : orig_state, 0, TCP_ENDPOINT_INACTIVE);
815 : :
816 : 61 : c->invalid = 1;
817 : 61 : --sizes.pending_valid;
818 : : }
819 : :
820 : 676 : p += sizeof(PendingConn);
821 : 676 : --sizes.pending_in_mem;
822 : : }
823 : :
824 : : // Full block expired, so delete it.
825 : 70 : first_block = b->next;
826 : :
827 [ + + ]: 70 : if ( b->next )
828 : 1 : b->next->prev = 0;
829 : : else
830 : 69 : last_block = 0;
831 : :
832 : 70 : delete b;
833 : :
834 : 70 : first_non_expired = 0;
835 : 70 : sizes.memory -= padded_sizeof(*b);
836 : : }
837 : : }
838 : :
839 : : void ConnCompressor::Event(const PendingConn* pending, double t,
840 : : const EventHandlerPtr& event, int orig_state,
841 : 773 : int orig_size, int resp_state, Val* arg)
842 : : {
843 [ + + ]: 773 : if ( ! conn_val )
844 : : {
845 [ - + ]: 690 : if ( ! event )
846 : 0 : return;
847 : :
848 : : // We only raise events if NewConn() would have actually
849 : : // instantiated the Connection.
850 : : bool flip_roles;
851 [ - + ]: 690 : if ( ! sessions->WantConnection(ntohs(SrcPort(pending)),
852 : : ntohs(DstPort(pending)),
853 : : TRANSPORT_TCP,
854 : : MakeFlags(pending),
855 : : flip_roles) )
856 : 0 : return;
857 : :
858 : 690 : conn_val = new RecordVal(connection_type);
859 : 690 : RecordVal* id_val = new RecordVal(conn_id);
860 : 690 : RecordVal* orig_endp = new RecordVal(endpoint);
861 : 690 : RecordVal* resp_endp = new RecordVal(endpoint);
862 : :
863 [ + + ]: 690 : if ( orig_state == TCP_ENDPOINT_INACTIVE )
864 : : {
865 [ + - ]: 607 : if ( pending->SYN )
866 : : orig_state = pending->ACK ?
867 : : TCP_ENDPOINT_SYN_ACK_SENT :
868 [ + + ]: 607 : TCP_ENDPOINT_SYN_SENT;
869 : : else
870 : 0 : orig_state = TCP_ENDPOINT_PARTIAL;
871 : : }
872 : :
873 : 690 : int tcp_state = TCP_ENDPOINT_INACTIVE;
874 : :
875 [ + + ]: 690 : if ( ! flip_roles )
876 : : {
877 : 524 : id_val->Assign(0, new AddrVal(SrcAddr(pending)));
878 : : id_val->Assign(1, new PortVal(ntohs(SrcPort(pending)),
879 : 524 : TRANSPORT_TCP));
880 : 524 : id_val->Assign(2, new AddrVal(DstAddr(pending)));
881 : : id_val->Assign(3, new PortVal(ntohs(DstPort(pending)),
882 : 524 : TRANSPORT_TCP));
883 : 524 : orig_endp->Assign(0, new Val(orig_size, TYPE_COUNT));
884 : 524 : orig_endp->Assign(1, new Val(orig_state, TYPE_COUNT));
885 : 524 : resp_endp->Assign(0, new Val(0, TYPE_COUNT));
886 : 524 : resp_endp->Assign(1, new Val(resp_state, TYPE_COUNT));
887 : : }
888 : : else
889 : : {
890 : 166 : id_val->Assign(0, new AddrVal(DstAddr(pending)));
891 : : id_val->Assign(1, new PortVal(ntohs(DstPort(pending)),
892 : 166 : TRANSPORT_TCP));
893 : 166 : id_val->Assign(2, new AddrVal(SrcAddr(pending)));
894 : : id_val->Assign(3, new PortVal(ntohs(SrcPort(pending)),
895 : 166 : TRANSPORT_TCP));
896 : 166 : orig_endp->Assign(0, new Val(0, TYPE_COUNT));
897 : 166 : orig_endp->Assign(1, new Val(resp_state, TYPE_COUNT));
898 : 166 : resp_endp->Assign(0, new Val(orig_size, TYPE_COUNT));
899 : 166 : resp_endp->Assign(1, new Val(orig_state, TYPE_COUNT));
900 : 166 : DBG_LOG(DBG_COMPRESSOR, "%s swapped direction", fmt_conn_id(pending));
901 : : }
902 : :
903 : 690 : conn_val->Assign(0, id_val);
904 : 690 : conn_val->Assign(1, orig_endp);
905 : 690 : conn_val->Assign(2, resp_endp);
906 : 690 : conn_val->Assign(3, new Val(pending->time, TYPE_TIME));
907 : : conn_val->Assign(4, new Val(t > 0 ? t - pending->time : 0,
908 [ + + ]: 690 : TYPE_INTERVAL)); // duration
909 : 690 : conn_val->Assign(5, new TableVal(string_set)); // service
910 : 690 : conn_val->Assign(6, new StringVal("cc=1")); // addl
911 : 690 : conn_val->Assign(7, new Val(0, TYPE_COUNT)); // hot
912 : 690 : conn_val->Assign(8, new StringVal("")); // history
913 : :
914 : 690 : conn_val->SetOrigin(0);
915 : : }
916 : :
917 : 773 : val_list* vl = new val_list;
918 [ - + ]: 773 : if ( arg )
919 : 0 : vl->append(arg);
920 : 773 : vl->append(conn_val->Ref());
921 : :
922 : 773 : mgr.QueueEvent(event, vl, SOURCE_LOCAL);
923 : : }
924 : :
925 : 1 : void ConnCompressor::Drain()
926 : : {
927 : 1 : IterCookie* cookie = conns.InitForIteration();
928 : : ConnData* c;
929 : :
930 : 1 : DoExpire(0);
931 : :
932 [ - + ]: 1 : while ( (c = conns.NextEntry(cookie)) )
933 : : {
934 : 0 : Unref(conn_val);
935 : 0 : conn_val = 0;
936 : :
937 [ # # ]: 0 : if ( IsConnPtr(c) )
938 : : {
939 : 0 : Connection* tc = MakeConnPtr(c);
940 : 0 : tc->Done();
941 : 0 : tc->Event(connection_state_remove, 0);
942 : 0 : Unref(tc);
943 : 0 : --sizes.connections;
944 : : }
945 : :
946 : : else
947 : : {
948 : 0 : PendingConn* pc = MakePendingConnPtr(c);
949 [ # # ]: 0 : if ( ! pc->invalid )
950 : : {
951 : : // Same discussion for size here than
952 : : // in DoExpire().
953 : : Event(pc, 0, connection_attempt,
954 : : TCP_ENDPOINT_INACTIVE, 0,
955 : 0 : TCP_ENDPOINT_INACTIVE);
956 : : Event(pc, 0, connection_state_remove,
957 : : TCP_ENDPOINT_INACTIVE, 0,
958 : 0 : TCP_ENDPOINT_INACTIVE);
959 : :
960 : 0 : --sizes.pending_valid;
961 : 0 : pc->invalid = 1;
962 : : }
963 : : }
964 : : }
965 : 1 : }
966 : :
967 : 22 : void ConnCompressor::Invalidate(HashKey* k)
968 : : {
969 : 22 : ConnData* c = (ConnData*) conns.Lookup(k);
970 : :
971 [ + - - + ]: 22 : assert(c && ! IsConnPtr(c));
[ - + ]
972 : 22 : PendingConn* pc = MakePendingConnPtr(c);
973 : :
974 : 22 : DBG_LOG(DBG_COMPRESSOR, "%s invalidate", fmt_conn_id(pc));
975 : :
976 [ + - ]: 22 : if ( ! pc->invalid )
977 : : {
978 : 22 : conns.Remove(&pc->key, sizeof(pc->key), pc->hash, true);
979 : 22 : pc->invalid = 1;
980 : 22 : --sizes.pending_valid;
981 : : }
982 : 22 : }
983 : :
984 : 0 : Connection* ConnCompressor::Insert(Connection* newconn)
985 : : {
986 : 0 : HashKey* key = newconn->Key();
987 : 0 : ConnData* c = conns.Lookup(key);
988 : 0 : Connection* old = 0;
989 : :
990 : : // Do we already have a Connection object?
991 [ # # ]: 0 : if ( c )
992 : : {
993 [ # # ]: 0 : if ( IsConnPtr(c) )
994 : 0 : old = MakeConnPtr(c);
995 : 0 : Remove(key);
996 : : }
997 : :
998 : 0 : conns.Insert(key, MakeMapPtr(newconn));
999 : 0 : return old;
1000 : : }
1001 : :
1002 : 938 : bool ConnCompressor::Remove(HashKey* k)
1003 : : {
1004 : 938 : ConnData* c = (ConnData*) conns.Lookup(k);
1005 [ - + ]: 938 : if ( ! c )
1006 : 0 : return false;
1007 : :
1008 [ + - ]: 938 : if ( IsConnPtr(c) )
1009 : : {
1010 : 938 : DBG_LOG(DBG_COMPRESSOR, "%s remove", fmt_conn_id(MakeConnPtr(c)));
1011 : 938 : conns.Remove(k);
1012 : 938 : --sizes.connections;
1013 : : }
1014 : : else
1015 : : {
1016 : 0 : PendingConn* pc = MakePendingConnPtr(c);
1017 : 0 : DBG_LOG(DBG_COMPRESSOR, "%s remove", fmt_conn_id(pc));
1018 : :
1019 : 0 : conns.Remove(&pc->key, sizeof(pc->key), pc->hash, true);
1020 : :
1021 [ # # ]: 0 : if ( ! pc->invalid )
1022 : : {
1023 : 0 : pc->invalid = 1;
1024 : 0 : --sizes.pending_valid;
1025 : : }
1026 : : }
1027 : :
1028 : 938 : return true;
1029 [ + - ][ + - ]: 6 : }
|