nfqws: udp protocol desync

This commit is contained in:
bol-van
2022-01-01 20:22:04 +03:00
parent 566e3d1536
commit 65830eb665
21 changed files with 1048 additions and 736 deletions

View File

@@ -12,88 +12,68 @@ static void ut_oom_recover(void *elem)
oom = true;
}
static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"};
#define _connswap \
memset(c2,0,sizeof(*c2)); \
c2->e1 = c->e2; \
c2->e2 = c->e1;
static void connswap4(const t_conn4 *c, t_conn4 *c2)
static void connswap(const t_conn *c, t_conn *c2)
{
_connswap
}
static void connswap6(const t_conn6 *c, t_conn6 *c2)
{
_connswap
memset(c2,0,sizeof(*c2));
c2->l3proto = c->l3proto;
c2->l4proto = c->l4proto;
c2->src = c->dst;
c2->dst = c->src;
c2->sport = c->dport;
c2->dport = c->sport;
}
#define _ConntrackPoolDestroy(v) \
t_conntrack##v *elem, *tmp; \
static void ConntrackPoolDestroyPool(t_conntrack_pool **pp)
{
t_conntrack_pool *elem, *tmp;
HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); free(elem); }
static void ConntrackPoolDestroy4(t_conntrack4 **pp)
{
_ConntrackPoolDestroy(4)
}
static void ConntrackPoolDestroy6(t_conntrack6 **pp)
{
_ConntrackPoolDestroy(6)
}
void ConntrackPoolDestroy(t_conntrack *p)
{
ConntrackPoolDestroy4(&p->pool4);
ConntrackPoolDestroy6(&p->pool6);
ConntrackPoolDestroyPool(&p->pool);
}
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, uint32_t timeout_udp)
{
p->timeout_syn = timeout_syn;
p->timeout_established = timeout_established;
p->timeout_fin = timeout_fin;
p->timeout_udp= timeout_udp;
p->t_purge_interval = purge_interval;
time(&p->t_last_purge);
p->pool4 = NULL;
p->pool6 = NULL;
p->pool = NULL;
}
#define _ConntrackExtractConn(v) \
memset(c,0,sizeof(*c)); \
if (bReverse) { \
c->e1.adr = ip->ip##v##_dst; \
c->e2.adr = ip->ip##v##_src; \
c->e1.port = htons(tcphdr->th_dport); \
c->e2.port = htons(tcphdr->th_sport); \
} else { \
c->e1.adr = ip->ip##v##_src; \
c->e2.adr = ip->ip##v##_dst; \
c->e1.port = htons(tcphdr->th_sport); \
c->e2.port = htons(tcphdr->th_dport); \
void ConntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr)
{
memset(c,0,sizeof(*c));
if (ip)
{
c->l3proto = IPPROTO_IP;
c->dst.ip = bReverse ? ip->ip_src : ip->ip_dst;
c->src.ip = bReverse ? ip->ip_dst : ip->ip_src;
}
void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr)
{
_ConntrackExtractConn()
}
void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr)
{
_ConntrackExtractConn(6)
else if (ip6)
{
c->l3proto = IPPROTO_IPV6;
c->dst.ip6 = bReverse ? ip6->ip6_src : ip6->ip6_dst;
c->src.ip6 = bReverse ? ip6->ip6_dst : ip6->ip6_src;
}
else
c->l3proto = -1;
extract_ports(tcphdr, udphdr, &c->l4proto, bReverse ? &c->dport : &c->sport, bReverse ? &c->sport : &c->dport);
}
#define _ConntrackPoolSearch(v) \
t_conntrack##v *t; \
HASH_FIND(hh, p, c, sizeof(*c), t); \
static t_conntrack_pool *ConntrackPoolSearch(t_conntrack_pool *p, const t_conn *c)
{
t_conntrack_pool *t;
HASH_FIND(hh, p, c, sizeof(*c), t);
return t;
t_conntrack4 *ConntrackPoolSearch4(t_conntrack4 *p, const t_conn4 *c)
{
_ConntrackPoolSearch(4)
}
t_conntrack6 *ConntrackPoolSearch6(t_conntrack6 *p, const t_conn6 *c)
{
_ConntrackPoolSearch(6)
}
static void ConntrackInitTrack(t_ctrack *t)
{
@@ -102,206 +82,218 @@ static void ConntrackInitTrack(t_ctrack *t)
time(&t->t_start);
}
#define _ConntrackNew(v) \
t_conntrack##v *new; \
if (!(new = calloc(1,sizeof(*new)))) return NULL; \
new->conn = *c; \
oom = false; \
HASH_ADD(hh, *pp, conn, sizeof(*c), new); \
if (oom) { free(new); return NULL; } \
ConntrackInitTrack(&new->track); \
static t_conntrack_pool *ConntrackNew(t_conntrack_pool **pp, const t_conn *c)
{
t_conntrack_pool *new;
if (!(new = malloc(sizeof(*new)))) return NULL;
new->conn = *c;
oom = false;
HASH_ADD(hh, *pp, conn, sizeof(*c), new);
if (oom) { free(new); return NULL; }
ConntrackInitTrack(&new->track);
return new;
static t_conntrack4 *ConntrackNew4(t_conntrack4 **pp, const t_conn4 *c)
{
_ConntrackNew(4)
}
static t_conntrack6 *ConntrackNew6(t_conntrack6 **pp, const t_conn6 *c)
{
_ConntrackNew(6)
}
// non-tcp packets are passed with tcphdr=NULL but len_payload filled
static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload)
{
uint8_t scale;
if (tcp_syn_segment(tcphdr))
{
if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry
t->seq0 = htonl(tcphdr->th_seq);
}
else if (tcp_synack_segment(tcphdr))
{
if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1;
t->ack0 = htonl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN|TH_RST))
{
t->state = FIN;
}
else
{
if (t->state==SYN)
{
t->state=ESTABLISHED;
if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1;
}
}
scale = tcp_find_scale_factor(tcphdr);
if (bReverse)
{
t->pos_orig = t->seq_last = htonl(tcphdr->th_ack);
t->ack_last = htonl(tcphdr->th_seq);
t->pos_reply = t->ack_last + len_payload;
t->pcounter_reply++;
t->pdcounter_reply+=!!len_payload;
t->winsize_reply = htons(tcphdr->th_win);
if (scale!=SCALE_NONE) t->scale_reply = scale;
}
else
{
t->seq_last = htonl(tcphdr->th_seq);
t->pos_orig = t->seq_last + len_payload;
t->pos_reply = t->ack_last = htonl(tcphdr->th_ack);
t->pcounter_orig++;
t->pdcounter_orig+=!!len_payload;
t->winsize_orig = htons(tcphdr->th_win);
if (scale!=SCALE_NONE) t->scale_orig = scale;
}
if (tcphdr)
{
if (tcp_syn_segment(tcphdr))
{
if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry
t->seq0 = htonl(tcphdr->th_seq);
}
else if (tcp_synack_segment(tcphdr))
{
if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1;
t->ack0 = htonl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN|TH_RST))
{
t->state = FIN;
}
else
{
if (t->state==SYN)
{
t->state=ESTABLISHED;
if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1;
}
}
scale = tcp_find_scale_factor(tcphdr);
if (bReverse)
{
t->pos_orig = t->seq_last = htonl(tcphdr->th_ack);
t->ack_last = htonl(tcphdr->th_seq);
t->pos_reply = t->ack_last + len_payload;
t->winsize_reply = htons(tcphdr->th_win);
if (scale!=SCALE_NONE) t->scale_reply = scale;
}
else
{
t->seq_last = htonl(tcphdr->th_seq);
t->pos_orig = t->seq_last + len_payload;
t->pos_reply = t->ack_last = htonl(tcphdr->th_ack);
t->winsize_orig = htons(tcphdr->th_win);
if (scale!=SCALE_NONE) t->scale_orig = scale;
}
}
else
{
if (bReverse)
{
t->ack_last=t->pos_reply;
t->pos_reply+=len_payload;
}
else
{
t->seq_last=t->pos_orig;
t->pos_orig+=len_payload;
}
}
time(&t->t_last);
}
#define _ConntrackPoolFeed(v) \
t_conn##v conn, connswap; \
t_conntrack##v *ctr; \
bool b_rev; \
ConntrackExtractConn##v(&conn,false,ip,tcphdr); \
if ((ctr=ConntrackPoolSearch##v(*pp,&conn))) \
{ \
ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); \
goto ok; \
} \
else \
{ \
connswap##v(&conn,&connswap); \
if ((ctr=ConntrackPoolSearch##v(*pp,&connswap))) \
{ \
ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); \
goto ok; \
} \
} \
b_rev = tcp_synack_segment(tcphdr); \
if (tcp_syn_segment(tcphdr) || b_rev) \
{ \
if ((ctr=ConntrackNew##v(pp, b_rev ? &connswap : &conn))) \
{ \
ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); \
goto ok; \
} \
} \
return false; \
ok: \
if (ctrack) *ctrack = &ctr->track; \
if (bReverse) *bReverse = b_rev; \
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 ConntrackPoolFeedPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
_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)
{
_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)
{
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;
}
t_conn conn, connswp;
t_conntrack_pool *ctr;
bool b_rev;
#define _ConntrackPoolDrop(v) \
t_conn##v conn, connswap; \
t_conntrack##v *t; \
ConntrackExtractConn##v(&conn,false,ip,tcphdr); \
if (!(t=ConntrackPoolSearch##v(*pp,&conn))) \
{ \
connswap##v(&conn,&connswap); \
t=ConntrackPoolSearch##v(*pp,&connswap); \
} \
if (!t) return false; \
HASH_DEL(*pp, t); free(t); \
return true;
static bool ConntrackPoolDrop4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr)
{
_ConntrackPoolDrop(4)
}
static bool ConntrackPoolDrop6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr)
{
_ConntrackPoolDrop(6)
}
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;
}
#define _ConntrackPoolPurge(v, pp) \
{ \
t_conntrack##v *t, *tmp; \
time_t tidle; \
HASH_ITER(hh, *pp , t, tmp) { \
tidle = tnow - t->track.t_last; \
if ( t->track.b_cutoff || \
t->track.state==SYN && tidle>=p->timeout_syn || \
t->track.state==ESTABLISHED && tidle>=p->timeout_established || \
t->track.state==FIN && tidle>=p->timeout_fin) \
{ \
HASH_DEL(*pp, t); free(t); \
} \
} \
ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr);
if ((ctr=ConntrackPoolSearch(*pp,&conn)))
{
ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload);
goto ok;
}
else
{
connswap(&conn,&connswp);
if ((ctr=ConntrackPoolSearch(*pp,&connswp)))
{
ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload);
goto ok;
}
}
b_rev = tcphdr && tcp_synack_segment(tcphdr);
if (tcphdr && tcp_syn_segment(tcphdr) || b_rev || udphdr)
{
if ((ctr=ConntrackNew(pp, b_rev ? &connswp : &conn)))
{
ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload);
goto ok;
}
}
return false;
ok:
if (ctrack) *ctrack = &ctr->track;
if (bReverse) *bReverse = b_rev;
return true;
}
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
return ConntrackPoolFeedPool(&p->pool,ip,ip6,tcphdr,udphdr,len_payload,ctrack,bReverse);
}
static bool ConntrackPoolDropPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr)
{
t_conn conn, connswp;
t_conntrack_pool *t;
ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr);
if (!(t=ConntrackPoolSearch(*pp,&conn)))
{
connswap(&conn,&connswp);
t=ConntrackPoolSearch(*pp,&connswp);
}
if (!t) return false;
HASH_DEL(*pp, t); free(t);
return true;
}
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr)
{
return ConntrackPoolDropPool(&p->pool,ip,ip6,tcphdr,udphdr);
}
void ConntrackPoolPurge(t_conntrack *p)
{
time_t tnow = time(NULL); \
time_t tidle, tnow = time(NULL);
t_conntrack_pool *t, *tmp;
if ((tnow - p->t_last_purge)>=p->t_purge_interval)
{
_ConntrackPoolPurge(4, &p->pool4);
_ConntrackPoolPurge(6, &p->pool6);
HASH_ITER(hh, p->pool , t, tmp) {
tidle = tnow - t->track.t_last;
if ( t->track.b_cutoff ||
t->conn.l4proto==IPPROTO_TCP && (
t->track.state==SYN && tidle>=p->timeout_syn ||
t->track.state==ESTABLISHED && tidle>=p->timeout_established ||
t->track.state==FIN && tidle>=p->timeout_fin) ||
t->conn.l4proto==IPPROTO_UDP &&
tidle>=p->timeout_udp)
{
HASH_DEL(p->pool, t); free(t);
}
}
p->t_last_purge = tnow;
}
}
static void taddr2str(uint8_t l3proto, const t_addr *a, char *buf, size_t bufsize)
{
if (!inet_ntop(family_from_proto(l3proto), a, buf, bufsize) && bufsize) *buf=0;
}
#define _ConntrackPoolDump(v,f) \
t_conntrack##v *t, *tmp; \
char sa1[40],sa2[40]; \
time_t tnow = time(NULL); \
HASH_ITER(hh, p, t, tmp) { \
*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)); \
printf("[%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d cutoff=%u wss_cutoff=%u d_cutoff=%u\n", \
sa1, t->conn.e1.port, sa2, t->conn.e2.port, \
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.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, \
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply,\
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0, \
t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0, \
t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, \
t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply, \
t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff); \
void ConntrackPoolDump(const t_conntrack *p)
{
t_conntrack_pool *t, *tmp;
char sa1[40],sa2[40];
time_t tnow = time(NULL);
HASH_ITER(hh, p->pool, t, tmp) {
taddr2str(t->conn.l3proto, &t->conn.src, sa1, sizeof(sa1));
taddr2str(t->conn.l3proto, &t->conn.dst, sa2, sizeof(sa2));
if (t->conn.l4proto==IPPROTO_TCP)
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d",
proto_name(t->conn.l4proto),
sa1, t->conn.sport, sa2, t->conn.dport,
t->conn.l4proto==IPPROTO_TCP ? 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.pdcounter_orig, (unsigned long long)t->track.pcounter_orig,
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply,
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0,
t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0,
t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig,
t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply);
else
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu rseq=%u pos_orig=%u rack=%u pos_reply=%u",
proto_name(t->conn.l4proto),
sa1, t->conn.sport, sa2, t->conn.dport,
t->conn.l4proto==IPPROTO_TCP ? 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.pdcounter_orig, (unsigned long long)t->track.pcounter_orig,
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply,
t->track.seq_last, t->track.pos_orig,
t->track.ack_last, t->track.pos_reply);
printf(" cutoff=%u wss_cutoff=%u d_cutoff=%u\n",
t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff);
};
void ConntrackPoolDump4(t_conntrack4 *p)
{
_ConntrackPoolDump(4,)
}
void ConntrackPoolDump6(t_conntrack6 *p)
{
_ConntrackPoolDump(6,6)
}
void ConntrackPoolDump(t_conntrack *p)
{
ConntrackPoolDump4(p->pool4);
ConntrackPoolDump6(p->pool6);
}