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)

Reply via email to