As a foundation for extending the proxy-protocol to include additional
information, I've implemented version 2 of the proxy protocol. As we
discussed in the "Extending PROXY protocol for SSL" thread, I made one
change to the protocol.
Version and Command are combined into one byte.
Length is now two bytes.
+struct proxy_hdr_v2 {
+ uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+ uint8_t cmd; /* protocol version and command */
+ uint8_t fam; /* protocol family and transport */
+ uint16_t len; /* number of following bytes part of the header */
+};
The server keyword "send-proxy-v2" will cause Version 2 of the protocol to
be sent.
Please let me know what you think.
--Dave
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 8609f17..f8412bb 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -41,7 +41,9 @@ int conn_fd_handler(int fd);
/* receive a PROXY protocol header over a connection */
int conn_recv_proxy(struct connection *conn, int flag);
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src,
struct sockaddr_storage *dst);
+int make_proxy_line(char *buf, int buf_len, unsigned int options, struct
connection *remote);
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src,
struct sockaddr_storage *dst);
+int make_proxy_line_v2(char *buf, int buf_len, unsigned int options, struct
connection *remote);
/* returns true is the transport layer is ready */
static inline int conn_xprt_ready(const struct connection *conn)
diff --git a/include/types/connection.h b/include/types/connection.h
index 5341a86..b3b85ab 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -245,6 +245,7 @@ struct connection {
enum obj_type obj_type; /* differentiates connection from applet
context */
unsigned char err_code; /* CO_ER_* */
signed short send_proxy_ofs; /* <0 = offset to (re)send from the end,
>0 = send all */
+ unsigned int send_proxy_opts; /* PROXY protocol option flags */
unsigned int flags; /* CO_FL_* */
const struct protocol *ctrl; /* operations at the socket layer */
const struct xprt_ops *xprt; /* operations at the transport layer */
@@ -265,6 +266,61 @@ struct connection {
} addr; /* addresses of the remote side, client for producer and server
for consumer */
};
+/* proxy protocol option flags */
+enum {
+ CO_PP_V2 = 0x0001
+};
+
+/* proxy protocol v2 definitions */
+#define PP2_SIGNATURE_LEN 12
+#define PP2_HEADER_LEN 16
+#define PP2_VERSION 0x20
+#define PP2_CMD_LOCAL 0x00
+#define PP2_CMD_PROXY 0x01
+#define PP2_FAM_UNSPEC 0x00
+#define PP2_FAM_INET 0x10
+#define PP2_FAM_INET6 0x20
+#define PP2_FAM_UNIX 0x30
+#define PP2_TRANS_UNSPEC 0x00
+#define PP2_TRANS_STREAM 0x01
+#define PP2_TRANS_DGRAM 0x02
+
+#define PP2_ADDR_LEN_UNSPEC 0
+#define PP2_ADDR_LEN_INET 12
+#define PP2_ADDR_LEN_INET6 36
+#define PP2_ADDR_LEN_UNIX 216
+
+#define PP2_HDR_LEN_UNSPEC (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
+#define PP2_HDR_LEN_INET (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
+#define PP2_HDR_LEN_INET6 (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
+#define PP2_HDR_LEN_UNIX (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
+
+struct proxy_hdr_v2 {
+ uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+ uint8_t cmd; /* protocol version and command */
+ uint8_t fam; /* protocol family and transport */
+ uint16_t len; /* number of following bytes part of the header */
+};
+
+union proxy_addr {
+ struct { /* for TCP/UDP over IPv4, len = 12 */
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ipv4_addr;
+ struct { /* for TCP/UDP over IPv6, len = 36 */
+ uint8_t src_addr[16];
+ uint8_t dst_addr[16];
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ipv6_addr;
+ struct { /* for AF_UNIX sockets, len = 216 */
+ uint8_t src_addr[108];
+ uint8_t dst_addr[108];
+ } unix_addr;
+};
+
#endif /* _TYPES_CONNECTION_H */
/*
diff --git a/include/types/server.h b/include/types/server.h
index 54ab813..4782607 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -57,6 +57,9 @@
#define SRV_SEND_PROXY 0x0800 /* this server talks the PROXY protocol */
#define SRV_NON_STICK 0x1000 /* never add connections allocated to this
server to a stick table */
+/* configured server options for send-proxy */
+#define SRV_PP_V2 0x0001 /* proxy protocol version 2 */
+
/* function which act on servers need to return various errors */
#define SRV_STATUS_OK 0 /* everything is OK. */
#define SRV_STATUS_INTERNAL 1 /* other unrecoverable errors. */
@@ -183,6 +186,7 @@ struct server {
int line; /* line where the section
appears */
struct eb32_node id; /* place in the tree of used
IDs */
} conf; /* config information */
+ int pp_opts; /* proxy protocol options */
};
/* Descriptor for a "server" keyword. The ->parse() function returns 0 in case
of
diff --git a/src/backend.c b/src/backend.c
index d878028..ad8812a 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1061,6 +1061,8 @@ int connect_server(struct session *s)
srv_conn->send_proxy_ofs = 0;
if (objt_server(s->target) && (objt_server(s->target)->state &
SRV_SEND_PROXY)) {
srv_conn->send_proxy_ofs = 1; /* must compute size */
+ if ((objt_server(s->target)->pp_opts & SRV_PP_V2))
+ srv_conn->send_proxy_opts |= CO_PP_V2;
cli_conn = objt_conn(s->req->prod->end);
if (cli_conn)
conn_get_to_addr(cli_conn);
diff --git a/src/connection.c b/src/connection.c
index 1483f18..2de928b 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -437,6 +437,24 @@ int conn_recv_proxy(struct connection *conn, int flag)
return 0;
}
+
+int make_proxy_line(char *buf, int buf_len, unsigned int options, struct
connection *remote)
+{
+ int ret = 0;
+
+ if (options & CO_PP_V2) {
+ ret = make_proxy_line_v2(buf, buf_len, options, remote);
+ }
+ else {
+ if (remote)
+ ret = make_proxy_line_v1(buf, buf_len,
&remote->addr.from, &remote->addr.to);
+ else
+ ret = make_proxy_line_v1(buf, buf_len, NULL, NULL);
+ }
+
+ return ret;
+}
+
/* Makes a PROXY protocol line from the two addresses. The output is sent to
* buffer <buf> for a maximum size of <buf_len> (including the trailing zero).
* It returns the number of bytes composing this line (including the trailing
@@ -444,7 +462,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
* TCP6 and "UNKNOWN" formats. If any of <src> or <dst> is null, UNKNOWN is
* emitted as well.
*/
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src,
struct sockaddr_storage *dst)
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src,
struct sockaddr_storage *dst)
{
int ret = 0;
@@ -516,3 +534,56 @@ int make_proxy_line(char *buf, int buf_len, struct
sockaddr_storage *src, struct
}
return ret;
}
+
+int make_proxy_line_v2(char *buf, int buf_len, unsigned int options, struct
connection *remote)
+{
+ const char pp2_signature[12] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D,
0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
+ int ret = 0;
+ struct proxy_hdr_v2 *hdr_p = (struct proxy_hdr_v2 *)buf;
+ union proxy_addr *addr_p = (union proxy_addr *)(buf + PP2_HEADER_LEN);
+ struct sockaddr_storage *src = NULL;
+ struct sockaddr_storage *dst = NULL;
+
+ if (buf_len < PP2_HEADER_LEN)
+ return 0;
+ memcpy(hdr_p->sig, pp2_signature, PP2_SIGNATURE_LEN);
+
+ if (remote) {
+ 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;
+ hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+ hdr_p->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
+ hdr_p->len = htons(PP2_ADDR_LEN_INET);
+ addr_p->ipv4_addr.src_addr = ((struct sockaddr_in
*)src)->sin_addr.s_addr;
+ addr_p->ipv4_addr.dst_addr = ((struct sockaddr_in
*)dst)->sin_addr.s_addr;
+ addr_p->ipv4_addr.src_port = ((struct sockaddr_in
*)src)->sin_port;
+ addr_p->ipv4_addr.dst_port = ((struct sockaddr_in
*)dst)->sin_port;
+ ret = PP2_HDR_LEN_INET;
+ }
+ else if (src && dst && src->ss_family == dst->ss_family &&
src->ss_family == AF_INET6) {
+ if (buf_len < PP2_HDR_LEN_INET6)
+ return 0;
+ hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+ hdr_p->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
+ hdr_p->len = htons(PP2_ADDR_LEN_INET6);
+ memcpy(addr_p->ipv6_addr.src_addr, &((struct sockaddr_in6
*)src)->sin6_addr, 16);
+ memcpy(addr_p->ipv6_addr.dst_addr, &((struct sockaddr_in6
*)dst)->sin6_addr, 16);
+ addr_p->ipv6_addr.src_port = ((struct sockaddr_in6
*)src)->sin6_port;
+ addr_p->ipv6_addr.dst_port = ((struct sockaddr_in6
*)dst)->sin6_port;
+ ret = PP2_HDR_LEN_INET6;
+ }
+ else {
+ if (buf_len < PP2_HDR_LEN_UNSPEC)
+ return 0;
+ hdr_p->cmd = PP2_VERSION | PP2_CMD_LOCAL;
+ hdr_p->fam = PP2_FAM_UNSPEC | PP2_TRANS_UNSPEC;
+ hdr_p->len = htons(PP2_ADDR_LEN_UNSPEC);
+ ret = PP2_HDR_LEN_UNSPEC;
+ }
+
+ return ret;
+}
diff --git a/src/server.c b/src/server.c
index 5ac5e37..cd651a0 100644
--- a/src/server.c
+++ b/src/server.c
@@ -615,6 +615,11 @@ int parse_server(const char *file, int linenum, char
**args, struct proxy *curpr
newsrv->state |= SRV_SEND_PROXY;
cur_arg ++;
}
+ else if (!defsrv && !strcmp(args[cur_arg],
"send-proxy-v2")) {
+ newsrv->state |= SRV_SEND_PROXY;
+ newsrv->pp_opts |= SRV_PP_V2;
+ cur_arg ++;
+ }
else if (!defsrv && !strcmp(args[cur_arg],
"check-send-proxy")) {
newsrv->check.send_proxy = 1;
cur_arg ++;
diff --git a/src/stream_interface.c b/src/stream_interface.c
index f23a9b0..afe4cb1 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -422,11 +422,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned
int flag)
if (conn->data == &si_conn_cb) {
struct stream_interface *si = conn->owner;
struct connection *remote =
objt_conn(si->ob->prod->end);
-
- if (remote)
- ret = make_proxy_line(trash.str, trash.size,
&remote->addr.from, &remote->addr.to);
- else
- ret = make_proxy_line(trash.str, trash.size,
NULL, NULL);
+ ret = make_proxy_line(trash.str, trash.size,
conn->send_proxy_opts, remote);
}
else {
/* The target server expects a LOCAL line to be sent
first. Retrieving
@@ -440,7 +436,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned
int flag)
if (!(conn->flags & CO_FL_ADDR_TO_SET))
goto out_wait;
- ret = make_proxy_line(trash.str, trash.size,
&conn->addr.from, &conn->addr.to);
+ ret = make_proxy_line(trash.str, trash.size,
conn->send_proxy_opts, conn);
}
if (!ret)