| /* | |
| 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 ""; | |
| } | |
| swoc::IPEndpoint | |
| IP::Socket() const | |
| { | |
| return swoc::IPEndpoint{*this}; | |
| } | |
| 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 |