2 * Routines for HAPROXY PROXY (v1/v2) dissection
3 * Copyright 2015, Alexis La Goutte (See AUTHORS)
4 * Copyright 2019 Peter Wu <peter@lekensteyn.nl>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * The PROXY protocol is a single, unfragmented header before the initial client
15 * packet. Following this header, the proxied protocol will take over and
18 * https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
20 * Requires "Try heuristics sub-dissectors first" in TCP protocol preferences.
25 #include <epan/packet.h>
26 #include <epan/expert.h>
27 #include <epan/to_str.h>
28 #include <wsutil/inet_addr.h>
29 #include <wsutil/strtoi.h>
30 #include <wsutil/utf8_entities.h>
32 #include "packet-tcp.h"
33 #include "packet-udp.h"
35 void proto_reg_handoff_proxy(void);
36 void proto_register_proxy(void);
38 static int proto_proxy
;
40 static int hf_proxy_version
;
42 static int hf_proxy_src_ipv4
;
43 static int hf_proxy_dst_ipv4
;
44 static int hf_proxy_src_ipv6
;
45 static int hf_proxy_dst_ipv6
;
46 static int hf_proxy_srcport
;
47 static int hf_proxy_dstport
;
50 static int hf_proxy1_magic
;
51 static int hf_proxy1_proto
;
52 static int hf_proxy1_unknown
;
55 static int hf_proxy2_magic
;
56 static int hf_proxy2_ver
;
57 static int hf_proxy2_cmd
;
58 static int hf_proxy2_addr_family
;
59 static int hf_proxy2_protocol
;
60 static int hf_proxy2_addr_family_protocol
;
61 static int hf_proxy2_len
;
62 static int hf_proxy2_src_unix
;
63 static int hf_proxy2_dst_unix
;
65 static int hf_proxy2_unknown
;
67 static int hf_proxy2_tlv
;
68 static int hf_proxy2_tlv_type
;
69 static int hf_proxy2_tlv_length
;
70 static int hf_proxy2_tlv_value
;
71 static int hf_proxy2_tlv_ssl_client
;
72 static int hf_proxy2_tlv_ssl_verify
;
73 static int hf_proxy2_tlv_ssl_version
;
74 static int hf_proxy2_tlv_ssl_cn
;
75 static int hf_proxy2_tlv_ssl_cipher
;
76 static int hf_proxy2_tlv_ssl_sig_alg
;
77 static int hf_proxy2_tlv_ssl_key_alg
;
79 static expert_field ei_proxy_header_length_too_small
;
80 static expert_field ei_proxy_bad_format
;
83 static int ett_proxy1
;
84 static int ett_proxy2
;
85 static int ett_proxy2_fampro
;
86 static int ett_proxy2_tlv
;
88 static dissector_handle_t proxy_v1_handle
;
89 static dissector_handle_t proxy_v2_handle
;
91 static const uint8_t proxy_v2_magic
[] = { 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a };
93 static const value_string proxy2_cmd_vals
[] = {
99 static const value_string proxy2_family_protocol_vals
[] = {
101 { 0x11, "TCP over IPv4" },
102 { 0x12, "UDP over IPv4" },
103 { 0x21, "TCP over IPv6" },
104 { 0x22, "UDP over IPv6" },
105 { 0x31, "UNIX stream" },
106 { 0x32, "UNIX datagram" },
110 static const value_string proxy2_family_vals
[] = {
117 #define PP2_TYPE_ALPN 0x01
118 #define PP2_TYPE_AUTHORITY 0x02
119 #define PP2_TYPE_CRC32C 0x03
120 #define PP2_TYPE_NOOP 0x04
121 #define PP2_TYPE_UNIQUE_ID 0x05
122 #define PP2_TYPE_SSL 0x20
123 #define PP2_SUBTYPE_SSL_VERSION 0x21
124 #define PP2_SUBTYPE_SSL_CN 0x22
125 #define PP2_SUBTYPE_SSL_CIPHER 0x23
126 #define PP2_SUBTYPE_SSL_SIG_ALG 0x24
127 #define PP2_SUBTYPE_SSL_KEY_ALG 0x25
128 #define PP2_TYPE_NETNS 0x30
129 #define PP2_TYPE_AWS 0xEA
131 static const value_string proxy2_tlv_vals
[] = {
133 { PP2_TYPE_ALPN
, "ALPN" },
134 { PP2_TYPE_AUTHORITY
, "AUTHORITY" },
135 { PP2_TYPE_CRC32C
, "CRC32C" },
136 { PP2_TYPE_NOOP
, "NOOP" },
137 { PP2_TYPE_UNIQUE_ID
, "UNIQUE_ID" },
138 { PP2_TYPE_SSL
, "SSL" },
139 { PP2_SUBTYPE_SSL_VERSION
, "SSL VERSION" },
140 { PP2_SUBTYPE_SSL_CN
, "SSL CN" },
141 { PP2_SUBTYPE_SSL_CIPHER
, "SSL CIPHER" },
142 { PP2_SUBTYPE_SSL_SIG_ALG
, "SSL SIG ALG" },
143 { PP2_SUBTYPE_SSL_KEY_ALG
, "SSL KEY ALG" },
144 { PP2_TYPE_NETNS
, "NETNS" },
145 { PP2_TYPE_AWS
, "AWS" },
149 /* XXX: The protocol specification says that the PROXY header is present
150 * only once, at the beginning of a TCP connection. If we ever do find
151 * the header more than once, we should use a wmem_tree. */
152 typedef struct _proxy_conv_info_t
{
158 uint32_t setup_frame
;
162 dissect_proxy_proxied(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
,
163 int offset
, void* data
, proxy_conv_info_t
*proxy_info
)
165 conversation_t
* conv
= find_or_create_conversation(pinfo
);
166 /* A PROXY header was parsed here or in a previous frame, and
167 * there's remaining data, so call the subdissector.
170 /* If this is the frame with the header, set a fence. */
171 col_append_str(pinfo
->cinfo
, COL_INFO
, " | ");
172 col_set_fence(pinfo
->cinfo
, COL_INFO
);
174 uint32_t srcport
, dstport
;
176 if (proxy_info
== NULL
) {
177 /* If we're not passed proxied information, e.g., a LOCAL command or
178 * transported over UDP (connectionless), use the outer addressing.
179 * Note that if the other dissector calls conversation_set_dissector()
180 * or similar then on the second pass we won't call the PROXY heuristic
181 * dissector for coalesced frames.
183 srcport
= pinfo
->srcport
;
184 dstport
= pinfo
->destport
;
185 ptype
= pinfo
->ptype
;
188 /* If we're passed proxied connection info, set the endpoint used for
189 * conversations to the proxied connection, so that if the subdissector
190 * calls conversation_set_dissector() it will not prevent calling the
191 * PROXY dissector on the header frame on the second pass.
192 * Determine our direction.
194 * XXX: Perhaps we should actually change the values in pinfo, but
195 * currently that doesn't work well with Follow Stream (whether we
196 * change them back before returning to the TCP dissector or not.)
198 if (addresses_equal(&pinfo
->src
, conversation_key_addr1(conv
->key_ptr
)) &&
199 (pinfo
->srcport
== conversation_key_port1(conv
->key_ptr
))) {
200 conversation_set_conv_addr_port_endpoints(pinfo
, &proxy_info
->src
, &proxy_info
->dst
,
201 CONVERSATION_PROXY
, proxy_info
->srcport
,
202 proxy_info
->dstport
);
203 srcport
= proxy_info
->srcport
;
204 dstport
= proxy_info
->dstport
;
205 ptype
= proxy_info
->ptype
;
208 conversation_set_conv_addr_port_endpoints(pinfo
, &proxy_info
->dst
, &proxy_info
->src
,
209 CONVERSATION_PROXY
, proxy_info
->dstport
,
210 proxy_info
->srcport
);
211 srcport
= proxy_info
->dstport
;
212 dstport
= proxy_info
->srcport
;
213 ptype
= proxy_info
->ptype
;
217 tvbuff_t
* next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
218 /* Allow subdissector to perform reassembly. */
219 if (pinfo
->can_desegment
> 0)
220 pinfo
->can_desegment
++;
223 /* Get the TCP conversation data associated with the transporting
224 * connection. Note that this means that decode_tcp_ports() can
225 * try tcp->server_port from the outer connection as well as
226 * the ports from the proxied connection.
228 decode_tcp_ports(next_tvb
, 0, pinfo
, tree
, srcport
, dstport
,
229 get_tcp_conversation_data(conv
, pinfo
), (struct tcpinfo
*)data
);
232 decode_udp_ports(next_tvb
, 0, pinfo
, tree
, srcport
, dstport
, -1);
235 /* Dissect UNIX and UNSPEC protocols as data */
236 call_data_dissector(next_tvb
, pinfo
, tree
);
238 if (pinfo
->desegment_len
> 0) {
239 /* If the subdissector requests desegmentation, adjust the
240 * desegment offset past the PROXY header.
242 pinfo
->desegment_offset
+= offset
;
244 return tvb_reported_length_remaining(tvb
, offset
);
248 // NOLINTNEXTLINE(misc-no-recursion)
249 dissect_proxy_v2_tlv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*proxy_tree
, int offset
, int next_offset
)
251 increment_dissection_depth(pinfo
);
252 while (offset
< next_offset
) {
253 uint32_t type
, length
;
255 proto_tree
*tlv_tree
;
257 ti_tlv
= proto_tree_add_item(proxy_tree
, hf_proxy2_tlv
, tvb
, offset
, 3, ENC_NA
);
258 tlv_tree
= proto_item_add_subtree(ti_tlv
, ett_proxy2_tlv
);
259 proto_tree_add_item_ret_uint(tlv_tree
, hf_proxy2_tlv_type
, tvb
, offset
, 1, ENC_NA
, &type
);
261 proto_tree_add_item_ret_uint(tlv_tree
, hf_proxy2_tlv_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &length
);
264 proto_item_append_text(ti_tlv
, ": (t=%u,l=%d) %s", type
, length
, val_to_str_const(type
, proxy2_tlv_vals
,"Unknown type") );
265 proto_item_set_len(ti_tlv
, 1 + 2 + length
);
267 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_value
, tvb
, offset
, length
, ENC_NA
);
269 case PP2_TYPE_SSL
: /* SSL */
270 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_client
, tvb
, offset
, 1, ENC_NA
);
272 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_verify
, tvb
, offset
, 4, ENC_NA
);
274 offset
= dissect_proxy_v2_tlv(tvb
, pinfo
, tlv_tree
, offset
, next_offset
);
276 case PP2_SUBTYPE_SSL_VERSION
: /* SSL Version */
277 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_version
, tvb
, offset
, length
, ENC_ASCII
);
278 proto_item_append_text(ti_tlv
, ": %s", tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, length
, ENC_ASCII
));
281 case PP2_SUBTYPE_SSL_CN
: /* SSL CommonName */
282 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_cn
, tvb
, offset
, length
, ENC_ASCII
);
283 proto_item_append_text(ti_tlv
, ": %s", tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, length
, ENC_ASCII
));
286 case PP2_SUBTYPE_SSL_CIPHER
: /* SSL Cipher */
287 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_cipher
, tvb
, offset
, length
, ENC_ASCII
);
290 case PP2_SUBTYPE_SSL_SIG_ALG
: /* SSL Signature Algorithm */
291 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_sig_alg
, tvb
, offset
, length
, ENC_ASCII
);
294 case PP2_SUBTYPE_SSL_KEY_ALG
: /* SSL Key Algorithm */
295 proto_tree_add_item(tlv_tree
, hf_proxy2_tlv_ssl_key_alg
, tvb
, offset
, length
, ENC_ASCII
);
303 decrement_dissection_depth(pinfo
);
309 is_proxy_v2(tvbuff_t
* tvb
)
312 int length
= tvb_reported_length(tvb
);
318 if (tvb_memeql(tvb
, offset
, (const uint8_t*)proxy_v2_magic
, sizeof(proxy_v2_magic
)) != 0) {
321 // TODO maybe check for "(hdr.v2.ver_cmd & 0xF0) == 0x20" as done in "9. Sample code" from
322 // https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt?
327 /* "a 108-byte buffer is always enough to store all the line and a trailing zero" */
328 #define PROXY_V1_MAX_LINE_LENGTH 107
331 is_proxy_v1(tvbuff_t
*tvb
, int *header_length
)
333 const int min_header_size
= sizeof("PROXY \r\n") - 1;
334 int length
= tvb_reported_length(tvb
);
337 if (length
< min_header_size
) {
341 if (tvb_memeql(tvb
, 0, (const uint8_t*)"PROXY ", 6) != 0) {
345 length
= MIN(length
, PROXY_V1_MAX_LINE_LENGTH
);
346 if (tvb_find_line_end(tvb
, 6, length
, &next_offset
, false) == -1) {
350 /* The line must end with a CRLF and not just a single CR or LF. */
351 if (tvb_memeql(tvb
, next_offset
- 2, (const uint8_t*)"\r\n", 2) != 0) {
356 *header_length
= next_offset
;
362 * Scan for the next non-empty token (terminated by a space). If invalid, add
363 * expert info for the remaining part and return false. Otherwise return true
364 * and the token length.
367 proxy_v1_get_token_length(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
, int header_length
, char *token
, int *token_length
)
369 int space_pos
= tvb_find_uint8(tvb
, offset
, header_length
- offset
, ' ');
370 if (space_pos
== -1) {
371 proto_tree_add_expert(tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, header_length
- offset
);
374 int length
= space_pos
- offset
;
375 if (token
&& length
) {
376 DISSECTOR_ASSERT(length
+ 1 < PROXY_V1_MAX_LINE_LENGTH
);
377 tvb_memcpy(tvb
, token
, offset
, length
);
378 token
[length
] = '\0';
380 *token_length
= length
;
385 dissect_proxy_v1_header(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
388 proto_tree
*proxy_tree
;
390 int header_length
= 0;
391 int token_length
= 0;
392 int tcp_ip_version
= 0;
393 uint16_t srcport
, dstport
;
394 char buffer
[PROXY_V1_MAX_LINE_LENGTH
];
395 uint32_t src_ipv4
, dst_ipv4
;
396 ws_in6_addr src_ipv6
, dst_ipv6
;
397 address src_addr
, dst_addr
;
399 if (!is_proxy_v1(tvb
, &header_length
)) {
403 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "PROXYv1");
405 ti
= proto_tree_add_item(tree
, proto_proxy
, tvb
, 0, header_length
, ENC_NA
);
406 proxy_tree
= proto_item_add_subtree(ti
, ett_proxy1
);
408 /* Skip "PROXY" plus a space. */
409 proto_tree_add_item(proxy_tree
, hf_proxy1_magic
, tvb
, offset
, 5, ENC_NA
);
412 /* Protocol and family */
413 if (!proxy_v1_get_token_length(tvb
, pinfo
, proxy_tree
, offset
, header_length
, buffer
, &token_length
)) {
414 return tvb_captured_length(tvb
);
416 proto_tree_add_item(proxy_tree
, hf_proxy1_proto
, tvb
, offset
, token_length
, ENC_NA
|ENC_ASCII
);
417 if (token_length
== 4) {
418 if (memcmp(buffer
, "TCP4", 4) == 0) {
420 } else if (memcmp(buffer
, "TCP6", 4) == 0) {
424 offset
+= token_length
+ 1;
426 switch (tcp_ip_version
) {
428 /* IPv4 source address */
429 if (!proxy_v1_get_token_length(tvb
, pinfo
, proxy_tree
, offset
, header_length
, buffer
, &token_length
)) {
430 return tvb_captured_length(tvb
);
432 if (!ws_inet_pton4(buffer
, &src_ipv4
)) {
433 proto_tree_add_expert_format(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
,
434 "Unrecognized IPv4 address");
435 return tvb_captured_length(tvb
);
437 proto_tree_add_ipv4(proxy_tree
, hf_proxy_src_ipv4
, tvb
, offset
, token_length
, src_ipv4
);
438 set_address(&src_addr
, AT_IPv4
, 4, &src_ipv4
);
439 offset
+= token_length
+ 1;
441 /* IPv4 destination address */
442 if (!proxy_v1_get_token_length(tvb
, pinfo
, proxy_tree
, offset
, header_length
, buffer
, &token_length
)) {
443 return tvb_captured_length(tvb
);
445 if (!ws_inet_pton4(buffer
, &dst_ipv4
)) {
446 proto_tree_add_expert_format(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
,
447 "Unrecognized IPv4 address");
448 return tvb_captured_length(tvb
);
450 proto_tree_add_ipv4(proxy_tree
, hf_proxy_dst_ipv4
, tvb
, offset
, token_length
, dst_ipv4
);
451 set_address(&dst_addr
, AT_IPv4
, 4, &dst_ipv4
);
452 offset
+= token_length
+ 1;
456 /* IPv6 source address */
457 if (!proxy_v1_get_token_length(tvb
, pinfo
, proxy_tree
, offset
, header_length
, buffer
, &token_length
)) {
458 return tvb_captured_length(tvb
);
460 if (!ws_inet_pton6(buffer
, &src_ipv6
)) {
461 proto_tree_add_expert_format(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
,
462 "Unrecognized IPv6 address");
463 return tvb_captured_length(tvb
);
465 proto_tree_add_ipv6(proxy_tree
, hf_proxy_src_ipv6
, tvb
, offset
, token_length
, &src_ipv6
);
466 set_address(&src_addr
, AT_IPv6
, sizeof(ws_in6_addr
), &src_ipv6
);
467 offset
+= token_length
+ 1;
469 /* IPv6 destination address */
470 if (!proxy_v1_get_token_length(tvb
, pinfo
, proxy_tree
, offset
, header_length
, buffer
, &token_length
)) {
471 return tvb_captured_length(tvb
);
473 if (!ws_inet_pton6(buffer
, &dst_ipv6
)) {
474 proto_tree_add_expert_format(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
,
475 "Unrecognized IPv6 address");
476 return tvb_captured_length(tvb
);
478 proto_tree_add_ipv6(proxy_tree
, hf_proxy_dst_ipv6
, tvb
, offset
, token_length
, &dst_ipv6
);
479 set_address(&dst_addr
, AT_IPv6
, sizeof(ws_in6_addr
), &dst_ipv6
);
480 offset
+= token_length
+ 1;
484 proto_tree_add_item(proxy_tree
, hf_proxy1_unknown
, tvb
, offset
, header_length
- 2 - offset
, ENC_NA
|ENC_ASCII
);
485 return tvb_captured_length(tvb
);
489 if (!proxy_v1_get_token_length(tvb
, pinfo
, proxy_tree
, offset
, header_length
, buffer
, &token_length
)) {
490 return tvb_captured_length(tvb
);
492 if (!ws_strtou16(buffer
, NULL
, &srcport
)) {
493 proto_tree_add_expert_format(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
,
494 "Unrecognized port");
495 return tvb_captured_length(tvb
);
497 proto_tree_add_uint(proxy_tree
, hf_proxy_srcport
, tvb
, offset
, token_length
, srcport
);
498 offset
+= token_length
+ 1;
500 /* Destination port */
501 token_length
= header_length
- 2 - offset
;
502 if (token_length
<= 0) {
503 proto_tree_add_expert(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
);
504 return tvb_captured_length(tvb
);
506 tvb_memcpy(tvb
, buffer
, offset
, token_length
);
507 buffer
[token_length
] = '\0';
508 if (!ws_strtou16(buffer
, NULL
, &dstport
)) {
509 proto_tree_add_expert_format(proxy_tree
, pinfo
, &ei_proxy_bad_format
, tvb
, offset
, token_length
,
510 "Unrecognized port");
511 return tvb_captured_length(tvb
);
513 proto_tree_add_uint(proxy_tree
, hf_proxy_dstport
, tvb
, offset
, token_length
, dstport
);
515 col_add_lstr(pinfo
->cinfo
, COL_INFO
, "PROXY ", address_to_str(pinfo
->pool
, &src_addr
),
516 " "UTF8_RIGHTWARDS_ARROW
" ", address_to_str(pinfo
->pool
, &dst_addr
), ", ",
517 COL_ADD_LSTR_TERMINATOR
);
518 col_append_ports(pinfo
->cinfo
, COL_INFO
, PT_TCP
, srcport
, dstport
);
519 conversation_t
* conv
= find_or_create_conversation(pinfo
);
520 proxy_conv_info_t
* proxy_info
= conversation_get_proto_data(conv
, proto_proxy
);
521 if (proxy_info
== NULL
) {
522 proxy_info
= wmem_new(wmem_file_scope(), proxy_conv_info_t
);
523 copy_address_wmem(wmem_file_scope(), &proxy_info
->src
, &src_addr
);
524 copy_address_wmem(wmem_file_scope(), &proxy_info
->dst
, &dst_addr
);
525 proxy_info
->ptype
= PT_TCP
;
526 proxy_info
->srcport
= srcport
;
527 proxy_info
->dstport
= dstport
;
528 proxy_info
->setup_frame
= pinfo
->num
;
529 conversation_add_proto_data(conv
, proto_proxy
, proxy_info
);
531 return header_length
;
535 dissect_proxy_v1(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
537 conversation_t
* conv
= find_or_create_conversation(pinfo
);
538 proxy_conv_info_t
* proxy_info
;
539 int offset
= dissect_proxy_v1_header(tvb
, pinfo
, tree
);
540 proxy_info
= conversation_get_proto_data(conv
, proto_proxy
);
541 if (proxy_info
&& pinfo
->num
>= proxy_info
->setup_frame
&&
542 tvb_reported_length_remaining(tvb
, offset
)) {
543 /* XXX: If this is a later frame, should we add some
544 * generated fields with the proxy header information,
545 * and a link back to the proxy setup frame? */
546 offset
+= dissect_proxy_proxied(tvb
, pinfo
, tree
, offset
, data
, proxy_info
);
552 dissect_proxy_v2_header(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
554 proto_item
*ti
, *ti_ver
;
555 proto_tree
*proxy_tree
, *fampro_tree
;
556 unsigned offset
= 0, next_offset
;
557 uint32_t header_len
, fam_pro
, cmd
;
558 address src_addr
= ADDRESS_INIT_NONE
, dst_addr
= ADDRESS_INIT_NONE
;
559 uint32_t srcport
, dstport
;
560 port_type ptype
= PT_NONE
;
562 if (!is_proxy_v2(tvb
)) {
566 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "PROXYv2");
568 ti
= proto_tree_add_item(tree
, proto_proxy
, tvb
, 0, -1, ENC_NA
);
570 proxy_tree
= proto_item_add_subtree(ti
, ett_proxy2
);
572 proto_tree_add_item(proxy_tree
, hf_proxy2_magic
, tvb
, offset
, 12, ENC_NA
);
575 proto_tree_add_item(proxy_tree
, hf_proxy2_ver
, tvb
, offset
, 1, ENC_NA
);
576 proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy2_cmd
, tvb
, offset
, 1, ENC_NA
, &cmd
);
577 ti_ver
= proto_tree_add_uint(proxy_tree
, hf_proxy_version
, tvb
, offset
, 1, 2);
578 proto_item_set_generated(ti_ver
);
581 ti
= proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy2_addr_family_protocol
, tvb
, offset
, 1, ENC_NA
, &fam_pro
);
582 fampro_tree
= proto_item_add_subtree(ti
, ett_proxy2_fampro
);
583 proto_tree_add_item(fampro_tree
, hf_proxy2_addr_family
, tvb
, offset
, 1, ENC_NA
);
584 proto_tree_add_item(fampro_tree
, hf_proxy2_protocol
, tvb
, offset
, 1, ENC_NA
);
587 proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy2_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &header_len
);
590 next_offset
= offset
+ header_len
;
593 case 0x11: /* TCP over IPv4 */
594 case 0x12: /* UDP over IPv4 */
595 proto_tree_add_item(proxy_tree
, hf_proxy_src_ipv4
, tvb
, offset
, 4, ENC_NA
);
596 set_address_tvb(&src_addr
, AT_IPv4
, 4, tvb
, offset
);
598 proto_tree_add_item(proxy_tree
, hf_proxy_dst_ipv4
, tvb
, offset
, 4, ENC_NA
);
599 set_address_tvb(&dst_addr
, AT_IPv4
, 4, tvb
, offset
);
601 proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy_srcport
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &srcport
);
603 proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy_dstport
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &dstport
);
605 ptype
= (fam_pro
& 1) ? PT_TCP
: PT_UDP
;
607 case 0x21: /* TCP over IPv6 */
608 case 0x22: /* UDP over IPv6 */
609 proto_tree_add_item(proxy_tree
, hf_proxy_src_ipv6
, tvb
, offset
, 16, ENC_NA
);
610 set_address_tvb(&src_addr
, AT_IPv6
, sizeof(ws_in6_addr
), tvb
, offset
);
612 proto_tree_add_item(proxy_tree
, hf_proxy_dst_ipv6
, tvb
, offset
, 16, ENC_NA
);
613 set_address_tvb(&dst_addr
, AT_IPv6
, sizeof(ws_in6_addr
), tvb
, offset
);
615 proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy_srcport
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &srcport
);
617 proto_tree_add_item_ret_uint(proxy_tree
, hf_proxy_dstport
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &dstport
);
619 ptype
= (fam_pro
& 1) ? PT_TCP
: PT_UDP
;
621 case 0x31: /* UNIX stream */
622 case 0x32: /* UNIX datagram */
623 proto_tree_add_item(proxy_tree
, hf_proxy2_src_unix
, tvb
, offset
, 108, ENC_NA
);
625 proto_tree_add_item(proxy_tree
, hf_proxy2_dst_unix
, tvb
, offset
, 108, ENC_NA
);
630 proto_tree_add_item(proxy_tree
, hf_proxy2_unknown
, tvb
, offset
, header_len
, ENC_NA
);
631 offset
+= header_len
;
636 if (offset
> next_offset
) {
637 proto_tree_add_expert(proxy_tree
, pinfo
, &ei_proxy_header_length_too_small
,
642 /* Do we have additional TLV to parse? */
643 if (offset
< next_offset
) {
645 offset
= dissect_proxy_v2_tlv(tvb
, pinfo
, proxy_tree
, offset
, next_offset
);
648 /* If the cmd is LOCAL, then ignore the proxy protocol block,
649 * even if address and protocol information exists. */
650 if (src_addr
.type
!= AT_NONE
) {
651 col_add_lstr(pinfo
->cinfo
, COL_INFO
, "PROXY ", address_to_str(pinfo
->pool
, &src_addr
),
652 " "UTF8_RIGHTWARDS_ARROW
" ", address_to_str(pinfo
->pool
, &dst_addr
), ", ",
653 COL_ADD_LSTR_TERMINATOR
);
654 col_append_ports(pinfo
->cinfo
, COL_INFO
, ptype
, srcport
, dstport
);
655 conversation_t
* conv
= find_or_create_conversation(pinfo
);
656 proxy_conv_info_t
* proxy_info
= conversation_get_proto_data(conv
, proto_proxy
);
657 if (proxy_info
== NULL
&& pinfo
->ptype
!= PT_UDP
&& cmd
!= 0) {
658 /* Don't add conversation info on connectionless transport (UDP) */
659 /* If the command is LOCAL, then the receiver "MUST use the real
660 * connection endpoints". */
661 proxy_info
= wmem_new(wmem_file_scope(), proxy_conv_info_t
);
662 copy_address_wmem(wmem_file_scope(), &proxy_info
->src
, &src_addr
);
663 copy_address_wmem(wmem_file_scope(), &proxy_info
->dst
, &dst_addr
);
664 proxy_info
->ptype
= PT_TCP
;
665 proxy_info
->srcport
= srcport
;
666 proxy_info
->dstport
= dstport
;
667 proxy_info
->setup_frame
= pinfo
->num
;
668 conversation_add_proto_data(conv
, proto_proxy
, proxy_info
);
676 dissect_proxy_v2(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data
)
678 conversation_t
* conv
= find_or_create_conversation(pinfo
);
679 proxy_conv_info_t
*proxy_info
;
680 int offset
= dissect_proxy_v2_header(tvb
, pinfo
, tree
);
681 proxy_info
= conversation_get_proto_data(conv
, proto_proxy
);
682 if (proxy_info
&& pinfo
->num
>= proxy_info
->setup_frame
&&
683 tvb_reported_length_remaining(tvb
, offset
)) {
684 /* XXX: If this is a later frame, should we add some
685 * generated fields with the proxy header information,
686 * and a link back to the proxy setup frame? */
687 offset
+= dissect_proxy_proxied(tvb
, pinfo
, tree
, offset
, data
, proxy_info
);
693 dissect_proxy_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
695 conversation_t
* conv
= find_or_create_conversation(pinfo
);
696 if (is_proxy_v2(tvb
)) {
697 conversation_set_dissector(conv
, proxy_v2_handle
);
698 dissect_proxy_v2(tvb
, pinfo
, tree
, data
);
700 } else if (is_proxy_v1(tvb
, NULL
)) {
701 conversation_set_dissector(conv
, proxy_v1_handle
);
702 dissect_proxy_v1(tvb
, pinfo
, tree
, data
);
709 dissect_proxy_heur_udp(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data
)
712 if (is_proxy_v2(tvb
)) {
713 offset
= dissect_proxy_v2(tvb
, pinfo
, tree
, data
);
714 if (offset
&& tvb_reported_length_remaining(tvb
, offset
)) {
715 /* When the transport is UDP, treat this as connectionless
716 * and just skip past the PROXY header after putting it
717 * in the tree. If this is DNS, for example, every request
718 * will have a PROXY header; the responses don't, and
719 * there's no good way to associate the PROXY information
720 * with the header that won't have problems if the UDP
721 * responses are out of order. Note if the proxied dissector
722 * calls conversation_set_dissector() then this dissector
723 * won't get called on the second pass. */
724 dissect_proxy_proxied(tvb
, pinfo
, tree
, offset
, data
, NULL
);
728 /* Proxy v1 is only for TCP */
729 } else if (is_proxy_v1(tvb
, NULL
)) {
730 dissect_proxy_v1(tvb
, pinfo
, tree
, data
);
737 proto_register_proxy(void)
740 expert_module_t
*expert_proxy
;
742 static hf_register_info hf
[] = {
744 { "Version", "proxy.version",
745 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
749 { &hf_proxy_src_ipv4
,
750 { "Source Address", "proxy.src.ipv4",
751 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
754 { &hf_proxy_dst_ipv4
,
755 { "Destination Address", "proxy.dst.ipv4",
756 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
759 { &hf_proxy_src_ipv6
,
760 { "Source Address", "proxy.src.ipv6",
761 FT_IPv6
, BASE_NONE
, NULL
, 0x0,
764 { &hf_proxy_dst_ipv6
,
765 { "Destination Address", "proxy.dst.ipv6",
766 FT_IPv6
, BASE_NONE
, NULL
, 0x0,
770 { "Source Port", "proxy.srcport",
771 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
775 { "Destination Port", "proxy.dstport",
776 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
781 { "PROXY v1 magic", "proxy.v1.magic",
782 FT_NONE
, BASE_NONE
, NULL
, 0x0,
786 { "Protocol", "proxy.v1.proto",
787 FT_STRING
, BASE_NONE
, NULL
, 0x0,
788 "Proxied protocol and family", HFILL
}
790 { &hf_proxy1_unknown
,
791 { "Unknown data", "proxy.v1.unknown",
792 FT_STRING
, BASE_NONE
, NULL
, 0x0,
797 { "Magic", "proxy.v2.magic",
798 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
802 { "Version", "proxy.v2.version",
803 FT_UINT8
, BASE_DEC
, NULL
, 0xF0,
807 { "Command", "proxy.v2.cmd",
808 FT_UINT8
, BASE_DEC
, VALS(proxy2_cmd_vals
), 0x0F,
811 { &hf_proxy2_addr_family_protocol
,
812 { "Address Family Protocol", "proxy.v2.addr_family_protocol",
813 FT_UINT8
, BASE_HEX
, VALS(proxy2_family_protocol_vals
), 0x00,
816 { &hf_proxy2_addr_family
,
817 { "Address Family", "proxy.v2.addr_family",
818 FT_UINT8
, BASE_HEX
, VALS(proxy2_family_vals
), 0xF0,
821 { &hf_proxy2_protocol
,
822 { "Protocol", "proxy.v2.protocol",
823 FT_UINT8
, BASE_HEX
, NULL
, 0x0F,
827 { "Length", "proxy.v2.length",
828 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
829 "Size of addresses and additional properties", HFILL
}
833 { &hf_proxy2_src_unix
,
834 { "Source Address", "proxy.v2.src.unix",
835 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
838 { &hf_proxy2_dst_unix
,
839 { "Destination Address", "proxy.v2.dst.unix",
840 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
844 { &hf_proxy2_unknown
,
845 { "Unknown data", "proxy.v2.unknown",
846 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
851 { "TLV", "proxy.v2.tlv",
852 FT_NONE
, BASE_NONE
, NULL
, 0x0,
855 { &hf_proxy2_tlv_type
,
856 { "Type", "proxy.v2.tlv.type",
857 FT_UINT8
, BASE_HEX
, VALS(proxy2_tlv_vals
), 0x0,
860 { &hf_proxy2_tlv_length
,
861 { "Length", "proxy.v2.tlv.length",
862 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
865 { &hf_proxy2_tlv_value
,
866 { "Value", "proxy.v2.tlv.value",
867 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
871 { &hf_proxy2_tlv_ssl_client
,
872 { "Client", "proxy.v2.tlv.ssl.client",
873 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
876 { &hf_proxy2_tlv_ssl_verify
,
877 { "Verify", "proxy.v2.tlv.ssl.verify",
878 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
881 { &hf_proxy2_tlv_ssl_version
,
882 { "Version", "proxy.v2.tlv.ssl.version",
883 FT_STRING
, BASE_NONE
, NULL
, 0x0,
886 { &hf_proxy2_tlv_ssl_cn
,
887 { "CN", "proxy.v2.tlv.ssl.cn",
888 FT_STRING
, BASE_NONE
, NULL
, 0x0,
889 "CommonName", HFILL
}
891 { &hf_proxy2_tlv_ssl_cipher
,
892 { "Cipher", "proxy.v2.tlv.ssl.cipher",
893 FT_STRING
, BASE_NONE
, NULL
, 0x0,
896 { &hf_proxy2_tlv_ssl_sig_alg
,
897 { "SIG ALG", "proxy.v2.tlv.ssl.sig_alg",
898 FT_STRING
, BASE_NONE
, NULL
, 0x0,
899 "Signature Algorithm", HFILL
}
901 { &hf_proxy2_tlv_ssl_key_alg
,
902 { "Key ALG", "proxy.v2.tlv.ssl.keu_alg",
903 FT_STRING
, BASE_NONE
, NULL
, 0x0,
904 "Key Algorithm", HFILL
}
908 static int *ett
[] = {
915 static ei_register_info ei
[] = {
916 { &ei_proxy_header_length_too_small
,
917 { "proxy.header.length_too_small", PI_MALFORMED
, PI_WARN
,
918 "Header length is too small", EXPFILL
}
920 { &ei_proxy_bad_format
,
921 { "proxy.bad_format", PI_MALFORMED
, PI_WARN
,
922 "Badly formatted PROXY header line", EXPFILL
}
926 proto_proxy
= proto_register_protocol("PROXY Protocol", "PROXY", "proxy");
928 proto_register_field_array(proto_proxy
, hf
, array_length(hf
));
929 proto_register_subtree_array(ett
, array_length(ett
));
931 expert_proxy
= expert_register_protocol(proto_proxy
);
932 expert_register_field_array(expert_proxy
, ei
, array_length(ei
));
934 proxy_v1_handle
= register_dissector("proxy_v1", dissect_proxy_v1
, proto_proxy
);
935 proxy_v2_handle
= register_dissector("proxy_v2", dissect_proxy_v2
, proto_proxy
);
939 proto_reg_handoff_proxy(void)
941 heur_dissector_add("tcp", dissect_proxy_heur
, "PROXY over TCP", "proxy_tcp", proto_proxy
, HEURISTIC_ENABLE
);
942 /* XXX: PROXY v1 is defined to be transported over TCP only. PROXY v2 is
943 * strongly implied to also only be transported over TCP (though the
944 * proxied connection can be UDP), but dnsdist (and others?) use PROXY
945 * over UDP, adding the header to every request (but not response.)
946 * Presumably the proxied payload can only be DGRAM, since there's no
947 * way to deal with desegmentation or out of order without TCP sequence
949 heur_dissector_add("udp", dissect_proxy_heur_udp
, "PROXY over UDP", "proxy_udp", proto_proxy
, HEURISTIC_ENABLE
);
953 * Editor modelines - https://www.wireshark.org/tools/modelines.html
958 * indent-tabs-mode: nil
961 * vi: set shiftwidth=4 tabstop=8 expandtab:
962 * :indentSize=4:tabSize=8:noTabs=true: