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