// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved. // Licensed under the Apache License, Version 2.0 (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 "ip_utilities.h" #include "connkey.h" using namespace std; // LCOV_EXCL_START Reason: temporary until we add relevant UT until 07/10 bool operator<(const IpAddress &this_ip_addr, const IpAddress &other_ip_addr) { if (this_ip_addr.ip_type < other_ip_addr.ip_type) return true; if (this_ip_addr.ip_type == IP_VERSION_4) return this_ip_addr.addr4_t.s_addr < other_ip_addr.addr4_t.s_addr; return memcmp(&this_ip_addr.addr6_t, &other_ip_addr.addr6_t, sizeof(struct in6_addr)) < 0; } bool operator==(const IpAddress &this_ip_addr, const IpAddress &other_ip_addr) { if (this_ip_addr.ip_type != other_ip_addr.ip_type) return false; if (this_ip_addr.ip_type == IP_VERSION_4) return this_ip_addr.addr4_t.s_addr == other_ip_addr.addr4_t.s_addr; return memcmp(&this_ip_addr.addr6_t, &other_ip_addr.addr6_t, sizeof(struct in6_addr)) == 0; } // LCOV_EXCL_STOP Maybe> extractAddressAndMaskSize(const string &cidr) { size_t delimiter_pos = cidr.find("/"); if (delimiter_pos == string::npos) return genError("provided value is not in CIDR notation: " + cidr); string address = cidr.substr(0, delimiter_pos); string mask_size = cidr.substr(delimiter_pos + 1, cidr.size() - delimiter_pos - 1); try { return make_pair(address, stoi(mask_size)); } catch(...) { return genError("failed to cast provided value to integer: " + mask_size); } return genError("failed to parse provided string as a CIDR: " + cidr); } template pair applyMaskOnAddress(const vector &oct, Integer mask) { Integer start = (oct[0] | oct[1] | oct[2] | oct[3]) & mask; Integer end = (oct[0] | oct[1] | oct[2] | oct[3]) | (~mask); return make_pair(start, end); } Maybe> createRangeFromCidrV4(const pair &cidr_values) { string address = cidr_values.first; int mask_size = cidr_values.second; vector oct; for (int i=3; i>=0; i--) { size_t delimiter_pos = address.find("."); string oct_str = address.substr(0, delimiter_pos); try { oct.push_back(static_cast(stoul(oct_str)) << (i * 8)); } catch (...) { return genError("failed to cast provided value to integer: " + oct_str); } if ((i == 0) != (delimiter_pos == string::npos)) { return genError("provided value is not in a correct ipv4 structure: " + makeSeparatedStr(oct, ".")); } address.erase(0, delimiter_pos + 1); } unsigned int mask = 0xffffffff; mask <<= (32 - mask_size); unsigned int start, end; tie(start, end) = applyMaskOnAddress(oct, mask); auto construct_address = [](unsigned int value) { stringstream address_stream; for (int i = 3; i >= 0; i--) { address_stream << ((value >> (i * 8)) & 0xff) << (i > 0 ? "." : ""); } return address_stream.str(); }; return make_pair(construct_address(start), construct_address(end)); } // LCOV_EXCL_START Reason: it is tested, but for some reason coverage doesn't catch it Maybe> createRangeFromCidrV6(const pair &cidr_values) { string address = cidr_values.first; int mask_size = cidr_values.second; // fill compressed zeros struct in6_addr v6; if (inet_pton(AF_INET6, address.c_str(), &v6) == -1) { return genError("faild to convert provided value to ipv6: " + address); }; struct in6_addr *addr = &v6; vector oct_from_str; for (int i=0; i<15; i+=2){ char hex[8]; unsigned int num; sprintf(hex, "%02x%02x", static_cast(addr->s6_addr[i]), static_cast(addr->s6_addr[i+1])); sscanf(hex, "%x", &num); oct_from_str.push_back(num); } uint64_t mask = 0xffffffffffffffff; function construct_address; int oct_offset; if (mask_size > 64) { oct_offset = 7; mask <<= (128 - mask_size); construct_address = [oct_from_str](uint64_t value, bool is_start) { (void)is_start; stringstream address_stream; for (int i = 0; i < 4; i++) { address_stream << hex << oct_from_str[i] << ":"; } for (int i = 3; i >= 0; i--) { address_stream << hex << (unsigned int)((value >> (i * 16)) & 0xffff) << (i > 0 ? ":" : ""); } return address_stream.str(); }; } else { oct_offset = 3; mask <<= (64 - mask_size); construct_address = [](uint64_t value, bool is_start) { stringstream address_stream; for (int i = 3; i >= 0; i--) { address_stream << hex << (unsigned int)((value >> (i * 16)) & 0xffff) << ":"; } address_stream << (is_start ? "0:0:0:0" : "ffff:ffff:ffff:ffff"); return address_stream.str(); }; } uint64_t start, end; vector oct; for (int i = 3; i >= 0; i--) { oct.push_back(static_cast(oct_from_str[oct_offset - i]) << (i * 16)); } tie(start, end) = applyMaskOnAddress(oct, mask); return make_pair( construct_address(start, true), construct_address(end, false) ); } // LCOV_EXCL_STOP namespace IPUtilities { Maybe> getInterfaceIPs() { struct ifaddrs *if_addr_list = nullptr; if (getifaddrs(&if_addr_list) == -1) { return genError(string("Failed to get interface IP's. Error: ") + strerror(errno)); } map interface_ips; for (struct ifaddrs *if_addr = if_addr_list; if_addr != nullptr; if_addr = if_addr->ifa_next) { if (if_addr->ifa_addr == nullptr) continue; if (if_addr->ifa_addr->sa_family != AF_INET && if_addr->ifa_addr->sa_family != AF_INET6) continue; char address_buffer[INET6_ADDRSTRLEN] = { '\0' }; if (if_addr->ifa_addr->sa_family == AF_INET) { struct in_addr addr = reinterpret_cast(if_addr->ifa_addr)->sin_addr; inet_ntop(AF_INET, &addr, address_buffer, INET_ADDRSTRLEN); string address_string(address_buffer); if (address_string.find("127.0.0.1") != string::npos) continue; IpAddress ip_addr; ip_addr.ip_type = IP_VERSION_4; memcpy(&ip_addr.ip.ipv4, &addr, sizeof(ip_addr.ip.ipv4)); interface_ips.emplace(ip_addr, address_string); } else { struct in6_addr addr = reinterpret_cast(if_addr->ifa_addr)->sin6_addr; inet_ntop(AF_INET6, &addr, address_buffer, INET6_ADDRSTRLEN); string address_string(address_buffer); if (address_string.find("::1") != string::npos) continue; IpAddress ip_addr; ip_addr.ip_type = IP_VERSION_6; memcpy(&ip_addr.ip.ipv6, &addr, sizeof(ip_addr.ip.ipv6)); interface_ips.emplace(ip_addr, address_string); } } if (if_addr_list != nullptr) freeifaddrs(if_addr_list); return interface_ips; } Maybe> createRangeFromCidr(const string &cidr) { auto cidr_values = extractAddressAndMaskSize(cidr); if (!cidr_values.ok()) return genError("Failed to create range from Cidr: " + cidr_values.getErr()); return cidr.find(".") != string::npos ? createRangeFromCidrV4(cidr_values.unpack()) : createRangeFromCidrV6(cidr_values.unpack()); } bool isIpAddrInRange(const IPRange &rule_ip_range, const IpAddress &ip_addr) { IpAddress min_ip = rule_ip_range.start; IpAddress max_ip = rule_ip_range.end; if (ip_addr.ip_type == IP_VERSION_4) { if (max_ip.ip_type != IP_VERSION_4) return 0; return memcmp(&ip_addr.ip.ipv4, &min_ip.ip.ipv4, sizeof(struct in_addr)) >= 0 && memcmp(&ip_addr.ip.ipv4, &max_ip.ip.ipv4, sizeof(struct in_addr)) <= 0; } if (ip_addr.ip_type == IP_VERSION_6) { if (max_ip.ip_type != IP_VERSION_6) return 0; return memcmp(&ip_addr.ip.ipv6, &min_ip.ip.ipv6, sizeof(struct in6_addr)) >= 0 && memcmp(&ip_addr.ip.ipv6, &max_ip.ip.ipv6, sizeof(struct in6_addr)) <= 0; } return 0; } string IpAddrToString(const IpAddress &address) { if (address.ip_type == IP_VERSION_6) { char ip_str[INET6_ADDRSTRLEN]; struct sockaddr_in6 sa6; sa6.sin6_family = AF_INET6; sa6.sin6_addr = address.ip.ipv6; inet_ntop(AF_INET6, &(sa6.sin6_addr), ip_str, INET6_ADDRSTRLEN); return move(string(ip_str)); } char ip_str[INET_ADDRSTRLEN]; struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_addr = address.ip.ipv4; inet_ntop(AF_INET, &(sa.sin_addr), ip_str, INET_ADDRSTRLEN); return move(string(ip_str)); } IpAddress createIpFromString(const string &ip_string) { IpAddress res_address = {0, IP_VERSION_ANY}; if (ip_string == "any") return res_address; auto maybe_ip_addr = IPAddr::createIPAddr(ip_string); if (!maybe_ip_addr.ok()) { return res_address; } IPAddr ip_addr = maybe_ip_addr.unpack(); res_address.ip_type = static_cast(ip_addr.getType()); if (ip_addr.getType() == IPType::V4) { res_address.addr4_t = ip_addr.getIPv4(); } else { res_address.addr6_t = ip_addr.getIPv6(); } return res_address; } IpAddress ConvertToIpAddress(const IPAddr &addr) { IpAddress address; switch (addr.getType()) { case IPType::UNINITIALIZED: { address.addr4_t = {0}; address.ip_type = IP_VERSION_ANY; break; } case IPType::V4: { address.addr4_t = addr.getIPv4(); // reference to a local variable ? address.ip_type = IP_VERSION_4; break; } case IPType::V6: { address.addr6_t = addr.getIPv6(); address.ip_type = IP_VERSION_6; break; } default: dbgAssert(false) << AlertInfo(AlertTeam::CORE, "ip utilities") << "Unsupported IP type"; } return address; } IpAttrFromString::operator Maybe() { auto ip_addr = IPAddr::createIPAddr(data); if (!ip_addr.ok()) return genError("Could not create IP address. Error: " + ip_addr.getErr()); return ConvertToIpAddress(ip_addr.unpackMove()); } IpAttrFromString::operator Maybe() { int value; try { value = stoi(data); } catch (...) { return genError("provided value is not a legal number. Value: " + data); } if (value > static_cast(UINT8_MAX) || value < 0) { return genError("provided value is not a legal ip protocol number. Value: " + data); } return static_cast(value); } IpAttrFromString::operator Maybe() { int value; try { value = stoi(data); } catch (...) { return genError("provided value is not a legal number. Value: " + data); } if (value > static_cast(UINT16_MAX) || value < 0) { return genError("provided value is not a legal port number. Value: " + data); } return static_cast(value); } }