mirror of
https://github.com/pavel-odintsov/fastnetmon
synced 2024-11-22 20:42:03 +01:00
Moved from gRPC based integration for GoBGP IPv4 announces to our own BGP messaging logic
This commit is contained in:
parent
af49358307
commit
e65727c849
@ -10,18 +10,13 @@
|
||||
|
||||
#include "../fastnetmon_configuration_scheme.hpp"
|
||||
|
||||
std::string gobgp_nexthop = "0.0.0.0";
|
||||
|
||||
bgp_community_attribute_element_t bgp_community_host;
|
||||
bgp_community_attribute_element_t bgp_community_subnet;
|
||||
|
||||
extern fastnetmon_configuration_t fastnetmon_global_configuration;
|
||||
|
||||
void gobgp_action_init() {
|
||||
logger << log4cpp::Priority::INFO << "GoBGP action module loaded";
|
||||
|
||||
if (configuration_map.count("gobgp_next_hop")) {
|
||||
gobgp_nexthop = configuration_map["gobgp_next_hop"];
|
||||
fastnetmon_global_configuration.gobgp_next_hop = configuration_map["gobgp_next_hop"];
|
||||
}
|
||||
|
||||
if (configuration_map.count("gobgp_next_hop_ipv6")) {
|
||||
@ -52,34 +47,14 @@ void gobgp_action_init() {
|
||||
fastnetmon_global_configuration.gobgp_announce_whole_subnet_ipv6 = configuration_map["gobgp_announce_whole_subnet_ipv6"] == "on";
|
||||
}
|
||||
|
||||
// Set them to safe defaults
|
||||
bgp_community_host.asn_number = 65001;
|
||||
bgp_community_host.community_number = 666;
|
||||
|
||||
if (configuration_map.count("gobgp_community_host")) {
|
||||
if (!read_bgp_community_from_string(configuration_map["gobgp_community_host"], bgp_community_host)) {
|
||||
logger << log4cpp::Priority::ERROR << "Cannot parse GoBGP community for host "
|
||||
<< configuration_map["gobgp_community_host"];
|
||||
}
|
||||
fastnetmon_global_configuration.gobgp_community_host = configuration_map["gobgp_community_host"];
|
||||
}
|
||||
|
||||
logger << log4cpp::Priority::INFO << "GoBGP host IPv4 community: " << bgp_community_host.asn_number << ":"
|
||||
<< bgp_community_host.community_number;
|
||||
|
||||
// Set them to safe defaults
|
||||
bgp_community_subnet.asn_number = 65001;
|
||||
bgp_community_subnet.community_number = 666;
|
||||
|
||||
if (configuration_map.count("gobgp_community_subnet")) {
|
||||
if (!read_bgp_community_from_string(configuration_map["gobgp_community_subnet"], bgp_community_subnet)) {
|
||||
logger << log4cpp::Priority::ERROR << "Cannot parse GoBGP community for subnet "
|
||||
<< configuration_map["gobgp_community_subnet"];
|
||||
}
|
||||
fastnetmon_global_configuration.gobgp_community_subnet = configuration_map["gobgp_community_subnet"];
|
||||
}
|
||||
|
||||
logger << log4cpp::Priority::INFO << "GoBGP subnet IPv4 community: " << bgp_community_subnet.asn_number << ":"
|
||||
<< bgp_community_subnet.community_number;
|
||||
|
||||
if (configuration_map.count("gobgp_community_host_ipv6")) {
|
||||
fastnetmon_global_configuration.gobgp_community_host_ipv6 = configuration_map["gobgp_community_host_ipv6"];
|
||||
}
|
||||
@ -187,6 +162,116 @@ void gobgp_ban_manage_ipv6(GrpcClient& gobgp_client,
|
||||
}
|
||||
}
|
||||
|
||||
void gobgp_ban_manage_ipv4(GrpcClient& gobgp_client, uint32_t client_ip, bool is_withdrawal, const attack_details_t& current_attack) {
|
||||
// Previously we used same next hop for both subnet and host
|
||||
uint32_t next_hop_as_integer_legacy = 0;
|
||||
|
||||
if (!convert_ip_as_string_to_uint_safe(fastnetmon_global_configuration.gobgp_next_hop, next_hop_as_integer_legacy)) {
|
||||
logger << log4cpp::Priority::ERROR
|
||||
<< "Could not decode next hop to numeric form: " << fastnetmon_global_configuration.gobgp_next_hop;
|
||||
return;
|
||||
}
|
||||
|
||||
// Starting July 2024, 1.1.8 we have capability to specify different next hops for host and subnet
|
||||
uint32_t gobgp_next_hop_host_ipv4 = 0;
|
||||
uint32_t gobgp_next_hop_subnet_ipv4 = 0;
|
||||
|
||||
// Read next hop for host
|
||||
if (fastnetmon_global_configuration.gobgp_next_hop_host_ipv4 != "") {
|
||||
if (!convert_ip_as_string_to_uint_safe(fastnetmon_global_configuration.gobgp_next_hop_host_ipv4, gobgp_next_hop_host_ipv4)) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode next hop to numeric form for gobgp_next_hop_host_ipv4: "
|
||||
<< fastnetmon_global_configuration.gobgp_next_hop_host_ipv4;
|
||||
// We do not stop processing here. If we failed then let's keep it zero
|
||||
}
|
||||
} else {
|
||||
// That's fine. It's expected to be empty on new installations
|
||||
}
|
||||
|
||||
|
||||
// Read next hop for subnet
|
||||
if (fastnetmon_global_configuration.gobgp_next_hop_subnet_ipv4 != "") {
|
||||
if (!convert_ip_as_string_to_uint_safe(fastnetmon_global_configuration.gobgp_next_hop_subnet_ipv4, gobgp_next_hop_subnet_ipv4)) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode next hop to numeric form for gobgp_next_hop_subnet_ipv4: "
|
||||
<< fastnetmon_global_configuration.gobgp_next_hop_subnet_ipv4;
|
||||
|
||||
// We do not stop processing here. If we failed then let's keep it zero
|
||||
}
|
||||
} else {
|
||||
// That's fine. It's expected to be empty on new installations
|
||||
}
|
||||
|
||||
// For backward compatibility with old deployments which still use value gobgp_next_hop we check new value and if it's zero use old one
|
||||
if (gobgp_next_hop_host_ipv4 == 0) {
|
||||
logger << log4cpp::Priority::INFO << "gobgp_next_hop_host_ipv4 is empty, will use global gobgp_next_hop: "
|
||||
<< fastnetmon_global_configuration.gobgp_next_hop;
|
||||
gobgp_next_hop_host_ipv4 = next_hop_as_integer_legacy;
|
||||
}
|
||||
|
||||
if (gobgp_next_hop_subnet_ipv4 == 0) {
|
||||
logger << log4cpp::Priority::INFO << "gobgp_next_hop_subnet_ipv4 is empty, will use global gobgp_next_hop: "
|
||||
<< fastnetmon_global_configuration.gobgp_next_hop;
|
||||
gobgp_next_hop_subnet_ipv4 = next_hop_as_integer_legacy;
|
||||
}
|
||||
|
||||
if (fastnetmon_global_configuration.gobgp_announce_whole_subnet) {
|
||||
IPv4UnicastAnnounce unicast_ipv4_announce;
|
||||
|
||||
std::vector<std::string> subnet_ipv4_communities;
|
||||
|
||||
subnet_ipv4_communities.push_back(fastnetmon_global_configuration.gobgp_community_subnet);
|
||||
|
||||
for (auto community_string : subnet_ipv4_communities) {
|
||||
bgp_community_attribute_element_t bgp_community_subnet;
|
||||
|
||||
if (!read_bgp_community_from_string(community_string, bgp_community_subnet)) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode BGP community for IPv4 subnet";
|
||||
// We may have multiple communities and other communities may be correct, skip only broken one
|
||||
continue;
|
||||
}
|
||||
|
||||
unicast_ipv4_announce.add_community(bgp_community_subnet);
|
||||
}
|
||||
|
||||
// By default use network from attack
|
||||
subnet_cidr_mask_t customer_network;
|
||||
customer_network.subnet_address = current_attack.customer_network.subnet_address;
|
||||
customer_network.cidr_prefix_length = current_attack.customer_network.cidr_prefix_length;
|
||||
|
||||
unicast_ipv4_announce.set_prefix(customer_network);
|
||||
unicast_ipv4_announce.set_next_hop(gobgp_next_hop_subnet_ipv4);
|
||||
|
||||
gobgp_client.AnnounceUnicastPrefixLowLevelIPv4(unicast_ipv4_announce, is_withdrawal);
|
||||
}
|
||||
|
||||
if (fastnetmon_global_configuration.gobgp_announce_host) {
|
||||
IPv4UnicastAnnounce unicast_ipv4_announce;
|
||||
|
||||
std::vector<std::string> host_ipv4_communities;
|
||||
|
||||
host_ipv4_communities.push_back(fastnetmon_global_configuration.gobgp_community_host);
|
||||
|
||||
for (auto community_string : host_ipv4_communities) {
|
||||
bgp_community_attribute_element_t bgp_community_host;
|
||||
|
||||
if (!read_bgp_community_from_string(community_string, bgp_community_host)) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode BGP community for IPv4 host: " << community_string;
|
||||
// We may have multiple communities and other communities may be correct, skip only broken one
|
||||
continue;
|
||||
}
|
||||
|
||||
unicast_ipv4_announce.add_community(bgp_community_host);
|
||||
}
|
||||
|
||||
subnet_cidr_mask_t host_address_as_subnet(client_ip, 32);
|
||||
|
||||
unicast_ipv4_announce.set_prefix(host_address_as_subnet);
|
||||
unicast_ipv4_announce.set_next_hop(gobgp_next_hop_host_ipv4);
|
||||
|
||||
gobgp_client.AnnounceUnicastPrefixLowLevelIPv4(unicast_ipv4_announce, is_withdrawal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gobgp_ban_manage(const std::string& action,
|
||||
bool ipv6,
|
||||
uint32_t client_ip,
|
||||
@ -209,35 +294,6 @@ void gobgp_ban_manage(const std::string& action,
|
||||
if (ipv6) {
|
||||
gobgp_ban_manage_ipv6(gobgp_client, client_ipv6, is_withdrawal, current_attack);
|
||||
} else {
|
||||
if (fastnetmon_global_configuration.gobgp_announce_whole_subnet) {
|
||||
// By default use network from attack
|
||||
subnet_cidr_mask_t customer_network;
|
||||
customer_network.subnet_address = current_attack.customer_network.subnet_address;
|
||||
customer_network.cidr_prefix_length = current_attack.customer_network.cidr_prefix_length;
|
||||
|
||||
std::string subnet_as_string_with_mask = convert_subnet_to_string(customer_network);
|
||||
|
||||
logger << log4cpp::Priority::INFO << action_name << " "
|
||||
<< convert_subnet_to_string(customer_network) << " to GoBGP";
|
||||
|
||||
// https://github.com/osrg/gobgp/blob/0aff30a74216f499b8abfabc50016b041b319749/internal/pkg/table/policy_test.go#L2870
|
||||
uint32_t community_as_32bit_int =
|
||||
uint32_t(bgp_community_subnet.asn_number << 16 | bgp_community_subnet.community_number);
|
||||
|
||||
gobgp_client.AnnounceUnicastPrefixIPv4(convert_ip_as_uint_to_string(customer_network.subnet_address),
|
||||
gobgp_nexthop, is_withdrawal,
|
||||
customer_network.cidr_prefix_length, community_as_32bit_int);
|
||||
}
|
||||
|
||||
if (fastnetmon_global_configuration.gobgp_announce_host) {
|
||||
std::string ip_as_string = convert_ip_as_uint_to_string(client_ip);
|
||||
std::string ip_as_string_with_mask = ip_as_string + "/32";
|
||||
|
||||
logger << log4cpp::Priority::INFO << action_name << " " << ip_as_string_with_mask << " to GoBGP";
|
||||
|
||||
uint32_t community_as_32bit_int = uint32_t(bgp_community_host.asn_number << 16 | bgp_community_host.community_number);
|
||||
|
||||
gobgp_client.AnnounceUnicastPrefixIPv4(ip_as_string, gobgp_nexthop, is_withdrawal, 32, community_as_32bit_int);
|
||||
}
|
||||
gobgp_ban_manage_ipv4(gobgp_client, client_ip, is_withdrawal, current_attack);
|
||||
}
|
||||
}
|
||||
|
@ -250,6 +250,9 @@ gobgp = off
|
||||
|
||||
# Configuration for IPv4 announces
|
||||
gobgp_next_hop = 0.0.0.0
|
||||
gobgp_next_hop_host_ipv4 = 0.0.0.0
|
||||
gobgp_next_hop_subnet_ipv4 = 0.0.0.0
|
||||
|
||||
gobgp_announce_host = on
|
||||
gobgp_announce_whole_subnet = off
|
||||
|
||||
|
@ -215,10 +215,6 @@ int unban_iteration_sleep_time = 60;
|
||||
|
||||
bool unban_enabled = true;
|
||||
|
||||
#ifdef ENABLE_GOBGP
|
||||
bool gobgp_enabled = false;
|
||||
#endif
|
||||
|
||||
#ifdef MONGO
|
||||
std::string mongodb_host = "localhost";
|
||||
unsigned int mongodb_port = 27017;
|
||||
@ -704,7 +700,7 @@ bool load_configuration_file() {
|
||||
#ifdef ENABLE_GOBGP
|
||||
// GoBGP configuration
|
||||
if (configuration_map.count("gobgp") != 0) {
|
||||
gobgp_enabled = configuration_map["gobgp"] == "on";
|
||||
fastnetmon_global_configuration.gobgp = configuration_map["gobgp"] == "on";
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1913,7 +1909,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
// We call init for each action
|
||||
#ifdef ENABLE_GOBGP
|
||||
if (gobgp_enabled) {
|
||||
if (fastnetmon_global_configuration.gobgp) {
|
||||
gobgp_action_init();
|
||||
}
|
||||
#endif
|
||||
|
@ -36,9 +36,19 @@ class fastnetmon_configuration_t {
|
||||
unsigned int graphite_push_period{ 1 };
|
||||
|
||||
// GoBGP
|
||||
bool gobgp{ false };
|
||||
|
||||
// IPv4
|
||||
bool gobgp_announce_host{ false };
|
||||
bool gobgp_announce_whole_subnet{ false };
|
||||
|
||||
std::string gobgp_community_host{ "65001:668" };
|
||||
std::string gobgp_community_subnet{ "65001:667" };
|
||||
std::string gobgp_next_hop{ "0.0.0.0" };
|
||||
std::string gobgp_next_hop_host_ipv4{ "0.0.0.0" };
|
||||
std::string gobgp_next_hop_subnet_ipv4{ "0.0.0.0" };
|
||||
|
||||
// IPv6
|
||||
bool gobgp_announce_host_ipv6{ false };
|
||||
bool gobgp_announce_whole_subnet_ipv6{ false };
|
||||
|
||||
|
@ -72,6 +72,10 @@
|
||||
#include <cppkafka/cppkafka.h>
|
||||
#endif
|
||||
|
||||
#include "fastnetmon_configuration_scheme.hpp"
|
||||
|
||||
extern fastnetmon_configuration_t fastnetmon_global_configuration;
|
||||
|
||||
extern uint64_t influxdb_writes_total;
|
||||
extern uint64_t influxdb_writes_failed;
|
||||
extern packet_buckets_storage_t<subnet_ipv6_cidr_mask_t> packet_buckets_ipv6_storage;
|
||||
@ -146,7 +150,6 @@ extern patricia_tree_t *lookup_tree_ipv4, *whitelist_tree_ipv4;
|
||||
extern patricia_tree_t *lookup_tree_ipv6, *whitelist_tree_ipv6;
|
||||
extern ban_settings_t global_ban_settings;
|
||||
extern bool exabgp_enabled;
|
||||
extern bool gobgp_enabled;
|
||||
extern int global_ban_time;
|
||||
extern bool notify_script_enabled;
|
||||
extern std::map<uint32_t, banlist_item_t> ban_list;
|
||||
@ -1279,7 +1282,7 @@ void call_blackhole_actions_per_host(attack_action_t attack_action,
|
||||
}
|
||||
|
||||
#ifdef ENABLE_GOBGP
|
||||
if (gobgp_enabled) {
|
||||
if (fastnetmon_global_configuration.gobgp) {
|
||||
logger << log4cpp::Priority::INFO << "Call GoBGP for " << action_name << " client started: " << client_ip_as_string;
|
||||
|
||||
boost::thread gobgp_thread(gobgp_ban_manage, action_name, ipv6, client_ip, client_ipv6, current_attack);
|
||||
|
@ -52,77 +52,6 @@ GrpcClient::GrpcClient(std::shared_ptr<grpc::Channel> channel) : stub_(apipb::Go
|
||||
|
||||
}
|
||||
|
||||
bool GrpcClient::AnnounceUnicastPrefixIPv4(std::string announced_address,
|
||||
std::string announced_prefix_nexthop,
|
||||
bool is_withdrawal,
|
||||
unsigned int cidr_mask,
|
||||
uint32_t community_as_32bit_int) {
|
||||
grpc::ClientContext context;
|
||||
|
||||
// Set timeout for API
|
||||
std::chrono::system_clock::time_point deadline =
|
||||
std::chrono::system_clock::now() + std::chrono::seconds(gobgp_client_connection_timeout);
|
||||
context.set_deadline(deadline);
|
||||
|
||||
auto gobgp_ipv4_unicast_route_family = new apipb::Family;
|
||||
gobgp_ipv4_unicast_route_family->set_afi(apipb::Family::AFI_IP);
|
||||
gobgp_ipv4_unicast_route_family->set_safi(apipb::Family::SAFI_UNICAST);
|
||||
|
||||
apipb::AddPathRequest request;
|
||||
request.set_table_type(apipb::TableType::GLOBAL);
|
||||
|
||||
apipb::Path* current_path = new apipb::Path;
|
||||
|
||||
current_path->set_allocated_family(gobgp_ipv4_unicast_route_family);
|
||||
|
||||
if (is_withdrawal) {
|
||||
current_path->set_is_withdraw(true);
|
||||
}
|
||||
|
||||
// Configure required announce
|
||||
google::protobuf::Any* current_nlri = new google::protobuf::Any;
|
||||
apipb::IPAddressPrefix current_ipaddrprefix;
|
||||
current_ipaddrprefix.set_prefix(announced_address);
|
||||
current_ipaddrprefix.set_prefix_len(cidr_mask);
|
||||
|
||||
current_nlri->PackFrom(current_ipaddrprefix);
|
||||
current_path->set_allocated_nlri(current_nlri);
|
||||
|
||||
// Updating OriginAttribute info for current_path
|
||||
google::protobuf::Any* current_origin = current_path->add_pattrs();
|
||||
apipb::OriginAttribute current_origin_t;
|
||||
current_origin_t.set_origin(0);
|
||||
current_origin->PackFrom(current_origin_t);
|
||||
|
||||
// Updating NextHopAttribute info for current_path
|
||||
google::protobuf::Any* current_next_hop = current_path->add_pattrs();
|
||||
apipb::NextHopAttribute current_next_hop_t;
|
||||
current_next_hop_t.set_next_hop(announced_prefix_nexthop);
|
||||
current_next_hop->PackFrom(current_next_hop_t);
|
||||
|
||||
// Updating CommunitiesAttribute for current_path
|
||||
google::protobuf::Any* current_communities = current_path->add_pattrs();
|
||||
apipb::CommunitiesAttribute current_communities_t;
|
||||
current_communities_t.add_communities(community_as_32bit_int);
|
||||
current_communities->PackFrom(current_communities_t);
|
||||
|
||||
request.set_allocated_path(current_path);
|
||||
|
||||
apipb::AddPathResponse response;
|
||||
|
||||
// Don't be confused by name, it also can withdraw announces
|
||||
auto status = stub_->AddPath(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
logger << log4cpp::Priority::ERROR << "AddPath request to BGP daemon failed with code: " << status.error_code()
|
||||
<< " message " << status.error_message();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Announce unicast or flow spec
|
||||
bool GrpcClient::AnnounceCommonPrefix(dynamic_binary_buffer_t binary_nlri,
|
||||
@ -195,6 +124,36 @@ bool GrpcClient::AnnounceCommonPrefix(dynamic_binary_buffer_t binary_nlri,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrpcClient::AnnounceUnicastPrefixLowLevelIPv4(const IPv4UnicastAnnounce& unicast_ipv4_announce, bool is_withdrawal) {
|
||||
logger << log4cpp::Priority::INFO << "Send IPv4 " << (is_withdrawal ? "withdrawal " : "")
|
||||
<< "unicast announce to BGP daemon: " << unicast_ipv4_announce.print();
|
||||
|
||||
dynamic_binary_buffer_t binary_nlri;
|
||||
auto binary_nlri_generation_result = unicast_ipv4_announce.generate_nlri(binary_nlri);
|
||||
|
||||
if (!binary_nlri_generation_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not encode NLRI for IPv4 unicast announce due to unsuccessful error code";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (binary_nlri.get_used_size() == 0 or binary_nlri.get_pointer() == NULL) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not encode NLRI for IPv4 unicast announce";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bgp_attributes = unicast_ipv4_announce.get_attributes();
|
||||
|
||||
if (bgp_attributes.size() == 0) {
|
||||
logger << log4cpp::Priority::ERROR << "We got zero number of attributes";
|
||||
return false;
|
||||
}
|
||||
|
||||
logger << log4cpp::Priority::DEBUG << "Got " << bgp_attributes.size() << " BGP attributes";
|
||||
|
||||
return AnnounceCommonPrefix(binary_nlri, bgp_attributes, is_withdrawal, AFI_IP, SAFI_UNICAST);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GrpcClient::AnnounceUnicastPrefixLowLevelIPv6(const IPv6UnicastAnnounce& unicast_ipv6_announce, bool is_withdrawal) {
|
||||
logger << log4cpp::Priority::INFO << "Send IPv6 " << (is_withdrawal ? "withdrawal " : "")
|
||||
|
@ -53,11 +53,7 @@ class GrpcClient {
|
||||
bool is_withdrawal,
|
||||
unsigned int afi,
|
||||
unsigned int safi);
|
||||
bool AnnounceUnicastPrefixIPv4(std::string announced_address,
|
||||
std::string announced_prefix_nexthop,
|
||||
bool is_withdrawal,
|
||||
unsigned int cidr_mask,
|
||||
uint32_t community_as_32bit_int);
|
||||
bool AnnounceUnicastPrefixLowLevelIPv4(const IPv4UnicastAnnounce& unicast_ipv4_announce, bool is_withdrawal);
|
||||
bool AnnounceUnicastPrefixLowLevelIPv6(const IPv6UnicastAnnounce& unicast_ipv6_announce, bool is_withdrawal);
|
||||
|
||||
private:
|
||||
|
@ -44,7 +44,6 @@ class lookup_tree_128bit_t {
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// TODO: rework get_packet_direction_ipv6 and make it public
|
||||
@ -88,7 +87,7 @@ class lookup_tree_32bit_t {
|
||||
// Lookup this network in Patricia tree
|
||||
bool lookup_network(const subnet_cidr_mask_t& subnet) const {
|
||||
prefix_t prefix_for_check_adreess;
|
||||
|
||||
|
||||
prefix_for_check_adreess.add.sin.s_addr = subnet.subnet_address;
|
||||
prefix_for_check_adreess.family = AF_INET;
|
||||
prefix_for_check_adreess.bitlen = subnet.cidr_prefix_length;
|
||||
@ -138,7 +137,7 @@ class lookup_tree_32bit_t {
|
||||
patricia_tree = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Allow access to private variables from tests
|
||||
friend class patricia_process_ipv4_Test;
|
||||
friend class patricia_positive_lookup_ipv4_check_data_field_value_Test;
|
||||
@ -152,4 +151,3 @@ class lookup_tree_32bit_t {
|
||||
private:
|
||||
patricia_tree_t* patricia_tree = nullptr;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user