| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "cripts/Lulu.hpp" |
| #include "cripts/Preamble.hpp" |
| #include <arpa/inet.h> |
| #include "tscore/ink_platform.h" |
| |
| constexpr unsigned NORMALIZED_TIME_QUANTUM = 3600; // 1 hour |
| |
| // Some common network ranges |
| const cripts::Matcher::Range::IP cripts::Net::Localhost({"127.0.0.1", "::1"}); |
| const cripts::Matcher::Range::IP cripts::Net::RFC1918({"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}); |
| |
| void |
| detail::ConnBase::Pacing::operator=(uint32_t val) |
| { |
| _ensure_initialized(_owner); |
| if (val == 0) { |
| val = Off; |
| } |
| |
| #ifdef SO_MAX_PACING_RATE |
| int connfd = _owner->FD(); |
| int res = setsockopt(connfd, SOL_SOCKET, SO_MAX_PACING_RATE, (char *)&val, sizeof(val)); |
| |
| // EBADF indicates possible client abort |
| if ((res < 0) && (errno != EBADF)) { |
| TSError("[fq_pacing] Error setting SO_MAX_PACING_RATE, errno=%d", errno); |
| } |
| #endif |
| _val = val; |
| } |
| |
| void |
| detail::ConnBase::TcpInfo::initialize() |
| { |
| #if defined(TCP_INFO) && defined(HAVE_STRUCT_TCP_INFO) |
| _ensure_initialized(_owner); |
| if (!_ready) { |
| int connfd = _owner->FD(); |
| |
| TSAssert(_owner->_state->txnp); |
| if (connfd < 0 || TSHttpTxnIsInternal(_owner->_state->txnp)) { // No TCPInfo for internal transactions |
| _ready = false; |
| // ToDo: Deal with errors? |
| } else { |
| if (getsockopt(connfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0) { |
| _ready = (info_len > 0); |
| } |
| } |
| } |
| #endif |
| } |
| |
| cripts::string_view |
| detail::ConnBase::TcpInfo::Log() |
| { |
| _ensure_initialized(_owner); |
| initialize(); |
| // We intentionally do not use the old tcpinfo that may be stored, since we may |
| // request this numerous times (measurements). Also make sure there's always a value. |
| _logging = "-"; |
| |
| if (_ready) { |
| // A lot of this is taken verbatim from header_rewrite, may want to rewrite this with sstreams |
| #if HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS |
| _logging = cripts::string(format("{};{};{};{}", info.tcpi_rtt, info.tcpi_rto, info.tcpi_snd_cwnd, info.tcpi_retrans)); |
| #elif HAVE_STRUCT_TCP_INFO___TCPI_RETRANS |
| _logging = cripts::string(fmt::format("{};{};{};{}", info.tcpi_rtt, info.tcpi_rto, info.tcpi_snd_cwnd, info.__tcpi_retrans)); |
| #endif |
| } |
| |
| return _logging; |
| } |
| |
| cripts::Certs::Client |
| detail::ConnBase::ClientCert() |
| { |
| _ensure_initialized(this); |
| return cripts::Certs::Client{*this}; |
| } |
| |
| cripts::Certs::Server |
| detail::ConnBase::ServerCert() |
| { |
| _ensure_initialized(this); |
| return cripts::Certs::Server{*this}; |
| } |
| |
| namespace cripts |
| { |
| |
| // This is mostly copied out of header_rewrite of course |
| cripts::string_view |
| IP::GetSV(unsigned ipv4_cidr, unsigned ipv6_cidr) |
| { |
| if (is_ip4()) { |
| auto addr = this->_addr._ip4.network_order(); |
| in_addr_t ip = addr & htonl(UINT32_MAX << (32 - ipv4_cidr)); |
| |
| if (inet_ntop(AF_INET, &ip, _str, INET_ADDRSTRLEN)) { |
| return {_str, strlen(_str)}; |
| } |
| } else if (is_ip6()) { |
| unsigned int v6_zero_bytes = (128 - ipv6_cidr) / 8; |
| int v6_mask = 0xff >> ((128 - ipv6_cidr) % 8); |
| auto addr = this->_addr._ip6.network_order(); |
| |
| // For later: |
| // swoc::FixedBuffer w(buff, buff_size); |
| // w.print("{}", addr & IPMask(addr.is_ip4() ? 24 : 64)); |
| // addr &= IPMask(addr.is_ip4() ? 24 : 64); |
| // |
| |
| if (v6_zero_bytes > 0) { |
| memset(&addr.s6_addr[16 - v6_zero_bytes], 0, v6_zero_bytes); |
| } |
| if (v6_mask != 0xff) { |
| addr.s6_addr[16 - v6_zero_bytes] &= v6_mask; |
| } |
| if (inet_ntop(AF_INET6, &addr, _str, INET6_ADDRSTRLEN)) { |
| return {_str, strlen(_str)}; |
| } |
| } |
| |
| return ""; |
| } |
| |
| sockaddr |
| IP::Socket() const |
| { |
| sockaddr addr = {}; |
| |
| this->copy_to(&addr); |
| return addr; |
| } |
| |
| uint64_t |
| IP::Hasher(unsigned ipv4_cidr, unsigned ipv6_cidr) |
| { |
| if (_hash == 0) { |
| if (is_ip4()) { |
| auto addr = this->_addr._ip4.network_order(); |
| in_addr_t ip = addr & htonl(UINT32_MAX << (32 - ipv4_cidr)); |
| |
| _hash = (0xffffffff00000000 | ip); |
| } else if (is_ip6()) { |
| unsigned int v6_zero_bytes = (128 - ipv6_cidr) / 8; |
| int v6_mask = 0xff >> ((128 - ipv6_cidr) % 8); |
| auto addr = this->_addr._ip6.network_order(); |
| |
| if (v6_zero_bytes > 0) { |
| memset(&addr.s6_addr[16 - v6_zero_bytes], 0, v6_zero_bytes); |
| } |
| if (v6_mask != 0xff) { |
| addr.s6_addr[16 - v6_zero_bytes] &= v6_mask; |
| } |
| |
| _hash = |
| (*reinterpret_cast<uint64_t const *>(addr.s6_addr) ^ *reinterpret_cast<uint64_t const *>(addr.s6_addr + sizeof(uint64_t))); |
| } else { |
| // Clearly this shouldn't happen, but lets not assert |
| } |
| } |
| |
| return _hash; |
| } |
| |
| bool |
| IP::Sample(double rate, uint32_t seed, unsigned ipv4_cidr, unsigned ipv6_cidr) |
| { |
| CAssert(rate >= 0.0 && rate <= 1.0); // For detecting bugs in a Cript, 0.0 and 1.0 are valid though |
| |
| if (_sampler == 0) { |
| // This only works until 2038 |
| uint32_t now = (Time::Local::Now().Epoch() / NORMALIZED_TIME_QUANTUM) * NORMALIZED_TIME_QUANTUM; |
| |
| if (is_ip4()) { |
| auto addr = this->_addr._ip4.network_order(); |
| in_addr_t ip = addr & htonl(UINT32_MAX << (32 - ipv4_cidr)); |
| |
| _sampler = (ip >> 16) ^ (ip & 0x00ff); // Fold them to 16 bits, mixing net and host |
| |
| } else if (is_ip6()) { |
| unsigned int v6_zero_bytes = (128 - ipv6_cidr) / 8; |
| int v6_mask = 0xff >> ((128 - ipv6_cidr) % 8); |
| auto addr = this->_addr._ip6.network_order(); |
| |
| if (v6_zero_bytes > 0) { |
| memset(&addr.s6_addr[16 - v6_zero_bytes], 0, v6_zero_bytes); |
| } |
| if (v6_mask != 0xff) { |
| addr.s6_addr[16 - v6_zero_bytes] &= v6_mask; |
| } |
| |
| _sampler = *reinterpret_cast<uint16_t const *>(addr.s6_addr) ^ |
| *reinterpret_cast<uint16_t const *>(addr.s6_addr + sizeof(uint16_t)) ^ |
| *reinterpret_cast<uint16_t const *>(addr.s6_addr + 2 * sizeof(uint16_t)) ^ |
| *reinterpret_cast<uint16_t const *>(addr.s6_addr + 3 * sizeof(uint16_t)); |
| } else { |
| // Clearly this shouldn't happen, but lets not assert |
| } |
| |
| _sampler ^= (now >> 16) ^ (now & 0x00ff); // Fold in the hourly normalized timestamp |
| if (seed) { |
| _sampler ^= (seed >> 16) ^ (seed & 0x00ff); // Fold in the seed as well |
| } |
| } |
| |
| // To avoid picking sequential folded IPs bucketize them with a modulo of the sampler (just in case) |
| return (_sampler % static_cast<uint16_t>(rate * UINT16_MAX)) == _sampler; |
| } |
| |
| Client::Connection & |
| Client::Connection::_get(cripts::Context *context) |
| { |
| _ensure_initialized(&context->_client.connection); |
| return context->_client.connection; |
| } |
| |
| void |
| Client::Connection::_initialize() |
| { |
| TSAssert(_state->ssnp); |
| TSAssert(_state->txnp); |
| |
| _vc = TSHttpSsnClientVConnGet(_state->ssnp); |
| _socket = TSHttpTxnClientAddrGet(_state->txnp); |
| super_type::_initialize(); |
| } |
| |
| int |
| Client::Connection::FD() const |
| { |
| int connfd = -1; |
| |
| TSAssert(_state->txnp); |
| TSHttpTxnClientFdGet(_state->txnp, &connfd); |
| |
| return connfd; |
| } |
| |
| int |
| Client::Connection::Count() const |
| { |
| TSAssert(_state->ssnp); |
| return TSHttpSsnTransactionCount(_state->ssnp); |
| } |
| |
| Server::Connection & |
| Server::Connection::_get(cripts::Context *context) |
| { |
| _ensure_initialized(&context->_server.connection); |
| return context->_server.connection; |
| } |
| |
| void |
| Server::Connection::_initialize() |
| { |
| TSAssert(_state->ssnp); |
| |
| _vc = TSHttpSsnServerVConnGet(_state->ssnp); |
| _socket = TSHttpTxnServerAddrGet(_state->txnp); |
| super_type::_initialize(); |
| } |
| |
| int |
| Server::Connection::FD() const |
| { |
| int connfd = -1; |
| |
| TSAssert(_state->txnp); |
| TSHttpTxnServerFdGet(_state->txnp, &connfd); |
| |
| return connfd; |
| } |
| |
| int |
| Server::Connection::Count() const |
| { |
| TSAssert(_state->txnp); |
| return TSHttpTxnServerSsnTransactionCount(_state->txnp); |
| } |
| |
| } // namespace cripts |