// 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 "gradual_deployment.h" #include #include #include #include "enum_range.h" #include "connkey.h" USE_DEBUG_FLAG(D_GRADUAL_DEPLOYMENT); using namespace std; class SetGradualDeploymentRanges : public ServerRest { public: void doCall() override { dbgFlow(D_GRADUAL_DEPLOYMENT) << "Set gradual policy API"; auto maybe_type = convertServiceStrToAttachmentType(attachment_type.get()); if (!maybe_type.ok()) { string error = "Failed to determine attachment type. Type: " + attachment_type.get() + ", error: " + maybe_type.getErr(); dbgWarning(D_GRADUAL_DEPLOYMENT) << error; throw JsonError(error); } dbgTrace(D_GRADUAL_DEPLOYMENT) << "Setting gradual policy for attachment of type: " << attachment_type.get(); auto i_gradual_deployment = Singleton::Consume::from(); auto set_policy_res = i_gradual_deployment->setPolicy(maybe_type.unpackMove(), ip_ranges.get()); if (!set_policy_res.ok()) throw JsonError(set_policy_res.getErr()); return; } private: C2S_PARAM(vector, ip_ranges) C2S_PARAM(string, attachment_type) Maybe convertServiceStrToAttachmentType(string &type) { transform(type.begin(), type.end(), type.begin(), ::tolower); if (type == "http-manager") return I_GradualDeployment::AttachmentType::NGINX; if (type == "access-control") return I_GradualDeployment::AttachmentType::KERNEL; return genError("unknown attachment type"); } }; class GradualDeployment::Impl : Singleton::Provide::From { public: void init() { dbgFlow(D_GRADUAL_DEPLOYMENT) << "Initializing Gradual Deployment Manager"; auto rest = Singleton::Consume::by(); rest->addRestCall(RestAction::SET, "gradual-deployment-policy"); dbgTrace(D_GRADUAL_DEPLOYMENT) << "Gradual Deployment Manager initialization is done successfully"; } Maybe setPolicy(I_GradualDeployment::AttachmentType type, const vector &str_ip_ranges) override { auto maybe_policy = parseIpRanges(str_ip_ranges); if (!maybe_policy.ok()) { auto error = "Failed to set gradual deployment policy. Error: " + maybe_policy.getErr(); dbgWarning(D_GRADUAL_DEPLOYMENT) << error; return genError(error); } ip_ranges_map[static_cast(type)] = maybe_policy.unpackMove(); return Maybe(); } vector getPolicy(I_GradualDeployment::AttachmentType type) override { vector res; for (const IPRange &range : ip_ranges_map[static_cast(type)]) { // Range is validated on insertion res.push_back(convertIpRangeToStr(range).unpack()); } return res; } vector & getParsedPolicy(I_GradualDeployment::AttachmentType type) override { return ip_ranges_map[static_cast(type)]; } private: IpAddress ConvertToIpAddress(const IPAddr &addr) { IpAddress address; switch (addr.getType()) { case IPType::V4: { address.addr4_t = addr.getIPv4(); 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, "gradual deployment") << "Unsupported IP type"; } return address; } Maybe createRangeFromStr(const string &range) { vector temp_params_list; boost::split(temp_params_list, range, boost::is_any_of("-")); if (temp_params_list.size() == 1) { Maybe maybe_ip = IPAddr::createIPAddr(temp_params_list[0]); if (!maybe_ip.ok()) return genError("Could not create IP address, " + maybe_ip.getErr()); IpAddress addr = move(ConvertToIpAddress(maybe_ip.unpackMove())); return move(IPRange{.start = addr, .end = addr}); } if (temp_params_list.size() == 2) { Maybe maybe_ip_min = IPAddr::createIPAddr(temp_params_list[0]); Maybe maybe_ip_max = IPAddr::createIPAddr(temp_params_list[1]); if (!maybe_ip_min.ok()) return genError("Could not create IP address, " + maybe_ip_min.getErr()); if (!maybe_ip_max.ok()) return genError("Could not create IP address, " + maybe_ip_max.getErr()); IPAddr min_addr = maybe_ip_min.unpackMove(); IPAddr max_addr = maybe_ip_max.unpackMove(); if (min_addr > max_addr) return genError("Could not create ip range - start greater then end"); IpAddress addr_min = move(ConvertToIpAddress(move(min_addr))); IpAddress addr_max = move(ConvertToIpAddress(move(max_addr))); if (addr_max.ip_type != addr_min.ip_type) return genError("Range IP's type does not match"); return move(IPRange{.start = move(addr_min), .end = move(addr_max)}); } return genError("Illegal range received: " + range); } Maybe> parseIpRanges(const vector &str_ip_ranges) { vector ip_ranges; for (const string &range : str_ip_ranges) { Maybe ip_range = createRangeFromStr(range); if (!ip_range.ok()) { return genError("Failed to parse gradual deployment IP range: " + ip_range.getErr()); } ip_ranges.push_back(ip_range.unpackMove()); } return move(ip_ranges); } Maybe convertIpRangeToStr(const IPRange &range) { if (range.start.ip_type != IP_VERSION_4 && range.start.ip_type != IP_VERSION_6) { return genError("Unknown IP type received: " + range.start.ip_type); } size_t len; int type; const void *in_addr_min; const void *in_addr_max; if (range.start.ip_type == IP_VERSION_4) { len = INET_ADDRSTRLEN; type = AF_INET; in_addr_min = &range.start.ip.ipv4; in_addr_max = &range.end.ip.ipv4; } else { len = INET6_ADDRSTRLEN; type = AF_INET6; in_addr_min = &range.start.ip.ipv6; in_addr_max = &range.end.ip.ipv6; } char str_min[len]; inet_ntop(type, in_addr_min, str_min, len); char str_max[len]; inet_ntop(type, in_addr_max, str_max, len); string start(str_min, strnlen(str_min, len)); string end(str_max, strnlen(str_max, len)); return start + "-" + end; } unordered_map> ip_ranges_map; }; GradualDeployment::GradualDeployment() : Component("GradualDeployment"), pimpl(make_unique()) {} GradualDeployment::~GradualDeployment() {} void GradualDeployment::init() { pimpl->init(); }