Skip to content

Commit 78b931b

Browse files
Simple TCP server to show how to retrieve original dest IP:port after an iptables redirect (istio#38)
* Simple TCP server to show how to retrieve original dest IP:port after an iptables redirect * Fixed style.
1 parent 8f944e6 commit 78b931b

File tree

3 files changed

+200
-0
lines changed

3 files changed

+200
-0
lines changed

contrib/tools/server/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
server:
2+
$(CC) -g -o server server.c
3+
4+
clean:
5+
rm server
6+

contrib/tools/server/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Sample TCP Server
2+
3+
This is a simple TCP server that listens on the specified port (3490 by default) and replies to incoming connections by providing info about src/destination ip/port.
4+
5+
It demonstrates the use of the getsockopt() system call with the ORIGINAL_DST option to retrieve the original destination ip/port after an iptables redirect.
6+
7+
So, for example, if you have the server listening on port 3490 on the local machine and an iptables rule like:
8+
9+
```
10+
iptables -t nat -I OUTPUT 1 -p tcp --dport 4000:5000 -j REDIRECT --to-port 3490
11+
```
12+
your will see:
13+
14+
```
15+
$ telnet localhost 3490
16+
FROM 127.0.0.1:44978, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:3490
17+
18+
$ telnet localhost 4100
19+
FROM 127.0.0.1:35476, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:4100
20+
21+
$ telnet 1.1.1.1 5000
22+
FROM 100.100.100.100:60275, TO 127.0.0.1:3490, ORIG DEST 1.1.1.1:5000
23+
```
24+
25+

contrib/tools/server/server.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
** server.c
3+
* Demo server to check that we can extract all parameters from an incoming
4+
*request
5+
* and respond to it
6+
*/
7+
8+
#include <arpa/inet.h>
9+
#include <errno.h>
10+
#include <linux/netfilter_ipv4.h>
11+
#include <netdb.h>
12+
#include <netinet/in.h>
13+
#include <signal.h>
14+
#include <stdio.h>
15+
#include <stdlib.h>
16+
#include <string.h>
17+
#include <sys/socket.h>
18+
#include <sys/types.h>
19+
#include <sys/wait.h>
20+
#include <unistd.h>
21+
22+
#define DEFAULT_PORT \
23+
"3490" // the port users will be connecting to, if not specified on command
24+
// line
25+
26+
#define QUEUE_SIZE 10 // pending connections queue size
27+
28+
void sigchld_handler(int s) {
29+
int saved_errno = errno;
30+
31+
while (waitpid(-1, NULL, WNOHANG) > 0)
32+
;
33+
34+
errno = saved_errno;
35+
}
36+
37+
int main(int argc, char *argv[]) {
38+
struct sigaction sa;
39+
const int yes = 1;
40+
char *port = DEFAULT_PORT;
41+
int rv;
42+
43+
if (argc > 1) {
44+
port = argv[1];
45+
}
46+
47+
struct addrinfo hints;
48+
memset(&hints, 0, sizeof hints);
49+
hints.ai_family = AF_INET;
50+
hints.ai_socktype = SOCK_STREAM;
51+
hints.ai_flags = AI_PASSIVE; // use my IP
52+
53+
struct addrinfo *server_info = NULL;
54+
if ((rv = getaddrinfo(NULL, port, &hints, &server_info)) != 0) {
55+
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
56+
return 1;
57+
}
58+
59+
struct addrinfo *p;
60+
int sockfd; // listen on sock_fd
61+
for (p = server_info; p != NULL; p = p->ai_next) {
62+
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
63+
perror("server: socket");
64+
continue;
65+
}
66+
67+
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
68+
perror("setsockopt");
69+
exit(1);
70+
}
71+
72+
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
73+
close(sockfd);
74+
perror("server: bind");
75+
continue;
76+
}
77+
78+
break;
79+
}
80+
81+
freeaddrinfo(server_info);
82+
83+
if (p == NULL) {
84+
fprintf(stderr, "server: failed to bind\n");
85+
exit(1);
86+
}
87+
if (listen(sockfd, QUEUE_SIZE) == -1) {
88+
perror("listen");
89+
exit(1);
90+
}
91+
92+
sa.sa_handler = sigchld_handler;
93+
sigemptyset(&sa.sa_mask);
94+
sa.sa_flags = SA_RESTART;
95+
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
96+
perror("sigaction");
97+
exit(1);
98+
}
99+
100+
char bind_addr_str[INET6_ADDRSTRLEN] = {0};
101+
struct sockaddr_in *bind_sock_addr = (struct sockaddr_in *)p->ai_addr;
102+
inet_ntop(p->ai_family, &bind_sock_addr->sin_addr, bind_addr_str,
103+
p->ai_addrlen);
104+
printf("server %s: waiting for connections on port %s:%u...\n", port,
105+
bind_addr_str, ntohs(bind_sock_addr->sin_port));
106+
107+
while (1) {
108+
socklen_t addr_len = sizeof(struct sockaddr_storage);
109+
int addr_str_len = INET6_ADDRSTRLEN;
110+
111+
struct sockaddr_storage their_addr = {
112+
0}; // connector's address information
113+
int accepted_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_len);
114+
if (accepted_fd == -1) {
115+
perror("accept");
116+
continue;
117+
}
118+
119+
struct sockaddr_in *src_sock_addr = (struct sockaddr_in *)&their_addr;
120+
char their_addr_str[INET6_ADDRSTRLEN] = {0};
121+
inet_ntop(their_addr.ss_family, &src_sock_addr->sin_addr, their_addr_str,
122+
addr_str_len);
123+
printf("server %s: got connection FROM %s:%u\n", port, their_addr_str,
124+
ntohs(src_sock_addr->sin_port));
125+
126+
struct sockaddr_storage my_addr = {0}; // my address information
127+
struct sockaddr_in *dst_sock_addr = (struct sockaddr_in *)&my_addr;
128+
char my_addr_str[INET6_ADDRSTRLEN] = {0};
129+
getsockname(accepted_fd, (struct sockaddr *)dst_sock_addr, &addr_len);
130+
inet_ntop(my_addr.ss_family, &dst_sock_addr->sin_addr, my_addr_str,
131+
addr_str_len);
132+
printf("server %s: got connection TO %s:%u\n", port, my_addr_str,
133+
ntohs(dst_sock_addr->sin_port));
134+
135+
struct sockaddr_storage orig_addr = {0}; // orig address information
136+
struct sockaddr_in *orig_sock_addr = (struct sockaddr_in *)&orig_addr;
137+
char orig_addr_str[INET6_ADDRSTRLEN] = {0};
138+
int status = getsockopt(accepted_fd, SOL_IP, SO_ORIGINAL_DST,
139+
orig_sock_addr, &addr_len);
140+
141+
if (status == 0) {
142+
inet_ntop(orig_addr.ss_family, &orig_sock_addr->sin_addr, orig_addr_str,
143+
addr_str_len);
144+
printf("server %s: ORIG DEST %s:%u\n", port, orig_addr_str,
145+
ntohs(orig_sock_addr->sin_port));
146+
} else {
147+
printf("Could not get orig destination from accepted socket.\n");
148+
}
149+
150+
if (!fork()) { // this is the child process
151+
152+
close(sockfd);
153+
char msg[256] = {0};
154+
snprintf(msg, 256, "FROM %s:%u, TO %s:%u, ORIG DEST %s:%u\n",
155+
their_addr_str, ntohs(src_sock_addr->sin_port), my_addr_str,
156+
ntohs(dst_sock_addr->sin_port), orig_addr_str,
157+
ntohs(orig_sock_addr->sin_port));
158+
159+
if (send(accepted_fd, msg, strlen(msg), 0) == -1) {
160+
perror("send");
161+
}
162+
close(accepted_fd);
163+
exit(0);
164+
}
165+
close(accepted_fd);
166+
}
167+
168+
return 0;
169+
}

0 commit comments

Comments
 (0)