1
0
Fork 0
mirror of https://github.com/M66B/NetGuard.git synced 2025-01-01 12:54:07 +00:00

Native UDP handling setup

This commit is contained in:
M66B 2016-01-20 09:24:34 +01:00
parent 7adbd8e4e4
commit 563b1e027b
3 changed files with 179 additions and 60 deletions

View file

@ -98,7 +98,6 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/objectFiles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard-rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />

View file

@ -34,15 +34,13 @@
#include "netguard.h"
#define PROFILE 1
// #define PROFILE 1
// TODO TCP fragmentation
// TODO TCP push
// TODO TCPv6
// TODO UDPv4
// TODO UDPv6
// TODO DHCP
// TODO log allowed traffic
// TODO fix warnings
// TODO non blocking send/write, handle EAGAIN/EWOULDBLOCK
@ -105,7 +103,7 @@ Java_eu_faircode_netguard_SinkholeService_jni_1start(
args->filter = filter;
for (int i = 0; i < args->count; i++)
log_android(ANDROID_LOG_INFO, "Allowed uid %d", args->uid[i]);
log_android(ANDROID_LOG_DEBUG, "Allowed uid %d", args->uid[i]);
// Start native thread
int err = pthread_create(&thread_id, NULL, handle_events, (void *) args);
@ -131,15 +129,9 @@ Java_eu_faircode_netguard_SinkholeService_jni_1stop(JNIEnv *env, jobject instanc
if (err != 0)
log_android(ANDROID_LOG_WARN, "pthread_join error %d: %s", err, strerror(err));
}
if (clear) {
struct session *s = session;
while (s != NULL) {
struct session *p = s;
s = s->next;
free(p);
}
session = NULL;
}
if (clear)
clear_sessions();
log_android(ANDROID_LOG_INFO, "Stopped thread %lu", thread_id);
} else
log_android(ANDROID_LOG_WARN, "Not running");
@ -149,13 +141,7 @@ JNIEXPORT void JNICALL
Java_eu_faircode_netguard_SinkholeService_jni_1done(JNIEnv *env, jobject instance) {
log_android(ANDROID_LOG_INFO, "Done");
struct session *s = session;
while (s != NULL) {
struct session *p = s;
s = s->next;
free(p);
}
session = NULL;
clear_sessions();
if (pcap_fn != NULL)
free(pcap_fn);
@ -183,8 +169,20 @@ Java_eu_faircode_netguard_SinkholeService_jni_1pcap(JNIEnv *env, jclass type, js
(*env)->ReleaseStringUTFChars(env, name_, name);
}
}
// Private functions
void clear_sessions() {
struct session *s = session;
while (s != NULL) {
close(s->socket);
struct session *p = s;
s = s->next;
free(p);
}
session = NULL;
}
void handle_signal(int sig, siginfo_t *info, void *context) {
log_android(ANDROID_LOG_DEBUG, "Signal %d", sig);
signaled = 1;
@ -224,6 +222,20 @@ void handle_events(void *a) {
signaled = 0;
// Open UDP socket
int usock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (usock < 0)
log_android(ANDROID_LOG_ERROR, "UDP socket error %d: %s", errno, strerror(errno));
protect_socket(args, usock);
struct sockaddr_in uaddr;
uaddr.sin_family = AF_INET;
uaddr.sin_addr.s_addr = INADDR_ANY;
uaddr.sin_port = 0;
if (bind(usock, (struct sockaddr *) &uaddr, sizeof(struct sockaddr)) < 0)
log_android(ANDROID_LOG_ERROR, "UDP bind error %d: %s", errno, strerror(errno));
// Loop
while (1) {
log_android(ANDROID_LOG_DEBUG, "Loop thread %lu", thread_id);
@ -232,7 +244,7 @@ void handle_events(void *a) {
ts.tv_sec = SELECT_TIMEOUT;
ts.tv_nsec = 0;
sigemptyset(&emptyset);
int max = get_selects(args, &rfds, &wfds, &efds);
int max = get_selects(args, usock, &rfds, &wfds, &efds);
int ready = pselect(max + 1, &rfds, &wfds, &efds, session == NULL ? NULL : &ts, &emptyset);
if (ready < 0) {
if (errno == EINTR) {
@ -270,7 +282,7 @@ void handle_events(void *a) {
#endif
// Check upstream
if (check_tun(args, &rfds, &wfds, &efds) < 0)
if (check_tun(args, usock, &rfds, &wfds, &efds) < 0)
break;
#ifdef PROFILE
@ -283,6 +295,9 @@ void handle_events(void *a) {
gettimeofday(&start, NULL);
#endif
// Check UDP
check_udp(args, usock, &rfds, &wfds, &efds);
// Check downstream
check_sockets(args, &rfds, &wfds, &efds);
@ -292,10 +307,12 @@ void handle_events(void *a) {
(end.tv_usec - start.tv_usec) / 1000.0;
if (mselapsed > 1)
log_android(ANDROID_LOG_INFO, "sockets %f", mselapsed);
}
#endif
}
}
close(usock);
free(args->uid);
free(args);
@ -308,20 +325,27 @@ void handle_events(void *a) {
// TODO report exit to Java
}
int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
int get_selects(const struct arguments *args, int usock, fd_set *rfds, fd_set *wfds,
fd_set *efds) {
time_t now = time(NULL);
// Select
// Initialize
FD_ZERO(rfds);
FD_ZERO(wfds);
FD_ZERO(efds);
// Always read tun
// Always select tun
FD_SET(args->tun, rfds);
FD_SET(args->tun, efds);
int max = args->tun;
// Always select UDP
FD_SET(usock, rfds);
FD_SET(usock, efds);
if (usock > max)
max = usock;
// Select sockets
struct session *last = NULL;
struct session *cur = session;
while (cur != NULL) {
@ -391,7 +415,8 @@ int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set
return max;
}
int check_tun(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
int check_tun(const struct arguments *args, int usock, fd_set *rfds, fd_set *wfds,
fd_set *efds) {
// Check tun error
if (FD_ISSET(args->tun, efds)) {
log_android(ANDROID_LOG_ERROR, "tun exception");
@ -412,7 +437,7 @@ int check_tun(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *
write_pcap_rec(buffer, length);
// Handle IP from tun
handle_ip(args, buffer, length);
handle_ip(args, usock, buffer, length);
}
else {
// tun eof
@ -424,6 +449,36 @@ int check_tun(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *
return 0;
}
void check_udp(const struct arguments *args, int usock, fd_set *rfds, fd_set *wfds,
fd_set *efds) {
if (FD_ISSET(usock, efds)) {
// Socket error
int serr = 0;
socklen_t optlen = sizeof(int);
int err = getsockopt(usock, SOL_SOCKET, SO_ERROR, &serr, &optlen);
if (err < 0)
log_android(ANDROID_LOG_ERROR, "UDP getsockopt error %d: %s", errno,
strerror(errno));
else if (serr)
log_android(ANDROID_LOG_ERROR, "UDP SO_ERROR %d: %s", serr, strerror(serr));
}
else if (FD_ISSET(usock, rfds)) {
// Socket receive
uint8_t buffer[MAX_PKTSIZE];
struct sockaddr_in client;
int clen = sizeof(client);
ssize_t bytes = recvfrom(
usock, buffer, sizeof(buffer), 0,
(struct sockaddr *) &client, (socklen_t *) &clen);
int sport = ntohs(client.sin_port);
char source[40];
inet_ntop(client.sin_family, &client.sin_addr, source, sizeof(source));
log_android(ANDROID_LOG_INFO, "UDP from %s/%u data %d", source, sport, bytes);
}
}
void check_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
struct session *cur = session;
while (cur != NULL) {
@ -519,7 +574,8 @@ void check_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_
}
}
if (cur->state != oldstate || cur->local_seq != oldlocal || cur->remote_seq != oldremote) {
if (cur->state != oldstate || cur->local_seq != oldlocal ||
cur->remote_seq != oldremote) {
char dest[20];
inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest));
log_android(ANDROID_LOG_INFO,
@ -533,7 +589,8 @@ void check_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_
}
}
void handle_ip(const struct arguments *args, const uint8_t *buffer, const uint16_t length) {
void handle_ip(const struct arguments *args, int usock, const uint8_t *buffer,
const uint16_t length) {
uint8_t protocol;
void *saddr;
void *daddr;
@ -686,7 +743,9 @@ void handle_ip(const struct arguments *args, const uint8_t *buffer, const uint16
// Handle allowed traffic
if (allowed) {
if (protocol == IPPROTO_TCP)
if (protocol == IPPROTO_UDP)
allowed = handle_udp(args, usock, buffer, length, uid);
else if (protocol == IPPROTO_TCP)
allowed = handle_tcp(args, buffer, length, uid);
else
allowed = 0;
@ -699,7 +758,45 @@ void handle_ip(const struct arguments *args, const uint8_t *buffer, const uint16
}
}
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_t length, int uid) {
jboolean handle_udp(const struct arguments *args, int usock,
const uint8_t *buffer, uint16_t length, int uid) {
// Check version
uint8_t version = (*buffer) >> 4;
if (version != 4)
return 0;
// Get headers
struct iphdr *iphdr = buffer;
uint8_t ipoptlen = (iphdr->ihl - 5) * 4;
struct udphdr *udphdr = buffer + sizeof(struct iphdr) + ipoptlen;
// Get data
uint16_t dataoff = sizeof(struct iphdr) + ipoptlen + sizeof(struct udphdr);
uint16_t datalen = length - dataoff;
// TODO make session
char dest[40];
inet_ntop(version == 4 ? AF_INET : AF_INET6, udphdr->dest, dest, sizeof(dest));
log_android(ANDROID_LOG_INFO, "Forwarding UDP to %s/%u data %d",
dest, ntohs(udphdr->dest), datalen);
struct sockaddr_in client;
client.sin_family = (version == 4 ? AF_INET : AF_INET6);
client.sin_addr.s_addr = iphdr->daddr;
client.sin_port = udphdr->dest;
if (sendto(usock, buffer + dataoff, datalen, 0, &client, sizeof(client)) != datalen) {
log_android(ANDROID_LOG_ERROR, "UDP sendto error %s:%s", errno, strerror(errno));
return 0;
}
return 1;
}
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_t length,
int uid) {
#ifdef PROFILE
float mselapsed;
struct timeval start, end;
@ -888,7 +985,8 @@ jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_
ntohl(tcphdr->seq) == cur->remote_seq &&
ntohl(tcphdr->ack_seq) < cur->local_seq) {
int confirm = cur->local_seq - ntohl(tcphdr->ack_seq);
log_android(ANDROID_LOG_INFO, "Simultaneous close %s/%u lport %u confirm %d",
log_android(ANDROID_LOG_INFO,
"Simultaneous close %s/%u lport %u confirm %d",
dest, ntohs(cur->dest), cur->lport, confirm);
write_ack(cur, confirm, args->tun);
}
@ -940,7 +1038,8 @@ jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_
else if (cur->state == TCP_CLOSING)
cur->state = TCP_TIME_WAIT;
else
log_android(ANDROID_LOG_ERROR, "Invalid ACK session %s/%u lport %u state %s",
log_android(ANDROID_LOG_ERROR,
"Invalid ACK session %s/%u lport %u state %s",
dest, ntohs(cur->dest), cur->lport, strstate(cur->state));
}
else {
@ -969,7 +1068,8 @@ jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_
log_android(ANDROID_LOG_ERROR, "Unknown packet session %s/%u lport %u",
dest, ntohs(cur->dest), cur->lport);
if (cur->state != oldstate || cur->local_seq != oldlocal || cur->remote_seq != oldremote)
if (cur->state != oldstate || cur->local_seq != oldlocal ||
cur->remote_seq != oldremote)
log_android(ANDROID_LOG_INFO,
"Session %s/%u lport %u new state %s local %u remote %u",
dest, ntohs(cur->dest), cur->lport, strstate(cur->state),
@ -1006,26 +1106,8 @@ int open_tcp(const struct session *cur, const struct arguments *args) {
}
// Protect
JNIEnv *env = args->env;
jobject instance = args->instance;
jclass cls = (*env)->GetObjectClass(env, instance);
jmethodID mid = (*env)->GetMethodID(env, cls, "protect", "(I)Z");
if (mid == 0) {
log_android(ANDROID_LOG_ERROR, "protect not found");
if (protect_socket(args, sock) < 0)
return -1;
}
else {
jboolean isProtected = (*env)->CallBooleanMethod(env, instance, mid, sock);
if (!isProtected)
log_android(ANDROID_LOG_ERROR, "protect failed");
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, ex);
}
}
// Set non blocking
uint8_t flags = fcntl(sock, F_GETFL, 0);
@ -1299,7 +1381,8 @@ jint get_uid(const int protocol, const int version,
}
if (port == sport) {
if (memcmp(version == 4 ? addr4 : addr6, saddr, version == 4 ? 4 : 16) == 0) {
if (memcmp(version == 4 ? addr4 : addr6, saddr, version == 4 ? 4 : 16) ==
0) {
uid = u;
break;
}
@ -1323,6 +1406,35 @@ jint get_uid(const int protocol, const int version,
return uid;
}
int protect_socket(struct arguments *args, int socket) {
JNIEnv *env = args->env;
jobject instance = args->instance;
jclass cls = (*env)->GetObjectClass(env, instance);
jmethodID mid = (*env)->GetMethodID(env, cls, "protect", "(I)Z");
if (mid == 0) {
log_android(ANDROID_LOG_ERROR, "protect method not found");
return -1;
}
jboolean isProtected = (*env)->CallBooleanMethod(env, instance, mid, socket);
if (!isProtected) {
log_android(ANDROID_LOG_ERROR, "protect failed");
return -1;
}
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex) {
log_android(ANDROID_LOG_ERROR, "protect exception");
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, ex);
return -1;
}
return 0;
}
uint16_t calc_checksum(uint8_t *buffer, uint16_t length) {
register uint32_t sum = 0;
register uint16_t *buf = buffer;

View file

@ -67,17 +67,25 @@ typedef struct pcaprec_hdr_s {
#define LINKTYPE_RAW 101
void clear_sessions();
void handle_signal(int sig, siginfo_t *info, void *context);
void handle_events(void *a);
int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
int get_selects(const struct arguments *args, int usock, fd_set *rfds, fd_set *wfds, fd_set *efds);
int check_tun(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
int check_tun(const struct arguments *args, int usock, fd_set *rfds, fd_set *wfds, fd_set *efds);
void check_udp(const struct arguments *args, int usock, fd_set *rfds, fd_set *wfds, fd_set *efds);
void check_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
void handle_ip(const struct arguments *args, const uint8_t *buffer, const uint16_t length);
void handle_ip(const struct arguments *args, int usock,
const uint8_t *buffer, const uint16_t length);
jboolean handle_udp(const struct arguments *args, int usock,
const uint8_t *buffer, uint16_t length, int uid);
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_t length, int uid);