File: transparent_proxy_test

package info (click to toggle)
sniproxy 0.6.1%2Bgit20240321-0.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 644 kB
  • sloc: ansic: 5,594; perl: 1,673; sh: 237; makefile: 131
file content (234 lines) | stat: -rwxr-xr-x 9,536 bytes parent folder | download | duplicates (5)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/env perl

use strict;
use warnings;
use File::Basename;
use lib dirname (__FILE__);
use TestUtils;
use TestHTTPD;
use File::Temp;


sub checked_system {
    my $result = system(@_);

    if ($result == -1) {
        die "@_ failed to execute: $!\n";
    } elsif ($result & 127) {
        printf STDERR "@_ died with signal %d, %s coredump\n", ($result & 127), ($result & 128) ? 'with' : 'without';
        exit 255;
    } elsif ($result >> 8) {
        exit $result >> 8;
    }
}

sub make_config($$) {
    my $proxy_port = shift;
    my $httpd_port = shift;

    my ($fh, $filename) = File::Temp::tempfile();
    my ($unused, $logfile) = File::Temp::tempfile();

    # Write out a test config file
    print $fh <<END;
# Minimal test configuration

user root

listen 192.0.2.5 $proxy_port {
    proto http
    source client
    access_log $logfile
}

table {
    localhost 192.0.2.2 $httpd_port
}
END

    close ($fh);

    return $filename;
}

sub proxy {
    my $config = shift;

    exec('ip', 'netns', 'exec', 'ns-test-proxy', @_, '../src/sniproxy', '-f', '-c', $config);
}

sub exec_cmd {
    exec(@_);
}

sub worker($$$$$) {
    my ($hostname, $path, $ip, $port, $requests) = @_;

    for (my $i = 0; $i < $requests; $i++) {
        system('ip', 'netns', 'exec', 'ns-test-clt',
                'curl',
                '-s', '-S',
                '-H', "Host: $hostname",
                '-o', '/dev/null',
                "http://$ip:$port/$path");

        if ($? == -1) {
            die "failed to execute: $!\n";
        } elsif ($? & 127) {
            printf STDERR "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without';
            exit 255;
        } elsif ($? >> 8) {
            exit $? >> 8;
        }
    }
    # Success
    exit 0;
}

