bugfix: payload ipv6 packets too
[prads.git] / src / cxt.c
blobfd3d8530a059d523c90c73a516c6ab690781ae2e
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));
160 } else if (af == AF_INET6) {
161 hash = CXT_HASH6(ip_src,ip_dst);
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;
263 int iter;
264 for (iter = 0; iter < BUCKET_SIZE; iter++) {
265 cxt = bucket[iter];
266 while (cxt != NULL) {
267 ended = 0;
268 curcxt++;
269 /* TCP */
270 if (cxt->proto == IP_PROTO_TCP) {
271 /* * FIN from both sides */
272 if (cxt->s_tcpFlags & TF_FIN && cxt->d_tcpFlags & TF_FIN
273 && (check_time - cxt->last_pkt_time) > 5) {
274 ended = 1;
275 } /* * RST from either side */
276 else if ((cxt->s_tcpFlags & TF_RST
277 || cxt->d_tcpFlags & TF_RST)
278 && (check_time - cxt->last_pkt_time) > 5) {
279 ended = 1;
281 else if ((check_time - cxt->last_pkt_time) > TCP_TIMEOUT) {
282 expired = 1;
285 /* UDP */
286 else if (cxt->proto == IP_PROTO_UDP
287 && (check_time - cxt->last_pkt_time) > 60) {
288 expired = 1;
290 /* ICMP */
291 else if (cxt->proto == IP_PROTO_ICMP
292 || cxt->proto == IP6_PROTO_ICMP) {
293 if ((check_time - cxt->last_pkt_time) > 60) {
294 expired = 1;
297 /* All Other protocols */
298 else if ((check_time - cxt->last_pkt_time) > TCP_TIMEOUT) {
299 expired = 1;
302 if (ended == 1 || expired == 1) {
303 /* remove from the hash */
304 if (cxt->prev)
305 cxt->prev->next = cxt->next;
306 if (cxt->next)
307 cxt->next->prev = cxt->prev;
308 connection *tmp = cxt;
310 if (config.cflags & CONFIG_CXWRITE) {
311 if (expired == 1)
312 log_connection(cxt, stdout, CX_EXPIRE);
313 else if (ended == 1)
314 log_connection(cxt, stdout, CX_ENDED);
316 ended = expired = 0;
318 cxt = cxt->prev;
320 //CLEAR_CXT(tmp);
321 del_connection(tmp, &bucket[iter]);
322 if (cxt == NULL) {
323 bucket[iter] = NULL;
325 } else {
326 cxt = cxt->prev;
332 void log_connection_all()
334 int i;
335 connection *cxt;
336 if(! (config.cflags & CONFIG_CXWRITE))
337 return;
338 for(i = 0; i < BUCKET_SIZE; i++) {
339 cxt = bucket[i];
340 while(cxt) {
341 log_connection(cxt, stdout, CX_HUMAN);
342 cxt = cxt->next;
347 void del_connection(connection * cxt, connection ** bucket_ptr)
349 connection *prev = cxt->prev; /* OLDER connections */
350 connection *next = cxt->next; /* NEWER connections */
352 if (prev == NULL) {
353 // beginning of list
354 *bucket_ptr = next;
355 // not only entry
356 if (next)
357 next->prev = NULL;
358 } else if (next == NULL) {
359 // at end of list!
360 prev->next = NULL;
361 } else {
362 // a node.
363 prev->next = next;
364 next->prev = prev;
368 * Free and set to NULL
370 free(cxt);
371 cxt = NULL;
374 void end_all_sessions()
376 connection *cxt;
377 int cxkey;
379 for (cxkey = 0; cxkey < BUCKET_SIZE; cxkey++) {
380 cxt = bucket[cxkey];
381 while (cxt != NULL) {
382 connection *tmp = cxt;
384 if(config.cflags & CONFIG_CXWRITE)
385 log_connection(cxt, stdout, CX_ENDED);
387 cxt = cxt->next;
388 del_connection(tmp, &bucket[cxkey]);
389 if (cxt == NULL) {
390 bucket[cxkey] = NULL;
396 /* vector comparisons to speed up cx tracking.
397 * meaning, compare source:port and dest:port at the same time.
399 * about vectors and potential improvements:
401 * all 64bit machines have at least SSE2 instructions
402 * *BUT* there is no guarantee we won't loose time on
403 * copying the vectors around.
404 * ... indeed, a quick objdump shows us that
405 * there is a shitton of mov instructions to align the addresses.
407 * Needs support to give improvements:
408 * the addresses should already be aligned as a 128-bit word
409 * in the connection tracking bucket.
411 * note, we can employ the same technique for ipv6 addresses, but
412 * one address at a time.
414 #ifdef VECTOR_CXTRACKER
415 // vector fill: srcprt,dstprt,srcip,dstip = 96 bytes. rest is 0
416 #define VEC_FILL(vec, _ipsrc,_ipdst,_portsrc,_portdst) do {\
417 vec.s[0] = (_portsrc); \
418 vec.s[1] = (_portdst); \
419 vec.w[1] = (_ipsrc); \
420 vec.w[2] = (_ipdst); \
421 vec.w[3] = 0; \
422 } while (0)
424 inline void cx_track_simd_ipv4(packetinfo *pi)
426 connection *cxt = NULL;
427 connection *head = NULL;
428 uint32_t hash;
430 // add to packetinfo ? dont through int32 around :)
431 hash = make_hash(pi);
432 cxt = bucket[hash];
433 head = cxt;
435 ip6v incoming;
436 ip6v compare;
437 VEC_FILL(incoming,
438 pi->ip_src.__u6_addr.__u6_addr32[0],
439 pi->ip_dst.__u6_addr.__u6_addr32[0],
440 pi->s_port,
441 pi->d_port);
442 while (cxt != NULL) {
443 VEC_FILL(compare,
444 cxt->s_ip.__u6_addr.__u6_addr32[0],
445 cxt->d_ip.__u6_addr.__u6_addr32[0],
446 cxt->s_port,
447 cxt->d_port);
449 // single-instruction compare -msse2
450 compare.v = __builtin_ia32_pcmpeqd128(incoming.v,compare.v);
451 // same thing, really. c == v iff c ^ v == 0
452 //compare.v = compare.v ^ incoming.v;
454 // 64-bit compare reduce
455 if(!(compare.i[0] & compare.i[1])){
456 //ok
457 dlog("[*] Updating src connection: %lu\n",cxt->cxid);
458 cxt_update_src(cxt,pi);
459 return;
462 // compare the other direction
463 VEC_FILL(compare,
464 cxt->d_ip.__u6_addr.__u6_addr32[0],
465 cxt->s_ip.__u6_addr.__u6_addr32[0],
466 cxt->d_port,
467 cxt->s_port);
469 compare.v = __builtin_ia32_pcmpeqd128(incoming.v,compare.v);
470 if(!(compare.i[0] & compare.i[1])){
471 dlog("[*] Updating dst connection: %lu\n",cxt->cxid);
472 cxt_update_dst(cxt,pi);
473 return;
475 cxt = cxt->next;
477 if (cxt == NULL) {
478 cxt = (connection *) connection_alloc();
479 //cxt = (connection *) calloc(1, sizeof(connection));
480 if (head != NULL) {
481 head->prev = cxt;
483 cxt = cxt_new(pi);
484 dlog("[*] New connection: %lu\n",cxt->cxid);
485 cxt->next = head;
486 bucket[hash] = cxt;
487 return;
489 printf("[*] Error in session tracking...\n");
490 exit (1);
493 #endif