Fix typo
[prads.git] / src / cxt.c
blobc8032a272e06c0584a35004afec53ee7f5be72c9
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 ips.s6_addr32[0] = pi->ip4->ip_src;
44 ipd.s6_addr32[0] = pi->ip4->ip_dst;
45 cxt->s_ip = ips;
46 cxt->d_ip = ipd;
49 cxt->s_port = pi->s_port;
50 cxt->d_port = pi->d_port;
51 cxt->proto = pi->proto;
52 cxt->hw_proto = ntohs(pi->eth_type);
54 cxt->check = 0x00;
55 cxt->c_asset = NULL;
56 cxt->s_asset = NULL;
57 cxt->reversed = 0;
59 return cxt;
62 int connection_tracking(packetinfo *pi)
64 static char ip_addr_s[INET6_ADDRSTRLEN];
65 static char ip_addr_d[INET6_ADDRSTRLEN];
66 cx_track(pi);
68 if(config.cflags & CONFIG_CONNECT){
69 log_connection(pi->cxt, CX_EXCESSIVE);
71 return 0;
74 int cxt_update_client(connection *cxt, packetinfo *pi)
76 cxt->last_pkt_time = pi->pheader->ts.tv_sec;
78 if(pi->tcph) cxt->s_tcpFlags |= pi->tcph->t_flags;
79 cxt->s_total_bytes += pi->packet_bytes;
80 cxt->s_total_pkts += 1;
82 pi->cxt = cxt;
83 pi->sc = SC_CLIENT;
84 if(!cxt->c_asset)
85 cxt->c_asset = pi->asset; // connection client asset
86 if (cxt->s_total_bytes > MAX_BYTE_CHECK
87 || cxt->s_total_pkts > MAX_PKT_CHECK) {
88 return 0; // Dont Check!
90 return SC_CLIENT;
93 int cxt_update_server(connection *cxt, packetinfo *pi)
95 cxt->last_pkt_time = pi->pheader->ts.tv_sec;
97 if(pi->tcph) cxt->d_tcpFlags |= pi->tcph->t_flags;
98 cxt->d_total_bytes += pi->packet_bytes;
99 cxt->d_total_pkts += 1;
101 pi->cxt = cxt;
102 pi->sc = SC_SERVER;
103 if(!cxt->s_asset)
104 cxt->s_asset = pi->asset; // server asset
105 if (cxt->d_total_bytes > MAX_BYTE_CHECK
106 || cxt->d_total_pkts > MAX_PKT_CHECK) {
107 return 0; // Dont check!
109 return SC_SERVER;
113 /* return value: client or server?
114 *** USED TO BE: 0 = dont check, 1 = client, 2 = server
115 * now returns 0, SC_CLIENT(=1), SC_SERVER(=2)
118 int cx_track(packetinfo *pi) {
119 struct in6_addr *ip_src;
120 struct in6_addr *ip_dst;
121 struct in6_addr ips;
122 struct in6_addr ipd;
123 uint16_t src_port = pi->s_port;
124 uint16_t dst_port = pi->d_port;
125 int af = pi->af;
126 connection *cxt = NULL;
127 connection *head = NULL;
128 uint32_t hash;
131 if(af== AF_INET6){
132 ip_src = &PI_IP6SRC(pi);
133 ip_dst = &PI_IP6DST(pi);
134 }else {
135 // ugly hack :(
136 // the way we do ip4/6 is DIRTY
137 ips.s6_addr32[0] = pi->ip4->ip_src;
138 ipd.s6_addr32[0] = pi->ip4->ip_dst;
139 ip_src = &ips;
140 ip_dst = &ipd;
143 // find the right connection bucket
144 if (af == AF_INET) {
145 hash = CXT_HASH4(IP4ADDR(ip_src),IP4ADDR(ip_dst),src_port,dst_port,pi->proto);
146 } else if (af == AF_INET6) {
147 hash = CXT_HASH6(ip_src,ip_dst,src_port,dst_port,pi->proto);
149 head = bucket[hash];
151 // search through the bucket
152 for (cxt = head; cxt != NULL; cxt = cxt->next) {
153 if (af != cxt->af)
154 continue;
156 // Two-way compare of given connection against connection table
157 if (af == AF_INET) {
158 if (CMP_CXT4(cxt,IP4ADDR(ip_src),src_port,IP4ADDR(ip_dst),dst_port)){
159 // Client sends first packet (TCP/SYN - UDP?) hence this is a client
160 return cxt_update_client(cxt, pi);
161 } else if (CMP_CXT4(cxt,IP4ADDR(ip_dst),dst_port,IP4ADDR(ip_src),src_port)) {
162 // This is a server (Maybe not when we start up but in the long run)
163 return cxt_update_server(cxt, pi);
165 } else if (af == AF_INET6) {
166 if (CMP_CXT6(cxt,ip_src,src_port,ip_dst,dst_port)){
167 return cxt_update_client(cxt, pi);
168 } else if (CMP_CXT6(cxt,ip_dst,dst_port,ip_src,src_port)){
169 return cxt_update_server(cxt, pi);
173 // bucket turned upside down didn't yeild anything. new connection
174 cxt = cxt_new(pi);
175 log_connection(cxt, CX_NEW);
177 /* * New connections are pushed on to the head of bucket[s_hash] */
178 cxt->next = head;
179 if (head != NULL) {
180 // are we doubly linked?
181 head->prev = cxt;
183 bucket[hash] = cxt;
184 pi->cxt = cxt;
186 /* * Return value should be 1, telling to do client service fingerprinting */
187 return 1;
190 void reverse_pi_cxt(packetinfo *pi)
192 uint8_t tmpFlags;
193 uint64_t tmp_pkts;
194 uint64_t tmp_bytes;
195 struct in6_addr tmp_ip;
196 uint16_t tmp_port;
197 connection *cxt;
199 cxt = pi->cxt;
201 /* First we chang the cxt */
202 /* cp src to tmp */
203 tmpFlags = cxt->s_tcpFlags;
204 tmp_pkts = cxt->s_total_pkts;
205 tmp_bytes = cxt->s_total_bytes;
206 tmp_ip = cxt->s_ip;
207 tmp_port = cxt->s_port;
209 /* cp dst to src */
210 cxt->s_tcpFlags = cxt->d_tcpFlags;
211 cxt->s_total_pkts = cxt->d_total_pkts;
212 cxt->s_total_bytes = cxt->d_total_bytes;
213 cxt->s_ip = cxt->d_ip;
214 cxt->s_port = cxt->d_port;
216 /* cp tmp to dst */
217 cxt->d_tcpFlags = tmpFlags;
218 cxt->d_total_pkts = tmp_pkts;
219 cxt->d_total_bytes = tmp_bytes;
220 cxt->d_ip = tmp_ip;
221 cxt->d_port = tmp_port;
223 /* Not taking any chances :P */
224 cxt->c_asset = cxt->s_asset = NULL;
225 cxt->check = 0x00;
227 /* Then we change pi */
228 if (pi->sc == SC_CLIENT)
229 pi->sc = SC_SERVER;
230 else
231 pi->sc = SC_CLIENT;
235 This sub marks sessions as ENDED on different criterias:
237 XXX: May be the fugliest code in PRADS :-(
240 void end_sessions()
243 connection *cxt;
244 int iter;
245 int cxstatus = CX_NONE;
246 time_t check_time = time(NULL);
248 log_rotate(check_time);
249 for (iter = 0; iter < BUCKET_SIZE; iter++) {
250 cxt = bucket[iter];
251 while (cxt != NULL) {
252 /* TCP */
253 if (cxt->proto == IP_PROTO_TCP) {
254 /* * FIN from both sides */
255 if (cxt->s_tcpFlags & TF_FIN && cxt->d_tcpFlags & TF_FIN
256 && (check_time - cxt->last_pkt_time) > 5) {
257 cxstatus = CX_ENDED;
258 } /* * RST from either side */
259 else if ((cxt->s_tcpFlags & TF_RST
260 || cxt->d_tcpFlags & TF_RST)
261 && (check_time - cxt->last_pkt_time) > 5) {
262 cxstatus = CX_ENDED;
264 else if ((check_time - cxt->last_pkt_time) > TCP_TIMEOUT) {
265 cxstatus = CX_EXPIRE;
268 /* UDP */
269 else if (cxt->proto == IP_PROTO_UDP
270 && (check_time - cxt->last_pkt_time) > 60) {
271 cxstatus = CX_EXPIRE;
273 /* ICMP */
274 else if (cxt->proto == IP_PROTO_ICMP
275 || cxt->proto == IP6_PROTO_ICMP) {
276 if ((check_time - cxt->last_pkt_time) > 60) {
277 cxstatus = CX_EXPIRE;
280 /* All Other protocols */
281 else if ((check_time - cxt->last_pkt_time) > TCP_TIMEOUT) {
282 cxstatus = CX_EXPIRE;
285 if (cxstatus == CX_ENDED || cxstatus == CX_EXPIRE) {
286 /* remove from the hash */
287 if (cxt->prev)
288 cxt->prev->next = cxt->next;
289 if (cxt->next)
290 cxt->next->prev = cxt->prev;
291 connection *tmp = cxt;
293 log_connection(cxt, cxstatus);
294 cxstatus = CX_NONE;
296 cxt = cxt->next;
298 del_connection(tmp, &bucket[iter]);
299 if (cxt == NULL) {
300 bucket[iter] = NULL;
302 } else {
303 cxt = cxt->next;
305 } // end while cxt
306 } // end for buckets
309 void log_connection_all()
311 int i;
312 connection *cxt;
313 if(! (config.cflags & CONFIG_CXWRITE))
314 return;
315 for(i = 0; i < BUCKET_SIZE; i++) {
316 cxt = bucket[i];
317 while(cxt) {
318 log_connection(cxt, CX_HUMAN);
319 cxt = cxt->next;
324 void del_connection(connection * cxt, connection ** bucket_ptr)
326 connection *prev = cxt->prev; /* OLDER connections */
327 connection *next = cxt->next; /* NEWER connections */
329 if (prev == NULL) {
330 // beginning of list
331 *bucket_ptr = next;
332 // not only entry
333 if (next)
334 next->prev = NULL;
335 } else if (next == NULL) {
336 // at end of list!
337 prev->next = NULL;
338 } else {
339 // a node.
340 prev->next = next;
341 next->prev = prev;
345 * Free and set to NULL
347 free(cxt);
348 cxt = NULL;
351 void end_all_sessions()
353 connection *cxt;
354 int cxkey;
355 FILE *cxtFile = NULL;
357 log_rotate(time(NULL));
358 for (cxkey = 0; cxkey < BUCKET_SIZE; cxkey++) {
359 cxt = bucket[cxkey];
360 while (cxt != NULL) {
361 connection *tmp = cxt;
363 log_connection(cxt, CX_ENDED);
364 cxt = cxt->next;
365 del_connection(tmp, &bucket[cxkey]);
366 if (cxt == NULL) {
367 bucket[cxkey] = NULL;
371 if (cxtFile != NULL) {
372 fclose(cxtFile);
376 void cxt_log_buckets(int dummy)
378 connection *cxt = NULL;
379 FILE *logfile = NULL;
380 int i;
381 int len;
383 logfile = fopen("/tmp/prads-buckets.log", "w");
384 if (!logfile)
385 return;
387 dlog("Recieved SIGUSR1 - Dumping bucketlist to logfile\n");
388 for (i = 0; i < BUCKET_SIZE; i++) {
389 len = 0;
390 for (cxt = bucket[i]; cxt; cxt = cxt->next)
391 len++;
392 if (len > 0)
393 fprintf(logfile, "%d in bucket[%5d]\n", len, i);
396 fflush(logfile);
397 fclose(logfile);
402 /* vector comparisons to speed up cx tracking.
403 * meaning, compare source:port and dest:port at the same time.
405 * about vectors and potential improvements:
406 * * all 64bit machines have at least SSE2 instructions * *BUT* there is no guarantee we won't loose time on
407 * copying the vectors around.
408 * ... indeed, a quick objdump shows us that
409 * there is a shitton of mov instructions to align the addresses.
411 * Needs support to give improvements:
412 * the addresses should already be aligned as a 128-bit word
413 * in the connection tracking bucket.
415 * note, we can employ the same technique for ipv6 addresses, but
416 * one address at a time.
418 #ifdef VECTOR_CXTRACKER
419 // vector fill: srcprt,dstprt,srcip,dstip = 96 bytes. rest is 0
420 #define VEC_FILL(vec, _ipsrc,_ipdst,_portsrc,_portdst) do {\
421 vec.s[0] = (_portsrc); \
422 vec.s[1] = (_portdst); \
423 vec.w[1] = (_ipsrc); \
424 vec.w[2] = (_ipdst); \
425 vec.w[3] = 0; \
426 } while (0)
428 inline void cx_track_simd_ipv4(packetinfo *pi)
430 connection *cxt = NULL;
431 connection *head = NULL;
432 uint32_t hash;
434 // add to packetinfo ? dont through int32 around :)
435 hash = make_hash(pi);
436 cxt = bucket[hash];
437 head = cxt;
439 ip6v incoming;
440 ip6v compare;
441 VEC_FILL(incoming,
442 pi->ip_src.__u6_addr.__u6_addr32[0],
443 pi->ip_dst.__u6_addr.__u6_addr32[0],
444 pi->s_port,
445 pi->d_port);
446 while (cxt != NULL) {
447 VEC_FILL(compare,
448 cxt->s_ip.__u6_addr.__u6_addr32[0],
449 cxt->d_ip.__u6_addr.__u6_addr32[0],
450 cxt->s_port,
451 cxt->d_port);
453 // single-instruction compare -msse2
454 compare.v = __builtin_ia32_pcmpeqd128(incoming.v,compare.v);
455 // same thing, really. c == v iff c ^ v == 0
456 //compare.v = compare.v ^ incoming.v;
458 // 64-bit compare reduce
459 if(!(compare.i[0] & compare.i[1])){
460 //ok
461 dlog("[*] Updating src connection: %lu\n",cxt->cxid);
462 cxt_update_src(cxt,pi);
463 return;
466 // compare the other direction
467 VEC_FILL(compare,
468 cxt->d_ip.__u6_addr.__u6_addr32[0],
469 cxt->s_ip.__u6_addr.__u6_addr32[0],
470 cxt->d_port,
471 cxt->s_port);
473 compare.v = __builtin_ia32_pcmpeqd128(incoming.v,compare.v);
474 if(!(compare.i[0] & compare.i[1])){
475 dlog("[*] Updating dst connection: %lu\n",cxt->cxid);
476 cxt_update_dst(cxt,pi);
477 return;
479 cxt = cxt->next;
481 if (cxt == NULL) {
482 cxt = (connection *) connection_alloc();
483 //cxt = (connection *) calloc(1, sizeof(connection));
484 if (head != NULL) {
485 head->prev = cxt;
487 cxt = cxt_new(pi);
488 dlog("[*] New connection: %lu\n",cxt->cxid);
489 cxt->next = head;
490 bucket[hash] = cxt;
491 return;
493 printf("[*] Error in session tracking...\n");
494 exit (1);
497 #endif