1 /** @file
2 *
3 * PROXY protocol definitions and parsers.
4 *
5 * @section license License
6 *
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24 #include "ProxyProtocol.h"
25
26 #include "I_EventSystem.h"
27 #include "I_NetVConnection.h"
28
29 #include "tscore/BufferWriter.h"
30 #include "tscore/ink_assert.h"
31 #include "tscore/ink_string.h"
32 #include "tscore/ink_inet.h"
33 #include "tscpp/util/TextView.h"
34
35 namespace
36 {
37 using namespace std::literals;
38
39 constexpr ts::TextView PPv1_CONNECTION_PREFACE = "PROXY"sv;
40 constexpr ts::TextView PPv2_CONNECTION_PREFACE = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"sv;
41
42 constexpr size_t PPv1_CONNECTION_HEADER_LEN_MIN = 15;
43
44 constexpr ts::TextView PPv1_PROTO_UNKNOWN = "UNKNOWN"sv;
45 constexpr ts::TextView PPv1_PROTO_TCP4 = "TCP4"sv;
46 constexpr ts::TextView PPv1_PROTO_TCP6 = "TCP6"sv;
47
48 constexpr std::string_view PPv1_DELIMITER = " "sv;
49
50 constexpr uint8_t PPv2_CMD_LOCAL = 0x20;
51 constexpr uint8_t PPv2_CMD_PROXY = 0x21;
52
53 constexpr uint8_t PPv2_PROTO_UNSPEC = 0x00;
54 constexpr uint8_t PPv2_PROTO_TCP4 = 0x11;
55 constexpr uint8_t PPv2_PROTO_UDP4 = 0x12;
56 constexpr uint8_t PPv2_PROTO_TCP6 = 0x21;
57 constexpr uint8_t PPv2_PROTO_UDP6 = 0x22;
58 constexpr uint8_t PPv2_PROTO_UNIX_STREAM = 0x31;
59 constexpr uint8_t PPv2_PROTO_UNIX_DATAGRAM = 0x32;
60
61 constexpr uint16_t PPv2_ADDR_LEN_INET = 4 + 4 + 2 + 2;
62 constexpr uint16_t PPv2_ADDR_LEN_INET6 = 16 + 16 + 2 + 2;
63 constexpr uint16_t PPv2_ADDR_LEN_UNIX = 108 + 108;
64
65 const ts::BWFSpec ADDR_ONLY_FMT{"::a"};
66
67 struct PPv2Hdr {
68 uint8_t sig[12]; ///< preface
69 uint8_t ver_cmd; ///< protocol version and command
70 uint8_t fam; ///< protocol family and transport
71 uint16_t len; ///< number of following bytes part of the header
72 union {
73 // for TCP/UDP over IPv4, len = 12 (PPv2_ADDR_LEN_INET)
74 struct {
75 uint32_t src_addr;
76 uint32_t dst_addr;
77 uint16_t src_port;
78 uint16_t dst_port;
79 } ip4;
80 // for TCP/UDP over IPv6, len = 36 (PPv2_ADDR_LEN_INET6)
81 struct {
82 uint8_t src_addr[16];
83 uint8_t dst_addr[16];
84 uint16_t src_port;
85 uint16_t dst_port;
86 } ip6;
87 // for AF_UNIX sockets, len = 216 (PPv2_ADDR_LEN_UNIX)
88 struct {
89 uint8_t src_addr[108];
90 uint8_t dst_addr[108];
91 } unix;
92 } addr;
93 };
94
95 /**
96 PROXY Protocol v1 Parser
97
98 @return read length
99 */
100 size_t
proxy_protocol_v1_parse(ProxyProtocol * pp_info,ts::TextView hdr)101 proxy_protocol_v1_parse(ProxyProtocol *pp_info, ts::TextView hdr)
102 {
103 ink_release_assert(hdr.size() >= PPv1_CONNECTION_HEADER_LEN_MIN);
104
105 // Find the terminating newline
106 ts::TextView::size_type pos = hdr.find('\n');
107 if (pos == hdr.npos) {
108 Debug("proxyprotocol_v1", "ssl_has_proxy_v1: LF not found");
109 return 0;
110 }
111
112 if (hdr[pos - 1] != '\r') {
113 Debug("proxyprotocol_v1", "ssl_has_proxy_v1: CR not found");
114 return 0;
115 }
116
117 ts::TextView token;
118
119 // All the cases are special and sequence, might as well unroll them.
120
121 // The header should begin with the PROXY preface
122 token = hdr.split_prefix_at(' ');
123 if (0 == token.size() || token != PPv1_CONNECTION_PREFACE) {
124 Debug("proxyprotocol_v1", "proxy_protov1_parse: header [%.*s] does not start with preface [%.*s]", static_cast<int>(hdr.size()),
125 hdr.data(), static_cast<int>(PPv1_CONNECTION_PREFACE.size()), PPv1_CONNECTION_PREFACE.data());
126 return 0;
127 }
128 Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = PREFACE", static_cast<int>(token.size()), token.data());
129
130 // The INET protocol family - TCP4, TCP6 or UNKNOWN
131 if (PPv1_PROTO_UNKNOWN.isPrefixOf(hdr)) {
132 Debug("proxyprotocol_v1", "proxy_protov1_parse: [UNKNOWN] = INET Family");
133
134 // Ignore anything presented before the CRLF
135 pp_info->version = ProxyProtocolVersion::V1;
136
137 return pos + 1;
138 } else if (PPv1_PROTO_TCP4.isPrefixOf(hdr)) {
139 token = hdr.split_prefix_at(' ');
140 if (0 == token.size()) {
141 return 0;
142 }
143
144 pp_info->ip_family = AF_INET;
145 } else if (PPv1_PROTO_TCP6.isPrefixOf(hdr)) {
146 token = hdr.split_prefix_at(' ');
147 if (0 == token.size()) {
148 return 0;
149 }
150
151 pp_info->ip_family = AF_INET6;
152 } else {
153 return 0;
154 }
155 Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = INET Family", static_cast<int>(token.size()), token.data());
156
157 // Next up is the layer 3 source address
158 // - 255.255.255.255 or ffff:f...f:ffff ffff:f...f:fff
159 token = hdr.split_prefix_at(' ');
160 if (0 == token.size()) {
161 return 0;
162 }
163 Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Source Address", static_cast<int>(token.size()), token.data());
164 if (0 != ats_ip_pton(token, &pp_info->src_addr)) {
165 return 0;
166 }
167
168 // Next is the layer3 destination address
169 // - 255.255.255.255 or ffff:f...f:ffff ffff:f...f:fff
170 token = hdr.split_prefix_at(' ');
171 if (0 == token.size()) {
172 return 0;
173 }
174 Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Destination Address", static_cast<int>(token.size()), token.data());
175 if (0 != ats_ip_pton(token, &pp_info->dst_addr)) {
176 return 0;
177 }
178
179 // Next is the TCP source port represented as a decimal number in the range of [0..65535] inclusive.
180 token = hdr.split_prefix_at(' ');
181 if (0 == token.size()) {
182 return 0;
183 }
184 Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Source Port", static_cast<int>(token.size()), token.data());
185
186 in_port_t src_port = ts::svtoi(token);
187 if (src_port == 0) {
188 Debug("proxyprotocol_v1", "proxy_protov1_parse: src port [%d] token [%.*s] failed to parse", src_port,
189 static_cast<int>(token.size()), token.data());
190 return 0;
191 }
192 pp_info->src_addr.port() = htons(src_port);
193
194 // Next is the TCP destination port represented as a decimal number in the range of [0..65535] inclusive.
195 // Final trailer is CR LF so split at CR.
196 token = hdr.split_prefix_at('\r');
197 if (0 == token.size() || token.find(0x20) != token.npos) {
198 return 0;
199 }
200 Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Destination Port", static_cast<int>(token.size()), token.data());
201
202 in_port_t dst_port = ts::svtoi(token);
203 if (dst_port == 0) {
204 Debug("proxyprotocol_v1", "proxy_protov1_parse: dst port [%d] token [%.*s] failed to parse", dst_port,
205 static_cast<int>(token.size()), token.data());
206 return 0;
207 }
208 pp_info->dst_addr.port() = htons(dst_port);
209
210 pp_info->version = ProxyProtocolVersion::V1;
211
212 return pos + 1;
213 }
214
215 /**
216 PROXY Protocol v2 Parser
217
218 TODO: TLVs Support
219
220 @return read length
221 */
222 size_t
proxy_protocol_v2_parse(ProxyProtocol * pp_info,const ts::TextView & msg)223 proxy_protocol_v2_parse(ProxyProtocol *pp_info, const ts::TextView &msg)
224 {
225 ink_release_assert(msg.size() >= PPv2_CONNECTION_HEADER_LEN);
226
227 const PPv2Hdr *hdr_v2 = reinterpret_cast<const PPv2Hdr *>(msg.data());
228
229 // Assuming PREFACE check is done
230
231 // length check
232 const uint16_t len = ntohs(hdr_v2->len);
233 const size_t total_len = PPv2_CONNECTION_HEADER_LEN + len;
234
235 if (msg.size() < total_len) {
236 return 0;
237 }
238
239 // protocol version and command
240 switch (hdr_v2->ver_cmd) {
241 case PPv2_CMD_LOCAL: {
242 // protocol byte should be UNSPEC (\x00) with LOCAL command
243 if (hdr_v2->fam != PPv2_PROTO_UNSPEC) {
244 return 0;
245 }
246
247 pp_info->version = ProxyProtocolVersion::V2;
248 pp_info->ip_family = AF_UNSPEC;
249
250 return total_len;
251 }
252 case PPv2_CMD_PROXY: {
253 switch (hdr_v2->fam) {
254 case PPv2_PROTO_TCP4: {
255 if (len < PPv2_ADDR_LEN_INET) {
256 return 0;
257 }
258
259 IpAddr src_addr(reinterpret_cast<in_addr_t>(hdr_v2->addr.ip4.src_addr));
260 pp_info->src_addr.assign(src_addr, hdr_v2->addr.ip4.src_port);
261
262 IpAddr dst_addr(reinterpret_cast<in_addr_t>(hdr_v2->addr.ip4.dst_addr));
263 pp_info->dst_addr.assign(dst_addr, hdr_v2->addr.ip4.dst_port);
264
265 pp_info->version = ProxyProtocolVersion::V2;
266 pp_info->ip_family = AF_INET;
267
268 break;
269 }
270 case PPv2_PROTO_TCP6: {
271 if (len < PPv2_ADDR_LEN_INET6) {
272 return 0;
273 }
274
275 IpAddr src_addr(reinterpret_cast<in6_addr const &>(hdr_v2->addr.ip6.src_addr));
276 pp_info->src_addr.assign(src_addr, hdr_v2->addr.ip6.src_port);
277
278 IpAddr dst_addr(reinterpret_cast<in6_addr const &>(hdr_v2->addr.ip6.dst_addr));
279 pp_info->dst_addr.assign(dst_addr, hdr_v2->addr.ip6.dst_port);
280
281 pp_info->version = ProxyProtocolVersion::V2;
282 pp_info->ip_family = AF_INET6;
283
284 break;
285 }
286 case PPv2_PROTO_UDP4:
287 [[fallthrough]];
288 case PPv2_PROTO_UDP6:
289 [[fallthrough]];
290 case PPv2_PROTO_UNIX_STREAM:
291 [[fallthrough]];
292 case PPv2_PROTO_UNIX_DATAGRAM:
293 [[fallthrough]];
294 case PPv2_PROTO_UNSPEC:
295 [[fallthrough]];
296 default:
297 // unsupported
298 return 0;
299 }
300
301 // TODO: Parse TLVs
302
303 return total_len;
304 }
305 default:
306 break;
307 }
308
309 return 0;
310 }
311
312 /**
313 Build PROXY Protocol v1
314 */
315 size_t
proxy_protocol_v1_build(uint8_t * buf,size_t max_buf_len,const ProxyProtocol & pp_info)316 proxy_protocol_v1_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info)
317 {
318 if (max_buf_len < PPv1_CONNECTION_HEADER_LEN_MAX) {
319 return 0;
320 }
321
322 ts::FixedBufferWriter bw{reinterpret_cast<char *>(buf), max_buf_len};
323
324 // preface
325 bw.write(PPv1_CONNECTION_PREFACE);
326 bw.write(PPv1_DELIMITER);
327
328 // the proxied INET protocol and family
329 if (pp_info.src_addr.isIp4()) {
330 bw.write(PPv1_PROTO_TCP4);
331 } else if (pp_info.src_addr.isIp6()) {
332 bw.write(PPv1_PROTO_TCP6);
333 } else {
334 bw.write(PPv1_PROTO_UNKNOWN);
335 }
336 bw.write(PPv1_DELIMITER);
337
338 // the layer 3 source address
339 bwformat(bw, ADDR_ONLY_FMT, pp_info.src_addr);
340 bw.write(PPv1_DELIMITER);
341
342 // the layer 3 destination address
343 bwformat(bw, ADDR_ONLY_FMT, pp_info.dst_addr);
344 bw.write(PPv1_DELIMITER);
345
346 // TCP source port
347 {
348 size_t len = ink_small_itoa(ats_ip_port_host_order(pp_info.src_addr), bw.auxBuffer(), bw.remaining());
349 bw.fill(len);
350 bw.write(PPv1_DELIMITER);
351 }
352
353 // TCP destination port
354 {
355 size_t len = ink_small_itoa(ats_ip_port_host_order(pp_info.dst_addr), bw.auxBuffer(), bw.remaining());
356 bw.fill(len);
357 }
358
359 bw.write("\r\n");
360
361 return bw.size();
362 }
363
364 /**
365 Build PROXY Protocol v2
366
367 UDP, Unix Domain Socket, and TLV fields are not supported yet
368 */
369 size_t
proxy_protocol_v2_build(uint8_t * buf,size_t max_buf_len,const ProxyProtocol & pp_info)370 proxy_protocol_v2_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info)
371 {
372 if (max_buf_len < PPv2_CONNECTION_HEADER_LEN) {
373 return 0;
374 }
375
376 ts::FixedBufferWriter bw{reinterpret_cast<char *>(buf), max_buf_len};
377
378 // # proxy_hdr_v2
379 // ## preface
380 bw.write(PPv2_CONNECTION_PREFACE);
381
382 // ## version and command
383 // TODO: support PPv2_CMD_LOCAL for health check
384 bw.write(static_cast<char>(PPv2_CMD_PROXY));
385
386 // ## family & address
387 // TODO: support UDP
388 switch (pp_info.src_addr.family()) {
389 case AF_INET:
390 bw.write(static_cast<char>(PPv2_PROTO_TCP4));
391 break;
392 case AF_INET6:
393 bw.write(static_cast<char>(PPv2_PROTO_TCP6));
394 break;
395 case AF_UNIX:
396 bw.write(static_cast<char>(PPv2_PROTO_UNIX_STREAM));
397 break;
398 default:
399 bw.write(static_cast<char>(PPv2_PROTO_UNSPEC));
400 break;
401 }
402
403 // ## len field. this will be set at the end of this function
404 const size_t len_field_offset = bw.size();
405 bw.fill(2);
406
407 ink_release_assert(bw.size() == PPv2_CONNECTION_HEADER_LEN);
408
409 // # proxy_addr
410 // TODO: support UDP
411 switch (pp_info.src_addr.family()) {
412 case AF_INET: {
413 bw.write(&ats_ip4_addr_cast(pp_info.src_addr), TS_IP4_SIZE);
414 bw.write(&ats_ip4_addr_cast(pp_info.dst_addr), TS_IP4_SIZE);
415 bw.write(&ats_ip_port_cast(pp_info.src_addr), TS_PORT_SIZE);
416 bw.write(&ats_ip_port_cast(pp_info.dst_addr), TS_PORT_SIZE);
417
418 break;
419 }
420 case AF_INET6: {
421 bw.write(&ats_ip6_addr_cast(pp_info.src_addr), TS_IP6_SIZE);
422 bw.write(&ats_ip6_addr_cast(pp_info.dst_addr), TS_IP6_SIZE);
423 bw.write(&ats_ip_port_cast(pp_info.src_addr), TS_PORT_SIZE);
424 bw.write(&ats_ip_port_cast(pp_info.dst_addr), TS_PORT_SIZE);
425
426 break;
427 }
428 case AF_UNIX: {
429 // unsupported yet
430 bw.fill(PPv2_ADDR_LEN_UNIX);
431 break;
432 }
433 default:
434 // do nothing
435 break;
436 }
437
438 // # Additional TLVs (pp2_tlv)
439 // unsupported yet
440
441 // Set len field (number of following bytes part of the header) in the hdr
442 uint16_t len = htons(bw.size() - PPv2_CONNECTION_HEADER_LEN);
443 memcpy(buf + len_field_offset, &len, sizeof(uint16_t));
444 return bw.size();
445 }
446
447 } // namespace
448
449 /**
450 PROXY Protocol Parser
451 */
452 size_t
proxy_protocol_parse(ProxyProtocol * pp_info,ts::TextView tv)453 proxy_protocol_parse(ProxyProtocol *pp_info, ts::TextView tv)
454 {
455 size_t len = 0;
456
457 // Parse the TextView before moving the bytes in the buffer
458 if (tv.size() >= PPv1_CONNECTION_HEADER_LEN_MIN && PPv1_CONNECTION_PREFACE.isPrefixOf(tv)) {
459 // Client must send at least 15 bytes to get a reasonable match.
460 len = proxy_protocol_v1_parse(pp_info, tv);
461 } else if (tv.size() >= PPv2_CONNECTION_HEADER_LEN && PPv2_CONNECTION_PREFACE.isPrefixOf(tv)) {
462 len = proxy_protocol_v2_parse(pp_info, tv);
463 } else {
464 // if we don't have the PROXY preface, we don't have a ProxyProtocol header
465 // TODO: print hexdump of buffer safely
466 Debug("proxyprotocol", "failed to find ProxyProtocol preface");
467 }
468
469 return len;
470 }
471
472 /**
473 PROXY Protocol Builder
474 */
475 size_t
proxy_protocol_build(uint8_t * buf,size_t max_buf_len,const ProxyProtocol & pp_info,ProxyProtocolVersion force_version)476 proxy_protocol_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info, ProxyProtocolVersion force_version)
477 {
478 ProxyProtocolVersion version = pp_info.version;
479 if (force_version != ProxyProtocolVersion::UNDEFINED) {
480 version = force_version;
481 }
482
483 size_t len = 0;
484
485 if (version == ProxyProtocolVersion::V1) {
486 len = proxy_protocol_v1_build(buf, max_buf_len, pp_info);
487 } else if (version == ProxyProtocolVersion::V2) {
488 len = proxy_protocol_v2_build(buf, max_buf_len, pp_info);
489 } else {
490 ink_abort("PROXY Protocol Version is undefined");
491 }
492
493 return len;
494 }
495
496 ProxyProtocolVersion
proxy_protocol_version_cast(int i)497 proxy_protocol_version_cast(int i)
498 {
499 switch (i) {
500 case 1:
501 case 2:
502 return static_cast<ProxyProtocolVersion>(i);
503 default:
504 return ProxyProtocolVersion::UNDEFINED;
505 }
506 }
507