void *handle_events(void *a) { int sdk; fd_set rfds; fd_set wfds; fd_set efds; struct timespec ts; sigset_t blockset; sigset_t emptyset; struct sigaction sa; struct arguments *args = (struct arguments *) a; log_android(ANDROID_LOG_WARN, "Start events tun=%d thread %x", args->tun, thread_id); // Attach to Java JNIEnv *env; jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL); if (rs != JNI_OK) { log_android(ANDROID_LOG_ERROR, "AttachCurrentThread failed"); return NULL; } args->env = env; // Get SDK version sdk = sdk_int(env); int maxsessions = 1024; struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim)) log_android(ANDROID_LOG_WARN, "getrlimit error %d: %s", errno, strerror(errno)); else { maxsessions = rlim.rlim_cur * 80 / 100; log_android(ANDROID_LOG_WARN, "getrlimit soft %d hard %d max sessions %d", rlim.rlim_cur, rlim.rlim_max, maxsessions); } // Block SIGUSR1 sigemptyset(&blockset); sigaddset(&blockset, SIGUSR1); sigprocmask(SIG_BLOCK, &blockset, NULL); /// Handle SIGUSR1 sa.sa_sigaction = handle_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGUSR1, &sa, NULL); // Terminate existing sessions not allowed anymore check_allowed(args); stopping = 0; signaled = 0; // Loop while (!stopping) { log_android(ANDROID_LOG_DEBUG, "Loop thread %x", thread_id); // Count sessions int isessions = get_icmp_sessions(); int usessions = get_udp_sessions(); int tsessions = get_tcp_sessions(); int sessions = isessions + usessions + tsessions; // Check sessions check_icmp_sessions(args, sessions, maxsessions); check_udp_sessions(args, sessions, maxsessions); check_tcp_sessions(args, sessions, maxsessions); // https://bugzilla.mozilla.org/show_bug.cgi?id=1093893 int idle = (tsessions + usessions + tsessions == 0 && sdk >= 16); log_android(ANDROID_LOG_DEBUG, "sessions ICMP %d UDP %d TCP %d max %d/%d idle %d sdk %d", isessions, usessions, tsessions, sessions, maxsessions, idle, sdk); // Next event time ts.tv_sec = (sdk < 16 ? 5 : get_select_timeout(sessions, maxsessions)); ts.tv_nsec = 0; sigemptyset(&emptyset); // Check if tun is writable FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(args->tun, &wfds); if (pselect(args->tun + 1, &rfds, &wfds, &efds, &ts, &emptyset) == 0) { log_android(ANDROID_LOG_WARN, "tun not writable"); continue; } // Select int max = get_selects(args, &rfds, &wfds, &efds); int ready = pselect(max + 1, &rfds, &wfds, &efds, idle ? NULL : &ts, &emptyset); if (ready < 0) { if (errno == EINTR) { if (stopping && signaled) { ; log_android(ANDROID_LOG_WARN, "pselect signaled tun %d thread %x", args->tun, thread_id); report_exit(args, NULL); break; } else { // TODO check if SIGUSR1 is free log_android(ANDROID_LOG_DEBUG, "pselect interrupted tun %d thread %x", args->tun, thread_id); continue; } } else if (errno == EBADF) { struct stat sb; if (fstat(args->tun, &sb) < 0) { log_android(ANDROID_LOG_ERROR, "tun socket %d select error %d: %s", args->tun, errno, strerror(errno)); report_exit(args, "tun socket %d select error %d: %s", args->tun, errno, strerror(errno)); } else { log_android(ANDROID_LOG_WARN, "pselect EBADF"); break; } } else { log_android(ANDROID_LOG_ERROR, "pselect tun %d thread %x error %d: %s", args->tun, thread_id, errno, strerror(errno)); report_exit(args, "pselect tun %d thread %x error %d: %s", args->tun, thread_id, errno, strerror(errno)); break; } } if (ready == 0) log_android(ANDROID_LOG_DEBUG, "pselect timeout"); else { log_android(ANDROID_LOG_DEBUG, "pselect ready %d", ready); if (pthread_mutex_lock(&lock)) log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed"); #ifdef PROFILE_EVENTS struct timeval start, end; float mselapsed; gettimeofday(&start, NULL); #endif // Check upstream int error = 0; if (check_tun(args, &rfds, &wfds, &efds, sessions, maxsessions) < 0) error = 1; else { #ifdef PROFILE_EVENTS gettimeofday(&end, NULL); mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_usec - start.tv_usec) / 1000.0; if (mselapsed > PROFILE_EVENTS) log_android(ANDROID_LOG_WARN, "tun %f", mselapsed); gettimeofday(&start, NULL); #endif // Check ICMP downstream check_icmp_sockets(args, &rfds, &wfds, &efds); // Check UDP downstream check_udp_sockets(args, &rfds, &wfds, &efds); // Check TCP downstream check_tcp_sockets(args, &rfds, &wfds, &efds); } if (pthread_mutex_unlock(&lock)) log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed"); if (error) break; #ifdef PROFILE_EVENTS gettimeofday(&end, NULL); mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_usec - start.tv_usec) / 1000.0; if (mselapsed > PROFILE_EVENTS) log_android(ANDROID_LOG_WARN, "sockets %f", mselapsed); #endif } } (*env)->DeleteGlobalRef(env, args->instance); // Detach from Java rs = (*jvm)->DetachCurrentThread(jvm); if (rs != JNI_OK) log_android(ANDROID_LOG_ERROR, "DetachCurrentThread failed"); // Cleanup free(args); log_android(ANDROID_LOG_WARN, "Stopped events tun=%d thread %x", args->tun, thread_id); thread_id = 0; return NULL; }
int main(int argc, char **argv) { int fds[5], ret; char addr[ETH_ALEN], a2[ETH_ALEN]; test_init(argc, argv); #ifdef TUN_NS if (unshare(CLONE_NEWNET)) { pr_perror("unshare"); return 1; } system("ip link set up dev lo"); #endif /* fd[0] -- opened file */ fds[0] = __open_tun(); if (fds[0] < 0) { pr_perror("No file 0"); return 1; } /* fd[1] -- opened file with tun device */ fds[1] = open_tun("tunx0", IFF_TUN); if (fds[1] < 0) { pr_perror("No file 1"); return 1; } /* fd[2] and [3] -- two-queued device, with 3 detached */ fds[2] = open_tun("tunx1", IFF_TUN | IFF_MULTI_QUEUE); if (fds[2] < 0) { pr_perror("No file 2"); return 1; } fds[3] = open_tun("tunx1", IFF_TUN | IFF_MULTI_QUEUE); if (fds[3] < 0) { pr_perror("No file 3"); return 1; } ret = set_tun_queue(fds[3], "tunx1", IFF_DETACH_QUEUE); if (ret < 0) return 1; /* special case -- persistent device */ ret = open_tun("tunx2", IFF_TUN); if (ret < 0) { pr_perror("No persistent device"); return 1; } if (ioctl(ret, TUNSETPERSIST, 1) < 0) { pr_perror("Can't make persistent"); return 1; } /* and one tap in fd[4] */ fds[4] = open_tun("tapx0", IFF_TAP); if (fds[4] < 0) { pr_perror("No tap"); return 1; } if (dev_get_hwaddr(fds[4], "tapx0", addr) < 0) { pr_perror("No hwaddr for tap?"); return 1; } close(ret); test_daemon(); test_waitsig(); /* check fds[0] is not attached to device */ ret = __attach_tun(fds[0], "tunx3", IFF_TUN); if (ret < 0) { any_fail = 1; fail("Opened tun file broken"); } /* check that fds[1] has device */ check_tun(fds[1], "tunx0", IFF_TUN); /* check that fds[2] and [3] are at MQ device with */ check_tun(fds[2], "tunx1", IFF_TUN | IFF_MULTI_QUEUE); check_tun(fds[3], "tunx1", IFF_TUN | IFF_MULTI_QUEUE); ret = set_tun_queue(fds[2], "tunx1", IFF_DETACH_QUEUE); if (ret < 0) { any_fail = 1; fail("Queue not attached"); } ret = set_tun_queue(fds[3], "tunx1", IFF_ATTACH_QUEUE); if (ret < 0) { any_fail = 1; fail("Queue not detached"); } /* check persistent device */ ret = open_tun("tunx2", IFF_TUN | IFF_TUN_EXCL); if (ret >= 0) { any_fail = 1; fail("Persistent device lost"); } else { ret = open_tun("tunx2", IFF_TUN); if (ret < 0) pr_perror("Can't attach tun2"); else ioctl(ret, TUNSETPERSIST, 0); } check_tun(fds[4], "tapx0", IFF_TAP); if (dev_get_hwaddr(fds[4], "tapx0", a2) < 0) { pr_perror("No hwaddr for tap? (2)"); any_fail = 1; } else if (memcmp(addr, a2, sizeof(addr))) { fail("Address mismatch on tap %x:%x -> %x:%x", (int)addr[0], (int)addr[1], (int)a2[0], (int)a2[1]); any_fail = 1; } if (!any_fail) pass(); return 0; }