summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2015-09-14 14:54:55 +0900
committerLorenzo Colitti <lorenzo@google.com>2015-09-18 16:31:55 +0900
commit660bdfdefcd4e8662e47c8ac9236fdb8df18a934 (patch)
tree6b43041228dec9d6286765566484f0e86346efa4
parenta9fe6b7c67383ea81aa8cbed9928dff4a517d944 (diff)
downloadextras-660bdfdefcd4e8662e47c8ac9236fdb8df18a934.tar.gz
Add a crash test for tcp_nuke_addr.
Bug: 23663111 Change-Id: I3b79362d6a53b7689168488527059ae506161d5f
-rw-r--r--tests/tcp_nuke_addr/Android.mk12
-rw-r--r--tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp148
2 files changed, 160 insertions, 0 deletions
diff --git a/tests/tcp_nuke_addr/Android.mk b/tests/tcp_nuke_addr/Android.mk
new file mode 100644
index 00000000..2eef6089
--- /dev/null
+++ b/tests/tcp_nuke_addr/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := tcp_nuke_addr_test
+
+LOCAL_C_INCLUDES += frameworks/native/include external/libcxx/include
+LOCAL_CPPFLAGS += -std=c++11 -Wall -Werror
+LOCAL_SHARED_LIBRARIES := libc++
+LOCAL_SRC_FILES := tcp_nuke_addr_test.cpp
+LOCAL_MODULE_TAGS := eng tests
+
+include $(BUILD_NATIVE_TEST)
diff --git a/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp b/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp
new file mode 100644
index 00000000..d153949e
--- /dev/null
+++ b/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp
@@ -0,0 +1,148 @@
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "utils/RWLock.h"
+
+#include <atomic>
+#include <mutex>
+#include <thread>
+
+// Defined only in ifc_utils.c, in the kernel, and in the NDK.
+#ifndef SIOCKILLADDR
+#define SIOCKILLADDR 0x8939
+#endif
+
+#ifndef TCP_LINGER2
+#define TCP_LINGER2 8
+#endif
+
+#define KILL_INTERVAL_MS 10
+#define CONNECT_THREADS 1
+
+#define PERROR_EXIT(msg) { do { perror((msg)); exit(1); } while (0); };
+
+
+// Ensures that sockets don't stay in TIME_WAIT state.
+void setSoLinger(int s) {
+ const struct linger l = {
+ 0, // off
+ 0, // 0 seconds
+ };
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1) {
+ PERROR_EXIT("SO_LINGER");
+ }
+ const int nolinger = -1;
+ if (setsockopt(s, SOL_TCP, TCP_LINGER2, &nolinger, sizeof(nolinger)) == -1) {
+ PERROR_EXIT("TCP_LINGER2");
+ }
+}
+
+
+// Binds to a random port on a random loopback address. We don't just use 127.0.0.1 because we don't
+// want this test to kill unrelated connections on loopback.
+int bindRandomAddr() {
+ sockaddr_in sin;
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ while (sin.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+ arc4random_buf(
+ ((uint8_t *) &sin.sin_addr.s_addr) + 1,
+ sizeof(sin.sin_addr.s_addr) - 1);
+ }
+
+ int listensock;
+ if ((listensock = socket(AF_INET, SOCK_STREAM, 0)) == -1) PERROR_EXIT("listensock");
+ if (bind(listensock, (sockaddr *) &sin, sizeof(sin)) == -1) PERROR_EXIT("bind");
+ if (listen(listensock, 10) == -1) PERROR_EXIT("listen");
+
+ return listensock;
+}
+
+
+// Thread that calls SIOCKILLADDR in a loop.
+void killSockets(sockaddr_in listenaddr, int intervalMs, android::RWLock *lock) {
+ ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ listenaddr.sin_port = 0;
+ strncpy(ifr.ifr_name, "lo", strlen("lo"));
+ memcpy(&ifr.ifr_addr, &listenaddr, sizeof(listenaddr));
+
+ int ioctlsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctlsock == -1) PERROR_EXIT("ioctlsock");
+ while(true) {
+ lock->writeLock();
+ if (ioctl(ioctlsock, SIOCKILLADDR, &ifr) != 0) {
+ PERROR_EXIT("SIOCKILLADDR failed, did you run 32-bit userspace on a 64-bit kernel?");
+ }
+ lock->unlock();
+ std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
+ }
+}
+
+
+// Thread that calls connect() in a loop.
+void connectLoop(sockaddr_in listenaddr, int listensock,
+ android::RWLock *lock, std::atomic<unsigned int> *attempts) {
+ while(true) {
+ int s = socket(AF_INET, SOCK_STREAM, 0);
+ setSoLinger(s);
+
+ // Don't call SIOCKILLADDR while connect() is running, or we'll end up with lots of
+ // connections in state FIN_WAITx or TIME_WAIT, which will then slow down future
+ // due to SYN retransmits.
+ lock->readLock();
+ if (connect(s, (sockaddr *) &listenaddr, sizeof(listenaddr)) == -1) PERROR_EXIT("connect");
+ lock->unlock();
+
+ send(s, "foo", 3, 0);
+ int acceptedsock = accept(listensock, NULL, 0);
+ if (close(acceptedsock) == -1) PERROR_EXIT("close");
+ if (close(s) == -1) PERROR_EXIT("close");
+
+ attempts->fetch_add(1);
+ }
+}
+
+
+// Thread that prints progress every second.
+void progressThread(std::atomic<unsigned int> *attempts) {
+ uint32_t elapsed = 0;
+ uint32_t total, previous = 0;
+ while (true) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ elapsed++;
+ total = attempts->load();
+ printf("%ds: %u cps, total %u\n", elapsed, total-previous, total);
+ fflush(stdout);
+ previous = total;
+ }
+}
+
+
+int main() {
+ int listensock = bindRandomAddr();
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(sin);
+ if (getsockname(listensock, (sockaddr *) &sin, &len) == -1) PERROR_EXIT("getsockname");
+
+ printf("Using address %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+
+ android::RWLock lock;
+ std::atomic<unsigned int> attempts;
+ attempts.store(0);
+
+ std::thread t0(killSockets, sin, KILL_INTERVAL_MS, &lock);
+ std::thread *connectThreads[CONNECT_THREADS];
+ for (size_t i = 0; i < CONNECT_THREADS; i++) {
+ connectThreads[i] = new std::thread(connectLoop, sin, listensock, &lock, &attempts);
+ }
+ std::thread t1(progressThread, &attempts);
+ t1.join();
+
+ return 0;
+}