Made the default flow bucket bigger (BUCKET_SIZE).
[prads.git] / src / cxt.c
blob763ce099ce4cd110d151776296b5706703ab9d53
1 #include <assert.h>
2 #include "common.h"
3 #include "prads.h"
4 #include "cxt.h"
5 #include "sys_func.h"
6 #include "config.h"
7 #include "output-plugins/log.h"
9 extern globalconfig config;
11 uint64_t cxtrackerid;
12 connection *bucket[BUCKET_SIZE];
14 void cxt_init()
16 cxtrackerid = 0;
19 /* freshly smelling connection :d */
20 connection *cxt_new(packetinfo *pi)
22 struct in6_addr ips;
23 struct in6_addr ipd;
24 connection *cxt;
25 cxtrackerid++;
26 cxt = (connection *) calloc(1, sizeof(connection));
27 assert(cxt);
28 cxt->cxid = cxtrackerid;
30 cxt->af = pi->af;
31 if(pi->tcph) cxt->s_tcpFlags |= pi->tcph->t_flags;
32 //cxt->s_tcpFlags |= (pi->tcph ? pi->tcph->t_flags : 0x00);//why??
33 //cxt->d_tcpFlags = 0x00;
34 cxt->s_total_bytes = pi->packet_bytes;
35 cxt->s_total_pkts = 1;
36 cxt->start_time = pi->pheader->ts.tv_sec;
37 cxt->last_pkt_time = pi->pheader->ts.tv_sec;
39 if(pi-> af== AF_INET6){
40 cxt->s_ip = PI_IP6SRC(pi);
41 cxt->d_ip = PI_IP6DST(pi);
42 }else {
43 // ugly hack :(
44 // the way we do ip4/6 is DIRTY
45 ips.s6_addr32[0] = pi->ip4->ip_src;
46 ipd.s6_addr32[0] = pi->ip4->ip_dst;
47 cxt->s_ip = ips;
48 cxt->d_ip = ipd;
51 cxt->s_port = pi->s_port;
52 cxt->d_port = pi->d_port;
53 cxt->proto = pi->proto;
55 cxt->check = 0x00;
56 cxt->c_asset = NULL;
57 cxt->s_asset = NULL;
58 cxt->reversed = 0;
60 return cxt;
63 int connection_tracking(packetinfo *pi)
65 static char ip_addr_s[INET6_ADDRSTRLEN];
66 static char ip_addr_d[INET6_ADDRSTRLEN];
67 struct in_addr *ipa;
68 cx_track(pi);
70 if(pi->af == AF_INET6) {
71 u_ntop(pi->ip6->ip_src, pi->af, ip_addr_s);
72 u_ntop(pi->ip6->ip_dst, pi->af, ip_addr_d);
73 } else {
74 ipa = pi->ip4->ip_src;
75 inet_ntop(pi->af, &ipa, ip_addr_s, INET6_ADDRSTRLEN);
76 ipa = pi->ip4->ip_dst;
77 inet_ntop(pi->af, &ipa, ip_addr_d, INET6_ADDRSTRLEN);
79 if(config.cflags & CONFIG_CONNECT)
80 printf("conn[%4lu] %s:%u -> %s:%u [%s]\n", pi->cxt->cxid,
81 ip_addr_s, ntohs(pi->s_port),
82 ip_addr_d, ntohs(pi->d_port),
83 pi->sc?pi->sc==SC_SERVER? "server":"client":"NONE");
84 return 0;
87 int cxt_update_client(connection *cxt, packetinfo *pi)
89 cxt->last_pkt_time = pi->pheader->ts.tv_sec;
91 if(pi->tcph) cxt->s_tcpFlags |= pi->tcph->t_flags;
92 cxt->s_total_bytes += pi->packet_bytes;
93 cxt->s_total_pkts += 1;
95 pi->cxt = cxt;
96 pi->sc = SC_CLIENT;
97 if(!cxt->c_asset)
98 cxt->c_asset = pi->asset; // connection client asset
99 if (cxt->s_total_bytes > MAX_BYTE_CHECK
100 || cxt->s_total_pkts > MAX_PKT_CHECK) {
101 return 0; // Dont Check!
103 return SC_CLIENT;
106 int cxt_update_server(connection *cxt, packetinfo *pi)
108 cxt->last_pkt_time = pi->pheader->ts.tv_sec;
110 if(pi->tcph) cxt->d_tcpFlags |= pi->tcph->t_flags;
111 cxt->d_total_bytes += pi->packet_bytes;
112 cxt->d_total_pkts += 1;
114 pi->cxt = cxt;
115 pi->sc = SC_SERVER;
116 if(!cxt->s_asset)
117 cxt->s_asset = pi->asset; // server asset
118 if (cxt->d_total_bytes > MAX_BYTE_CHECK
119 || cxt->d_total_pkts > MAX_PKT_CHECK) {
120 return 0; // Dont check!
122 return SC_SERVER;
126 /* return value: client or server?
127 *** USED TO BE: 0 = dont check, 1 = client, 2 = server
128 * now returns 0, SC_CLIENT(=1), SC_SERVER(=2)
131 int cx_track(packetinfo *pi) {
132 struct in6_addr *ip_src;
133 struct in6_addr *ip_dst;
134 struct in6_addr ips;
135 struct in6_addr ipd;
136 uint16_t src_port = pi->s_port;
137 uint16_t dst_port = pi->d_port;
138 int af = pi->af;
139 connection *cxt = NULL;
140 connection *head = NULL;
141 uint32_t hash;
144 if(af== AF_INET6){
145 ip_src = &PI_IP6SRC(pi);
146 ip_dst = &PI_IP6DST(pi);
147 }else {
148 // ugly hack :(
149 // the way we do ip4/6 is DIRTY
150 // FIX IT?!!?
151 ips.s6_addr32[0] = pi->ip4->ip_src;
152 ipd.s6_addr32[0] = pi->ip4->ip_dst;
153 ip_src = &ips;
154 ip_dst = &ipd;
157 // find the right connection bucket
158 if (af == AF_INET) {
159 hash = CXT_HASH4(IP4ADDR(ip_src),IP4ADDR(ip_dst),src_port,dst_port,pi->proto);
160 } else if (af == AF_INET6) {
161 hash = CXT_HASH6(ip_src,ip_dst,src_port,dst_port,pi->proto);
163 cxt = bucket[hash];
164 head = cxt;
166 // search through the bucket
167 while (cxt != NULL) {
168 // Two-way compare of given connection against connection table
169 if (af == AF_INET) {
170 if (CMP_CXT4(cxt,IP4ADDR(ip_src),src_port,IP4ADDR(ip_dst),dst_port)){
171 // Client sends first packet (TCP/SYN - UDP?) hence this is a client
172 return cxt_update_client(cxt, pi);
173 } else if (CMP_CXT4(cxt,IP4ADDR(ip_dst),dst_port,IP4ADDR(ip_src),src_port)) {
174 // This is a server (Maybe not when we start up but in the long run)
175 return cxt_update_server(cxt, pi);
177 } else if (af == AF_INET6) {
178 if (CMP_CXT6(cxt,ip_src,src_port,ip_dst,dst_port)){
179 return cxt_update_client(cxt, pi);
180 } else if (CMP_CXT6(cxt,ip_dst,dst_port,ip_src,src_port)){
181 return cxt_update_server(cxt, pi);
184 cxt = cxt->next;
186 // bucket turned upside down didn't yeild anything. new connection
187 cxt = cxt_new(pi);
188 if(config.cflags & CONFIG_CXWRITE)
189 log_connection(cxt, stdout, CX_NEW);
191 /* * New connections are pushed on to the head of bucket[s_hash] */
192 cxt->next = head;
193 if (head != NULL) {
194 // are we doubly linked?
195 head->prev = cxt;
197 bucket[hash] = cxt;
198 pi->cxt = cxt;
200 /* * Return value should be 1, telling to do client service fingerprinting */
201 return 1;
204 void reverse_pi_cxt(packetinfo *pi)
206 uint8_t tmpFlags;
207 uint64_t tmp_pkts;
208 uint64_t tmp_bytes;
209 struct in6_addr tmp_ip;
210 uint16_t tmp_port;
211 connection *cxt;
213 cxt = pi->cxt;
215 /* First we chang the cxt */
216 /* cp src to tmp */
217 tmpFlags = cxt->s_tcpFlags;
218 tmp_pkts = cxt->s_total_pkts;
219 tmp_bytes = cxt->s_total_bytes;
220 tmp_ip = cxt->s_ip;
221 tmp_port = cxt->s_port;
223 /* cp dst to src */
224 cxt->s_tcpFlags = cxt->d_tcpFlags;
225 cxt->s_total_pkts = cxt->d_total_pkts;
226 cxt->s_total_bytes = cxt->d_total_bytes;
227 cxt->s_ip = cxt->d_ip;
228 cxt->s_port = cxt->d_port;
230 /* cp tmp to dst */
231 cxt->d_tcpFlags = tmpFlags;
232 cxt->d_total_pkts = tmp_pkts;
233 cxt->d_total_bytes = tmp_bytes;
234 cxt->d_ip = tmp_ip;
235 cxt->d_port = tmp_port;
237 /* Not taking any chances :P */
238 cxt->c_asset = cxt->s_asset = NULL;
239 cxt->check = 0x00;
241 /* Then we change pi */
242 if (pi->sc == SC_CLIENT)
243 pi->sc = SC_SERVER;
244 else
245 pi->sc = SC_CLIENT;
249 This sub marks sessions as ENDED on different criterias:
251 XXX: May be the fugliest code in PRADS :-(
254 void end_sessions()
257 connection *cxt;
258 time_t check_time;
259 check_time = time(NULL);
260 int ended, expired = 0;
261 uint32_t curcxt = 0;
262 FILE *cxtFile = NULL;
264 if (config.cxtlogdir[0] != '\0') {
265 if (config.dev == 0x00) {
266 sprintf(config.cxtfname, "%sstats.pcap.%ld", config.cxtlogdir, check_time); // True?
267 } else {
268 sprintf(config.cxtfname, "%sstats.%s.%ld", config.cxtlogdir, config.dev, check_time);
272 int iter;
273 for (iter = 0; iter < BUCKET_SIZE; iter++) {
274 cxt = bucket[iter];
275 while (cxt != NULL) {
276 ended = 0;
277 curcxt++;
278 /* TCP */
279 if (cxt->proto == IP_PROTO_TCP) {
280 /* * FIN from both sides */
281 if (cxt->s_tcpFlags & TF_FIN && cxt->d_tcpFlags & TF_FIN
282 && (check_time - cxt->last_pkt_time) > 5) {
283 ended = 1;
284 } /* * RST from either side */
285 else if ((cxt->s_tcpFlags & TF_RST
286 || cxt->d_tcpFlags & TF_RST)
287 && (check_time - cxt->last_pkt_time) > 5) {
288 ended = 1;
290 else if ((check_time - cxt->last_pkt_time) > TCP_TIMEOUT) {
291 expired = 1;
294 /* UDP */
295 else if (cxt->proto == IP_PROTO_UDP
296 && (check_time - cxt->last_pkt_time) > 60) {
297 expired = 1;
299 /* ICMP */
300 else if (cxt->proto == IP_PROTO_ICMP
301 || cxt->proto == IP6_PROTO_ICMP) {
302 if ((check_time - cxt->last_pkt_time) > 60) {
303 expired = 1;
306 /* All Other protocols */
307 else if ((check_time - cxt->last_pkt_time) > TCP_TIMEOUT) {
308 expired = 1;
311 if (ended == 1 || expired == 1) {
312 /* remove from the hash */
313 if (cxt->prev)
314 cxt->prev->next = cxt->next;
315 if (cxt->next)
316 cxt->next->prev = cxt->prev;
317 connection *tmp = cxt;
319 if (config.cxtfname[0] != '\0' ) {
320 if (cxtFile == NULL) {
321 cxtFile = fopen(config.cxtfname, "w");
322 dlog("Opened file: %s\n",config.cxtfname);
324 if (cxtFile == NULL) {
325 dlog("[*] ERROR: Cant open file %s\n",config.cxtfname);
326 } else {
327 log_connection(cxt, cxtFile, CX_NONE);
330 if (config.cflags & CONFIG_CXWRITE) {
331 if (expired == 1)
332 log_connection(cxt, stdout, CX_EXPIRE);
333 else if (ended == 1)
334 log_connection(cxt, stdout, CX_ENDED);
336 ended = expired = 0;
338 cxt = cxt->prev;
340 //CLEAR_CXT(tmp);
341 del_connection(tmp, &bucket[iter]);
342 if (cxt == NULL) {
343 bucket[iter] = NULL;
345 } else {
346 cxt = cxt->prev;
350 if (cxtFile != NULL) {
351 fclose(cxtFile);
355 void log_connection_all()
357 int i;
358 connection *cxt;
359 if(! (config.cflags & CONFIG_CXWRITE))
360 return;
361 for(i = 0; i < BUCKET_SIZE; i++) {
362 cxt = bucket[i];
363 while(cxt) {
364 log_connection(cxt, stdout, CX_HUMAN);
365 cxt = cxt->next;
370 void del_connection(connection * cxt, connection ** bucket_ptr)
372 connection *prev = cxt->prev; /* OLDER connections */
373 connection *next = cxt->next; /* NEWER connections */
375 if (prev == NULL) {
376 // beginning of list
377 *bucket_ptr = next;
378 // not only entry
379 if (next)
380 next->prev = NULL;
381 } else if (next == NULL) {
382 // at end of list!
383 prev->next = NULL;
384 } else {
385 // a node.
386 prev->next = next;
387 next->prev = prev;
391 * Free and set to NULL
393 free(cxt);
394 cxt = NULL;
397 void end_all_sessions()
399 connection *cxt;
400 int cxkey;
401 FILE *cxtFile = NULL;
403 if (config.cxtlogdir[0] != '\0') {
404 if (config.dev == 0x00) {
405 sprintf(config.cxtfname, "%sstats.pcap.%ld", config.cxtlogdir, time(NULL)); // True?
406 } else {
407 sprintf(config.cxtfname, "%sstats.%s.%ld", config.cxtlogdir, config.dev, time(NULL));
411 for (cxkey = 0; cxkey < BUCKET_SIZE; cxkey++) {
412 cxt = bucket[cxkey];
413 while (cxt != NULL) {
414 connection *tmp = cxt;
416 if (config.cxtfname[0] != '\0' ) {
417 if (cxtFile == NULL) {
418 cxtFile = fopen(config.cxtfname, "w");
419 dlog("Opened file: %s\n",config.cxtfname);
421 if (cxtFile == NULL) {
422 dlog("[*] ERROR: Cant open file %s\n",config.cxtfname);
423 } else {
424 log_connection(cxt, cxtFile, CX_NONE);
428 if(config.cflags & CONFIG_CXWRITE)
429 log_connection(cxt, stdout, CX_ENDED);
431 cxt = cxt->next;
432 del_connection(tmp, &bucket[cxkey]);
433 if (cxt == NULL) {
434 bucket[cxkey] = NULL;
438 if (cxtFile != NULL) {
439 fclose(cxtFile);
443 /* vector comparisons to speed up cx tracking.
444 * meaning, compare source:port and dest:port at the same time.
446 * about vectors and potential improvements:
448 * all 64bit machines have at least SSE2 instructions
449 * *BUT* there is no guarantee we won't loose time on
450 * copying the vectors around.
451 * ... indeed, a quick objdump shows us that
452 * there is a shitton of mov instructions to align the addresses.
454 * Needs support to give improvements:
455 * the addresses should already be aligned as a 128-bit word
456 * in the connection tracking bucket.
458 * note, we can employ the same technique for ipv6 addresses, but
459 * one address at a time.
461 #ifdef VECTOR_CXTRACKER
462 // vector fill: srcprt,dstprt,srcip,dstip = 96 bytes. rest is 0
463 #define VEC_FILL(vec, _ipsrc,_ipdst,_portsrc,_portdst) do {\
464 vec.s[0] = (_portsrc); \
465 vec.s[1] = (_portdst); \
466 vec.w[1] = (_ipsrc); \
467 vec.w[2] = (_ipdst); \
468 vec.w[3] = 0; \
469 } while (0)
471 inline void cx_track_simd_ipv4(packetinfo *pi)
473 connection *cxt = NULL;
474 connection *head = NULL;
475 uint32_t hash;
477 // add to packetinfo ? dont through int32 around :)
478 hash = make_hash(pi);
479 cxt = bucket[hash];
480 head = cxt;
482 ip6v incoming;
483 ip6v compare;
484 VEC_FILL(incoming,
485 pi->ip_src.__u6_addr.__u6_addr32[0],
486 pi->ip_dst.__u6_addr.__u6_addr32[0],
487 pi->s_port,
488 pi->d_port);
489 while (cxt != NULL) {
490 VEC_FILL(compare,
491 cxt->s_ip.__u6_addr.__u6_addr32[0],
492 cxt->d_ip.__u6_addr.__u6_addr32[0],
493 cxt->s_port,
494 cxt->d_port);
496 // single-instruction compare -msse2
497 compare.v = __builtin_ia32_pcmpeqd128(incoming.v,compare.v);
498 // same thing, really. c == v iff c ^ v == 0
499 //compare.v = compare.v ^ incoming.v;
501 // 64-bit compare reduce
502 if(!(compare.i[0] & compare.i[1])){
503 //ok
504 dlog("[*] Updating src connection: %lu\n",cxt->cxid);
505 cxt_update_src(cxt,pi);
506 return;
509 // compare the other direction
510 VEC_FILL(compare,
511 cxt->d_ip.__u6_addr.__u6_addr32[0],
512 cxt->s_ip.__u6_addr.__u6_addr32[0],
513 cxt->d_port,
514 cxt->s_port);
516 compare.v = __builtin_ia32_pcmpeqd128(incoming.v,compare.v);
517 if(!(compare.i[0] & compare.i[1])){
518 dlog("[*] Updating dst connection: %lu\n",cxt->cxid);
519 cxt_update_dst(cxt,pi);
520 return;
522 cxt = cxt->next;
524 if (cxt == NULL) {
525 cxt = (connection *) connection_alloc();
526 //cxt = (connection *) calloc(1, sizeof(connection));
527 if (head != NULL) {
528 head->prev = cxt;
530 cxt = cxt_new(pi);
531 dlog("[*] New connection: %lu\n",cxt->cxid);
532 cxt->next = head;
533 bucket[hash] = cxt;
534 return;
536 printf("[*] Error in session tracking...\n");
537 exit (1);
540 #endif