MEDIUM: proxy_protocol: Support sending unique IDs using PPv2

This patch adds the `unique-id` option to `proxy-v2-options`. If this
option is set a unique ID will be generated based on the `unique-id-format`
while sending the proxy protocol v2 header and stored as the unique id for
the first stream of the connection.

This feature is meant to be used in `tcp` mode. It works on HTTP mode, but
might result in inconsistent unique IDs for the first request on a keep-alive
connection, because the unique ID for the first stream is generated earlier
than the others.

Now that we can send unique IDs in `tcp` mode the `%ID` log variable is made
available in TCP mode.
diff --git a/src/connection.c b/src/connection.c
index 728c0ec..4e48aa94 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1241,12 +1241,12 @@
 }
 
 /* Note: <remote> is explicitly allowed to be NULL */
-int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote)
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm)
 {
 	int ret = 0;
 
 	if (srv && (srv->pp_opts & SRV_PP_V2)) {
-		ret = make_proxy_line_v2(buf, buf_len, srv, remote);
+		ret = make_proxy_line_v2(buf, buf_len, srv, remote, strm);
 	}
 	else {
 		if (remote && conn_get_src(remote) && conn_get_dst(remote))
@@ -1353,7 +1353,7 @@
 }
 
 /* Note: <remote> is explicitly allowed to be NULL */
-int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote)
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm)
 {
 	const char pp2_signature[] = PP2_SIGNATURE;
 	void *tlv_crc32c_p = NULL;
@@ -1468,6 +1468,20 @@
 		}
 	}
 
+	if (srv->pp_opts & SRV_PP_V2_UNIQUE_ID) {
+		struct session* sess = strm_sess(strm);
+		struct ist unique_id = stream_generate_unique_id(strm, &sess->fe->format_unique_id);
+
+		value = unique_id.ptr;
+		value_len = unique_id.len;
+
+		if (value_len >= 0) {
+			if ((buf_len - ret) < sizeof(struct tlv))
+				return 0;
+			ret += make_tlv(&buf[ret], (buf_len - ret), PP2_TYPE_UNIQUE_ID, value_len, value);
+		}
+	}
+
 #ifdef USE_OPENSSL
 	if (srv->pp_opts & SRV_PP_V2_SSL) {
 		struct tlv_ssl *tlv;
diff --git a/src/log.c b/src/log.c
index 8f502ac..2cac074 100644
--- a/src/log.c
+++ b/src/log.c
@@ -137,7 +137,7 @@
 	{ "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL },  /* client cookie */
 	{ "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL },  /* server cookie */
 	{ "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
-	{ "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
+	{ "ID", LOG_FMT_UNIQUEID, PR_MODE_TCP, LW_BYTES, NULL }, /* Unique ID */
 	{ "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL },   /* status code */
 	{ "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL },   /* date GMT */
 	{ "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL },      /* Time active (tr to end) */
diff --git a/src/server.c b/src/server.c
index 9655702..908439d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -656,6 +656,8 @@
 			newsrv->pp_opts |= SRV_PP_V2_AUTHORITY;
 		} else if (!strcmp(p, "crc32c")) {
 			newsrv->pp_opts |= SRV_PP_V2_CRC32C;
+		} else if (!strcmp(p, "unique-id")) {
+			newsrv->pp_opts |= SRV_PP_V2_UNIQUE_ID;
 		} else
 			goto fail;
 	}
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 47cbd96..7bb864d 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -354,9 +354,12 @@
 		if (cs && cs->data_cb == &si_conn_cb) {
 			struct stream_interface *si = cs->data;
 			struct conn_stream *remote_cs = objt_cs(si_opposite(si)->end);
+			struct stream *strm = si_strm(si);
+
 			ret = make_proxy_line(trash.area, trash.size,
 					      objt_server(conn->target),
-					      remote_cs ? remote_cs->conn : NULL);
+					      remote_cs ? remote_cs->conn : NULL,
+					      strm);
 			/* We may not have a conn_stream yet, if we don't
 			 * know which mux to use, because it will be decided
 			 * during the SSL handshake. In this case, there should
@@ -371,7 +374,8 @@
 
 			ret = make_proxy_line(trash.area, trash.size,
 			                      objt_server(conn->target),
-					      objt_conn(sess->origin));
+					      objt_conn(sess->origin),
+					      NULL);
 		}
 		else {
 			/* The target server expects a LOCAL line to be sent first. Retrieving
@@ -381,7 +385,8 @@
 				goto out_wait;
 
 			ret = make_proxy_line(trash.area, trash.size,
-					      objt_server(conn->target), conn);
+					      objt_server(conn->target), conn,
+					      NULL);
 		}
 
 		if (!ret)