1 /** @file
2 
3   A brief file description
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 "tscore/ink_config.h"
25 #include "P_Net.h"
26 #include "HttpConfig.h"
27 #include "HttpSessionAccept.h"
28 #include "ReverseProxy.h"
29 #include "HttpSessionManager.h"
30 #ifdef USE_HTTP_DEBUG_LISTS
31 #include "Http1ClientSession.h"
32 #endif
33 #include "HttpPages.h"
34 #include "HttpTunnel.h"
35 #include "tscore/Tokenizer.h"
36 #include "P_SSLNextProtocolAccept.h"
37 #include "ProtocolProbeSessionAccept.h"
38 #include "http2/Http2SessionAccept.h"
39 #include "HttpConnectionCount.h"
40 #include "HttpProxyServerMain.h"
41 #if TS_USE_QUIC == 1
42 #include "P_QUICNetProcessor.h"
43 #include "P_QUICNextProtocolAccept.h"
44 #include "http3/Http3SessionAccept.h"
45 #endif
46 
47 #include <vector>
48 
49 HttpSessionAccept *plugin_http_accept             = nullptr;
50 HttpSessionAccept *plugin_http_transparent_accept = nullptr;
51 
52 static SLL<SSLNextProtocolAccept> ssl_plugin_acceptors;
53 static Ptr<ProxyMutex> ssl_plugin_mutex;
54 
55 std::mutex proxyServerMutex;
56 std::condition_variable proxyServerCheck;
57 bool et_net_threads_ready = false;
58 
59 std::mutex etUdpMutex;
60 std::condition_variable etUdpCheck;
61 bool et_udp_threads_ready = false;
62 
63 extern int num_of_net_threads;
64 extern int num_accept_threads;
65 
66 /// Global BufferWriter format name functions.
67 namespace
68 {
69 void
TS_bwf_thread(ts::BufferWriter & w,ts::BWFSpec const & spec)70 TS_bwf_thread(ts::BufferWriter &w, ts::BWFSpec const &spec)
71 {
72   bwformat(w, spec, this_thread());
73 }
74 void
TS_bwf_ethread(ts::BufferWriter & w,ts::BWFSpec const & spec)75 TS_bwf_ethread(ts::BufferWriter &w, ts::BWFSpec const &spec)
76 {
77   bwformat(w, spec, this_ethread());
78 }
79 } // namespace
80 
81 // File / process scope initializations
__anon5735c1be0202() 82 static bool HTTP_SERVER_INITIALIZED __attribute__((unused)) = []() -> bool {
83   ts::bwf_register_global("ts-thread", &TS_bwf_thread);
84   ts::bwf_register_global("ts-ethread", &TS_bwf_ethread);
85   return true;
86 }();
87 
88 bool
ssl_register_protocol(const char * protocol,Continuation * contp)89 ssl_register_protocol(const char *protocol, Continuation *contp)
90 {
91   SCOPED_MUTEX_LOCK(lock, ssl_plugin_mutex, this_ethread());
92 
93   for (SSLNextProtocolAccept *ssl = ssl_plugin_acceptors.head; ssl; ssl = ssl_plugin_acceptors.next(ssl)) {
94     if (!ssl->registerEndpoint(protocol, contp)) {
95       return false;
96     }
97   }
98 
99   return true;
100 }
101 
102 /////////////////////////////////////////////////////////////////
103 //
104 //  main()
105 //
106 /////////////////////////////////////////////////////////////////
107 
108 /** Data about an acceptor.
109 
110     This is used to separate setting up the proxy ports and
111     starting to accept on them.
112 
113 */
114 struct HttpProxyAcceptor {
115   /// Accept continuation.
116   Continuation *_accept = nullptr;
117   /// Options for @c NetProcessor.
118   NetProcessor::AcceptOptions _net_opt;
119 
120   /// Default constructor.
HttpProxyAcceptorHttpProxyAcceptor121   HttpProxyAcceptor() {}
122 };
123 
124 /** Global acceptors.
125 
126     This is parallel to @c HttpProxyPort::global(), each generated
127     from the corresponding port descriptor.
128 
129     @internal We use @c Continuation instead of @c HttpAccept because
130     @c SSLNextProtocolAccept is a subclass of @c Cont instead of @c
131     HttpAccept.
132 */
133 std::vector<HttpProxyAcceptor> HttpProxyAcceptors;
134 
135 // Called from InkAPI.cc
136 NetProcessor::AcceptOptions
make_net_accept_options(const HttpProxyPort * port,unsigned nthreads)137 make_net_accept_options(const HttpProxyPort *port, unsigned nthreads)
138 {
139   NetProcessor::AcceptOptions net;
140 
141   net.accept_threads = nthreads;
142 
143   REC_ReadConfigInteger(net.packet_mark, "proxy.config.net.sock_packet_mark_in");
144   REC_ReadConfigInteger(net.packet_tos, "proxy.config.net.sock_packet_tos_in");
145   REC_ReadConfigInteger(net.recv_bufsize, "proxy.config.net.sock_recv_buffer_size_in");
146   REC_ReadConfigInteger(net.send_bufsize, "proxy.config.net.sock_send_buffer_size_in");
147   REC_ReadConfigInteger(net.sockopt_flags, "proxy.config.net.sock_option_flag_in");
148   REC_ReadConfigInteger(net.defer_accept, "proxy.config.net.defer_accept");
149 
150 #ifdef TCP_FASTOPEN
151   REC_ReadConfigInteger(net.tfo_queue_length, "proxy.config.net.sock_option_tfo_queue_size_in");
152 #endif
153 
154   if (port) {
155     net.f_inbound_transparent = port->m_inbound_transparent_p;
156     net.f_mptcp               = port->m_mptcp;
157     net.ip_family             = port->m_family;
158     net.local_port            = port->m_port;
159     net.f_proxy_protocol      = port->m_proxy_protocol;
160 
161     if (port->m_inbound_ip.isValid()) {
162       net.local_ip = port->m_inbound_ip;
163     } else if (AF_INET6 == port->m_family && HttpConfig::m_master.inbound_ip6.isIp6()) {
164       net.local_ip = HttpConfig::m_master.inbound_ip6;
165     } else if (AF_INET == port->m_family && HttpConfig::m_master.inbound_ip4.isIp4()) {
166       net.local_ip = HttpConfig::m_master.inbound_ip4;
167     }
168   }
169   return net;
170 }
171 
172 static void
MakeHttpProxyAcceptor(HttpProxyAcceptor & acceptor,HttpProxyPort & port,unsigned nthreads)173 MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned nthreads)
174 {
175   NetProcessor::AcceptOptions &net_opt = acceptor._net_opt;
176   HttpSessionAccept::Options accept_opt;
177 
178   net_opt = make_net_accept_options(&port, nthreads);
179 
180   accept_opt.f_outbound_transparent = port.m_outbound_transparent_p;
181   accept_opt.transport_type         = port.m_type;
182   accept_opt.setHostResPreference(port.m_host_res_preference);
183   accept_opt.setTransparentPassthrough(port.m_transparent_passthrough);
184   accept_opt.setSessionProtocolPreference(port.m_session_protocol_preference);
185 
186   if (port.m_outbound_ip4.isValid()) {
187     accept_opt.outbound_ip4 = port.m_outbound_ip4;
188   } else if (HttpConfig::m_master.outbound_ip4.isValid()) {
189     accept_opt.outbound_ip4 = HttpConfig::m_master.outbound_ip4;
190   }
191 
192   if (port.m_outbound_ip6.isValid()) {
193     accept_opt.outbound_ip6 = port.m_outbound_ip6;
194   } else if (HttpConfig::m_master.outbound_ip6.isValid()) {
195     accept_opt.outbound_ip6 = HttpConfig::m_master.outbound_ip6;
196   }
197 
198   // OK the way this works is that the fallback for each port is a protocol
199   // probe acceptor. For SSL ports, we can stack a NPN+ALPN acceptor in front
200   // of that, and these ports will fall back to the probe if no NPN+ALPN endpoint
201   // was negotiated.
202 
203   // XXX the protocol probe should be a configuration option.
204 
205   ProtocolProbeSessionAccept *probe = new ProtocolProbeSessionAccept();
206   HttpSessionAccept *http           = nullptr; // don't allocate this unless it will be used.
207   probe->proxyPort                  = &port;
208   probe->proxy_protocol_ipmap       = &HttpConfig::m_master.config_proxy_protocol_ipmap;
209 
210   if (port.m_session_protocol_preference.intersects(HTTP_PROTOCOL_SET)) {
211     http = new HttpSessionAccept(accept_opt);
212     probe->registerEndpoint(ProtocolProbeSessionAccept::PROTO_HTTP, http);
213   }
214 
215   if (port.m_session_protocol_preference.intersects(HTTP2_PROTOCOL_SET)) {
216     probe->registerEndpoint(ProtocolProbeSessionAccept::PROTO_HTTP2, new Http2SessionAccept(accept_opt));
217   }
218 
219   if (port.isSSL()) {
220     SSLNextProtocolAccept *ssl = new SSLNextProtocolAccept(probe, port.m_transparent_passthrough);
221 
222     // ALPN selects the first server-offered protocol,
223     // so make sure that we offer the newest protocol first.
224     // But since registerEndpoint prepends you want to
225     // register them backwards, so you'd want to register
226     // the least important protocol first:
227     // http/1.0, http/1.1, h2
228 
229     ssl->enableProtocols(port.m_session_protocol_preference);
230     ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_1_0, http);
231     ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_1_1, http);
232     ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_2_0, new Http2SessionAccept(accept_opt));
233 
234     SCOPED_MUTEX_LOCK(lock, ssl_plugin_mutex, this_ethread());
235     ssl_plugin_acceptors.push(ssl);
236     ssl->proxyPort   = &port;
237     acceptor._accept = ssl;
238 #if TS_USE_QUIC == 1
239   } else if (port.isQUIC()) {
240     QUICNextProtocolAccept *quic = new QUICNextProtocolAccept();
241 
242     quic->enableProtocols(port.m_session_protocol_preference);
243 
244     // HTTP/0.9 over QUIC draft-27 (for interop only, will be removed)
245     quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC_D27, new Http3SessionAccept(accept_opt));
246 
247     // HTTP/3 draft-27
248     quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3_D27, new Http3SessionAccept(accept_opt));
249 
250     // HTTP/0.9 over QUIC (for interop only, will be removed)
251     quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC, new Http3SessionAccept(accept_opt));
252 
253     // HTTP/3
254     quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, new Http3SessionAccept(accept_opt));
255 
256     quic->proxyPort  = &port;
257     acceptor._accept = quic;
258 #endif
259   } else {
260     acceptor._accept = probe;
261   }
262 }
263 
264 /// Do all pre-thread initialization / setup.
265 void
prep_HttpProxyServer()266 prep_HttpProxyServer()
267 {
268   httpSessionManager.init();
269 }
270 
271 /** Set up all the accepts and sockets.
272  */
273 void
init_accept_HttpProxyServer(int n_accept_threads)274 init_accept_HttpProxyServer(int n_accept_threads)
275 {
276   HttpProxyPort::Group &proxy_ports = HttpProxyPort::global();
277 
278   init_reverse_proxy();
279   http_pages_init();
280 
281 #ifdef USE_HTTP_DEBUG_LISTS
282   ink_mutex_init(&debug_sm_list_mutex);
283   ink_mutex_init(&debug_cs_list_mutex);
284 #endif
285 
286   // Used to give plugins the ability to create http requests
287   //   The equivalent of the connecting to localhost on the  proxy
288   //   port but without going through the operating system
289   //
290   if (plugin_http_accept == nullptr) {
291     plugin_http_accept = new HttpSessionAccept();
292   }
293 
294   // Same as plugin_http_accept except outbound transparent.
295   if (!plugin_http_transparent_accept) {
296     HttpSessionAccept::Options ha_opt;
297     ha_opt.setOutboundTransparent(true);
298     plugin_http_transparent_accept = new HttpSessionAccept(ha_opt);
299   }
300 
301   if (!ssl_plugin_mutex) {
302     ssl_plugin_mutex = new_ProxyMutex();
303   }
304 
305   // Do the configuration defined ports.
306   // Assign temporary empty objects of proxy ports size
307   HttpProxyAcceptors.assign(proxy_ports.size(), HttpProxyAcceptor());
308   for (int i = 0, n = proxy_ports.size(); i < n; ++i) {
309     MakeHttpProxyAcceptor(HttpProxyAcceptors.at(i), proxy_ports[i], n_accept_threads);
310   }
311 }
312 
313 /** Increment the counter to keep track of how many et_net threads
314  *  we have started. This function is scheduled at the start of each
315  *  et_net thread using schedule_spawn(). We also check immediately
316  *  after incrementing the counter to see whether all of the et_net
317  *  threads have started such that we can notify main() to call
318  *  start_HttpProxyServer().
319  */
320 void
init_HttpProxyServer()321 init_HttpProxyServer()
322 {
323   if (eventProcessor.has_tg_started(ET_NET)) {
324     std::unique_lock<std::mutex> lock(proxyServerMutex);
325     et_net_threads_ready = true;
326     lock.unlock();
327     proxyServerCheck.notify_one();
328   }
329 
330 #if TS_USE_QUIC == 1
331   if (eventProcessor.has_tg_started(ET_UDP)) {
332     std::unique_lock<std::mutex> lock(etUdpMutex);
333     et_udp_threads_ready = true;
334     lock.unlock();
335     etUdpCheck.notify_one();
336   }
337 #endif
338 }
339 
340 void
start_HttpProxyServer()341 start_HttpProxyServer()
342 {
343   static bool called_once           = false;
344   HttpProxyPort::Group &proxy_ports = HttpProxyPort::global();
345 
346   ///////////////////////////////////
347   // start accepting connections   //
348   ///////////////////////////////////
349 
350   ink_assert(!called_once);
351   ink_assert(proxy_ports.size() == HttpProxyAcceptors.size());
352 
353   for (int i = 0, n = proxy_ports.size(); i < n; ++i) {
354     HttpProxyAcceptor &acceptor = HttpProxyAcceptors[i];
355     HttpProxyPort &port         = proxy_ports[i];
356     if (port.isSSL()) {
357       if (nullptr == sslNetProcessor.main_accept(acceptor._accept, port.m_fd, acceptor._net_opt)) {
358         return;
359       }
360 #if TS_USE_QUIC == 1
361     } else if (port.isQUIC()) {
362       if (nullptr == quic_NetProcessor.main_accept(acceptor._accept, port.m_fd, acceptor._net_opt)) {
363         return;
364       }
365 #endif
366     } else if (!port.isPlugin()) {
367       if (nullptr == netProcessor.main_accept(acceptor._accept, port.m_fd, acceptor._net_opt)) {
368         return;
369       }
370     }
371     // XXX although we make a good pretence here, I don't believe that NetProcessor::main_accept() ever actually returns
372     // NULL. It would be useful to be able to detect errors and spew them here though.
373   }
374 
375   // Set up stat page for http connection count
376   statPagesManager.register_http("connection_count", register_ShowConnectionCount);
377 
378   // Alert plugins that connections will be accepted.
379   APIHook *hook = lifecycle_hooks->get(TS_LIFECYCLE_PORTS_READY_HOOK);
380   while (hook) {
381     hook->invoke(TS_EVENT_LIFECYCLE_PORTS_READY, nullptr);
382     hook = hook->next();
383   }
384 }
385 
386 void
stop_HttpProxyServer()387 stop_HttpProxyServer()
388 {
389   sslNetProcessor.stop_accept();
390   netProcessor.stop_accept();
391 }
392