sub main {
    my $proxy_port = $ENV{SNI_PROXY_PORT} || 8080;
    my $httpd_port = $ENV{TEST_HTTPD_PORT} || 8081;
    my $workers = $ENV{WORKERS} || 10;
    my $iterations = $ENV{ITERATIONS} || 10;

    my $config = make_config($proxy_port, $httpd_port);

    unless ($> == 0 and $^O eq 'linux') {
        print STDERR "This test requires Linux and root privileges\n";
        exit 77;
    }

    # Setup a test network using network namespaces with the ns-test-proxy
    # namespace configured like a normal transparent proxy box.
    #
    #   +-----------------------------------------------------------+
    #   |           Host (default) Network Namespace                |
    #   |                                                           |
    #   |   Route 192.0.2.4/30 via 192.0.2.1 dev veth-srv-proxy     |
    #   |                                                           |
    #   |   TestHTTPD process                   o veth-srv-proxy    |
    #   |                                       | 192.0.2.2/30      |
    #   |   +-----------------------------------|---------------+   |
    #   |   |       SNIproxy Network Namespace  |               |   |
    #   |   |             ns-test-proxy         o veth-proxy-srv|   |
    #   |   |                                     192.0.2.1/30  |   |
    #   |   |   IP Forwarding enabled                           |   |
    #   |   |   IPTables and ip rules configured (see below)    |   |
    #   |   |                                                   |   |
    #   |   |                                   o veth-proxy-clt|   |
    #   |   |   sniproxy process                | 192.0.2.5/30  |   |
    #   |   |                                   |               |   |
    #   |   +-----------------------------------|---------------+   |
    #   |                                       |                   |
    #   |   +-----------------------------------|---------------+   |
    #   |   |       Client Network Namespace    | veth-clt-proxy|   |
    #   |   |             ns-test-clt           o 192.0.2.6/30  |   |
    #   |   |                                                   |   |
    #   |   |   Route default via 192.0.2.5 dev veth-clt-proxy  |   |
    #   |   |                                                   |   |
    #   |   |   curl process                                    |   |
    #   |   |                                                   |   |
    #   |   +---------------------------------------------------+   |
    #   |                                                           |
    #   +-----------------------------------------------------------+
    #
    # IPTables NAT rules:
    #   Chain POSTROUTING (policy ACCEPT)
    #   target     prot opt source               destination
    #   MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0
    #
    # IPTables Mangle rules:
    #   Chain PREROUTING (policy ACCEPT)
    #   target     prot opt source               destination
    #   DIVERT     tcp  --  0.0.0.0/0            0.0.0.0/0            socket
    #
    #   Chain DIVERT (1 references)
    #   target     prot opt source               destination
    #   MARK       all  --  0.0.0.0/0            0.0.0.0/0            MARK set 0x1
    #   ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
    #
    # IP rules:
    #   0:	from all lookup local
    #   32765:	from all fwmark 0x1 lookup 100
    #   32766:	from all lookup main
    #   32767:	from all lookup default
    #
    # IP route table 100:
    #   local default dev lo scope host

    checked_system('ip', 'netns', 'add', 'ns-test-proxy');
    checked_system('ip', 'netns', 'add', 'ns-test-clt');

    checked_system('ip', 'link', 'add', 'veth-proxy-srv', 'type', 'veth', 'peer', 'name', 'veth-srv-proxy');

    checked_system('ip', 'link', 'set', 'veth-srv-proxy', 'up');
    checked_system('ip', 'addr', 'add', '192.0.2.2/30', 'dev', 'veth-srv-proxy');
    checked_system('ip', 'route', 'add', '192.0.2.4/30', 'via', '192.0.2.1', 'dev', 'veth-srv-proxy');

    checked_system('ip', 'link', 'set', 'veth-proxy-srv', 'up', 'netns', 'ns-test-proxy');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ip', 'addr', 'add', '192.0.2.1/30', 'dev', 'veth-proxy-srv');

    checked_system('ip', 'link', 'add', 'veth-proxy-clt', 'type', 'veth', 'peer', 'name', 'veth-clt-proxy');

    checked_system('ip', 'link', 'set', 'veth-proxy-clt', 'up', 'netns', 'ns-test-proxy');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ip', 'addr', 'add', '192.0.2.5/30', 'dev', 'veth-proxy-clt');

    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'sysctl', 'net.ipv4.conf.all.forwarding=1');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'iptables', '-t', 'nat', '-A', 'POSTROUTING', '-o', 'veth-proxy-clt', '-j', 'MASQUERADE');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'iptables', '-t', 'mangle', '-N', 'DIVERT');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'iptables', '-t', 'mangle', '-A', 'PREROUTING', '-p', 'tcp', '-m', 'socket', '-j', 'DIVERT');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'iptables', '-t', 'mangle', '-A', 'DIVERT', '-j', 'MARK', '--set-mark', '1');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'iptables', '-t', 'mangle', '-A', 'DIVERT', '-j', 'ACCEPT');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ip', 'rule', 'add', 'fwmark', '1', 'lookup', '100');
    checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ip', 'route', 'add', 'local', '0.0.0.0/0', 'dev', 'lo', 'table', '100');

    #checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'iptables-save');
    #checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ip', '-d', 'addr', 'show');
    #checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ip', '-d', 'route', 'show');

    checked_system('ip', 'link', 'set', 'veth-clt-proxy', 'up', 'netns', 'ns-test-clt');
    checked_system('ip', 'netns', 'exec', 'ns-test-clt', 'ip', 'addr', 'add', '192.0.2.6/30', 'dev', 'veth-clt-proxy');

    #checked_system('ip', 'netns', 'exec', 'ns-test-clt', 'ip', '-d', 'addr', 'show');
    #checked_system('ip', 'netns', 'exec', 'ns-test-clt', 'ip', '-d', 'route', 'show');

    my $proxy_pid = start_child('proxy', \&proxy, $config, @ARGV);
    my $httpd_pid = start_child('server', \&TestHTTPD::httpd, ip => '192.0.2.2', port => $httpd_port);
    my $tcpdump1_pid = start_child('tcpdump', \&exec_cmd, 'ip', 'netns', 'exec', 'ns-test-proxy', 'tcpdump', '-npi' ,'veth-proxy-clt', '-s', '0', '-w', 'veth-proxy-clt.pcap');
    my $tcpdump2_pid = start_child('tcpdump', \&exec_cmd, 'ip', 'netns', 'exec', 'ns-test-proxy', 'tcpdump', '-npi' ,'veth-proxy-srv', '-s', '0', '-w', 'veth-proxy-srv.pcap');

    #checked_system('ip', 'netns', 'exec', 'ns-test-proxy', 'ss', '-lptn');

    # Wait for proxy to load and parse config
    wait_for_port(ip => '192.0.2.2', port => $httpd_port);
    wait_for_port(ip => '192.0.2.5', port => $proxy_port);

    for (my $i = 0; $i < $workers; $i++) {
        start_child('worker', \&worker, 'localhost', '', '192.0.2.5', $proxy_port, $iterations);
    }

    # Wait for all our children to finish
    wait_for_type('worker');

    # Give the proxy a second to flush buffers and close server connections
    sleep 1;

    # Orderly shutdown of the server
    kill 15, $proxy_pid;
    kill 15, $httpd_pid;
    kill 15, $tcpdump1_pid;
    kill 15, $tcpdump2_pid;
    sleep 1;

    # Delete our test configuration
    unlink($config);

    # Kill off any remaining children
    reap_children();

    # Cleanup our network name spaces
    checked_system('ip', 'netns', 'delete', 'ns-test-clt');
    checked_system('ip', 'netns', 'delete', 'ns-test-proxy');
}

main();