File: tcp_nuke_addr_test.cpp

package info (click to toggle)
android-platform-tools 29.0.6-28
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 365,224 kB
  • sloc: cpp: 1,049,638; java: 460,532; ansic: 375,452; asm: 301,257; xml: 134,509; python: 92,731; perl: 62,008; sh: 26,753; makefile: 3,210; javascript: 3,172; yacc: 1,403; lex: 455; awk: 368; ruby: 183; sql: 140
file content (148 lines) | stat: -rw-r--r-- 4,411 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <arpa/inet.h>
#include <linux/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>

#include <atomic>
#include <mutex>
#include <thread>

#include "utils/RWLock.h"

// 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);
    for (size_t i = 0; i < CONNECT_THREADS; i++) {
        std::thread(connectLoop, sin, listensock, &lock, &attempts).detach();
    }

    progressThread(&attempts);
    return 0;
}