Host is now extracted and blocked immediately

This commit also introduces the pkt_context_t to remove the need to store a
temporary cur_pkt, reducing chance to introduce bugs in VPN mode.
This commit is contained in:
emanuele-f
2021-12-07 16:50:34 +01:00
parent a48514db65
commit 28ff23e26e
7 changed files with 210 additions and 215 deletions
+31 -31
View File
@@ -271,67 +271,66 @@ static void remove_connection(pcapdroid_t *pd, pcap_conn_t *conn) {
// Determines when a connection gets closed
static void update_connection_status(pcapdroid_t *nc, pcap_conn_t *conn, zdtun_pkt_t *pkt, uint8_t dir) {
// NOTE: pcap_conn_t neeeded below in remove_connection
if((conn->data->status >= CONN_STATUS_CLOSED) || (pkt->flags & ZDTUN_PKT_IS_FRAGMENT))
return;
zdtun_conn_status_t old_status = conn->data->status;
zdtun_5tuple_t *tuple = &conn->tuple;
pd_conn_t *data = conn->data;
if(conn->tuple.ipproto == IPPROTO_TCP) {
if(tuple->ipproto == IPPROTO_TCP) {
struct tcphdr *tcp = pkt->tcp;
conn->data->tcp_flags[dir] |= tcp->th_flags;
uint8_t seen_flags = conn->data->tcp_flags[0] & conn->data->tcp_flags[1];
data->tcp_flags[dir] |= tcp->th_flags;
uint8_t seen_flags = data->tcp_flags[0] & data->tcp_flags[1];
if(tcp->th_flags & TH_RST)
conn->data->status = CONN_STATUS_RESET;
data->status = CONN_STATUS_RESET;
else if(seen_flags & TH_FIN) {
// closed when both the peers have sent FIN and the last FIN was acknowledged
if(!conn->data->last_ack)
conn->data->last_ack = true; // wait for the last ACK
if(!data->last_ack)
data->last_ack = true; // wait for the last ACK
else if(tcp->th_flags & TH_ACK)
conn->data->status = CONN_STATUS_CLOSED;
} else if(conn->data->status < CONN_STATUS_CONNECTED) {
data->status = CONN_STATUS_CLOSED;
} else if(data->status < CONN_STATUS_CONNECTED) {
const uint8_t syn_ack_flags = TH_SYN | TH_ACK;
// the 3-way-handshake is complete when both the peers have sent the SYN+ACK flags
if((pkt->l7_len > 0) ||
((seen_flags & syn_ack_flags) == syn_ack_flags))
conn->data->status = CONN_STATUS_CONNECTED;
data->status = CONN_STATUS_CONNECTED;
else
conn->data->status = CONN_STATUS_CONNECTING;
data->status = CONN_STATUS_CONNECTING;
}
} else {
if(conn->data->status < CONN_STATUS_CONNECTED)
conn->data->status = CONN_STATUS_CONNECTED;
if(data->status < CONN_STATUS_CONNECTED)
data->status = CONN_STATUS_CONNECTED;
if((conn->tuple.ipproto == IPPROTO_UDP) &&
if((tuple->ipproto == IPPROTO_UDP) &&
pkt->l7_len >= sizeof(dns_packet_t) &&
(conn->tuple.dst_port == ntohs(53))) {
(tuple->dst_port == ntohs(53))) {
const dns_packet_t *dns = (dns_packet_t *)pkt->l7;
if((dns->flags & DNS_FLAGS_MASK) == DNS_TYPE_REQUEST)
conn->data->pending_dns_queries++;
data->pending_dns_queries++;
else if((dns->flags & DNS_FLAGS_MASK) == DNS_TYPE_RESPONSE) {
conn->data->pending_dns_queries--;
data->pending_dns_queries--;
// Close the connection as soon as all the responses arrive
if(conn->data->pending_dns_queries == 0) {
conn->data->status = CONN_STATUS_CLOSED;
if(data->pending_dns_queries == 0) {
data->status = CONN_STATUS_CLOSED;
// Remove the connection from the hash to ensure that if the DNS connection is
// reused for a new query, it will generated a new connection, to properly
// extract and handle the new DNS query. This also happens for AAAA + A queries.
conn->data->to_purge = true;
data->to_purge = true;
remove_connection(nc, conn);
}
}
}
}
if(old_status != conn->data->status) {
conn->data->update_type |= CONN_UPDATE_STATS;
pd_notify_connection_update(nc, &conn->tuple, conn->data);
}
// no need to call pd_notify_connection_update, it will be called as part of pd_account_stats
}
/* ******************************************************* */
@@ -346,9 +345,6 @@ static void handle_packet(pcapdroid_t *pd, pcapd_hdr_t *hdr, const char *buffer)
return;
}
struct timeval tv = hdr->ts;
pd_set_current_packet(pd, &pkt, is_tx, &tv);
if((pkt.flags & ZDTUN_PKT_IS_FRAGMENT) &&
(pkt.tuple.src_port == 0) && (pkt.tuple.dst_port == 0)) {
// This fragment cannot be mapped to the original src/dst ports. This may happen if the first
@@ -366,7 +362,6 @@ static void handle_packet(pcapdroid_t *pd, pcapd_hdr_t *hdr, const char *buffer)
}
HASH_FIND(hh, pd->root.connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
if(!conn) {
// is_tx may be wrong, search in the other direction
is_tx = !is_tx;
@@ -419,10 +414,15 @@ static void handle_packet(pcapdroid_t *pd, pcapd_hdr_t *hdr, const char *buffer)
}
}
conn->data->last_update_ms = pd->now_ms;
// like last_seen but monotonic
conn->data->root.last_update_ms = pd->now_ms;
struct timeval tv = hdr->ts;
pkt_context_t pinfo;
pd_process_packet(pd, &pkt, is_tx, &conn->tuple, conn->data, &tv, &pinfo);
pd_account_stats(pd, &conn->tuple, conn->data);
update_connection_status(pd, conn, &pkt, !is_tx);
pd_account_stats(pd, &pinfo);
}
/* ******************************************************* */
@@ -445,7 +445,7 @@ static void purge_expired_connections(pcapdroid_t *pd, uint8_t purge_all) {
break;
}
if(purge_all || (pd->now_ms >= (conn->data->last_update_ms + timeout))) {
if(purge_all || (pd->now_ms >= (conn->data->root.last_update_ms + timeout))) {
//log_d("IDLE (type=%d)", conn->tuple.ipproto);
// The connection data will be purged
+56 -57
View File
@@ -86,36 +86,48 @@ static struct timeval* get_pkt_timestamp(pcapdroid_t *pd, struct timeval *tv) {
return tv;
}
// use the last pkt timestamp
log_w("clock_gettime failed[%d]: %s", errno, strerror(errno));
return &pd->cur_pkt.tv;
return tv;
}
/* ******************************************************* */
static int net2tun(zdtun_t *zdt, zdtun_pkt_t *pkt, const zdtun_conn_t *conn_info) {
static int remote2vpn(zdtun_t *zdt, zdtun_pkt_t *pkt, const zdtun_conn_t *conn_info) {
if(!running)
// e.g. during zdtun_finalize
return 0;
pcapdroid_t *pd = (pcapdroid_t*) zdtun_userdata(zdt);
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn_info);
pd_conn_t *data = zdtun_conn_get_userdata(conn_info);
struct timeval tv;
pd_refresh_time(pd);
pd_set_current_packet(pd, pkt, false, get_pkt_timestamp(pd, &tv));
// if this is called inside zdtun_forward, account the egress packet first
if(data->vpn.fw_pctx) {
pd_account_stats(pd, data->vpn.fw_pctx);
data->vpn.fw_pctx = NULL;
}
if(data->to_block) // NOTE: blocked_pkts accounted in pd_account_stats
return 0;
struct timeval tv;
pkt_context_t pctx;
pd_refresh_time(pd);
pd_process_packet(pd, pkt, false, tuple, data, get_pkt_timestamp(pd, &tv), &pctx);
if(data->to_block) {
data->blocked_pkts++;
// Returning -1 will result into an error condition on the connection, forcing a connection
// close. Closing the connection is mandatory as it's not possible to handle dropped packets
// via zdtun, since data received via the zdtun TCP sockets must be delivered to the client.
return -1;
}
int rv = write(pd->vpn.tunfd, pkt->buf, pkt->len);
if(rv < 0) {
if(errno == ENOBUFS) {
char buf[256];
// Do not abort, the connection will be terminated
log_e("Got ENOBUFS %s", zdtun_5tuple2str(zdtun_conn_get_5tuple(conn_info), buf, sizeof(buf)));
log_e("Got ENOBUFS %s", zdtun_5tuple2str(tuple, buf, sizeof(buf)));
} else if(errno == EIO) {
log_i("Got I/O error (terminating?)");
running = false;
@@ -126,31 +138,25 @@ static int net2tun(zdtun_t *zdt, zdtun_pkt_t *pkt, const zdtun_conn_t *conn_info
} else if(rv != pkt->len) {
log_f("partial zdt write (%d / %d)", rv, pkt->len);
rv = -1;
} else
} else {
// Success
rv = 0;
pd_account_stats(pd, &pctx);
}
return rv;
}
/* ******************************************************* */
static void check_socks5_redirection(pcapdroid_t *pd, zdtun_pkt_t *pkt, zdtun_conn_t *conn) {
pd_conn_t *data = zdtun_conn_get_userdata(conn);
if((pkt->tuple.ipproto == IPPROTO_TCP) && (((data->sent_pkts + data->rcvd_pkts) == 0)))
zdtun_conn_proxy(conn);
}
/* ******************************************************* */
/*
* If the packet contains a DNS request directed to the IP address used internally by PCAPdroid,
* then rewrite the server address with the actual DNS server.
* Moreover, if a private DNS connection is detected in opportunistic mode (block_private_dns true),
* then block this connection to force the fallback to non-private DNS mode.
*/
static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn) {
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn);
static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn, pkt_context_t *pctx) {
const zdtun_5tuple_t *tuple = pctx->tuple;
if(new_dns_server != 0) {
// Reload DNS server
@@ -164,7 +170,7 @@ static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn) {
log_d("Using new DNS server");
}
if(zdtun_conn_get_5tuple(conn)->ipproto == IPPROTO_ICMP)
if(pctx->tuple->ipproto == IPPROTO_ICMP)
return true;
bool is_internal_dns = (tuple->ipver == 4) && (tuple->dst_ip.ip4 == pd->vpn.internal_dns);
@@ -199,7 +205,7 @@ static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn) {
return(true);
if((tuple->ipproto == IPPROTO_UDP) && (ntohs(tuple->dst_port) == 53)) {
zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
zdtun_pkt_t *pkt = pctx->pkt;
int dns_length = pkt->l7_len;
if(dns_length >= sizeof(dns_packet_t)) {
@@ -245,7 +251,6 @@ static int handle_new_connection(zdtun_t *zdt, zdtun_conn_t *conn_info) {
}
zdtun_conn_set_userdata(conn_info, data);
data->to_block = !check_dns_req_allowed(pd, conn_info);
/* accept connection */
return(0);
@@ -276,30 +281,13 @@ static void destroy_connection(zdtun_t *zdt, const zdtun_conn_t *conn_info) {
/* ******************************************************* */
static void on_packet(zdtun_t *zdt, const zdtun_pkt_t *pkt, uint8_t from_tun, const zdtun_conn_t *conn_info) {
if(!running)
// e.g. during zdtun_finalize
return;
pcapdroid_t *pd = ((pcapdroid_t*)zdtun_userdata(zdt));
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn_info);
// This is called after remote2vpn or zdtun_forward
// No need to call pd_notify_connection_update, pd_account_stats is executed before
static void update_conn_status(zdtun_t *zdt, const zdtun_pkt_t *pkt, uint8_t from_tun, const zdtun_conn_t *conn_info) {
pd_conn_t *data = zdtun_conn_get_userdata(conn_info);
if(!data) {
log_e("Missing data in connection");
return;
}
// Update the connection status
data->status = zdtun_conn_get_status(conn_info);
if(data->to_block) {
data->blocked_pkts++;
data->last_seen = pd->cur_pkt.ms;
return;
}
pd_account_stats(pd, tuple, data);
if(data->status >= CONN_STATUS_CLOSED)
data->to_purge = true;
}
@@ -326,8 +314,8 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
pd->vpn.known_dns_servers = ndpi_ptree_create();
zdtun_callbacks_t callbacks = {
.send_client = net2tun,
.account_packet = on_packet,
.send_client = remote2vpn,
.account_packet = update_conn_status,
.on_socket_open = protectSocketCallback,
.on_connection_open = handle_new_connection,
.on_connection_close = destroy_connection,
@@ -406,9 +394,6 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
goto housekeeping;
}
struct timeval tv;
pd_set_current_packet(pd, &pkt, true, get_pkt_timestamp(pd, &tv));
if((pkt.tuple.ipver == 6) && (!pd->ipv6.enabled)) {
char buf[512];
@@ -438,18 +423,28 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
goto housekeeping;
}
// Process the packet
struct timeval tv;
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn);
pkt_context_t pctx;
pd_conn_t *data = zdtun_conn_get_userdata(conn);
pd_process_packet(pd, &pkt, true, tuple, data, get_pkt_timestamp(pd, &tv), &pctx);
data->vpn.fw_pctx = &pctx;
if(data->sent_pkts == 0) {
data->to_block |= !check_dns_req_allowed(pd, conn, &pctx);
if(pd->socks5.enabled && !data->to_block && (tuple->ipproto == IPPROTO_TCP))
zdtun_conn_proxy(conn);
}
if(data->to_block) {
data->blocked_pkts++;
data->last_seen = pd->cur_pkt.ms;
if(!data->first_seen)
data->first_seen = data->last_seen;
goto housekeeping;
}
if(pd->socks5.enabled)
check_socks5_redirection(pd, &pkt, conn);
// NOTE: zdtun_forward may cause nested calls to remote2vpn
if(zdtun_forward(zdt, &pkt, conn) != 0) {
char buf[512];
@@ -459,6 +454,10 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
pd->num_dropped_connections++;
zdtun_destroy_conn(zdt, conn);
goto housekeeping;
} else if(data->vpn.fw_pctx) {
// not accounted in remote2vpn, account here
pd_account_stats(pd, data->vpn.fw_pctx);
data->vpn.fw_pctx = NULL;
}
} else {
pd_refresh_time(pd);
+5 -5
View File
@@ -66,13 +66,13 @@ int pcap_rec_size(int pkt_len) {
/* Dumps a packet into the provided buffer. The buffer must have at least pcap_rec_size()
* bytes available */
void pcap_dump_rec(u_char *buffer, pcapdroid_t *pd, pd_conn_t *conn) {
const zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
void pcap_dump_rec(pcapdroid_t *pd, u_char *buffer, pkt_context_t *pctx) {
const zdtun_pkt_t *pkt = pctx->pkt;
struct pcaprec_hdr_s *pcap_rec = (pcaprec_hdr_s*) buffer;
int offset = 0;
pcap_rec->ts_sec = pd->cur_pkt.tv.tv_sec;
pcap_rec->ts_usec = pd->cur_pkt.tv.tv_usec;
pcap_rec->ts_sec = pctx->tv.tv_sec;
pcap_rec->ts_usec = pctx->tv.tv_usec;
pcap_rec->incl_len = pcap_rec_size(pkt->len) - (int)sizeof(struct pcaprec_hdr_s);
pcap_rec->orig_len = pkt->len;
buffer += sizeof(struct pcaprec_hdr_s);
@@ -107,7 +107,7 @@ void pcap_dump_rec(u_char *buffer, pcapdroid_t *pd, pd_conn_t *conn) {
// Populate the custom data
pcapdroid_trailer_t *cdata = (pcapdroid_trailer_t*)(buffer + offset);
fill_custom_data(cdata, pd, conn);
fill_custom_data(cdata, pd, pctx->data);
//clock_t start = clock();
cdata->fcs = crc32(buffer, pcap_rec->incl_len - 4, 0);
+1 -1
View File
@@ -60,6 +60,6 @@ typedef struct pcapdroid_trailer {
void pcap_set_pcapdroid_trailer(uint8_t enabled);
void pcap_build_hdr(struct pcap_hdr_s *pcap_hdr);
int pcap_rec_size(int pkt_len);
void pcap_dump_rec(u_char *buffer, pcapdroid_t *pd, pd_conn_t *conn);
void pcap_dump_rec(pcapdroid_t *pd, u_char *buffer, pkt_context_t *pctx);
#endif // __MY_PCAP_H__
+94 -99
View File
@@ -348,7 +348,6 @@ static void check_blacklisted_domain(pcapdroid_t *pd, pd_conn_t *data, const zdt
pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int uid) {
pd_conn_t *data = pd_calloc(1, sizeof(pd_conn_t));
if(!data) {
log_e("calloc(pd_conn_t) failed with code %d/%s",
errno, strerror(errno));
@@ -386,6 +385,7 @@ pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int u
inet_ntop(family, &dst_ip, resip, sizeof(resip));
log_d("Host LRU cache HIT: %s -> %s", resip, data->info);
data->info_from_lru = true;
if(data->uid != UID_UNKNOWN) {
// When a DNS request is followed by a TLS connection or similar, mark the DNS request
@@ -477,6 +477,44 @@ static bool is_numeric_host(const char *host) {
/* ******************************************************* */
static void process_ndpi_data(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn_t *data) {
char *found_info = NULL;
switch(data->l7proto.master_protocol) {
case NDPI_PROTOCOL_DNS:
if(data->ndpi_flow->host_server_name[0])
found_info = (char*)data->ndpi_flow->host_server_name;
break;
case NDPI_PROTOCOL_HTTP:
if(data->ndpi_flow->host_server_name[0] &&
!is_numeric_host((char*)data->ndpi_flow->host_server_name))
found_info = (char*)data->ndpi_flow->host_server_name;
if(!data->url && data->ndpi_flow->http.url) {
data->url = pd_strndup(data->ndpi_flow->http.url, 256);
data->update_type |= CONN_UPDATE_INFO;
}
break;
case NDPI_PROTOCOL_TLS:
if(data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name[0])
found_info = (char*)data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name;
break;
}
if(found_info && (!data->info || data->info_from_lru)) {
if(data->info)
pd_free(data->info);
data->info = pd_strndup(found_info, 256);
data->info_from_lru = false;
check_blacklisted_domain(pd, data, tuple);
data->update_type |= CONN_UPDATE_INFO;
}
}
/* ******************************************************* */
/* Stop the DPI detection and determine the l7proto of the connection. */
void pd_giveup_dpi(pcapdroid_t *pd, pd_conn_t *data, const zdtun_5tuple_t *tuple) {
if(!data->ndpi_flow)
@@ -495,40 +533,7 @@ void pd_giveup_dpi(pcapdroid_t *pd, pd_conn_t *data, const zdtun_5tuple_t *tuple
log_d("nDPI completed[ipver=%d, proto=%d] -> l7proto: app=%d, master=%d",
tuple->ipver, tuple->ipproto, data->l7proto.app_protocol, data->l7proto.master_protocol);
switch (data->l7proto.master_protocol) {
case NDPI_PROTOCOL_DNS:
if(data->ndpi_flow->host_server_name[0]) {
if(data->info)
pd_free(data->info);
data->info = pd_strndup((char*)data->ndpi_flow->host_server_name, 256);
}
break;
case NDPI_PROTOCOL_HTTP:
if(data->ndpi_flow->host_server_name[0] &&
(!data->info || !is_numeric_host((char*)data->ndpi_flow->host_server_name))) {
if(data->info)
pd_free(data->info);
data->info = pd_strndup((char*) data->ndpi_flow->host_server_name, 256);
}
if(data->ndpi_flow->http.url)
data->url = pd_strndup(data->ndpi_flow->http.url, 256);
break;
case NDPI_PROTOCOL_TLS:
if(data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name[0]) {
if(data->info)
pd_free(data->info);
data->info = pd_strndup(data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name, 256);
}
break;
}
// TODO early match
check_blacklisted_domain(pd, data, tuple);
data->update_type |= CONN_UPDATE_INFO;
process_ndpi_data(pd, tuple, data);
conn_free_ndpi(data);
}
@@ -540,11 +545,12 @@ static int is_plaintext(char c) {
/* ******************************************************* */
static void process_request_data(pcapdroid_t *pd, pd_conn_t *data) {
const zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
static void process_request_data(pcapdroid_t *pd, pkt_context_t *pctx) {
const zdtun_pkt_t *pkt = pctx->pkt;
pd_conn_t *data = pctx->data;
if(pkt->l7_len > 0) {
if(pd->cur_pkt.is_tx && is_plaintext(pkt->l7[0])) {
if(pctx->is_tx && is_plaintext(pkt->l7[0])) {
int request_len = data->request_data ? (int)strlen(data->request_data) : 0;
int num_chars = min(MAX_PLAINTEXT_LENGTH - request_len, pkt->l7_len);
@@ -657,10 +663,11 @@ static void process_dns_reply(pd_conn_t *data, pcapdroid_t *pd, const struct zdt
/* ******************************************************* */
static void process_ndpi_packet(pcapdroid_t *pd, pd_conn_t *data) {
bool giveup = ((data->sent_pkts + data->rcvd_pkts) >= MAX_DPI_PACKETS);
zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
bool is_tx = pd->cur_pkt.is_tx;
static void perform_dpi(pcapdroid_t *pd, pkt_context_t *pctx) {
pd_conn_t *data = pctx->data;
bool giveup = ((data->sent_pkts + data->rcvd_pkts + 1) >= MAX_DPI_PACKETS);
zdtun_pkt_t *pkt = pctx->pkt;
bool is_tx = pctx->is_tx;
u_int16_t old_proto = data->l7proto.master_protocol;
data->l7proto = ndpi_detection_process_packet(pd->ndpi, data->ndpi_flow, (const u_char *)pkt->buf,
@@ -672,7 +679,7 @@ static void process_ndpi_packet(pcapdroid_t *pd, pd_conn_t *data) {
data->update_type |= CONN_UPDATE_INFO;
if((!data->request_done) && !data->ndpi_flow->packet.tcp_retransmission)
process_request_data(pd, data);
process_request_data(pd, pctx);
bool is_dns = ((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS) ||
(data->l7proto.app_protocol == NDPI_PROTOCOL_DNS));
@@ -681,7 +688,22 @@ static void process_ndpi_packet(pcapdroid_t *pd, pd_conn_t *data) {
if(giveup || ((data->l7proto.app_protocol != NDPI_PROTOCOL_UNKNOWN) &&
!ndpi_extra_dissection_possible(pd->ndpi, data->ndpi_flow)))
pd_giveup_dpi(pd, data, &pkt->tuple);
pd_giveup_dpi(pd, data, &pkt->tuple); // calls process_ndpi_data
else
process_ndpi_data(pd, &pkt->tuple, data);
if(((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS) || (data->l7proto.app_protocol == NDPI_PROTOCOL_DNS))
&& (data->uid == UID_NETD)
&& (data->sent_pkts + data->rcvd_pkts == 0)
&& ((netd_resolve_waiting > 0) || ((next_connections_dump - NETD_RESOLVE_DELAY_MS) < pd->now_ms))) {
if(netd_resolve_waiting == 0) {
// Wait before sending the dump to possibly resolve netd DNS connections uid.
// Only delay for the first DNS request, to avoid excessive delay.
log_d("Adding netd resolution delay");
next_connections_dump += NETD_RESOLVE_DELAY_MS;
}
netd_resolve_waiting++;
}
}
/* ******************************************************* */
@@ -1178,12 +1200,8 @@ static int check_blocked_conn_cb(zdtun_t *zdt, const zdtun_conn_t *conn_info, vo
/* ******************************************************* */
/* Perfom periodic tasks. This should be called after processing a packet or after some time has
* passed (e.g. after a select with no packet). pd->cur_pkt.pkt is invalidated after this call. */
* passed (e.g. after a select with no packet). */
void pd_housekeeping(pcapdroid_t *pd) {
// can reach housekeeping from any state except housekeeping
assert(pd->pkt_phase != PKT_PHASE_HOUSEKEEPING);
pd->pkt_phase = PKT_PHASE_HOUSEKEEPING;
if(pd->capture_stats.new_stats
&& ((pd->now_ms - pd->capture_stats.last_update_ms) >= CAPTURE_STATS_UPDATE_FREQUENCY_MS) ||
dump_capture_stats_now) {
@@ -1233,9 +1251,6 @@ void pd_housekeeping(pcapdroid_t *pd) {
if(pd->zdt)
zdtun_iter_connections(pd->zdt, check_blocked_conn_cb, pd);
}
// avoid using freed data
pd->cur_pkt.pkt = NULL;
}
/* ******************************************************* */
@@ -1244,9 +1259,6 @@ void pd_housekeeping(pcapdroid_t *pd) {
void pd_refresh_time(pcapdroid_t *pd) {
struct timespec ts;
// can be reached by any phase
pd->pkt_phase = PKT_PHASE_REFRESH_TIME;
if(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts)) {
log_d("clock_gettime failed[%d]: %s", errno, strerror(errno));
return;
@@ -1286,32 +1298,35 @@ void fill_custom_data(struct pcapdroid_trailer *cdata, pcapdroid_t *pd, pd_conn_
/* ******************************************************* */
/* Set the current packet data. The packet pointer is available via pd->cur_pkt.pkt until the next
* call to pd_housekeeping. This must be called after pd_refresh_time. */
void pd_set_current_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, struct timeval *tv) {
assert(pd->pkt_phase == PKT_PHASE_REFRESH_TIME);
pd->pkt_phase = PKT_PHASE_PKT_SET;
/* Process the packet (e.g. perform DPI) and fill the packet context. */
void pd_process_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, const zdtun_5tuple_t *tuple,
pd_conn_t *data, struct timeval *tv, pkt_context_t *pctx) {
pctx->pkt = pkt;
pctx->tv = *tv;
pctx->ms = (uint64_t)tv->tv_sec * 1000 + tv->tv_usec / 1000;
pctx->is_tx = is_tx;
pctx->tuple = tuple;
pctx->data = data;
pd->cur_pkt.pkt = pkt;
pd->cur_pkt.tv = *tv;
pd->cur_pkt.ms = (uint64_t)tv->tv_sec * 1000 + tv->tv_usec / 1000;
pd->cur_pkt.is_tx = is_tx;
data->last_seen = pctx->ms;
if(!data->first_seen)
data->first_seen = data->last_seen;
if(data->ndpi_flow &&
(!(pkt->flags & ZDTUN_PKT_IS_FRAGMENT) || (pkt->flags & ZDTUN_PKT_IS_FIRST_FRAGMENT))) {
// nDPI cannot handle fragments, since they miss the L4 layer (see ndpi_iph_is_valid_and_not_fragmented)
perform_dpi(pd, pctx);
}
}
/* ******************************************************* */
/* Update the stats for the current packet. */
void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn_t *data) {
zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
/* Update the stats for the current packet and dump it if requested. */
void pd_account_stats(pcapdroid_t *pd, pkt_context_t *pctx) {
zdtun_pkt_t *pkt = pctx->pkt;
pd_conn_t *data = pctx->data;
assert(pd->pkt_phase == PKT_PHASE_PKT_SET);
pd->pkt_phase = PKT_PHASE_ACCOUNT_STATS;
data->last_seen = pd->cur_pkt.ms;
if(!data->first_seen)
data->first_seen = data->last_seen;
if(pd->cur_pkt.is_tx) {
if(pctx->is_tx) {
data->sent_pkts++;
data->sent_bytes += pkt->len;
pd->capture_stats.sent_pkts++;
@@ -1323,32 +1338,13 @@ void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn
pd->capture_stats.rcvd_bytes += pkt->len;
}
if(data->ndpi_flow &&
(!(pkt->flags & ZDTUN_PKT_IS_FRAGMENT) || (pkt->flags & ZDTUN_PKT_IS_FIRST_FRAGMENT))) {
// nDPI cannot handle fragments, since they miss the L4 layer (see ndpi_iph_is_valid_and_not_fragmented)
process_ndpi_packet(pd, data);
if(((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS) || (data->l7proto.app_protocol == NDPI_PROTOCOL_DNS))
&& (data->uid == UID_NETD)
&& (data->sent_pkts + data->rcvd_pkts == 1)
&& ((netd_resolve_waiting > 0) || ((next_connections_dump - NETD_RESOLVE_DELAY_MS) < pd->now_ms))) {
if(netd_resolve_waiting == 0) {
// Wait before sending the dump to possibly resolve netd DNS connections uid.
// Only delay for the first DNS request, to avoid excessive delay.
log_d("Adding netd resolution delay");
next_connections_dump += NETD_RESOLVE_DELAY_MS;
}
netd_resolve_waiting++;
}
}
/* New stats to notify */
pd->capture_stats.new_stats = true;
data->update_type |= CONN_UPDATE_STATS;
pd_notify_connection_update(pd, conn_tuple, data);
pd_notify_connection_update(pd, pctx->tuple, pctx->data);
if (pd->pcap_dump.buffer) {
if(pd->pcap_dump.buffer) {
int rec_size = pcap_rec_size(pkt->len);
if ((JAVA_PCAP_BUFFER_SIZE - pd->pcap_dump.buffer_idx) <= rec_size) {
@@ -1360,8 +1356,8 @@ void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn
log_e("Invalid buffer size [size=%d, idx=%d, tot_size=%d]",
JAVA_PCAP_BUFFER_SIZE, pd->pcap_dump.buffer_idx, rec_size);
else {
pcap_dump_rec((u_char *) pd->pcap_dump.buffer + pd->pcap_dump.buffer_idx,
pd, data);
pcap_dump_rec(pd, (u_char *) pd->pcap_dump.buffer + pd->pcap_dump.buffer_idx,
pctx);
pd->pcap_dump.buffer_idx += rec_size;
}
@@ -1486,7 +1482,6 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
memset(&pd.stats, 0, sizeof(pd.stats));
pd.pkt_phase = PKT_PHASE_HOUSEKEEPING;
pd_refresh_time(&pd);
last_connections_dump = pd.now_ms;
next_connections_dump = last_connections_dump + 500 /* first update after 500 ms */;
+22 -21
View File
@@ -57,15 +57,6 @@ typedef struct {
u_int64_t last_update_ms;
} capture_stats_t;
// This tracks the packet processing phases, to ensure that the API functions are called in
// the correct order.
typedef enum {
PKT_PHASE_REFRESH_TIME = 0, // pd_refresh_time
PKT_PHASE_PKT_SET, // pd_set_current_packet
PKT_PHASE_ACCOUNT_STATS, // pd_account_stats
PKT_PHASE_HOUSEKEEPING, // pd_housekeeping
} pkt_processing_phase_t;
typedef struct {
jint incr_id; // an incremental number which identifies a specific connection
@@ -74,7 +65,15 @@ typedef struct {
struct ndpi_id_struct *src_id, *dst_id;
ndpi_protocol l7proto;
uint64_t last_update_ms; // like last_seen but monotonic (only root)
union {
struct {
uint64_t last_update_ms; // like last_seen but monotonic
} root;
struct {
struct pkt_context *fw_pctx; // context for the forwarded packet
} vpn;
};
jlong first_seen;
jlong last_seen;
jlong sent_bytes;
@@ -92,6 +91,7 @@ typedef struct {
};
bool pending_notification;
bool to_purge;
bool info_from_lru;
bool request_done;
bool blacklisted_ip;
bool blacklisted_domain;
@@ -118,13 +118,21 @@ typedef struct {
UT_hash_handle hh;
} uid_to_app_t;
typedef struct pkt_context {
zdtun_pkt_t *pkt;
struct timeval tv; // Packet timestamp, need by pcap_dump_rec
uint64_t ms; // Packet timestamp in ms
bool is_tx;
const zdtun_5tuple_t *tuple;
pd_conn_t *data;
} pkt_context_t;
typedef struct pcap_conn pcap_conn_t;
typedef struct {
JNIEnv *env;
jobject capture_service;
jint sdk_ver;
pkt_processing_phase_t pkt_phase;
int new_conn_id;
uint64_t now_ms; // Monotonic timestamp, see pd_refresh_time
struct ndpi_detection_module_struct *ndpi;
@@ -164,14 +172,6 @@ typedef struct {
} root;
};
// populated via pd_set_current_packet
struct {
zdtun_pkt_t *pkt;
struct timeval tv; // Packet timestamp, need by pcap_dump_rec
uint64_t ms; // Packet timestamp in ms
bool is_tx;
} cur_pkt;
struct {
bool enabled;
// the crc32 implementation requires 4-bytes aligned accesses.
@@ -270,8 +270,9 @@ extern bool block_private_dns;
// capture API
void pd_refresh_time(pcapdroid_t *pd);
void pd_set_current_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, struct timeval *tv);
void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn_t *data);
void pd_process_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, const zdtun_5tuple_t *tuple,
pd_conn_t *data, struct timeval *tv, pkt_context_t *pctx);
void pd_account_stats(pcapdroid_t *pd, pkt_context_t *pctx);
void pd_housekeeping(pcapdroid_t *pd);
pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int uid);
void pd_destroy_connection(pd_conn_t *data);