Dear haproxy list,

We've been working on a project that involves using haproxy to proxy
between multiple networks represented as multiple network namespaces on the
proxy host.

The setup is something like this:

net1 on VLAN 1 (namespace 1) -\
net2 on VLAN 2 (namespace 2) -- haproxy ==== proxy (namespace 0)
net3 on VLAN 3 (namespace 3) -/

The haproxy config is something like this:

= 8< =
frontend clients
  bind 127.0.0.1:50022 namespace 1 transparent
  default_backend scb

backend server
  mode tcp
  server server1 192.168.122.4:2222 namespace 0 send-proxy-v2
= 8< =

The first patch makes it possible to create binds and servers in specific
namespaces. Haproxy then creates sockets in the appropriate network
namespace when creating listeners or server-side connections. (More
information is available in the commit message.)

The second patch extends the proxy protocol (v2) so that the originating
namespace of the connection is represented as a new TLV value.

These changes allow us to use haproxy in front of a non-namespace-aware
proxy or server to implement multi-tenancy in a simple and straightforward
way.

We'd love to receive feedback on the approach in general, or the patches in
particular.

Cheers,
Saras
From ff497ec77277ee19ebbf200953ed2e92b58356ff Mon Sep 17 00:00:00 2001
From: Sarkozi Laszlo <laszlo.sark...@balabit.com>
Date: Fri, 29 Aug 2014 11:42:31 +0200
Subject: [PATCH 1/2] namespace: add namespace support for bind and server
 stanzas in config

This patch makes it possible to create binds and servers in separate
namespaces.  This can be used to proxy between multiple completely independent
virtual networks (with possibly overlapping IP addresses) and a
non-namespace-aware proxy implementation that supports the proxy protocol (v2).

The setup is something like this:

net1 on VLAN 1 (namespace 1) -\
net2 on VLAN 2 (namespace 2) -- haproxy ==== proxy (namespace 0)
net3 on VLAN 3 (namespace 3) -/

The proxy is configured to make server connections through haproxy and sending
the expected source/target addresses to haproxy using the proxy protocol.

The network namespace setup on the haproxy node is something like this:

= 8< =
$ cat setup.sh
ip netns add 1
ip link add link eth1 type vlan id 1
ip link set eth1.1 netns 1
ip netns exec 1 ip addr add 192.168.91.2/24 dev eth1.1
ip netns exec 1 ip link set eth1.$id up
...
= 8< =

= 8< =
$ cat haproxy.cfg
frontend clients
  bind 127.0.0.1:50022 namespace 1 transparent
  default_backend scb

backend server
  mode tcp
  server server1 192.168.122.4:2222 namespace 0 send-proxy-v2
= 8< =

A bind line creates the listener in the specified namespace, and connections
originating from that listener also have their network namespace set to
that of the listener.

A server line either forces the connection to be made in a specified
namespace or may use the namespace from the client-side connection if that
was set.
---
 Makefile                   |  3 +-
 include/common/namespace.h |  5 ++++
 include/proto/backend.h    |  1 +
 include/proto/connection.h | 10 +++++++
 include/types/connection.h |  6 +++-
 include/types/listener.h   |  3 ++
 include/types/server.h     |  4 +++
 src/backend.c              | 43 +++++++++++++++++++++++++++
 src/connection.c           | 10 +++++++
 src/haproxy.c              |  5 ++++
 src/namespace.c            | 74 ++++++++++++++++++++++++++++++++++++++++++++++
 src/proto_tcp.c            | 50 +++++++++++++++++++++++++++----
 src/server.c               | 17 +++++++++++
 src/session.c              |  5 ++++
 14 files changed, 229 insertions(+), 7 deletions(-)
 create mode 100644 include/common/namespace.h
 create mode 100644 src/namespace.c

diff --git a/Makefile b/Makefile
index 0d1d13a..bd91c81 100644
--- a/Makefile
+++ b/Makefile
@@ -657,7 +657,8 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocol.o \
        src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
        src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
        src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o \
-       src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o
+       src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o \
+       src/namespace.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
               $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
