1
0
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:
Pavel Odintsov 2024-07-14 21:38:04 +03:00
parent af49358307
commit e65727c849
8 changed files with 167 additions and 146 deletions

@ -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;
};