Skip to content

Commit

Permalink
Firebase: Merge from master. (#53)
Browse files Browse the repository at this point in the history
* Simple TCP server to show how to retrieve original dest IP:port after an iptables redirect (#38)

* Simple TCP server to show how to retrieve original dest IP:port after an iptables redirect

* Fixed style.

* Rebase Envoy (#41)

* Update prototype to use iptables (#42)

* Rebase to fixed Envoy (#43)

* Handle HEAD request. (#34)

* Handle HEAD request.

* Try with GET if HEAD fails.

* Address comments.

* Format file.

* Expose bazel target (#48)

* Try again (#49)
  • Loading branch information
sarvaniv authored Jan 24, 2017
1 parent e3eb7c8 commit fe30dc1
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 18 deletions.
13 changes: 12 additions & 1 deletion contrib/endpoints/src/api_manager/context/service_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const double kDefaultTraceSampleQps = 0.1;
// The time window to send intermediate report for Grpc streaming (second).
// Default to 10s.
const int kIntermediateReportInterval = 10;

const char kHTTPHeadMethod[] = "HEAD";
const char kHTTPGetMethod[] = "GET";
}

ServiceContext::ServiceContext(std::unique_ptr<ApiManagerEnvInterface> env,
Expand Down Expand Up @@ -74,7 +77,15 @@ MethodCallInfo ServiceContext::GetMethodCallInfo(
if (config_ == nullptr) {
return MethodCallInfo();
}
return config_->GetMethodCallInfo(http_method, url, query_params);
MethodCallInfo method_call_info =
config_->GetMethodCallInfo(http_method, url, query_params);
// HEAD should be treated as GET unless it is specified from service_config.
if (method_call_info.method_info == nullptr &&
http_method == kHTTPHeadMethod) {
method_call_info =
config_->GetMethodCallInfo(kHTTPGetMethod, url, query_params);
}
return method_call_info;
}

const std::string& ServiceContext::project_id() const {
Expand Down
6 changes: 6 additions & 0 deletions contrib/tools/server/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
server:
$(CC) -g -o server server.c

clean:
rm server

25 changes: 25 additions & 0 deletions contrib/tools/server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Sample TCP Server

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.

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.

So, for example, if you have the server listening on port 3490 on the local machine and an iptables rule like:

```
iptables -t nat -I OUTPUT 1 -p tcp --dport 4000:5000 -j REDIRECT --to-port 3490
```
your will see:

```
$ telnet localhost 3490
FROM 127.0.0.1:44978, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:3490
$ telnet localhost 4100
FROM 127.0.0.1:35476, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:4100
$ telnet 1.1.1.1 5000
FROM 100.100.100.100:60275, TO 127.0.0.1:3490, ORIG DEST 1.1.1.1:5000
```


169 changes: 169 additions & 0 deletions contrib/tools/server/server.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
** server.c
* Demo server to check that we can extract all parameters from an incoming
*request
* and respond to it
*/

#include <arpa/inet.h>
#include <errno.h>
#include <linux/netfilter_ipv4.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define DEFAULT_PORT \
"3490" // the port users will be connecting to, if not specified on command
// line

#define QUEUE_SIZE 10 // pending connections queue size

void sigchld_handler(int s) {
int saved_errno = errno;

while (waitpid(-1, NULL, WNOHANG) > 0)
;

errno = saved_errno;
}

int main(int argc, char *argv[]) {
struct sigaction sa;
const int yes = 1;
char *port = DEFAULT_PORT;
int rv;

if (argc > 1) {
port = argv[1];
}

struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP

struct addrinfo *server_info = NULL;
if ((rv = getaddrinfo(NULL, port, &hints, &server_info)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}

struct addrinfo *p;
int sockfd; // listen on sock_fd
for (p = server_info; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}

if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}

break;
}

freeaddrinfo(server_info);

if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, QUEUE_SIZE) == -1) {
perror("listen");
exit(1);
}

sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}

char bind_addr_str[INET6_ADDRSTRLEN] = {0};
struct sockaddr_in *bind_sock_addr = (struct sockaddr_in *)p->ai_addr;
inet_ntop(p->ai_family, &bind_sock_addr->sin_addr, bind_addr_str,
p->ai_addrlen);
printf("server %s: waiting for connections on port %s:%u...\n", port,
bind_addr_str, ntohs(bind_sock_addr->sin_port));

while (1) {
socklen_t addr_len = sizeof(struct sockaddr_storage);
int addr_str_len = INET6_ADDRSTRLEN;

struct sockaddr_storage their_addr = {
0}; // connector's address information
int accepted_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_len);
if (accepted_fd == -1) {
perror("accept");
continue;
}

struct sockaddr_in *src_sock_addr = (struct sockaddr_in *)&their_addr;
char their_addr_str[INET6_ADDRSTRLEN] = {0};
inet_ntop(their_addr.ss_family, &src_sock_addr->sin_addr, their_addr_str,
addr_str_len);
printf("server %s: got connection FROM %s:%u\n", port, their_addr_str,
ntohs(src_sock_addr->sin_port));

struct sockaddr_storage my_addr = {0}; // my address information
struct sockaddr_in *dst_sock_addr = (struct sockaddr_in *)&my_addr;
char my_addr_str[INET6_ADDRSTRLEN] = {0};
getsockname(accepted_fd, (struct sockaddr *)dst_sock_addr, &addr_len);
inet_ntop(my_addr.ss_family, &dst_sock_addr->sin_addr, my_addr_str,
addr_str_len);
printf("server %s: got connection TO %s:%u\n", port, my_addr_str,
ntohs(dst_sock_addr->sin_port));

struct sockaddr_storage orig_addr = {0}; // orig address information
struct sockaddr_in *orig_sock_addr = (struct sockaddr_in *)&orig_addr;
char orig_addr_str[INET6_ADDRSTRLEN] = {0};
int status = getsockopt(accepted_fd, SOL_IP, SO_ORIGINAL_DST,
orig_sock_addr, &addr_len);

if (status == 0) {
inet_ntop(orig_addr.ss_family, &orig_sock_addr->sin_addr, orig_addr_str,
addr_str_len);
printf("server %s: ORIG DEST %s:%u\n", port, orig_addr_str,
ntohs(orig_sock_addr->sin_port));
} else {
printf("Could not get orig destination from accepted socket.\n");
}

if (!fork()) { // this is the child process

close(sockfd);
char msg[256] = {0};
snprintf(msg, 256, "FROM %s:%u, TO %s:%u, ORIG DEST %s:%u\n",
their_addr_str, ntohs(src_sock_addr->sin_port), my_addr_str,
ntohs(dst_sock_addr->sin_port), orig_addr_str,
ntohs(orig_sock_addr->sin_port));

if (send(accepted_fd, msg, strlen(msg), 0) == -1) {
perror("send");
}
close(accepted_fd);
exit(0);
}
close(accepted_fd);
}

return 0;
}
2 changes: 2 additions & 0 deletions src/envoy/prototype/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
################################################################################
#

package(default_visibility = ["//visibility:public"])

cc_binary(
name = "envoy_esp",
srcs = [
Expand Down
13 changes: 13 additions & 0 deletions src/envoy/prototype/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ This Proxy will use Envoy and talk to Mixer server.
go run echo.go
```

* Modify your iptables:

```
sudo iptables -t nat -A OUTPUT -p tcp --dport 9090 -j REDIRECT --to-port 9092
```

Once you are done, you should remove this rule:

```
sudo iptables -t nat -D OUTPUT -p tcp --dport 9090 -j REDIRECT --to-port 9092
```


* Start Envoy proxy, run

```
Expand Down
11 changes: 9 additions & 2 deletions src/envoy/prototype/envoy-esp.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"listeners": [
{
"port": 9092,
"bind_to_port": true,
"use_original_dst": true,
"filters": []
},
{
"port": 9090,
"bind_to_port": false,
"filters": [
{
"type": "read",
Expand All @@ -26,7 +33,7 @@
},
"access_log": [
{
"path": "/tmp/access.envoy"
"path": "/dev/stdout"
}
],
"filters": [
Expand All @@ -50,7 +57,7 @@
}
],
"admin": {
"access_log_path": "/tmp/access.envoy",
"access_log_path": "/dev/stdout",
"port": 9001
},
"cluster_manager": {
Expand Down
2 changes: 1 addition & 1 deletion src/envoy/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,6 @@ cc_test(
native.new_git_repository(
name = "envoy_git",
remote = "https://github.com/lyft/envoy.git",
commit = "6b1336a786ebe56c45a1a349ddf706e0526c1ec1", # 2017-01-03
commit = "c36bb76e304ab4c5584a07303e2953a170888319",
build_file_content = BUILD,
)
21 changes: 7 additions & 14 deletions test/backend/echo/echo.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,22 @@ import (
"io/ioutil"
"net/http"
"strconv"
"sync"
"time"
)

var (
port = flag.Int("port", 8080, "default http port")

mu sync.Mutex
requests = 0
data = 0
)

func handler(w http.ResponseWriter, r *http.Request) {
fmt.Printf("%v %v %v %v\n", r.Method, r.URL, r.Proto, r.RemoteAddr)
for name, headers := range r.Header {
for _, h := range headers {
fmt.Printf("%v: %v\n", name, h)
}
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand All @@ -51,24 +54,14 @@ func handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write(body)

mu.Lock()
requests++
data += len(body)
defer mu.Unlock()
fmt.Printf("Requests Requests: %v Data: %v\n", requests, data)
}

func main() {
flag.Parse()

go func() {
for {
mu.Lock()
fmt.Printf("Requests Requests: %v Data: %v\n", requests, data)
mu.Unlock()
time.Sleep(time.Second)
}
}()

fmt.Printf("Listening on port %v\n", *port)

http.HandleFunc("/", handler)
Expand Down

0 comments on commit fe30dc1

Please sign in to comment.