diff --git a/include/common/namespace.h b/include/common/namespace.h
new file mode 100644
index 0000000..530ed62
--- /dev/null
+++ b/include/common/namespace.h
@@ -0,0 +1,5 @@
+#ifndef NAMESPACE_H
+
+int namespace_socket(const char *ns_name, int domain, int type, int protocol);
+
+#endif
diff --git a/include/proto/backend.h b/include/proto/backend.h
index 56df516..7d7a911 100644
--- a/include/proto/backend.h
+++ b/include/proto/backend.h
@@ -32,6 +32,7 @@
 
 int assign_server(struct session *s);
 int assign_server_address(struct session *s);
+int set_srv_conn_network_namespace(struct session *s, struct connection *cli_conn, struct connection *srv_conn);
 int assign_server_and_queue(struct session *s);
 int connect_server(struct session *s);
 int srv_redispatch_connect(struct session *t);
diff --git a/include/proto/connection.h b/include/proto/connection.h
index c9972b1..f8743bb 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -162,6 +162,14 @@ void conn_update_sock_polling(struct connection *c);
  */
 void conn_update_data_polling(struct connection *c);
 
+/*
+ * Delete connection <conn>'s network_namspace variable if it's come from proxy
+ * protocol. Allocated memory depends on CO_FL_NAMESPACE_RECV flag. The connection
+ * flags are updated with the new flags at the end of the
+ * operation.
+ */
+void conn_clear_network_namespace(struct connection* conn);
+
 /* Refresh the connection's polling flags from its file descriptor status.
  * This should be called at the beginning of a connection handler.
  */
@@ -458,6 +466,7 @@ static inline void conn_init(struct connection *conn)
 	conn->t.sock.fd = -1; /* just to help with debugging */
 	conn->err_code = CO_ER_NONE;
 	conn->target = NULL;
+	conn->network_namespace = NULL;
 }
 
 /* Tries to allocate a new connection and initialized its main fields. The
@@ -477,6 +486,7 @@ static inline struct connection *conn_new()
 /* Releases a connection previously allocated by conn_new() */
 static inline void conn_free(struct connection *conn)
 {
+	conn_clear_network_namespace(conn);
 	pool_free2(pool2_connection, conn);
 }
 
