This commit is contained in:
bol-van 2021-03-18 17:22:03 +03:00
parent b27cfbc813
commit 2e46070c0f

View File

@ -1,294 +1,294 @@
#include "conntrack.h" #include "conntrack.h"
#include "darkmagic.h" #include "darkmagic.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <stdio.h> #include <stdio.h>
#undef uthash_nonfatal_oom #undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt) #define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
static bool oom = false; static bool oom = false;
static void ut_oom_recover(void *elem) static void ut_oom_recover(void *elem)
{ {
oom = true; oom = true;
} }
static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"}; static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"};
#define _connswap \ #define _connswap \
memset(c2,0,sizeof(*c2)); \ memset(c2,0,sizeof(*c2)); \
c2->e1 = c->e2; \ c2->e1 = c->e2; \
c2->e2 = c->e1; c2->e2 = c->e1;
static void connswap4(const t_conn4 *c, t_conn4 *c2) static void connswap4(const t_conn4 *c, t_conn4 *c2)
{ {
_connswap _connswap
} }
static void connswap6(const t_conn6 *c, t_conn6 *c2) static void connswap6(const t_conn6 *c, t_conn6 *c2)
{ {
_connswap _connswap
} }
#define _ConntrackPoolDestroy(v) \ #define _ConntrackPoolDestroy(v) \
t_conntrack##v *elem, *tmp; \ t_conntrack##v *elem, *tmp; \
HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); free(elem); } HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); free(elem); }
static void ConntrackPoolDestroy4(t_conntrack4 **pp) static void ConntrackPoolDestroy4(t_conntrack4 **pp)
{ {
_ConntrackPoolDestroy(4) _ConntrackPoolDestroy(4)
} }
static void ConntrackPoolDestroy6(t_conntrack6 **pp) static void ConntrackPoolDestroy6(t_conntrack6 **pp)
{ {
_ConntrackPoolDestroy(6) _ConntrackPoolDestroy(6)
} }
void ConntrackPoolDestroy(t_conntrack *p) void ConntrackPoolDestroy(t_conntrack *p)
{ {
ConntrackPoolDestroy4(&p->pool4); ConntrackPoolDestroy4(&p->pool4);
ConntrackPoolDestroy6(&p->pool6); ConntrackPoolDestroy6(&p->pool6);
} }
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin) void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin)
{ {
p->timeout_syn = timeout_syn; p->timeout_syn = timeout_syn;
p->timeout_established = timeout_established; p->timeout_established = timeout_established;
p->timeout_fin = timeout_fin; p->timeout_fin = timeout_fin;
p->t_purge_interval = purge_interval; p->t_purge_interval = purge_interval;
time(&p->t_last_purge); time(&p->t_last_purge);
p->pool4 = NULL; p->pool4 = NULL;
p->pool6 = NULL; p->pool6 = NULL;
} }
#define _ConntrackExtractConn(v) \ #define _ConntrackExtractConn(v) \
memset(c,0,sizeof(*c)); \ memset(c,0,sizeof(*c)); \
if (bReverse) { \ if (bReverse) { \
c->e1.adr = ip->ip##v##_dst; \ c->e1.adr = ip->ip##v##_dst; \
c->e2.adr = ip->ip##v##_src; \ c->e2.adr = ip->ip##v##_src; \
c->e1.port = htons(tcphdr->th_dport); \ c->e1.port = htons(tcphdr->th_dport); \
c->e2.port = htons(tcphdr->th_sport); \ c->e2.port = htons(tcphdr->th_sport); \
} else { \ } else { \
c->e1.adr = ip->ip##v##_src; \ c->e1.adr = ip->ip##v##_src; \
c->e2.adr = ip->ip##v##_dst; \ c->e2.adr = ip->ip##v##_dst; \
c->e1.port = htons(tcphdr->th_sport); \ c->e1.port = htons(tcphdr->th_sport); \
c->e2.port = htons(tcphdr->th_dport); \ c->e2.port = htons(tcphdr->th_dport); \
} }
void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr) void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr)
{ {
_ConntrackExtractConn() _ConntrackExtractConn()
} }
void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr) void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr)
{ {
_ConntrackExtractConn(6) _ConntrackExtractConn(6)
} }
#define _ConntrackPoolSearch(v) \ #define _ConntrackPoolSearch(v) \
t_conntrack##v *t; \ t_conntrack##v *t; \
HASH_FIND(hh, p, c, sizeof(*c), t); \ HASH_FIND(hh, p, c, sizeof(*c), t); \
return t; return t;
t_conntrack4 *ConntrackPoolSearch4(t_conntrack4 *p, const t_conn4 *c) t_conntrack4 *ConntrackPoolSearch4(t_conntrack4 *p, const t_conn4 *c)
{ {
_ConntrackPoolSearch(4) _ConntrackPoolSearch(4)
} }
t_conntrack6 *ConntrackPoolSearch6(t_conntrack6 *p, const t_conn6 *c) t_conntrack6 *ConntrackPoolSearch6(t_conntrack6 *p, const t_conn6 *c)
{ {
_ConntrackPoolSearch(6) _ConntrackPoolSearch(6)
} }
static void ConntrackInitTrack(t_ctrack *t) static void ConntrackInitTrack(t_ctrack *t)
{ {
memset(t,0,sizeof(*t)); memset(t,0,sizeof(*t));
time(&t->t_start); time(&t->t_start);
} }
#define _ConntrackNew(v) \ #define _ConntrackNew(v) \
t_conntrack##v *new; \ t_conntrack##v *new; \
if (!(new = calloc(1,sizeof(*new)))) return NULL; \ if (!(new = calloc(1,sizeof(*new)))) return NULL; \
new->conn = *c; \ new->conn = *c; \
oom = false; \ oom = false; \
HASH_ADD(hh, *pp, conn, sizeof(*c), new); \ HASH_ADD(hh, *pp, conn, sizeof(*c), new); \
if (oom) { free(new); return NULL; } \ if (oom) { free(new); return NULL; } \
ConntrackInitTrack(&new->track); \ ConntrackInitTrack(&new->track); \
return new; return new;
static t_conntrack4 *ConntrackNew4(t_conntrack4 **pp, const t_conn4 *c) static t_conntrack4 *ConntrackNew4(t_conntrack4 **pp, const t_conn4 *c)
{ {
_ConntrackNew(4) _ConntrackNew(4)
} }
static t_conntrack6 *ConntrackNew6(t_conntrack6 **pp, const t_conn6 *c) static t_conntrack6 *ConntrackNew6(t_conntrack6 **pp, const t_conn6 *c)
{ {
_ConntrackNew(6) _ConntrackNew(6)
} }
static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload) static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload)
{ {
if (tcp_syn_segment(tcphdr)) if (tcp_syn_segment(tcphdr))
{ {
if (t->state==FIN) ConntrackInitTrack(t); // erase current entry if (t->state==FIN) ConntrackInitTrack(t); // erase current entry
if (t->state==SYN) t->seq0 = htonl(tcphdr->th_seq); if (t->state==SYN) t->seq0 = htonl(tcphdr->th_seq);
} }
else if (tcp_synack_segment(tcphdr)) else if (tcp_synack_segment(tcphdr))
{ {
if (t->state==FIN) ConntrackInitTrack(t); // erase current entry if (t->state==FIN) ConntrackInitTrack(t); // erase current entry
if (t->state==SYN) if (t->state==SYN)
{ {
if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1; if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1;
t->ack0 = htonl(tcphdr->th_seq); t->ack0 = htonl(tcphdr->th_seq);
} }
} }
else if (tcphdr->th_flags & (TH_FIN|TH_RST)) else if (tcphdr->th_flags & (TH_FIN|TH_RST))
{ {
t->state = FIN; t->state = FIN;
} }
else else
{ {
if (t->state==SYN) if (t->state==SYN)
{ {
t->state=ESTABLISHED; t->state=ESTABLISHED;
if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1; if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1;
} }
} }
if (bReverse) if (bReverse)
{ {
t->seq_last = htonl(tcphdr->th_ack); t->seq_last = htonl(tcphdr->th_ack);
t->ack_last = htonl(tcphdr->th_seq) + len_payload; t->ack_last = htonl(tcphdr->th_seq) + len_payload;
t->pcounter_reply++; t->pcounter_reply++;
} }
else else
{ {
t->seq_last = htonl(tcphdr->th_seq) + len_payload; t->seq_last = htonl(tcphdr->th_seq) + len_payload;
t->ack_last = htonl(tcphdr->th_ack); t->ack_last = htonl(tcphdr->th_ack);
t->pcounter_orig++; t->pcounter_orig++;
} }
time(&t->t_last); time(&t->t_last);
} }
#define _ConntrackPoolFeed(v) \ #define _ConntrackPoolFeed(v) \
t_conn##v conn, connswap; \ t_conn##v conn, connswap; \
t_conntrack##v *ctr; \ t_conntrack##v *ctr; \
bool b_rev; \ bool b_rev; \
ConntrackExtractConn##v(&conn,false,ip,tcphdr); \ ConntrackExtractConn##v(&conn,false,ip,tcphdr); \
if ((ctr=ConntrackPoolSearch##v(*pp,&conn))) \ if ((ctr=ConntrackPoolSearch##v(*pp,&conn))) \
{ \ { \
ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); \ ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); \
goto ok; \ goto ok; \
} \ } \
else \ else \
{ \ { \
connswap##v(&conn,&connswap); \ connswap##v(&conn,&connswap); \
if ((ctr=ConntrackPoolSearch##v(*pp,&connswap))) \ if ((ctr=ConntrackPoolSearch##v(*pp,&connswap))) \
{ \ { \
ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); \ ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); \
goto ok; \ goto ok; \
} \ } \
} \ } \
b_rev = tcp_synack_segment(tcphdr); \ b_rev = tcp_synack_segment(tcphdr); \
if (tcp_syn_segment(tcphdr) || b_rev) \ if (tcp_syn_segment(tcphdr) || b_rev) \
{ \ { \
if ((ctr=ConntrackNew##v(pp, b_rev ? &connswap : &conn))) \ if ((ctr=ConntrackNew##v(pp, b_rev ? &connswap : &conn))) \
{ \ { \
ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); \ ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); \
goto ok; \ goto ok; \
} \ } \
} \ } \
return false; \ return false; \
ok: \ ok: \
if (ctrack) *ctrack = &ctr->track; \ if (ctrack) *ctrack = &ctr->track; \
if (bReverse) *bReverse = b_rev; \ if (bReverse) *bReverse = b_rev; \
return true; return true;
static bool ConntrackPoolFeed4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse) static bool ConntrackPoolFeed4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse)
{ {
_ConntrackPoolFeed(4) _ConntrackPoolFeed(4)
} }
static bool ConntrackPoolFeed6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse) static bool ConntrackPoolFeed6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse)
{ {
_ConntrackPoolFeed(6) _ConntrackPoolFeed(6)
} }
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse) bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{ {
return ip ? ConntrackPoolFeed4(&p->pool4,ip,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : ip6 ? ConntrackPoolFeed6(&p->pool6,ip6,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : false; return ip ? ConntrackPoolFeed4(&p->pool4,ip,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : ip6 ? ConntrackPoolFeed6(&p->pool6,ip6,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : false;
} }
#define _ConntrackPoolDrop(v) \ #define _ConntrackPoolDrop(v) \
t_conn##v conn, connswap; \ t_conn##v conn, connswap; \
t_conntrack##v *t; \ t_conntrack##v *t; \
ConntrackExtractConn##v(&conn,false,ip,tcphdr); \ ConntrackExtractConn##v(&conn,false,ip,tcphdr); \
if (!(t=ConntrackPoolSearch##v(*pp,&conn))) \ if (!(t=ConntrackPoolSearch##v(*pp,&conn))) \
{ \ { \
connswap##v(&conn,&connswap); \ connswap##v(&conn,&connswap); \
t=ConntrackPoolSearch##v(*pp,&connswap); \ t=ConntrackPoolSearch##v(*pp,&connswap); \
} \ } \
if (!t) return false; \ if (!t) return false; \
HASH_DEL(*pp, t); free(t); \ HASH_DEL(*pp, t); free(t); \
return true; return true;
static bool ConntrackPoolDrop4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr) static bool ConntrackPoolDrop4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr)
{ {
_ConntrackPoolDrop(4) _ConntrackPoolDrop(4)
} }
static bool ConntrackPoolDrop6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr) static bool ConntrackPoolDrop6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr)
{ {
_ConntrackPoolDrop(6) _ConntrackPoolDrop(6)
} }
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr) bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr)
{ {
return ip ? ConntrackPoolDrop4(&p->pool4,ip,tcphdr) : ip6 ? ConntrackPoolDrop6(&p->pool6,ip6,tcphdr) : false; return ip ? ConntrackPoolDrop4(&p->pool4,ip,tcphdr) : ip6 ? ConntrackPoolDrop6(&p->pool6,ip6,tcphdr) : false;
} }
#define _ConntrackPoolPurge(v, pp) \ #define _ConntrackPoolPurge(v, pp) \
{ \ { \
t_conntrack##v *t, *tmp; \ t_conntrack##v *t, *tmp; \
time_t tidle; \ time_t tidle; \
HASH_ITER(hh, *pp , t, tmp) { \ HASH_ITER(hh, *pp , t, tmp) { \
tidle = tnow - t->track.t_last; \ tidle = tnow - t->track.t_last; \
if ( t->track.b_cutoff || \ if ( t->track.b_cutoff || \
t->track.state==SYN && tidle>=p->timeout_syn || \ t->track.state==SYN && tidle>=p->timeout_syn || \
t->track.state==ESTABLISHED && tidle>=p->timeout_established || \ t->track.state==ESTABLISHED && tidle>=p->timeout_established || \
t->track.state==FIN && tidle>=p->timeout_fin) \ t->track.state==FIN && tidle>=p->timeout_fin) \
{ \ { \
HASH_DEL(*pp, t); free(t); \ HASH_DEL(*pp, t); free(t); \
} \ } \
} \ } \
} }
void ConntrackPoolPurge(t_conntrack *p) void ConntrackPoolPurge(t_conntrack *p)
{ {
time_t tnow = time(NULL); \ time_t tnow = time(NULL); \
if ((tnow - p->t_last_purge)>=p->t_purge_interval) if ((tnow - p->t_last_purge)>=p->t_purge_interval)
{ {
_ConntrackPoolPurge(4, &p->pool4); _ConntrackPoolPurge(4, &p->pool4);
_ConntrackPoolPurge(6, &p->pool6); _ConntrackPoolPurge(6, &p->pool6);
p->t_last_purge = tnow; p->t_last_purge = tnow;
} }
} }
#define _ConntrackPoolDump(v,f) \ #define _ConntrackPoolDump(v,f) \
t_conntrack##v *t, *tmp; \ t_conntrack##v *t, *tmp; \
char sa1[40],sa2[40]; \ char sa1[40],sa2[40]; \
time_t tnow = time(NULL); \ time_t tnow = time(NULL); \
HASH_ITER(hh, p, t, tmp) { \ HASH_ITER(hh, p, t, tmp) { \
*sa1=0; inet_ntop(AF_INET##f, &t->conn.e1.adr, sa1, sizeof(sa1)); \ *sa1=0; inet_ntop(AF_INET##f, &t->conn.e1.adr, sa1, sizeof(sa1)); \
*sa2=0; inet_ntop(AF_INET##f, &t->conn.e2.adr, sa2, sizeof(sa2)); \ *sa2=0; inet_ntop(AF_INET##f, &t->conn.e2.adr, sa2, sizeof(sa2)); \
printf("[%s]:%u => [%s]:%u : %s : t0=%lld last=t0+%lld now=last+%lld cutoff=%u packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u\n", \ printf("[%s]:%u => [%s]:%u : %s : t0=%lld last=t0+%lld now=last+%lld cutoff=%u packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u\n", \
sa1, t->conn.e1.port, sa2, t->conn.e2.port, \ sa1, t->conn.e1.port, sa2, t->conn.e2.port, \
connstate_s[t->track.state], \ connstate_s[t->track.state], \
(unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), \ (unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), \
t->track.b_cutoff, \ t->track.b_cutoff, \
(unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pcounter_reply, \ (unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pcounter_reply, \
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - t->track.ack0); \ t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - t->track.ack0); \
}; };
void ConntrackPoolDump4(t_conntrack4 *p) void ConntrackPoolDump4(t_conntrack4 *p)
{ {
_ConntrackPoolDump(4,) _ConntrackPoolDump(4,)
} }
void ConntrackPoolDump6(t_conntrack6 *p) void ConntrackPoolDump6(t_conntrack6 *p)
{ {
_ConntrackPoolDump(6,6) _ConntrackPoolDump(6,6)
} }
void ConntrackPoolDump(t_conntrack *p) void ConntrackPoolDump(t_conntrack *p)
{ {
ConntrackPoolDump4(p->pool4); ConntrackPoolDump4(p->pool4);
ConntrackPoolDump6(p->pool6); ConntrackPoolDump6(p->pool6);
} }