From 928eab71a6973906b42f58c9144f51542106fefb Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 5 Nov 2017 08:08:20 +0100 Subject: [PATCH] Cache uids --- app/src/main/jni/netguard/ip.c | 194 +++++++++++++++++---------- app/src/main/jni/netguard/netguard.h | 18 ++- 2 files changed, 141 insertions(+), 71 deletions(-) diff --git a/app/src/main/jni/netguard/ip.c b/app/src/main/jni/netguard/ip.c index 6d8fd199..606c0119 100644 --- a/app/src/main/jni/netguard/ip.c +++ b/app/src/main/jni/netguard/ip.c @@ -328,53 +328,85 @@ jint get_uid(const int version, const int protocol, const void *daddr, const uint16_t dport) { jint uid = -1; + char source[INET6_ADDRSTRLEN + 1]; char dest[INET6_ADDRSTRLEN + 1]; + inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source)); inet_ntop(version == 4 ? AF_INET : AF_INET6, daddr, dest, sizeof(dest)); - log_android(ANDROID_LOG_INFO, "get uid v%d p%d %u > %s/%u", - version, protocol, sport, dest, dport); + + struct timeval time; + gettimeofday(&time, NULL); + long now = (time.tv_sec * 1000) + (time.tv_usec / 1000); // Check IPv6 table first if (version == 4) { + int8_t saddr128[16]; + memset(saddr128, 0, 10); + saddr128[10] = (uint8_t) 0xFF; + saddr128[11] = (uint8_t) 0xFF; + memcpy(saddr128 + 12, saddr, 4); + int8_t daddr128[16]; memset(daddr128, 0, 10); daddr128[10] = (uint8_t) 0xFF; daddr128[11] = (uint8_t) 0xFF; memcpy(daddr128 + 12, daddr, 4); - uid = get_uid_sub(6, protocol, saddr, sport, daddr128, dport); + + uid = get_uid_sub(6, protocol, saddr128, sport, daddr128, dport, source, dest, now); + log_android(ANDROID_LOG_DEBUG, "uid v%d p%d %s/%u > %s/%u => %d as inet6", + version, protocol, source, sport, dest, dport, uid); } - if (uid < 0) - uid = get_uid_sub(version, protocol, saddr, sport, daddr, dport); + if (uid == -1) { + uid = get_uid_sub(version, protocol, saddr, sport, daddr, dport, source, dest, now); + log_android(ANDROID_LOG_DEBUG, "uid v%d p%d %s/%u > %s/%u => %d fallback", + version, protocol, source, sport, dest, dport, uid); + } - if (uid < 0) - log_android(ANDROID_LOG_ERROR, "uid v%d p%d %u > %s/%u not found", - version, protocol, sport, dest, dport); + if (uid == -1) + log_android(ANDROID_LOG_WARN, "uid v%d p%d %s/%u > %s/%u => not found", + version, protocol, source, sport, dest, dport); + else if (uid >= 0) + log_android(ANDROID_LOG_INFO, "uid v%d p%d %s/%u > %s/%u => %d", + version, protocol, source, sport, dest, dport, uid); return uid; } +int uid_cache_size = 0; +struct uid_cache_entry *uid_cache = NULL; + jint get_uid_sub(const int version, const int protocol, const void *saddr, const uint16_t sport, - const void *daddr, const uint16_t dport) { - char line[250]; - char hex[16 * 2 + 1]; - int fields; - uint8_t _daddr4[4]; - uint8_t _daddr6[16]; - int _sport; - int _dport; - jint uid = -1; - -#ifdef PROFILE_UID - float mselapsed; - struct timeval start, end; - gettimeofday(&start, NULL); -#endif - + const void *daddr, const uint16_t dport, + const char *source, const char *dest, + long now) { // NETLINK is not available on Android due to SELinux policies :-( // http://stackoverflow.com/questions/27148536/netlink-implementation-for-the-android-ndk // https://android.googlesource.com/platform/system/sepolicy/+/master/private/app.te (netlink_tcpdiag_socket) + static uint8_t zero[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + // Check cache + for (int i = 0; i < uid_cache_size; i++) + if (now - uid_cache[i].time <= UID_MAX_AGE && + uid_cache[i].version == version && + uid_cache[i].protocol == protocol && + uid_cache[i].sport == sport && + (uid_cache[i].dport == dport || uid_cache[i].dport == 0) && + (memcmp(uid_cache[i].saddr, saddr, version == 4 ? 4 : 16) == 0 || + memcmp(uid_cache[i].saddr, zero, version == 4 ? 4 : 16) == 0) && + (memcmp(uid_cache[i].daddr, daddr, version == 4 ? 4 : 16) == 0 || + memcmp(uid_cache[i].daddr, zero, version == 4 ? 4 : 16) == 0)) { + + log_android(ANDROID_LOG_INFO, "uid v%d p%d %s/%u > %s/%u => %d (from cache)", + version, protocol, source, sport, dest, dport, uid_cache[i].uid); + + if (protocol == IPPROTO_UDP) + return -2; + else + return uid_cache[i].uid; + } + // Get proc file name char *fn = NULL; if (protocol == IPPROTO_ICMP && version == 4) @@ -386,68 +418,92 @@ jint get_uid_sub(const int version, const int protocol, else if (protocol == IPPROTO_UDP) fn = (version == 4 ? "/proc/net/udp" : "/proc/net/udp6"); else - return uid; + return -1; // Open proc file FILE *fd = fopen(fn, "r"); if (fd == NULL) { log_android(ANDROID_LOG_ERROR, "fopen %s error %d: %s", fn, errno, strerror(errno)); - return uid; + return -2; } + jint uid = -1; + + char line[250]; + int fields; + + char shex[16 * 2 + 1]; + uint8_t _saddr[16]; + int _sport; + + char dhex[16 * 2 + 1]; + uint8_t _daddr[16]; + int _dport; + + jint _uid; + // Scan proc file - jint u; - int i = 0; + int l = 0; *line = 0; + int c = 0; + int ws = (version == 4 ? 1 : 4); + const char *fmt = (version == 4 + ? "%*d: %8s:%X %8s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld" + : "%*d: %32s:%X %32s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld"); while (fgets(line, sizeof(line), fd) != NULL) { - if (i++) { - *hex = 0; - _sport = -1; - _dport = -1; - u = -1; - if (version == 4) - fields = sscanf( - line, - "%*d: %*X:%X %8s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld", - &_sport, hex, &_dport, &u); - else - fields = sscanf( - line, - "%*d: %*X:%X %32s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld", - &_sport, hex, &_dport, &u); + if (!l++) + continue; - if (fields == 4 && (version == 4 ? strlen(hex) == 8 : strlen(hex) == 32)) { - if (_sport > 0 && u >= 0) { - hex2bytes(hex, version == 4 ? _daddr4 : _daddr6); - if (version == 4) - ((uint32_t *) _daddr4)[0] = htonl(((uint32_t *) _daddr4)[0]); - else - for (int w = 0; w < 4; w++) - ((uint32_t *) _daddr6)[w] = htonl(((uint32_t *) _daddr6)[w]); + fields = sscanf(line, fmt, shex, &_sport, dhex, &_dport, &_uid); + if (fields == 5 && strlen(shex) == ws * 8 && strlen(dhex) == ws * 8) { + hex2bytes(shex, _saddr); + hex2bytes(dhex, _daddr); - if (_sport == sport) { - uid = u; - if (_dport == dport && - memcmp(version == 4 ? _daddr4 : _daddr6, daddr, - version == 4 ? 4 : 16) == 0) - break; - } - } - } else - log_android(ANDROID_LOG_ERROR, "Invalid field #%d: %s", fields, line); + for (int w = 0; w < ws; w++) + ((uint32_t *) _saddr)[w] = htonl(((uint32_t *) _saddr)[w]); + + for (int w = 0; w < ws; w++) + ((uint32_t *) _daddr)[w] = htonl(((uint32_t *) _daddr)[w]); + + if (_sport == sport && + (_dport == dport || _dport == 0) && + (memcmp(_saddr, saddr, (size_t) (ws * 4)) == 0 || + memcmp(_saddr, zero, (size_t) (ws * 4)) == 0) && + (memcmp(_daddr, daddr, (size_t) (ws * 4)) == 0 || + memcmp(_daddr, zero, (size_t) (ws * 4)) == 0)) + uid = _uid; + + for (; c < uid_cache_size; c++) + if (now - uid_cache[c].time > UID_MAX_AGE) + break; + + if (c >= uid_cache_size) { + if (uid_cache_size == 0) + uid_cache = malloc(sizeof(struct uid_cache_entry)); + else + uid_cache = realloc(uid_cache, + sizeof(struct uid_cache_entry) * + (uid_cache_size + 1)); + c = uid_cache_size; + uid_cache_size++; + } + + uid_cache[c].version = (uint8_t) version; + uid_cache[c].protocol = (uint8_t) protocol; + memcpy(uid_cache[c].saddr, _saddr, (size_t) (ws * 4)); + uid_cache[c].sport = (uint16_t) _sport; + memcpy(uid_cache[c].daddr, daddr, (size_t) (ws * 4)); + uid_cache[c].dport = (uint16_t) _dport; + uid_cache[c].uid = _uid; + uid_cache[c].time = now; + } else { + log_android(ANDROID_LOG_ERROR, "Invalid field #%d: %s", fields, line); + return -2; } } if (fclose(fd)) log_android(ANDROID_LOG_ERROR, "fclose %s error %d: %s", fn, errno, strerror(errno)); -#ifdef PROFILE_UID - gettimeofday(&end, NULL); - mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 + - (end.tv_usec - start.tv_usec) / 1000.0; - if (mselapsed > PROFILE_UID) - log_android(ANDROID_LOG_WARN, "get uid ip %f", mselapsed); -#endif - return uid; } diff --git a/app/src/main/jni/netguard/netguard.h b/app/src/main/jni/netguard/netguard.h index ca099354..3b9cd35c 100644 --- a/app/src/main/jni/netguard/netguard.h +++ b/app/src/main/jni/netguard/netguard.h @@ -34,7 +34,6 @@ #define TAG "NetGuard.JNI" -// #define PROFILE_UID 5 // #define PROFILE_JNI 5 #define EPOLL_TIMEOUT 3600 // seconds @@ -61,6 +60,8 @@ #define SESSION_MAX 512 // number #define SESSION_LIMIT 30 // percent +#define UID_MAX_AGE 30000 // milliseconds + #define SOCKS5_NONE 1 #define SOCKS5_HELLO 2 #define SOCKS5_AUTH 3 @@ -189,6 +190,17 @@ struct ng_session { struct ng_session *next; }; +struct uid_cache_entry { + uint8_t version; + uint8_t protocol; + uint8_t saddr[16]; + uint16_t sport; + uint8_t daddr[16]; + uint16_t dport; + jint uid; + long time; +}; + // IPv6 struct ip6_hdr_pseudo { @@ -461,7 +473,9 @@ jint get_uid(const int version, const int protocol, jint get_uid_sub(const int version, const int protocol, const void *saddr, const uint16_t sport, - const void *daddr, const uint16_t dport); + const void *daddr, const uint16_t dport, + const char *source, const char *dest, + long now); int protect_socket(const struct arguments *args, int socket);