diff --git a/include/types/connection.h b/include/types/connection.h
index b317007..89f4f38 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -119,7 +119,10 @@ enum {
 	 */
 	CO_FL_POLL_SOCK     = CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN,
 
-	/* unused : 0x10000000, 0x20000000, 0x40000000 */
+	CO_FL_NAMESPACE_SET = 0x10000000, /* receive a network namespace from config*/
+	CO_FL_NAMESPACE_RECV = 0x20000000, /* receive a network namespace from proxy protocol */
+
+	/* unused : 0x40000000 */
 
 	/* This last flag indicates that the transport layer is used (for instance
 	 * by logs) and must not be cleared yet. The last call to conn_xprt_close()
@@ -261,6 +264,7 @@ struct connection {
 		} sock;
 	} t;
 	enum obj_type *target;        /* the target to connect to (server, proxy, applet, ...) */
+	char *network_namespace;      /* contains network namespace name or NULL */
 	struct {
 		struct sockaddr_storage from;	/* client address, or address to spoof when connecting to the server */
 		struct sockaddr_storage to;	/* address reached by the client, or address to connect to */
diff --git a/include/types/listener.h b/include/types/listener.h
index 83b63af..7af8288 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -92,6 +92,7 @@ enum li_state {
 #define LI_O_TCP_FO     0x0100  /* enable TCP Fast Open (linux >= 3.7) */
 #define LI_O_V6ONLY     0x0200  /* bind to IPv6 only on Linux >= 2.4.21 */
 #define LI_O_V4V6       0x0400  /* bind to IPv4/IPv6 on Linux >= 2.4.21 */
+#define LI_O_NAMESPACE_SET 0x0800 /* namespace was set in the config */
 
 /* Note: if a listener uses LI_O_UNLIMITED, it is highly recommended that it adds its own
  * maxconn setting to the global.maxsock value so that its resources are reserved.
@@ -177,6 +178,8 @@ struct listener {
 	int maxseg;			/* for TCP, advertised MSS */
 	char *interface;		/* interface name or NULL */
 
+	char *network_namespace;        /* contains network namespace name or NULL. Network namespace comes from configuration */
+
 	struct list by_fe;              /* chaining in frontend's list of listeners */
 	struct list by_bind;            /* chaining in bind_conf's list of listeners */
 	struct bind_conf *bind_conf;	/* "bind" line settings, include SSL settings among other things */
diff --git a/include/types/server.h b/include/types/server.h
index 94f9a0f..9d5c6ed 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -85,6 +85,8 @@ enum srv_admin {
 #define SRV_F_BACKUP       0x0001        /* this server is a backup server */
 #define SRV_F_MAPPORTS     0x0002        /* this server uses mapped ports */
 #define SRV_F_NON_STICK    0x0004        /* never add connections allocated to this server to a stick table */
+#define SRV_F_USE_CONFIGURED_NAMESPACE 0x0010
+#define SRV_F_USE_NAMESPACE_FROM_PROXY_PROTOCOL 0x0020
 
 /* configured server options for send-proxy (server->pp_opts) */
 #define SRV_PP_V1          0x0001        /* proxy protocol version 1 */
@@ -191,6 +193,7 @@ struct server {
 	unsigned lb_nodes_now;                  /* number of lb_nodes placed in the tree (C-HASH) */
 	struct tree_occ *lb_nodes;              /* lb_nodes_tot * struct tree_occ */
 
+	char *network_namespace;                /* contains network namespace name or NULL. Network namespace comes from configuration */
 	/* warning, these structs are huge, keep them at the bottom */
 	struct sockaddr_storage addr;		/* the address to connect to */
 	struct protocol *proto;	                /* server address protocol */
@@ -223,6 +226,7 @@ struct server {
 		char *client_crt;		/* client certificate to send */
 	} ssl_ctx;
 #endif
+
 	struct {
 		const char *file;		/* file where the section appears */
 		int line;			/* line where the section appears */
diff --git a/src/backend.c b/src/backend.c
index a96b767..9b10cd3 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -719,6 +719,45 @@ int assign_server(struct session *s)
 	return err;
 }
 
+/*
+ * This function assigns a server connection network namespace.
+ * The network namespace is taken from the configuration file.
+ * If flag SRV_F_USE_NAMESPACE_FROM_PROXY_PROTOCOL is set server connection
+ * relay the client network namespace. If flag SRV_F_USE_CONFIGURED_NAMESPACE
+ * is set server connection namespace comes form configuration.
+ *
+ * It may return :
+ *   SRV_STATUS_OK       if everything is OK.
+ *   SRV_STATUS_INTERNAL for other unrecoverable errors.
+ *
+ * Upon successful return, the connection flag CO_FL_NAMESPACE_SET and
+ * CO_FL_NAMESPACE_RECV and network namespace are set.
+ *
+ */
+int set_srv_conn_network_namespace(struct session *s, struct connection *cli_conn, struct connection *srv_conn)
+{
+	if ((objt_server(s->target)->flags & SRV_F_USE_NAMESPACE_FROM_PROXY_PROTOCOL) &&
+		(objt_server(s->target)->flags & SRV_F_USE_CONFIGURED_NAMESPACE) )
+			return SRV_STATUS_INTERNAL;
+
+	if ((objt_server(s->target)->flags & SRV_F_USE_NAMESPACE_FROM_PROXY_PROTOCOL) &&
+			   (cli_conn->flags & CO_FL_NAMESPACE_SET)){
+
+		conn_clear_network_namespace(srv_conn);
+		srv_conn->network_namespace = strdup(cli_conn->network_namespace);
+		srv_conn->flags |= CO_FL_NAMESPACE_SET;
+		srv_conn->flags |= CO_FL_NAMESPACE_RECV;
+	}
+
+	if (objt_server(s->target)->flags & SRV_F_USE_CONFIGURED_NAMESPACE){
+			conn_clear_network_namespace(srv_conn);
+
+			srv_conn->network_namespace = (objt_server(s->target)->network_namespace);
+			srv_conn->flags |= CO_FL_NAMESPACE_SET;
+	}
+
+	return SRV_STATUS_OK;
+}
 
 /*
  * This function assigns a server address to a session, and sets SN_ADDR_SET.
@@ -802,6 +841,10 @@ int assign_server_address(struct session *s)
 		return SRV_STATUS_INTERNAL;
 	}
 
+	/* Configure network namespace */
+	if(set_srv_conn_network_namespace(s,cli_conn,srv_conn) == SRV_STATUS_INTERNAL)
+		return SRV_STATUS_INTERNAL;
+
 	s->flags |= SN_ADDR_SET;
 	return SRV_STATUS_OK;
 }
diff --git a/src/connection.c b/src/connection.c
index 3af6d9a..e0b6172 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -217,6 +217,16 @@ void conn_update_sock_polling(struct connection *c)
 	c->flags = f;
 }
 
+void conn_clear_network_namespace(struct connection* conn)
+{
+	if((conn->flags & CO_FL_NAMESPACE_RECV) && (conn->network_namespace != NULL))
+	{
+		free(conn->network_namespace);
+		conn->network_namespace = NULL;
+		conn->flags &= ~CO_FL_NAMESPACE_RECV;
+	}
+}
+
 /* This handshake handler waits a PROXY protocol header at the beginning of the
  * raw data stream. The header looks like this :
  *
diff --git a/src/haproxy.c b/src/haproxy.c
index 13c3d26..df02401 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1176,6 +1176,9 @@ void deinit(void)
 			free(s->agent.bi);
 			free(s->agent.bo);
 			free((char*)s->conf.file);
+			if (s->network_namespace != NULL) {
+				free(s->network_namespace);
+			}
 			free(s);
 			s = s_next;
 		}/* end while(s) */
@@ -1187,6 +1190,8 @@ void deinit(void)
 			LIST_DEL(&l->by_bind);
 			free(l->name);
 			free(l->counters);
+			if(l->network_namespace != NULL)
+				free(l->network_namespace);
 			free(l);
 		}
 
diff --git a/src/namespace.c b/src/namespace.c
new file mode 100644
index 0000000..20d027d
--- /dev/null
+++ b/src/namespace.c
@@ -0,0 +1,74 @@
+#include <common/namespace.h>
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sched.h>
+
+static int open_named_namespace(const char *ns_name) {
+    char buf[512];
+    int fd;
+
+    snprintf(buf, sizeof(buf), "/var/run/netns/%s", ns_name);
+    fd = open(buf, O_RDONLY);
+
+    return fd;
+}
+
+static int open_current_namespace() {
+    char buf[512];
+    int fd;
+
+    snprintf(buf, sizeof(buf), "/proc/%d/ns/net", getpid());
+    fd = open(buf, O_RDONLY);
+
+    return fd;
+}
+
+static int socketat(int default_ns, int target_ns, int domain, int type, int protocol) {
+    int sock;
+
+    if (setns(target_ns, CLONE_NEWNET) == -1)
+        return -1;
+
+    sock = socket(domain, type, protocol);
+
+    if (setns(default_ns, CLONE_NEWNET) == -1) {
+        close(sock);
+        return -1;
+    }
+
+    return sock;
+}
+
+int namespace_socket(const char *ns_name, int domain, int type, int protocol) {
+    int default_ns, target_ns, sock;
+
+    if (ns_name) {
+        default_ns = open_current_namespace();
+        if (default_ns == -1) {
+            return -1;
+        }
+
+        target_ns = open_named_namespace(ns_name);
+        if (target_ns == -1) {
+            close(default_ns);
+            return -1;
+        }
+
+        sock = socketat(default_ns, target_ns, domain, type, protocol);
+
+        close(target_ns);
+        close(default_ns);
+    }
+    else {
+        sock = socket(domain, type, protocol);
+    }
+
+    return sock;
+}
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 72dc92b..94cb091 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -33,6 +33,7 @@
 #include <common/errors.h>
 #include <common/mini-clist.h>
 #include <common/standard.h>
+#include <common/namespace.h>
 
 #include <types/global.h>
 #include <types/capture.h>
@@ -284,6 +285,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 	struct server *srv;
 	struct proxy *be;
 	struct conn_src *src;
+	int namespace_set = conn->flags & CO_FL_NAMESPACE_SET;
 
 	conn->flags = CO_FL_WAIT_L4_CONN; /* connection in progress */
 
@@ -301,7 +303,13 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 		return SN_ERR_INTERNAL;
 	}
 
-	if ((fd = conn->t.sock.fd = socket(conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+	if (namespace_set) {
+		fd = conn->t.sock.fd = namespace_socket(conn->network_namespace, conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP);
+	} else {
+		fd = conn->t.sock.fd = socket(conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP);
+	}
+
+	if (fd == -1) {
 		qfprintf(stderr, "Cannot get a server socket.\n");
 
 		if (errno == ENFILE) {
@@ -732,10 +740,18 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
 	fd = listener->fd;
 	ext = (fd >= 0);
 
-	if (!ext && (fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		err |= ERR_RETRYABLE | ERR_ALERT;
-		msg = "cannot create listening socket";
-		goto tcp_return;
+	if (!ext) {
+		if (listener->options & LI_O_NAMESPACE_SET) {
+			fd = namespace_socket(listener->network_namespace, listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
+		} else {
+			fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
+		}
+
+		if (fd == -1) {
+			err |= ERR_RETRYABLE | ERR_ALERT;
+			msg = "cannot create listening socket";
+			goto tcp_return;
+		}
 	}
 
 	if (fd >= global.maxsock) {
@@ -1994,6 +2010,29 @@ static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, stru
 }
 #endif
 
+/* parse the "namespace" bind keyword */
+static int bind_parse_namespace(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+	struct listener *l;
+	char *namespace = NULL;
+
+	if (!*args[cur_arg + 1]) {
+		memprintf(err, "'%s' : missing namespace id", args[cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+	namespace = args[cur_arg + 1];
+
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		l->network_namespace = strdup(namespace);
+		if(l->network_namespace == NULL){
+			Alert("Cannot allocate memory for network namespace: '%s'.\n",args[cur_arg + 1]);
+			return ERR_ALERT | ERR_FATAL;
+		}
+		l->options |= LI_O_NAMESPACE_SET;
+	}
+	return 0;
+}
+
 static struct cfg_kw_list cfg_kws = {ILH, {
 	{ CFG_LISTEN, "tcp-request",  tcp_parse_tcp_req },
 	{ CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
@@ -2053,6 +2092,7 @@ static struct bind_kw_list bind_kws = { "TCP", { }, {
 	{ "v4v6",          bind_parse_v4v6,         0 }, /* force socket to bind to IPv4+IPv6 */
 	{ "v6only",        bind_parse_v6only,       0 }, /* force socket to bind to IPv6 only */
 #endif
+	{ "namespace",     bind_parse_namespace,    1 },
 	/* the versions with the NULL parse function*/
 	{ "defer-accept",  NULL,  0 },
 	{ "interface",     NULL,  1 },
diff --git a/src/server.c b/src/server.c
index fdb63cc..7fab99a 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1501,6 +1501,23 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
+			else if (!defsrv && !strcmp(args[cur_arg], "namespace")) {
+				// FIXME: set a flag indicating that the namespace has been set
+				char *arg = args[cur_arg + 1];
+				if (!strcmp(arg, "fromproxy")) {
+					newsrv->flags |= SRV_F_USE_NAMESPACE_FROM_PROXY_PROTOCOL;
+				} else {
+					newsrv->network_namespace = strdup(args[cur_arg + 1]);
+					if( newsrv->network_namespace == NULL)
+					{
+						Alert("Cannot allocate memory for network namespace: '%s'.\n",args[cur_arg + 1]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					newsrv->flags |= SRV_F_USE_CONFIGURED_NAMESPACE;
+				}
+				cur_arg += 2;
+			}
 			else {
 				static int srv_dumped;
 				struct srv_kw *kw;
diff --git a/src/session.c b/src/session.c
index aeaa7e1..10532ae 100644
--- a/src/session.c
+++ b/src/session.c
@@ -91,6 +91,11 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
 	cli_conn->flags |= CO_FL_ADDR_FROM_SET;
 	cli_conn->target = &l->obj_type;
 
+	if (l->options & LI_O_NAMESPACE_SET) {
+		cli_conn->flags |= CO_FL_NAMESPACE_SET;
+		cli_conn->network_namespace = l->network_namespace;
+	}
+
 	if (unlikely((s = pool_alloc2(pool2_session)) == NULL))
 		goto out_free_conn;
 
-- 
1.9.1

From 68244eb01c5488455a7ff08395ba5dd8ee025bde Mon Sep 17 00:00:00 2001
From: KOVACS Krisztian <hid...@balabit.com>
Date: Wed, 6 Aug 2014 16:59:25 +0200
Subject: [PATCH 2/2] namespace: add support for namespace metainfo to proxy
 protocol v2

The namespace name is wrapped into a TLV with id 0x30.
---
 include/types/connection.h |  1 +
 src/connection.c           | 81 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/include/types/connection.h b/include/types/connection.h
index 89f4f38..a75d45d 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -334,6 +334,7 @@ struct proxy_hdr_v2 {
 #define PP2_TYPE_SSL           0x20
 #define PP2_TYPE_SSL_VERSION   0x21
 #define PP2_TYPE_SSL_CN        0x22
+#define PP2_TYPE_NETWORK_NAMESPACE 0x30
 
 struct tlv {
 	uint8_t type;
diff --git a/src/connection.c b/src/connection.c
index e0b6172..9446b1e 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -227,6 +227,37 @@ void conn_clear_network_namespace(struct connection* conn)
 	}
 }
 
+/*
+ * Get data length from tlv
+ */
+static int get_tlv_length(struct tlv * src)
+{
+	return (src->length_hi << 8) | src->length_lo;
+}
+
+/*
+ * Get tlv data from tlv struct. Function return value is length of data
+ */
+static int get_tlv_data(struct tlv * src,char *dst, int dst_length)
+{
+	int length = get_tlv_length(src);
+	if(dst_length < length)
+		return -1;
+	strncpy(dst, (char *)src->value, length);
+	dst[length] = '\0';
+	return length;
+}
+
+static int search_tlv(int type, unsigned int len, char *tlv_packets)
+{
+	int parsed_length = 0;
+
+	while (parsed_length <= len && type != tlv_packets[parsed_length])
+		parsed_length += get_tlv_length((struct tlv*) &tlv_packets[parsed_length]);
+
+	return parsed_length;
+}
+
 /* This handshake handler waits a PROXY protocol header at the beginning of the
  * raw data stream. The header looks like this :
  *
@@ -255,6 +286,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
 	char *line, *end;
 	struct proxy_hdr_v2 *hdr_v2;
 	const char v2sig[] = PP2_SIGNATURE;
+	int tlv_length = 0;
 
 	/* we might have been called just after an asynchronous shutr */
 	if (conn->flags & CO_FL_SOCK_RD_SH)
@@ -432,6 +464,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
 
 	switch (hdr_v2->ver_cmd & PP2_CMD_MASK) {
 	case 0x01: /* PROXY command */
+
 		switch (hdr_v2->fam) {
 		case 0x11:  /* TCPv4 */
 			((struct sockaddr_in *)&conn->addr.from)->sin_family = AF_INET;
@@ -441,6 +474,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
 			((struct sockaddr_in *)&conn->addr.to)->sin_addr.s_addr = hdr_v2->addr.ip4.dst_addr;
 			((struct sockaddr_in *)&conn->addr.to)->sin_port = hdr_v2->addr.ip4.dst_port;
 			conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET;
+			tlv_length = ntohs(hdr_v2->len) - PP2_ADDR_LEN_INET;
 			break;
 		case 0x21:  /* TCPv6 */
 			((struct sockaddr_in6 *)&conn->addr.from)->sin6_family = AF_INET6;
@@ -450,8 +484,40 @@ int conn_recv_proxy(struct connection *conn, int flag)
 			memcpy(&((struct sockaddr_in6 *)&conn->addr.to)->sin6_addr, hdr_v2->addr.ip6.dst_addr, 16);
 			((struct sockaddr_in6 *)&conn->addr.to)->sin6_port = hdr_v2->addr.ip6.dst_port;
 			conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET;
+			tlv_length = ntohs(hdr_v2->len) - PP2_ADDR_LEN_INET6;
 			break;
 		}
+
+		/*tlv parsing*/
+		if(tlv_length)
+		{
+			// data from:
+			char * tlv_packets = &trash.str[trash.len-tlv_length];
+
+			int tlv_first_byte = 0;
+
+			/*search tlv data, we interested in*/
+			tlv_first_byte = search_tlv(PP2_TYPE_NETWORK_NAMESPACE, tlv_length, tlv_packets);
+
+			/*if tlv_lenght*/
+			if(tlv_first_byte < tlv_length){
+				int network_namspace_leght = 0;
+				network_namspace_leght = get_tlv_length((struct tlv*) &tlv_packets[tlv_first_byte]) + 1;
+				/*if the buffer is not enough for namespace*/
+				conn_clear_network_namespace(conn);
+				conn->network_namespace = (char*) malloc(network_namspace_leght * sizeof(char));
+				conn->flags |= CO_FL_NAMESPACE_RECV;
+
+				if(-1 == get_tlv_data((struct tlv*) &tlv_packets[tlv_first_byte], conn->network_namespace, network_namspace_leght)){
+					goto bad_header;
+				}
+				else
+				{
+					conn->flags |= CO_FL_NAMESPACE_SET;
+				}
+			}
+		}
+
 		/* unsupported protocol, keep local connection address */
 		break;
 	case 0x00: /* LOCAL command */
@@ -601,7 +667,6 @@ int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, str
 	return ret;
 }
 
-#ifdef USE_OPENSSL
 static int make_tlv(char *dest, int dest_len, char type, uint16_t length, char *value)
 {
 	struct tlv *tlv;
@@ -617,18 +682,18 @@ static int make_tlv(char *dest, int dest_len, char type, uint16_t length, char *
 	memcpy(tlv->value, value, length);
 	return length + sizeof(*tlv);
 }
-#endif
 
 int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote)
 {
 	const char pp2_signature[] = PP2_SIGNATURE;
 	int ret = 0;
+	int tlv_len = 0;
 	struct proxy_hdr_v2 *hdr = (struct proxy_hdr_v2 *)buf;
 	struct sockaddr_storage null_addr = {0};
 	struct sockaddr_storage *src = &null_addr;
 	struct sockaddr_storage *dst = &null_addr;
+
 #ifdef USE_OPENSSL
-	int tlv_len = 0;
 	char *value = NULL;
 	struct tlv_ssl *tlv;
 	int ssl_tlv_len = 0;
@@ -643,6 +708,7 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec
 		src = &remote->addr.from;
 		dst = &remote->addr.to;
 	}
+
 	if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET) {
 		if (buf_len < PP2_HDR_LEN_INET)
 			return 0;
@@ -707,6 +773,15 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec
 		ret += ssl_tlv_len;
 	}
 #endif
+	//PP2_TYPE_NETWORK_NAMESPACE
+
+	if( remote && (remote->flags & CO_FL_NAMESPACE_SET) ){
+		if ((buf_len - ret) < sizeof(struct tlv))
+					return 0;
+
+		tlv_len = make_tlv(&buf[ret], buf_len, PP2_TYPE_NETWORK_NAMESPACE, strlen(remote->network_namespace), remote->network_namespace);
+		ret += tlv_len;
+	}
 
 	hdr->len = htons((uint16_t)(ret - PP2_HEADER_LEN));
 
-- 
1.9.1

Reply via email to