diff --git a/nfq2/conntrack.c b/nfq2/conntrack.c index 0e81583..0828195 100644 --- a/nfq2/conntrack.c +++ b/nfq2/conntrack.c @@ -102,8 +102,8 @@ static void ConntrackInitTrack(t_ctrack *t) { memset(t, 0, sizeof(*t)); t->l7proto = L7_UNKNOWN; - t->scale_orig = t->scale_reply = SCALE_NONE; - time(&t->t_start); + t->pos.scale_orig = t->pos.scale_reply = SCALE_NONE; + time(&t->pos.t_start); rawpacket_queue_init(&t->delayed); lua_newtable(params.L); t->lua_state = luaL_ref(params.L, LUA_REGISTRYINDEX); @@ -136,86 +136,86 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr if (bReverse) { - t->pcounter_reply++; - t->pdcounter_reply += !!len_payload; - t->pbcounter_reply += len_payload; + t->pos.pcounter_reply++; + t->pos.pdcounter_reply += !!len_payload; + t->pos.pbcounter_reply += len_payload; } else { - t->pcounter_orig++; - t->pdcounter_orig += !!len_payload; - t->pbcounter_orig += len_payload; + t->pos.pcounter_orig++; + t->pos.pdcounter_orig += !!len_payload; + t->pos.pbcounter_orig += len_payload; } if (tcphdr) { if (tcp_syn_segment(tcphdr)) { - if (t->state != SYN) ConntrackReInitTrack(t); // erase current entry - t->seq0 = ntohl(tcphdr->th_seq); + if (t->pos.state != SYN) ConntrackReInitTrack(t); // erase current entry + t->pos.seq0 = ntohl(tcphdr->th_seq); } else if (tcp_synack_segment(tcphdr)) { // ignore SA dups uint32_t seq0 = ntohl(tcphdr->th_ack) - 1; - if (t->state != SYN && t->seq0 != seq0) + if (t->pos.state != SYN && t->pos.seq0 != seq0) ConntrackReInitTrack(t); // erase current entry - if (!t->seq0) t->seq0 = seq0; - t->ack0 = ntohl(tcphdr->th_seq); + if (!t->pos.seq0) t->pos.seq0 = seq0; + t->pos.ack0 = ntohl(tcphdr->th_seq); } else if (tcphdr->th_flags & (TH_FIN | TH_RST)) { - t->state = FIN; + t->pos.state = FIN; } else { - if (t->state == SYN) + if (t->pos.state == SYN) { - t->state = ESTABLISHED; - if (!bReverse && !t->ack0) t->ack0 = ntohl(tcphdr->th_ack) - 1; + t->pos.state = ESTABLISHED; + if (!bReverse && !t->pos.ack0) t->pos.ack0 = ntohl(tcphdr->th_ack) - 1; } } scale = tcp_find_scale_factor(tcphdr); mss = ntohs(tcp_find_mss(tcphdr)); if (bReverse) { - t->pos_orig = t->seq_last = ntohl(tcphdr->th_ack); - t->ack_last = ntohl(tcphdr->th_seq); - t->pos_reply = t->ack_last + len_payload; - t->winsize_reply = ntohs(tcphdr->th_win); - t->winsize_reply_calc = t->winsize_reply; - if (t->scale_reply != SCALE_NONE) t->winsize_reply_calc <<= t->scale_reply; - if (mss && !t->mss_reply) t->mss_reply = mss; - if (scale != SCALE_NONE) t->scale_reply = scale; + t->pos.pos_orig = t->pos.seq_last = ntohl(tcphdr->th_ack); + t->pos.ack_last = ntohl(tcphdr->th_seq); + t->pos.pos_reply = t->pos.ack_last + len_payload; + t->pos.winsize_reply = ntohs(tcphdr->th_win); + t->pos.winsize_reply_calc = t->pos.winsize_reply; + if (t->pos.scale_reply != SCALE_NONE) t->pos.winsize_reply_calc <<= t->pos.scale_reply; + if (mss && !t->pos.mss_reply) t->pos.mss_reply = mss; + if (scale != SCALE_NONE) t->pos.scale_reply = scale; } else { - t->seq_last = ntohl(tcphdr->th_seq); - t->pos_orig = t->seq_last + len_payload; - t->pos_reply = t->ack_last = ntohl(tcphdr->th_ack); - t->winsize_orig = ntohs(tcphdr->th_win); - t->winsize_orig_calc = t->winsize_orig; - if (t->scale_orig != SCALE_NONE) t->winsize_orig_calc <<= t->scale_orig; - if (mss && !t->mss_reply) t->mss_orig = mss; - if (scale != SCALE_NONE) t->scale_orig = scale; + t->pos.seq_last = ntohl(tcphdr->th_seq); + t->pos.pos_orig = t->pos.seq_last + len_payload; + t->pos.pos_reply = t->pos.ack_last = ntohl(tcphdr->th_ack); + t->pos.winsize_orig = ntohs(tcphdr->th_win); + t->pos.winsize_orig_calc = t->pos.winsize_orig; + if (t->pos.scale_orig != SCALE_NONE) t->pos.winsize_orig_calc <<= t->pos.scale_orig; + if (mss && !t->pos.mss_reply) t->pos.mss_orig = mss; + if (scale != SCALE_NONE) t->pos.scale_orig = scale; } } else { if (bReverse) { - t->ack_last = t->pos_reply; - t->pos_reply += len_payload; + t->pos.ack_last = t->pos.pos_reply; + t->pos.pos_reply += len_payload; } else { - t->seq_last = t->pos_orig; - t->pos_orig += len_payload; + t->pos.seq_last = t->pos.pos_orig; + t->pos.pos_orig += len_payload; } } - time(&t->t_last); + time(&t->pos.t_last); } static bool ConntrackPoolDoubleSearchPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse) @@ -317,12 +317,12 @@ void ConntrackPoolPurge(t_conntrack *p) if ((tnow - p->t_last_purge) >= p->t_purge_interval) { HASH_ITER(hh, p->pool, t, tmp) { - tidle = tnow - t->track.t_last; + tidle = tnow - t->track.pos.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->track.pos.state == SYN && tidle >= p->timeout_syn) || + (t->track.pos.state == ESTABLISHED && tidle >= p->timeout_established) || + (t->track.pos.state == FIN && tidle >= p->timeout_fin)) ) || (t->conn.l4proto == IPPROTO_UDP && tidle >= p->timeout_udp) ) { @@ -349,21 +349,21 @@ void ConntrackPoolDump(const t_conntrack *p) printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu orig=d%llu/n%llu/b%llu reply=d%llu/n%llu/b%lld ", 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.pbcounter_orig, - (unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply, (unsigned long long)t->track.pbcounter_reply); + t->conn.l4proto == IPPROTO_TCP ? connstate_s[t->track.pos.state] : "-", + (unsigned long long)t->track.pos.t_start, (unsigned long long)(t->track.pos.t_last - t->track.pos.t_start), (unsigned long long)(tnow - t->track.pos.t_last), + (unsigned long long)t->track.pos.pdcounter_orig, (unsigned long long)t->track.pos.pcounter_orig, (unsigned long long)t->track.pos.pbcounter_orig, + (unsigned long long)t->track.pos.pdcounter_reply, (unsigned long long)t->track.pos.pcounter_reply, (unsigned long long)t->track.pos.pbcounter_reply); if (t->conn.l4proto == IPPROTO_TCP) printf("seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u mss_orig=%u mss_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d", - 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.mss_orig, t->track.mss_reply, - 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.pos.seq0, t->track.pos.seq_last - t->track.pos.seq0, t->track.pos.pos_orig - t->track.pos.seq0, + t->track.pos.ack0, t->track.pos.ack_last - t->track.pos.ack0, t->track.pos.pos_reply - t->track.pos.ack0, + t->track.pos.mss_orig, t->track.pos.mss_reply, + t->track.pos.winsize_orig, t->track.pos.scale_orig == SCALE_NONE ? -1 : t->track.pos.scale_orig, + t->track.pos.winsize_reply, t->track.pos.scale_reply == SCALE_NONE ? -1 : t->track.pos.scale_reply); else printf("rseq=%u pos_orig=%u rack=%u pos_reply=%u", - t->track.seq_last, t->track.pos_orig, - t->track.ack_last, t->track.pos_reply); + t->track.pos.seq_last, t->track.pos.pos_orig, + t->track.pos.ack_last, t->track.pos.pos_reply); printf(" req_retrans=%u cutoff=%u lua_in_cutoff=%u lua_out_cutoff=%u hostname=%s l7proto=%s\n", t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_lua_in_cutoff, t->track.b_lua_out_cutoff, t->track.hostname, l7proto_str(t->track.l7proto)); }; diff --git a/nfq2/conntrack.h b/nfq2/conntrack.h index 57144b6..c306be5 100644 --- a/nfq2/conntrack.h +++ b/nfq2/conntrack.h @@ -17,6 +17,7 @@ #include #include +#include "conntrack_base.h" #include "packet_queue.h" #include "protocol.h" @@ -48,35 +49,18 @@ typedef struct { size_t size_present; // how many bytes already stored in 'packet' } t_reassemble; -// SYN - SYN or SYN/ACK received -// ESTABLISHED - any except SYN or SYN/ACK received -// FIN - FIN or RST received -typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate; typedef struct { bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache uint8_t ipproto; + // this block of data can change between delayed (queued) packets. need to remeber this data for each packet for further replay + t_ctrack_position pos; + struct desync_profile *dp; // desync profile cache bool dp_search_complete; - // common state - time_t t_start, t_last; - uint64_t pcounter_orig, pcounter_reply; // packet counter - uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload) - uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq. - uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current - uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current - - // tcp only state, not used in udp - t_connstate state; - uint32_t seq0, ack0; // starting seq and ack - uint16_t winsize_orig, winsize_reply; // last seen window size - uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none - uint32_t winsize_orig_calc, winsize_reply_calc; // calculated window size - uint16_t mss_orig, mss_reply; - uint8_t req_retrans_counter; // number of request retransmissions bool req_seq_present,req_seq_finalized,req_seq_abandoned; uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions) diff --git a/nfq2/conntrack_base.h b/nfq2/conntrack_base.h new file mode 100644 index 0000000..0103782 --- /dev/null +++ b/nfq2/conntrack_base.h @@ -0,0 +1,25 @@ +#pragma once + +// SYN - SYN or SYN/ACK received +// ESTABLISHED - any except SYN or SYN/ACK received +// FIN - FIN or RST received +typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate; + +typedef struct +{ + time_t t_last, t_start; + + uint64_t pcounter_orig, pcounter_reply; // packet counter + uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload) + uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq. + uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current + uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current + + // tcp only state, not used in udp + t_connstate state; + uint32_t seq0, ack0; // starting seq and ack + uint16_t winsize_orig, winsize_reply; // last seen window size + uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none + uint32_t winsize_orig_calc, winsize_reply_calc; // calculated window size + uint16_t mss_orig, mss_reply; +} t_ctrack_position; diff --git a/nfq2/desync.c b/nfq2/desync.c index 21e74a5..54eca9b 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -249,9 +249,9 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho { if (!ctrack->req_seq_finalized || ctrack->req_seq_abandoned) return false; - if (!seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end)) + if (!seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end)) { - DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end); + DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end); ctrack_stop_retrans_counter(ctrack); auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname, client_ip_port, l7proto); return false; @@ -344,14 +344,14 @@ static bool send_delayed(t_ctrack *ctrack) return true; } -static bool rawpacket_queue_csum_fix(struct rawpacket_tailhead *q, const struct dissect *dis, const struct sockaddr_storage* dst, uint32_t fwmark, uint32_t desync_fwmark, const char *ifin, const char *ifout) +static bool rawpacket_queue_csum_fix(struct rawpacket_tailhead *q, const struct dissect *dis, const t_ctrack_position *pos, const struct sockaddr_storage* dst, uint32_t fwmark, uint32_t desync_fwmark, const char *ifin, const char *ifout) { // this breaks const pointer to l4 header if (dis->tcp) verdict_tcp_csum_fix(VERDICT_PASS, (struct tcphdr *)dis->tcp, dis->transport_len, dis->ip, dis->ip6); else if (dis->udp) verdict_udp_csum_fix(VERDICT_PASS, (struct udphdr *)dis->udp, dis->transport_len, dis->ip, dis->ip6); - return rawpacket_queue(q, dst, fwmark, desync_fwmark, ifin, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload); + return rawpacket_queue(q, dst, fwmark, desync_fwmark, ifin, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload, pos); } @@ -360,7 +360,7 @@ static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, si ReasmClear(reasm); if (sz <= szMax) { - uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->seq_last : 0; + uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->pos.seq_last : 0; if (ReasmInit(reasm, sz, seq)) { ReasmFeed(reasm, seq, data_payload, len_payload); @@ -382,7 +382,7 @@ static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, con { if (ctrack && !ReasmIsEmpty(reasm)) { - uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->seq_last : (uint32_t)reasm->size_present; + uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->pos.seq_last : (uint32_t)reasm->size_present; if (ReasmFeed(reasm, seq, data_payload, len_payload)) { DLOG("reassemble : feeding data payload size=%zu. now we have %zu/%zu\n", len_payload, reasm->size_present, reasm->size); @@ -429,7 +429,7 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect * // if used in postnat chain, dropping initial packet will cause conntrack connection teardown // so we need to workaround this. // SYN and SYN,ACK checks are for conntrack-less mode - if (ctrack && (params.server ? ctrack->pcounter_reply : ctrack->pcounter_orig) == 1 || dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp))) + if (ctrack && (params.server ? ctrack->pos.pcounter_reply : ctrack->pos.pcounter_orig) == 1 || dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp))) { if (dis->len_pkt > *len_mod_pkt) DLOG_ERR("linux postnat conntrack workaround cannot be applied\n"); @@ -458,60 +458,60 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect * } -static uint64_t pos_get(const t_ctrack *ctrack, char mode, bool bReply) +static uint64_t pos_get(const t_ctrack_position *pos, char mode, bool bReply) { - if (ctrack) + if (pos) { switch (mode) { - case 'n': return bReply ? ctrack->pcounter_reply : ctrack->pcounter_orig; - case 'd': return bReply ? ctrack->pdcounter_reply : ctrack->pdcounter_orig; - case 's': return bReply ? (ctrack->ack_last - ctrack->ack0) : (ctrack->seq_last - ctrack->seq0); - case 'b': return bReply ? ctrack->pbcounter_reply : ctrack->pbcounter_orig; + case 'n': return bReply ? pos->pcounter_reply : pos->pcounter_orig; + case 'd': return bReply ? pos->pdcounter_reply : pos->pdcounter_orig; + case 's': return bReply ? (pos->ack_last - pos->ack0) : (pos->seq_last - pos->seq0); + case 'b': return bReply ? pos->pbcounter_reply : pos->pbcounter_orig; } } return 0; } -static bool check_pos_from(const t_ctrack *ctrack, bool bReply, const struct packet_range *range) +static bool check_pos_from(const t_ctrack_position *pos, bool bReply, const struct packet_range *range) { - uint64_t pos; + uint64_t ps; if (range->from.mode == 'x') return false; if (range->from.mode != 'a') { - if (ctrack) + if (pos) { - pos = pos_get(ctrack, range->from.mode, bReply); - return pos >= range->from.pos; + ps = pos_get(pos, range->from.mode, bReply); + return ps >= range->from.pos; } else return false; } return true; } -static bool check_pos_to(const t_ctrack *ctrack, bool bReply, const struct packet_range *range) +static bool check_pos_to(const t_ctrack_position *pos, bool bReply, const struct packet_range *range) { - uint64_t pos; + uint64_t ps; if (range->to.mode == 'x') return false; if (range->to.mode != 'a') { - if (ctrack) + if (pos) { - pos = pos_get(ctrack, range->to.mode, bReply); - return (pos < range->to.pos) || !range->upper_cutoff && (pos == range->to.pos); + ps = pos_get(pos, range->to.mode, bReply); + return (ps < range->to.pos) || !range->upper_cutoff && (ps == range->to.pos); } else return false; } return true; } -static bool check_pos_cutoff(const t_ctrack *ctrack, bool bReply, const struct packet_range *range) +static bool check_pos_cutoff(const t_ctrack_position *pos, bool bReply, const struct packet_range *range) { - bool bto = check_pos_to(ctrack, bReply, range); - return ctrack ? !bto : (!bto || !check_pos_from(ctrack, bReply, range)); + bool bto = check_pos_to(pos, bReply, range); + return pos ? !bto : (!bto || !check_pos_from(pos, bReply, range)); } -static bool check_pos_range(const t_ctrack *ctrack, bool bReply, const struct packet_range *range) +static bool check_pos_range(const t_ctrack_position *pos, bool bReply, const struct packet_range *range) { - return check_pos_from(ctrack, bReply, range) && check_pos_to(ctrack, bReply, range); + return check_pos_from(pos, bReply, range) && check_pos_to(pos, bReply, range); } @@ -645,6 +645,7 @@ static uint8_t desync( const char *ifout, bool bIncoming, t_ctrack *ctrack, + const t_ctrack_position *pos, t_l7payload l7payload, const struct dissect *dis, uint8_t *mod_pkt, size_t *len_mod_pkt, @@ -674,6 +675,7 @@ static uint8_t desync( DLOG("lua out cutoff\n"); return verdict; } + if (!pos) pos = &ctrack->pos; } if (LIST_FIRST(&dp->lua_desync)) { @@ -693,12 +695,12 @@ static uint8_t desync( { if (lua_instance_cutoff_check(&ctx, bIncoming)) DLOG("* lua '%s' : voluntary cutoff\n", instance); - else if (check_pos_cutoff(ctrack, bIncoming, range)) + else if (check_pos_cutoff(pos, bIncoming, range)) { DLOG("* lua '%s' : %s pos %c%llu %c%llu is beyond range %c%u%c%c%u (ctrack %s)\n", instance, sDirection, - range->from.mode, pos_get(ctrack, range->from.mode, bIncoming), - range->to.mode, pos_get(ctrack, range->to.mode, bIncoming), + range->from.mode, pos_get(pos, range->from.mode, bIncoming), + range->to.mode, pos_get(pos, range->to.mode, bIncoming), range->from.mode, range->from.pos, range->upper_cutoff ? '<' : '-', range->to.mode, range->to.pos, @@ -721,7 +723,7 @@ static uint8_t desync( // create arg table that persists across multiple desync function calls lua_createtable(params.L, 0, 12 + !!dp->name + !!ctrack + !!dis->tcp + 3*!!replay_piece_count); lua_pushf_dissect(dis); - lua_pushf_ctrack(ctrack); + lua_pushf_ctrack(ctrack, pos); lua_pushf_int("profile_n", dp->n); if (dp->name) lua_pushf_str("profile_name", dp->name); lua_pushf_bool("outgoing", !bIncoming); @@ -743,8 +745,8 @@ static uint8_t desync( if (dis->tcp) { // recommended mss value for generated packets - if (ctrack && ctrack->mss_orig) - lua_pushf_int("tcp_mss", ctrack->mss_orig); + if (pos && pos->mss_orig) + lua_pushf_int("tcp_mss", pos->mss_orig); else lua_pushf_global("tcp_mss", "DEFAULT_MSS"); } @@ -760,12 +762,12 @@ static uint8_t desync( if (!lua_instance_cutoff_check(&ctx, bIncoming)) { range = bIncoming ? &func->range_in : &func->range_out; - if (check_pos_range(ctrack, bIncoming, range)) + if (check_pos_range(pos, bIncoming, range)) { DLOG("* lua '%s' : %s pos %c%llu %c%llu in range %c%u%c%c%u\n", instance, sDirection, - range->from.mode, pos_get(ctrack, range->from.mode, bIncoming), - range->to.mode, pos_get(ctrack, range->to.mode, bIncoming), + range->from.mode, pos_get(pos, range->from.mode, bIncoming), + range->to.mode, pos_get(pos, range->to.mode, bIncoming), range->from.mode, range->from.pos, range->upper_cutoff ? '<' : '-', range->to.mode, range->to.pos); @@ -810,8 +812,8 @@ static uint8_t desync( else DLOG("* lua '%s' : %s pos %c%llu %c%llu out of range %c%u%c%c%u\n", instance, sDirection, - range->from.mode, pos_get(ctrack, range->from.mode, bIncoming), - range->to.mode, pos_get(ctrack, range->to.mode, bIncoming), + range->from.mode, pos_get(pos, range->from.mode, bIncoming), + range->to.mode, pos_get(pos, range->to.mode, bIncoming), range->from.mode, range->from.pos, range->upper_cutoff ? '<' : '-', range->to.mode, range->to.pos); @@ -905,7 +907,13 @@ static void setup_direction( } } -static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout, const struct dissect *dis, uint8_t *mod_pkt, size_t *len_mod_pkt) +static uint8_t dpi_desync_tcp_packet_play( + unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, + uint32_t fwmark, + const char *ifin, const char *ifout, + const t_ctrack_position *pos, + const struct dissect *dis, + uint8_t *mod_pkt, size_t *len_mod_pkt) { uint8_t verdict = VERDICT_PASS; @@ -972,7 +980,6 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in // in absence of conntrack guess direction by presence of interface names. won't work on BSD bReverseFixed = ctrack ? (bReverse ^ params.server) : (bReverse = ifin && ifin && (!ifout || !*ifout)); setup_direction(dis, bReverseFixed, &src, &dst, &sdip4, &sdip6, &sdport); - ifname = bReverse ? ifin : ifout; #ifdef HAS_FILTER_SSID ssid = wlan_ssid_search_ifname(ifname); @@ -992,7 +999,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in if (!hostname && !bReverse) { if (ipcache_get_hostname(sdip4, sdip6, host, sizeof(host), &hostname_is_ip) && *host) - if (!(hostname = ctrack_replay->hostname = strdup(host))) + if (!(hostname = ctrack->hostname = strdup(host))) DLOG_ERR("strdup(host): out of memory\n"); } } @@ -1061,7 +1068,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in // process reply packets for auto hostlist mode // by looking at RSTs or HTTP replies we decide whether original request looks like DPI blocked // we only process first-sequence replies. do not react to subsequent redirects or RSTs - if (!params.server && ctrack && ctrack->hostname && ctrack->hostname_ah_check && (ctrack->ack_last - ctrack->ack0) == 1) + if (!params.server && ctrack && ctrack->hostname && ctrack->hostname_ah_check && (ctrack->pos.ack_last - ctrack->pos.ack0) == 1) { bool bFail = false; @@ -1151,8 +1158,8 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in // we do not reassemble http if (!ctrack->req_seq_present) { - ctrack->req_seq_start = ctrack->seq_last; - ctrack->req_seq_end = ctrack->pos_orig - 1; + ctrack->req_seq_start = ctrack->pos.seq_last; + ctrack->req_seq_end = ctrack->pos.pos_orig - 1; ctrack->req_seq_present = ctrack->req_seq_finalized = true; DLOG("req retrans : tcp seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end); } @@ -1176,7 +1183,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in { // do not reasm retransmissions if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig) && !ctrack->req_seq_abandoned && - !(ctrack->req_seq_finalized && seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end))) + !(ctrack->req_seq_finalized && seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end))) { // do not reconstruct unexpected large payload (they are feeding garbage ?) if (!reasm_orig_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload)) @@ -1187,19 +1194,19 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in if (!ctrack->req_seq_present) { // lower bound of request seq interval - ctrack->req_seq_start = ctrack->seq_last; + ctrack->req_seq_start = ctrack->pos.seq_last; ctrack->req_seq_present = true; } // upper bound of request seq interval // it can grow on every packet until request is complete. then interval is finalized and never touched again. - ctrack->req_seq_end = ctrack->pos_orig - 1; + ctrack->req_seq_end = ctrack->pos.pos_orig - 1; DLOG("req retrans : seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end); ctrack->req_seq_finalized |= bReqFull; } if (!ReasmIsEmpty(&ctrack->reasm_orig)) { - if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &dst, fwmark, desync_fwmark, ifin, ifout)) + if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout)) { DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed)); } @@ -1217,7 +1224,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in } } } - else if (ctrack && (ctrack->seq_last - ctrack->seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload)) + else if (ctrack && (ctrack->pos.seq_last - ctrack->pos.seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload)) { DLOG("packet contains telegram mtproto2 initial\n"); // mtproto detection requires aes. react only on the first tcp data packet. do not detect if ctrack unavailable. @@ -1238,7 +1245,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in } if (ctrack && ctrack->req_seq_finalized) { - uint32_t dseq = ctrack->seq_last - ctrack->req_seq_end; + uint32_t dseq = ctrack->pos.seq_last - ctrack->req_seq_end; // do not react to 32-bit overflowed sequence numbers. allow 16 Mb grace window then cutoff. if (dseq >= 0x1000000 && !(dseq & 0x80000000)) ctrack->req_seq_abandoned = true; } @@ -1344,7 +1351,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2)); DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload)); } - verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0); + verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0); pass: return (!bReverseFixed && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict; @@ -1361,7 +1368,13 @@ static void quic_reasm_cancel(t_ctrack *ctrack, const char *reason) } -static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout, const struct dissect *dis, uint8_t *mod_pkt, size_t *len_mod_pkt) +static uint8_t dpi_desync_udp_packet_play( + unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, + uint32_t fwmark, + const char *ifin, const char *ifout, + const t_ctrack_position *pos, + const struct dissect *dis, + uint8_t *mod_pkt, size_t *len_mod_pkt) { uint8_t verdict = VERDICT_PASS; @@ -1465,7 +1478,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in if (!hostname && !bReverse) { if (ipcache_get_hostname(sdip4, sdip6, host, sizeof(host), &hostname_is_ip) && *host) - if (!(hostname = ctrack_replay->hostname = strdup(host))) + if (!(hostname = ctrack->hostname = strdup(host))) DLOG_ERR("strdup(host): out of memory\n"); } } @@ -1594,7 +1607,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in } if (!ReasmIsEmpty(&ctrack->reasm_orig)) { - if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &dst, fwmark, desync_fwmark, ifin, ifout)) + if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout)) { DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed)); } @@ -1634,7 +1647,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in if (!reasm_orig_start(ctrack, IPPROTO_UDP, UDP_MAX_REASM, UDP_MAX_REASM, clean, clean_len)) goto pass_reasm_cancel; } - if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &dst, fwmark, desync_fwmark, ifin, ifout)) + if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout)) { DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed)); } @@ -1793,7 +1806,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2)); DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload)); } - verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt); + verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt); pass: return (!bReverse && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict; @@ -1842,6 +1855,7 @@ static void packet_debug(bool replay, const struct dissect *dis) static uint8_t dpi_desync_packet_play( unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout, + const t_ctrack_position *pos, const uint8_t *data_pkt, size_t len_pkt, uint8_t *mod_pkt, size_t *len_mod_pkt) { @@ -1857,7 +1871,7 @@ static uint8_t dpi_desync_packet_play( case IPPROTO_TCP: if (dis.tcp) { - verdict = dpi_desync_tcp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, &dis, mod_pkt, len_mod_pkt); + verdict = dpi_desync_tcp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, pos, &dis, mod_pkt, len_mod_pkt); // we fix csum before pushing to replay queue if (!replay_piece_count) verdict_tcp_csum_fix(verdict, (struct tcphdr *)dis.tcp, dis.transport_len, dis.ip, dis.ip6); } @@ -1865,7 +1879,7 @@ static uint8_t dpi_desync_packet_play( case IPPROTO_UDP: if (dis.udp) { - verdict = dpi_desync_udp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, &dis, mod_pkt, len_mod_pkt); + verdict = dpi_desync_udp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, pos, &dis, mod_pkt, len_mod_pkt); // we fix csum before pushing to replay queue if (!replay_piece_count) verdict_udp_csum_fix(verdict, (struct udphdr *)dis.udp, dis.transport_len, dis.ip, dis.ip6); } @@ -1877,7 +1891,7 @@ static uint8_t dpi_desync_packet_play( uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, const uint8_t *data_pkt, size_t len_pkt, uint8_t *mod_pkt, size_t *len_mod_pkt) { ipcachePurgeRateLimited(¶ms.ipcache, params.ipcache_lifetime); - return dpi_desync_packet_play(0, 0, 0, fwmark, ifin, ifout, data_pkt, len_pkt, mod_pkt, len_mod_pkt); + return dpi_desync_packet_play(0, 0, 0, fwmark, ifin, ifout, NULL, data_pkt, len_pkt, mod_pkt, len_mod_pkt); } @@ -1895,7 +1909,7 @@ static bool replay_queue(struct rawpacket_tailhead *q) { DLOG("REPLAYING delayed packet #%u offset %zu\n", i+1, offset); modlen = sizeof(mod); - uint8_t verdict = dpi_desync_packet_play(i, count, offset, rp->fwmark_orig, rp->ifin, rp->ifout, rp->packet, rp->len, mod, &modlen); + uint8_t verdict = dpi_desync_packet_play(i, count, offset, rp->fwmark_orig, rp->ifin, rp->ifout, rp->pos_present ? &rp->pos : NULL, rp->packet, rp->len, mod, &modlen); switch (verdict & VERDICT_MASK) { case VERDICT_MODIFY: diff --git a/nfq2/lua.c b/nfq2/lua.c index abead75..32e3556 100644 --- a/nfq2/lua.c +++ b/nfq2/lua.c @@ -1167,21 +1167,23 @@ void lua_pushf_dissect(const struct dissect *dis) lua_rawset(params.L,-3); } -void lua_pushf_ctrack(const t_ctrack *ctrack) +void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos) { LUA_STACK_GUARD_ENTER(params.L) + if (!pos) pos = &ctrack->pos; + lua_pushliteral(params.L, "track"); if (ctrack) { lua_createtable(params.L, 0, 13 + (ctrack->ipproto == IPPROTO_TCP)); - lua_pushf_int("pcounter_orig", ctrack->pcounter_orig); - lua_pushf_int("pdcounter_orig", ctrack->pdcounter_orig); - lua_pushf_int("pbcounter_orig", ctrack->pbcounter_orig); - lua_pushf_int("pcounter_reply", ctrack->pcounter_reply); - lua_pushf_int("pdcounter_reply", ctrack->pdcounter_reply); - lua_pushf_int("pbcounter_reply", ctrack->pbcounter_reply); + lua_pushf_int("pcounter_orig", pos->pcounter_orig); + lua_pushf_int("pdcounter_orig", pos->pdcounter_orig); + lua_pushf_int("pbcounter_orig", pos->pbcounter_orig); + lua_pushf_int("pcounter_reply", pos->pcounter_reply); + lua_pushf_int("pdcounter_reply", pos->pdcounter_reply); + lua_pushf_int("pbcounter_reply", pos->pbcounter_reply); if (ctrack->incoming_ttl) lua_pushf_int("incoming_ttl", ctrack->incoming_ttl); else @@ -1197,20 +1199,20 @@ void lua_pushf_ctrack(const t_ctrack *ctrack) { lua_pushliteral(params.L, "tcp"); lua_createtable(params.L, 0, 14); - lua_pushf_int("seq0", ctrack->seq0); - lua_pushf_int("seq", ctrack->seq_last); - lua_pushf_int("ack0", ctrack->ack0); - lua_pushf_int("ack", ctrack->ack_last); - lua_pushf_int("pos_orig", ctrack->pos_orig - ctrack->seq0); - lua_pushf_int("winsize_orig", ctrack->winsize_orig); - lua_pushf_int("winsize_orig_calc", ctrack->winsize_orig_calc); - lua_pushf_int("scale_orig", ctrack->scale_orig); - lua_pushf_int("mss_orig", ctrack->mss_orig); - lua_pushf_int("pos_reply", ctrack->pos_reply - ctrack->ack0); - lua_pushf_int("winsize_reply", ctrack->winsize_reply); - lua_pushf_int("winsize_reply_calc", ctrack->winsize_reply_calc); - lua_pushf_int("scale_reply", ctrack->scale_reply); - lua_pushf_int("mss_reply", ctrack->mss_reply); + lua_pushf_int("seq0", pos->seq0); + lua_pushf_int("seq", pos->seq_last); + lua_pushf_int("ack0", pos->ack0); + lua_pushf_int("ack", pos->ack_last); + lua_pushf_int("pos_orig", pos->pos_orig - pos->seq0); + lua_pushf_int("winsize_orig", pos->winsize_orig); + lua_pushf_int("winsize_orig_calc", pos->winsize_orig_calc); + lua_pushf_int("scale_orig", pos->scale_orig); + lua_pushf_int("mss_orig", pos->mss_orig); + lua_pushf_int("pos_reply", pos->pos_reply - pos->ack0); + lua_pushf_int("winsize_reply", pos->winsize_reply); + lua_pushf_int("winsize_reply_calc", pos->winsize_reply_calc); + lua_pushf_int("scale_reply", pos->scale_reply); + lua_pushf_int("mss_reply", pos->mss_reply); lua_rawset(params.L,-3); } } diff --git a/nfq2/lua.h b/nfq2/lua.h index b3ec0a9..974b432 100644 --- a/nfq2/lua.h +++ b/nfq2/lua.h @@ -71,7 +71,7 @@ void lua_pushf_iphdr(const struct ip *ip, size_t len); void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len); void lua_push_dissect(const struct dissect *dis); void lua_pushf_dissect(const struct dissect *dis); -void lua_pushf_ctrack(const t_ctrack *ctrack); +void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos); void lua_pushf_args(const struct ptr_list_head *args, int idx_desync); void lua_pushf_pos(const char *name, const struct packet_pos *pos); void lua_pushf_range(const char *name, const struct packet_range *range); diff --git a/nfq2/packet_queue.c b/nfq2/packet_queue.c index 950ec66..7a7a3ec 100644 --- a/nfq2/packet_queue.c +++ b/nfq2/packet_queue.c @@ -26,7 +26,7 @@ void rawpacket_queue_destroy(struct rawpacket_tailhead *q) while((rp = rawpacket_dequeue(q))) rawpacket_free(rp); } -struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload) +struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_position *pos) { struct rawpacket *rp = malloc(sizeof(struct rawpacket)); if (!rp) return NULL; @@ -52,6 +52,15 @@ struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sock memcpy(rp->packet,data,len); rp->len=len; rp->len_payload=len_payload; + + // make a copy for replay + if (pos) + { + rp->pos = *pos; + rp->pos_present = true; + } + else + rp->pos_present = false; TAILQ_INSERT_TAIL(q, rp, next); diff --git a/nfq2/packet_queue.h b/nfq2/packet_queue.h index 0db3d01..92484e4 100644 --- a/nfq2/packet_queue.h +++ b/nfq2/packet_queue.h @@ -6,6 +6,8 @@ #include #include +#include "conntrack_base.h" + struct rawpacket { struct sockaddr_storage dst; @@ -14,6 +16,8 @@ struct rawpacket uint32_t fwmark; size_t len, len_payload; uint8_t *packet; + t_ctrack_position pos; + bool pos_present; TAILQ_ENTRY(rawpacket) next; }; TAILQ_HEAD(rawpacket_tailhead, rawpacket); @@ -22,6 +26,6 @@ void rawpacket_queue_init(struct rawpacket_tailhead *q); void rawpacket_queue_destroy(struct rawpacket_tailhead *q); bool rawpacket_queue_empty(const struct rawpacket_tailhead *q); unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q); -struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload); +struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_position *pos); struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q); void rawpacket_free(struct rawpacket *rp);