Fix bogus connections with root due to fragments

Connections with random ports sometimes appeared while capturing packets
as root, in particular while exporting data via the UDP exporter. These
were actually IP fragments, whose data was misinterpreted due to the
lack of L4 headers.
This commit is contained in:
emanuele-f
2021-07-01 18:15:49 +02:00
parent 4291d606c5
commit 51dc0dde7e
7 changed files with 53 additions and 11 deletions
@@ -53,7 +53,8 @@ public class HTTPServer extends NanoHTTPD implements PcapDumper {
return(r);
}
/* Creates a new Response and add it to the active responses. */
/* Creates a new Response and add it to the active responses.
* NOTE: socket protect not needed for inbound connections. */
private synchronized Response newPcapStream() {
/* NOTE: response length is unknown */
Response res = newChunkedResponse(Status.OK, PCAP_MIME, new ChunkedInputStream());
+8 -1
View File
@@ -59,6 +59,7 @@ typedef struct {
int client;
char bpf[512];
zdtun_t *tun;
pcap_t *pd;
int dlink;
int ipoffset;
@@ -585,6 +586,10 @@ static int run_pcap_dump(int uid_filter, const char *bpf) {
time_t next_stats_update = 0;
uid_resolver_t *resolver = NULL;
uid_lru_t *lru = NULL;
zdtun_callbacks_t callbacks = {.send_client = (void*)1};
if(!(rt.tun = zdtun_init(&callbacks, NULL)))
goto cleanup;
if(!(resolver = init_uid_resolver_from_proc()))
goto cleanup;
@@ -662,7 +667,7 @@ static int run_pcap_dump(int uid_filter, const char *bpf) {
pkt += to_skip;
hdr->caplen -= to_skip;
if(zdtun_parse_pkt((const char*)pkt, hdr->caplen, &zpkt) == 0) {
if(zdtun_parse_pkt(rt.tun, (const char*)pkt, hdr->caplen, &zpkt) == 0) {
if(!is_tx) {
// Packet from the internet, swap src and dst
tupleSwapPeers(&zpkt.tuple);
@@ -711,6 +716,8 @@ static int run_pcap_dump(int uid_filter, const char *bpf) {
cleanup:
finish_pcapd_capture(&rt);
if(rt.tun)
zdtun_finalize(rt.tun);
if(resolver)
destroy_uid_resolver(resolver);
if(lru)
@@ -364,11 +364,17 @@ int run_proxy(vpnproxy_data_t *proxy) {
if (size > 0) {
zdtun_pkt_t pkt;
if (zdtun_parse_pkt(buffer, size, &pkt) != 0) {
if(zdtun_parse_pkt(tun, buffer, size, &pkt) != 0) {
log_d("zdtun_parse_pkt failed");
goto housekeeping;
}
if(pkt.flags & ZDTUN_PKT_IS_FRAGMENT) {
log_d("discarding IP fragment");
proxy->num_discarded_fragments++;
goto housekeeping;
}
proxy->last_pkt = &pkt;
proxy->last_conn_blocked = false;
@@ -433,7 +439,7 @@ int run_proxy(vpnproxy_data_t *proxy) {
}
}
ztdun_finalize(tun);
zdtun_finalize(tun);
return(0);
}
+29 -5
View File
@@ -195,12 +195,22 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca
pcap_conn_t *conn = NULL;
uint8_t from_tun = (hdr->flags & PCAPD_FLAG_TX); // NOTE: the direction uses an heuristic so it may be wrong
// NOTE: only IP packets supported
if(zdtun_parse_pkt(buffer, hdr->len, &pkt) != 0) {
if(zdtun_parse_pkt(proxy->tun, buffer, hdr->len, &pkt) != 0) {
log_d("zdtun_parse_pkt failed");
return;
}
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
// IP fragment is lost or was not captured (e.g. for packets matching the BPF of getPcapDumperBpf).
// In such a case, we can only ignore the packet as we cannot determine the connection it belongs to.
//log_d("unmatched IP fragment (ID = 0x%04x)", pkt.ip4->id);
proxy->num_discarded_fragments++;
return;
}
if(!from_tun) {
// Packet from the internet, swap src and dst
tupleSwapPeers(&pkt.tuple);
@@ -216,6 +226,12 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca
HASH_FIND(hh, *connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
if(!conn) {
if((pkt.flags & ZDTUN_PKT_IS_FRAGMENT) && !(pkt.flags & ZDTUN_PKT_IS_FIRST_FRAGMENT)) {
log_d("ignoring fragment as it cannot start a connection");
proxy->num_discarded_fragments++;
return;
}
// assume from_tun was correct
from_tun = !from_tun;
tupleSwapPeers(&pkt.tuple);
@@ -316,15 +332,21 @@ static void purge_expired_connections(vpnproxy_data_t *proxy, pcap_conn_t **conn
/* ******************************************************* */
int run_root(vpnproxy_data_t *proxy) {
int sock = connectPcapd(proxy);
int sock = -1;
int rv = -1;
char buffer[65535];
pcap_conn_t *connections = NULL;
u_int64_t next_purge_ms;
zdtun_callbacks_t callbacks = {.send_client = (void*)1};
if(sock < 0)
if((proxy->tun = zdtun_init(&callbacks, NULL)) == NULL)
return(-1);
if((sock = connectPcapd(proxy)) < 0) {
rv = -1;
goto cleanup;
}
refresh_time(proxy);
next_purge_ms = proxy->now_ms + PERIODIC_PURGE_TIMEOUT_MS;
@@ -381,6 +403,8 @@ int run_root(vpnproxy_data_t *proxy) {
cleanup:
purge_expired_connections(proxy, &connections, 1 /* purge_all */);
close(sock);
if(proxy->tun) zdtun_finalize(proxy->tun);
if(sock > 0) close(sock);
return rv;
}
+4 -1
View File
@@ -915,7 +915,9 @@ void account_packet(vpnproxy_data_t *proxy, const zdtun_pkt_t *pkt, uint8_t from
proxy->capture_stats.rcvd_bytes += pkt->len;
}
if(data->ndpi_flow) {
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(data, proxy, pkt, from_tun);
if((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS)
@@ -1070,6 +1072,7 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
ndpi_ptree_destroy(proxy.known_dns_servers);
log_d("Host LRU cache size: %d", ip_lru_size(proxy.ip_to_host));
log_d("Discarded fragments: %ld", proxy.num_discarded_fragments);
ip_lru_destroy(proxy.ip_to_host);
logcallback = NULL;
+1
View File
@@ -106,6 +106,7 @@ typedef struct vpnproxy_data {
ip_lru_t *ip_to_host;
uint64_t now_ms;
u_int num_dropped_pkts;
long num_discarded_fragments;
u_int32_t num_dropped_connections;
u_int32_t num_dns_requests;
conn_array_t new_conns;