mirror of
https://github.com/pavel-odintsov/fastnetmon
synced 2024-11-23 13:22:36 +01:00
Added new BGP Flow Spec native logic
This commit is contained in:
parent
b590031f5a
commit
1b716fbbf8
@ -191,6 +191,7 @@ set(FASTNETMON_NOTIFY_SCRIPT_PATH_DEFAULT "/usr/local/bin/notify_about_attack.sh
|
||||
set(FASTNETMON_NETWORK_WHITELIST_PATH "/etc/networks_whitelist")
|
||||
set(FASTNETMON_NETWORKS_LIST_PATH "/etc/networks_list")
|
||||
set(FASTNETMON_BACKTRACE_PATH "/var/log/fastnetmon_backtrace.dump")
|
||||
set(FASTNETMON_WHITELIST_RULES_PATH "/etc/whitelist_rules")
|
||||
|
||||
# For FreeBSD based platforms we need to adjust them
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR ${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
|
||||
@ -347,6 +348,13 @@ add_library(ipfix_rfc STATIC ipfix_fields/ipfix_rfc.cpp)
|
||||
|
||||
add_library(bgp_protocol STATIC bgp_protocol.cpp)
|
||||
|
||||
# Here we store some service code for getting IP protocol name by number
|
||||
add_library(iana_ip_protocols STATIC iana_ip_protocols.cpp)
|
||||
|
||||
# BGP Flow Spec
|
||||
add_library(bgp_protocol_flow_spec STATIC bgp_protocol_flow_spec.cpp)
|
||||
target_link_libraries(bgp_protocol_flow_spec iana_ip_protocols bgp_protocol)
|
||||
|
||||
# Our logic library
|
||||
add_library(fastnetmon_logic STATIC fastnetmon_logic.cpp)
|
||||
|
||||
@ -944,7 +952,7 @@ target_link_libraries(fastnetmon fastnetmon_pcap_format)
|
||||
|
||||
target_link_libraries(fastnetmon ipfix_rfc)
|
||||
|
||||
target_link_libraries(fastnetmon_logic bgp_protocol exabgp_action)
|
||||
target_link_libraries(fastnetmon_logic bgp_protocol bgp_protocol_flow_spec exabgp_action)
|
||||
|
||||
target_link_libraries(fastnetmon_logic protobuf_traffic_format)
|
||||
|
||||
|
909
src/bgp_protocol_flow_spec.cpp
Normal file
909
src/bgp_protocol_flow_spec.cpp
Normal file
@ -0,0 +1,909 @@
|
||||
#include "bgp_protocol.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "fast_library.hpp"
|
||||
|
||||
// inet_ntoa
|
||||
#include "network_data_structures.hpp"
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "bgp_protocol_flow_spec.hpp"
|
||||
|
||||
bool read_flow_spec_fragmentation_types_from_string(const std::string& string_form, flow_spec_fragmentation_types_t& fragment_flag) {
|
||||
// Unify case for better experience with this function
|
||||
std::string string_form_lowercase = boost::algorithm::to_lower_copy(string_form);
|
||||
|
||||
if (string_form_lowercase == "dont-fragment") {
|
||||
fragment_flag = flow_spec_fragmentation_types_t::FLOW_SPEC_DONT_FRAGMENT;
|
||||
} else if (string_form_lowercase == "is-fragment") {
|
||||
fragment_flag = flow_spec_fragmentation_types_t::FLOW_SPEC_IS_A_FRAGMENT;
|
||||
} else if (string_form_lowercase == "first-fragment") {
|
||||
fragment_flag = flow_spec_fragmentation_types_t::FLOW_SPEC_FIRST_FRAGMENT;
|
||||
} else if (string_form_lowercase == "last-fragment") {
|
||||
fragment_flag = flow_spec_fragmentation_types_t::FLOW_SPEC_LAST_FRAGMENT;
|
||||
} else if (string_form_lowercase == "not-a-fragment") {
|
||||
fragment_flag = flow_spec_fragmentation_types_t::FLOW_SPEC_NOT_A_FRAGMENT;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string flow_spec_fragmentation_flags_to_string(flow_spec_fragmentation_types_t const& fragment_flag) {
|
||||
// https://github.com/Exa-Networks/exabgp/blob/71157d560096ec20084cf96cfe0f60203721e93b/lib/exabgp/protocol/ip/fragment.py
|
||||
|
||||
if (fragment_flag == flow_spec_fragmentation_types_t::FLOW_SPEC_DONT_FRAGMENT) {
|
||||
return "dont-fragment";
|
||||
} else if (fragment_flag == flow_spec_fragmentation_types_t::FLOW_SPEC_IS_A_FRAGMENT) {
|
||||
return "is-fragment";
|
||||
} else if (fragment_flag == flow_spec_fragmentation_types_t::FLOW_SPEC_FIRST_FRAGMENT) {
|
||||
return "first-fragment";
|
||||
} else if (fragment_flag == flow_spec_fragmentation_types_t::FLOW_SPEC_LAST_FRAGMENT) {
|
||||
return "last-fragment";
|
||||
} else if (fragment_flag == flow_spec_fragmentation_types_t::FLOW_SPEC_NOT_A_FRAGMENT) {
|
||||
return "not-a-fragment";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool read_flow_spec_action_type_from_string(const std::string& string_form, bgp_flow_spec_action_types_t& action_type) {
|
||||
if (string_form == "accept") {
|
||||
action_type = bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_ACCEPT;
|
||||
} else if (string_form == "discard") {
|
||||
action_type = bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_DISCARD;
|
||||
} else if (string_form == "rate-limit") {
|
||||
action_type = bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_RATE_LIMIT;
|
||||
} else if (string_form == "redirect") {
|
||||
action_type = bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_REDIRECT;
|
||||
} else if (string_form == "mark") {
|
||||
action_type = bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_MARK;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string serialize_action_type(const bgp_flow_spec_action_types_t& action_type) {
|
||||
if (action_type == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_ACCEPT) {
|
||||
return "accept";
|
||||
} else if (action_type == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_DISCARD) {
|
||||
return "discard";
|
||||
} else if (action_type == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_RATE_LIMIT) {
|
||||
return "rate-limit";
|
||||
} else if (action_type == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_REDIRECT) {
|
||||
return "redirect";
|
||||
} else if (action_type == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_MARK) {
|
||||
return "mark";
|
||||
} else {
|
||||
// TODO: add return code for notifying about this case
|
||||
return std::string("");
|
||||
}
|
||||
}
|
||||
|
||||
bool read_flow_spec_tcp_flags_from_strig(const std::string& string_form, flow_spec_tcp_flagset_t& flagset) {
|
||||
// Unify case for better experience with this function
|
||||
std::string string_form_lowercase = boost::algorithm::to_lower_copy(string_form);
|
||||
|
||||
std::vector<std::string> tcp_flags;
|
||||
|
||||
// Split line by "|"
|
||||
boost::split(tcp_flags, string_form_lowercase, boost::is_any_of("|"), boost::token_compress_on);
|
||||
|
||||
for (auto tcp_flag_string : tcp_flags) {
|
||||
if (tcp_flag_string == "syn") {
|
||||
flagset.syn_flag = true;
|
||||
} else if (tcp_flag_string == "ack") {
|
||||
flagset.ack_flag = true;
|
||||
} else if (tcp_flag_string == "fin") {
|
||||
flagset.fin_flag = true;
|
||||
} else if (tcp_flag_string == "urgent") {
|
||||
flagset.urg_flag = true;
|
||||
} else if (tcp_flag_string == "push") {
|
||||
flagset.psh_flag = true;
|
||||
} else if (tcp_flag_string == "rst") {
|
||||
flagset.rst_flag = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string flow_spec_tcp_flagset_to_string(flow_spec_tcp_flagset_t const& tcp_flagset) {
|
||||
std::vector<std::string> output;
|
||||
|
||||
if (tcp_flagset.syn_flag) {
|
||||
output.push_back("syn");
|
||||
}
|
||||
|
||||
if (tcp_flagset.ack_flag) {
|
||||
output.push_back("ack");
|
||||
}
|
||||
|
||||
if (tcp_flagset.fin_flag) {
|
||||
output.push_back("fin");
|
||||
}
|
||||
|
||||
if (tcp_flagset.rst_flag) {
|
||||
output.push_back("rst");
|
||||
}
|
||||
|
||||
if (tcp_flagset.urg_flag) {
|
||||
output.push_back("urgent");
|
||||
}
|
||||
|
||||
if (tcp_flagset.psh_flag) {
|
||||
output.push_back("push");
|
||||
}
|
||||
|
||||
return boost::algorithm::join(output, "|");
|
||||
}
|
||||
|
||||
bool operator==(const bgp_flow_spec_action_t& lhs, const bgp_flow_spec_action_t& rhs) {
|
||||
if (lhs.get_type() != rhs.get_type()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Action types are equal
|
||||
if (lhs.get_type() == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_RATE_LIMIT) {
|
||||
return lhs.get_rate_limit() == rhs.get_rate_limit();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!=(const bgp_flow_spec_action_t& lhs, const bgp_flow_spec_action_t& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
// It does not check UUID
|
||||
bool operator==(const flow_spec_rule_t& lhs, const flow_spec_rule_t& rhs) {
|
||||
// Compare source subnets
|
||||
|
||||
// IPv4
|
||||
if (lhs.source_subnet_ipv4_used != rhs.source_subnet_ipv4_used) {
|
||||
return false;
|
||||
} else {
|
||||
if (lhs.source_subnet_ipv4_used) {
|
||||
// If they have values
|
||||
if (lhs.source_subnet_ipv4 != rhs.source_subnet_ipv4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
if (lhs.source_subnet_ipv6_used != rhs.source_subnet_ipv6_used) {
|
||||
return false;
|
||||
} else {
|
||||
if (lhs.source_subnet_ipv6_used) {
|
||||
// If they have values
|
||||
if (lhs.source_subnet_ipv6 != rhs.source_subnet_ipv6) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare destination subnets
|
||||
|
||||
// IPv4
|
||||
if (lhs.destination_subnet_ipv4_used != rhs.destination_subnet_ipv4_used) {
|
||||
return false;
|
||||
} else {
|
||||
if (lhs.destination_subnet_ipv4_used) {
|
||||
if (lhs.destination_subnet_ipv4 != rhs.destination_subnet_ipv4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
if (lhs.destination_subnet_ipv6_used != rhs.destination_subnet_ipv6_used) {
|
||||
return false;
|
||||
} else {
|
||||
if (lhs.destination_subnet_ipv6_used) {
|
||||
if (lhs.destination_subnet_ipv6 != rhs.destination_subnet_ipv6) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare actions
|
||||
if (lhs.action != rhs.action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.source_ports != rhs.source_ports) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.destination_ports != rhs.destination_ports) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.packet_lengths != rhs.packet_lengths) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This one is non standard compliant field and it cannot be used for BGP flow spec announces
|
||||
if (lhs.vlans != rhs.vlans) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This one is non standard compliant field and it cannot be used for BGP flow spec announces
|
||||
if (lhs.ttls != rhs.ttls) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.ipv4_nexthops != rhs.ipv4_nexthops) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.protocols != rhs.protocols) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.tcp_flags != rhs.tcp_flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.fragmentation_flags != rhs.fragmentation_flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const flow_spec_rule_t& lhs, const flow_spec_rule_t& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator!=(const flow_spec_tcp_flagset_t& lhs, const flow_spec_tcp_flagset_t& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator==(const flow_spec_tcp_flagset_t& lhs, const flow_spec_tcp_flagset_t& rhs) {
|
||||
if (lhs.syn_flag == rhs.syn_flag && lhs.ack_flag == rhs.ack_flag && lhs.rst_flag == rhs.rst_flag &&
|
||||
lhs.psh_flag == rhs.psh_flag && lhs.urg_flag == rhs.urg_flag && lhs.fin_flag == rhs.fin_flag) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"source_prefix": "4.0.0.0\/24",
|
||||
"destination_prefix": "127.0.0.0\/24",
|
||||
"destination_ports": [ 80 ],
|
||||
"source_ports": [ 53, 5353 ],
|
||||
"packet_lengths": [ 777, 1122 ],
|
||||
"protocols": [ "tcp" ],
|
||||
"fragmentation_flags":[ "is-fragment", "dont-fragment" ],
|
||||
"tcp_flags": [ "syn" ],
|
||||
"action_type": "rate-limit",
|
||||
"action": { "rate": 1024 }
|
||||
}
|
||||
*/
|
||||
|
||||
bool read_flow_spec_from_json_to_native_format(const std::string& json_encoded_flow_spec, flow_spec_rule_t& flow_spec_rule, bool require_action) {
|
||||
using json = nlohmann::json;
|
||||
|
||||
// We explicitly disable exceptions
|
||||
auto json_doc = json::parse(json_encoded_flow_spec, nullptr, false);
|
||||
|
||||
if (json_doc.is_discarded()) {
|
||||
logger << log4cpp::Priority::ERROR << "Cannot decode Flow Spec rule from JSON: '" << json_encoded_flow_spec << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (json_doc.contains("source_prefix")) {
|
||||
std::string source_prefix_string;
|
||||
|
||||
try {
|
||||
source_prefix_string = json_doc["source_prefix"].get<std::string>();
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON encoded source_prefix";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source_prefix_string.find(":") != std::string::npos) {
|
||||
subnet_ipv6_cidr_mask_t subnet_cidr_mask;
|
||||
|
||||
bool conversion_result = read_ipv6_subnet_from_string(subnet_cidr_mask, source_prefix_string);
|
||||
|
||||
if (!conversion_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON encoded IPv6 source_prefix";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.set_source_subnet_ipv6(subnet_cidr_mask);
|
||||
} else {
|
||||
subnet_cidr_mask_t subnet_cidr_mask;
|
||||
bool conversion_result =
|
||||
convert_subnet_from_string_to_binary_with_cidr_format_safe(source_prefix_string, subnet_cidr_mask);
|
||||
|
||||
if (!conversion_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON encoded source_prefix";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.set_source_subnet_ipv4(subnet_cidr_mask);
|
||||
}
|
||||
}
|
||||
|
||||
if (json_doc.contains("destination_prefix")) {
|
||||
std::string destination_prefix_string;
|
||||
|
||||
try {
|
||||
destination_prefix_string = json_doc["destination_prefix"].get<std::string>();
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON encoded destination_prefix";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destination_prefix_string.find(":") != std::string::npos) {
|
||||
subnet_ipv6_cidr_mask_t subnet_cidr_mask;
|
||||
|
||||
bool conversion_result = read_ipv6_subnet_from_string(subnet_cidr_mask, destination_prefix_string);
|
||||
|
||||
if (!conversion_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON encoded IPv6 destination_prefix";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.set_destination_subnet_ipv6(subnet_cidr_mask);
|
||||
} else {
|
||||
subnet_cidr_mask_t subnet_cidr_mask;
|
||||
bool conversion_result =
|
||||
convert_subnet_from_string_to_binary_with_cidr_format_safe(destination_prefix_string, subnet_cidr_mask);
|
||||
|
||||
if (!conversion_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse json encoded destination_prefix";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.set_destination_subnet_ipv4(subnet_cidr_mask);
|
||||
}
|
||||
}
|
||||
|
||||
if (json_doc.contains("destination_ports")) {
|
||||
std::vector<int32_t> ports_vector_as_ints;
|
||||
|
||||
try {
|
||||
ports_vector_as_ints = json_doc["destination_ports"].get<std::vector<int32_t>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode destination_ports " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode destination_ports";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto port : ports_vector_as_ints) {
|
||||
if (!valid_port(port)) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse destination_ports element: bad range " << port;
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_destination_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (json_doc.contains("source_ports")) {
|
||||
std::vector<int32_t> ports_vector_as_ints;
|
||||
|
||||
try {
|
||||
ports_vector_as_ints = json_doc["source_ports"].get<std::vector<int32_t>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode source_ports " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode source_ports";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto port : ports_vector_as_ints) {
|
||||
if (!valid_port(port)) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse source_ports element: bad range " << port;
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_source_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (json_doc.contains("packet_lengths")) {
|
||||
std::vector<int32_t> packet_lengths_vector_as_ints;
|
||||
|
||||
try {
|
||||
packet_lengths_vector_as_ints = json_doc["packet_lengths"].get<std::vector<int32_t>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode packet_lengths " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode packet_lengths";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto packet_length : packet_lengths_vector_as_ints) {
|
||||
if (packet_length < 0) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse packet_lengths element, it must be positive: " << packet_length;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Should we drop it?
|
||||
if (packet_length > 1500) {
|
||||
logger << log4cpp::Priority::ERROR
|
||||
<< "Could not parse packet_lengths element, it must not exceed 1500: " << packet_length;
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_packet_length(packet_length);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this logic is not covered by tests
|
||||
|
||||
if (json_doc.contains("vlans")) {
|
||||
std::vector<int32_t> vlans_vector_as_ints;
|
||||
|
||||
try {
|
||||
vlans_vector_as_ints = json_doc["vlans"].get<std::vector<int32_t>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode vlans " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode vlans";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto vlan : vlans_vector_as_ints) {
|
||||
if (vlan < 0) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse vlan element, bad range: " << vlan;
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_vlan(vlan);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this logic is not covered by tests
|
||||
if (json_doc.contains("ttls")) {
|
||||
// TODO: I'm not sure that it can handle such small unsigned well
|
||||
std::vector<uint8_t> ttls_vector_as_ints;
|
||||
|
||||
try {
|
||||
ttls_vector_as_ints = json_doc["ttls"].get<std::vector<uint8_t>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode TTLs " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode TTLs";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto ttl : ttls_vector_as_ints) {
|
||||
flow_spec_rule.add_ttl(ttl);
|
||||
}
|
||||
}
|
||||
|
||||
if (json_doc.contains("protocols")) {
|
||||
std::vector<std::string> protocols_vector_as_strings;
|
||||
|
||||
try {
|
||||
protocols_vector_as_strings = json_doc["protocols"].get<std::vector<std::string>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode protocols " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode protocols";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& protocol_as_string : protocols_vector_as_strings) {
|
||||
ip_protocol_t protocol;
|
||||
|
||||
bool result = read_protocol_from_string(protocol_as_string, protocol);
|
||||
|
||||
if (!result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse this " << protocol_as_string << " as protocol";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_protocol(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this logic is not covered by tests
|
||||
if (json_doc.contains("ipv4_nexthops")) {
|
||||
std::vector<std::string> next_hops_vector_as_strings;
|
||||
|
||||
try {
|
||||
next_hops_vector_as_strings = json_doc["ipv4_nexthops"].get<std::vector<std::string>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode ipv4_nexthops " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode ipv4_nexthops";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& next_hop_as_string : next_hops_vector_as_strings) {
|
||||
uint32_t next_hop_ipv4 = 0;
|
||||
|
||||
auto ip_parser_result = convert_ip_as_string_to_uint_safe(next_hop_as_string, next_hop_ipv4);
|
||||
|
||||
if (!ip_parser_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse this " << next_hop_as_string << " as IPv4 address";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_ipv4_nexthop(next_hop_ipv4);
|
||||
}
|
||||
}
|
||||
|
||||
if (json_doc.contains("fragmentation_flags")) {
|
||||
std::vector<std::string> fragmentation_flags_vector_as_strings;
|
||||
|
||||
try {
|
||||
fragmentation_flags_vector_as_strings = json_doc["fragmentation_flags"].get<std::vector<std::string>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode fragmentation_flags " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode fragmentation_flags";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& fragmentation_flag_as_string : fragmentation_flags_vector_as_strings) {
|
||||
flow_spec_fragmentation_types_t fragment_flag;
|
||||
|
||||
bool result = read_flow_spec_fragmentation_types_from_string(fragmentation_flag_as_string, fragment_flag);
|
||||
|
||||
if (!result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse this " << fragmentation_flag_as_string
|
||||
<< " as flow spec fragmentation flag";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_fragmentation_flag(fragment_flag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (json_doc.contains("tcp_flags")) {
|
||||
std::vector<std::string> tcp_flags_vector_as_strings;
|
||||
|
||||
try {
|
||||
tcp_flags_vector_as_strings = json_doc["tcp_flags"].get<std::vector<std::string>>();
|
||||
} catch (nlohmann::json::exception& e) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode tcp_flags " << e.what();
|
||||
return false;
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not decode tcp_flags";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& tcp_flag_as_string : tcp_flags_vector_as_strings) {
|
||||
flow_spec_tcp_flagset_t flagset;
|
||||
|
||||
bool result = read_flow_spec_tcp_flags_from_strig(tcp_flag_as_string, flagset);
|
||||
|
||||
if (!result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse this " << tcp_flag_as_string << " as flow spec tcp option flag";
|
||||
return false;
|
||||
}
|
||||
|
||||
flow_spec_rule.add_tcp_flagset(flagset);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip action section when we do not need it
|
||||
if (!require_action) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action_t bgp_flow_spec_action;
|
||||
|
||||
if (!json_doc.contains("action_type")) {
|
||||
logger << log4cpp::Priority::ERROR << "We have no action_type in JSON and it's mandatory";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string action_as_string;
|
||||
|
||||
try {
|
||||
action_as_string = json_doc["action_type"].get<std::string>();
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON encoded action_type";
|
||||
return false;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action_types_t action_type;
|
||||
bool result = read_flow_spec_action_type_from_string(action_as_string, action_type);
|
||||
|
||||
if (!result) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse action type: " << action_as_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action.set_type(action_type);
|
||||
|
||||
// And in this case we should extract rate_limit number
|
||||
if (bgp_flow_spec_action.get_type() == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_RATE_LIMIT) {
|
||||
if (json_doc.contains("action")) {
|
||||
auto json_action_doc = json_doc["action"];
|
||||
|
||||
if (!json_action_doc.contains("rate")) {
|
||||
logger << log4cpp::Priority::ERROR << "Absent rate argument for rate limit action";
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t rate = 0;
|
||||
|
||||
try {
|
||||
rate = json_action_doc["rate"].get<int32_t>();
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON document for rate";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (rate < 0) {
|
||||
logger << log4cpp::Priority::ERROR << "Rate validation failed, it must be positive: " << rate;
|
||||
return false;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action.set_rate_limit(rate);
|
||||
} else {
|
||||
// We assume zero rate in this case
|
||||
}
|
||||
} else if (bgp_flow_spec_action.get_type() == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_REDIRECT) {
|
||||
if (!json_doc.contains("action")) {
|
||||
logger << log4cpp::Priority::ERROR << "Action need to be provided for redirect";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto json_action_doc = json_doc["action"];
|
||||
|
||||
if (!json_action_doc.contains("redirect_target_as")) {
|
||||
logger << log4cpp::Priority::ERROR << "Absent redirect_target_as argument for redirect action";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t redirect_target_as = 0;
|
||||
|
||||
try {
|
||||
redirect_target_as = json_action_doc["redirect_target_as"].get<uint16_t>();
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON document for redirect_target_as";
|
||||
return false;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action.set_redirect_as(redirect_target_as);
|
||||
|
||||
uint32_t redirect_target_value = 0;
|
||||
|
||||
try {
|
||||
redirect_target_value = json_action_doc["redirect_target_value"].get<uint32_t>();
|
||||
} catch (...) {
|
||||
logger << log4cpp::Priority::ERROR << "Could not parse JSON document for redirect_target_value";
|
||||
return false;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action.set_redirect_value(redirect_target_value);
|
||||
}
|
||||
|
||||
flow_spec_rule.set_action(bgp_flow_spec_action);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encode flow spec announce into JSON representation
|
||||
bool encode_flow_spec_to_json(const flow_spec_rule_t& flow_spec_rule, std::string& json_encoded_flow_spec, bool add_uuid) {
|
||||
nlohmann::json flow_json;
|
||||
|
||||
bool encoding_result = encode_flow_spec_to_json_raw(flow_spec_rule, add_uuid, flow_json);
|
||||
|
||||
if (!encoding_result) {
|
||||
logger << log4cpp::Priority::ERROR << "Cannot encode Flow Spec into JSON";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string json_as_text = flow_json.dump();
|
||||
|
||||
// Remove ugly useless escaping for flow spec destination and source subnets
|
||||
// I.e. 127.0.0.1\/32
|
||||
boost::replace_all(json_as_text, "\\", "");
|
||||
|
||||
json_encoded_flow_spec = json_as_text;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encode flow spec in JSON object representation
|
||||
bool encode_flow_spec_to_json_raw(const flow_spec_rule_t& flow_spec_rule, bool add_uuid, nlohmann::json& flow_json) {
|
||||
// UUID is quite important for us, let's add it
|
||||
if (add_uuid) {
|
||||
flow_json["uuid"] = flow_spec_rule.get_announce_uuid_as_string();
|
||||
}
|
||||
|
||||
if (flow_spec_rule.source_subnet_ipv4_used) {
|
||||
flow_json["source_prefix"] = convert_ipv4_subnet_to_string(flow_spec_rule.source_subnet_ipv4);
|
||||
} else if (flow_spec_rule.source_subnet_ipv6_used) {
|
||||
flow_json["source_prefix"] = convert_ipv6_subnet_to_string(flow_spec_rule.source_subnet_ipv6);
|
||||
}
|
||||
|
||||
if (flow_spec_rule.destination_subnet_ipv4_used) {
|
||||
flow_json["destination_prefix"] = convert_ipv4_subnet_to_string(flow_spec_rule.destination_subnet_ipv4);
|
||||
} else if (flow_spec_rule.destination_subnet_ipv6_used) {
|
||||
flow_json["destination_prefix"] = convert_ipv6_subnet_to_string(flow_spec_rule.destination_subnet_ipv6);
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.destination_ports.empty()) {
|
||||
flow_json["destination_ports"] = flow_spec_rule.destination_ports;
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.source_ports.empty()) {
|
||||
flow_json["source_ports"] = flow_spec_rule.source_ports;
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.packet_lengths.empty()) {
|
||||
flow_json["packet_lengths"] = flow_spec_rule.packet_lengths;
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.vlans.empty()) {
|
||||
flow_json["vlans"] = flow_spec_rule.vlans;
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.ttls.empty()) {
|
||||
flow_json["ttls"] = flow_spec_rule.ttls;
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.protocols.empty()) {
|
||||
flow_json["protocols"] = nlohmann::json::array();
|
||||
|
||||
for (auto protocol : flow_spec_rule.protocols) {
|
||||
std::string protocol_name = get_ip_protocol_name(protocol);
|
||||
|
||||
// We use lowercase format
|
||||
boost::algorithm::to_lower(protocol_name);
|
||||
|
||||
flow_json["protocols"].push_back(protocol_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!flow_spec_rule.fragmentation_flags.empty()) {
|
||||
flow_json["fragmentation_flags"] = nlohmann::json::array();
|
||||
|
||||
for (auto fragment_flag : flow_spec_rule.fragmentation_flags) {
|
||||
std::string fragmentation_flag_as_string = flow_spec_fragmentation_flags_to_string(fragment_flag);
|
||||
|
||||
// For some reasons we cannot convert it to string
|
||||
if (fragmentation_flag_as_string == "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
flow_json["fragmentation_flags"].push_back(fragmentation_flag_as_string);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have TCP in protocols list explicitly, we add flags
|
||||
bool we_have_tcp_protocol_in_list = find(flow_spec_rule.protocols.begin(), flow_spec_rule.protocols.end(),
|
||||
ip_protocol_t::TCP) != flow_spec_rule.protocols.end();
|
||||
|
||||
if (!flow_spec_rule.tcp_flags.empty() && we_have_tcp_protocol_in_list) {
|
||||
flow_json["tcp_flags"] = nlohmann::json::array();
|
||||
|
||||
for (auto tcp_flag : flow_spec_rule.tcp_flags) {
|
||||
std::string tcp_flags_as_string = flow_spec_tcp_flagset_to_string(tcp_flag);
|
||||
|
||||
// For some reasons we cannot encode it, skip iteration
|
||||
if (tcp_flags_as_string == "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
flow_json["tcp_flags"].push_back(tcp_flags_as_string);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode action structure
|
||||
flow_json["action_type"] = serialize_action_type(flow_spec_rule.action.get_type());
|
||||
|
||||
// We add sub document action when arguments needed
|
||||
if (flow_spec_rule.action.get_type() == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_RATE_LIMIT) {
|
||||
nlohmann::json action_json;
|
||||
action_json["rate"] = flow_spec_rule.action.get_rate_limit();
|
||||
|
||||
flow_json["action"] = action_json;
|
||||
} else if (flow_spec_rule.action.get_type() == bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_REDIRECT) {
|
||||
nlohmann::json action_json;
|
||||
|
||||
action_json["redirect_target_as"] = flow_spec_rule.action.get_redirect_as();
|
||||
action_json["redirect_target_value"] = flow_spec_rule.action.get_redirect_value();
|
||||
|
||||
flow_json["action"] = action_json;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bgp_flowspec_one_byte_byte_encoded_tcp_flags_t return_in_one_byte_encoding(const flow_spec_tcp_flagset_t& flagset) {
|
||||
bgp_flowspec_one_byte_byte_encoded_tcp_flags_t one_byte_flags{};
|
||||
|
||||
if (flagset.syn_flag) {
|
||||
one_byte_flags.syn = 1;
|
||||
}
|
||||
|
||||
if (flagset.fin_flag) {
|
||||
one_byte_flags.fin = 1;
|
||||
}
|
||||
|
||||
if (flagset.urg_flag) {
|
||||
one_byte_flags.urg = 1;
|
||||
}
|
||||
|
||||
if (flagset.ack_flag) {
|
||||
one_byte_flags.ack = 1;
|
||||
}
|
||||
|
||||
if (flagset.psh_flag) {
|
||||
one_byte_flags.psh = 1;
|
||||
}
|
||||
|
||||
if (flagset.rst_flag) {
|
||||
one_byte_flags.rst = 1;
|
||||
}
|
||||
|
||||
return one_byte_flags;
|
||||
}
|
||||
|
||||
flow_spec_tcp_flagset_t convert_one_byte_encoding_to_flowset(const bgp_flowspec_one_byte_byte_encoded_tcp_flags_t& one_byte_flags) {
|
||||
flow_spec_tcp_flagset_t flagset;
|
||||
|
||||
if (one_byte_flags.syn == 1) {
|
||||
flagset.syn_flag = true;
|
||||
}
|
||||
|
||||
if (one_byte_flags.fin == 1) {
|
||||
flagset.fin_flag = true;
|
||||
}
|
||||
|
||||
if (one_byte_flags.urg == 1) {
|
||||
flagset.urg_flag = true;
|
||||
}
|
||||
|
||||
if (one_byte_flags.ack == 1) {
|
||||
flagset.ack_flag = true;
|
||||
}
|
||||
|
||||
|
||||
if (one_byte_flags.psh == 1) {
|
||||
flagset.psh_flag = true;
|
||||
}
|
||||
|
||||
if (one_byte_flags.rst == 1) {
|
||||
flagset.rst_flag = true;
|
||||
}
|
||||
|
||||
return flagset;
|
||||
}
|
||||
|
||||
// Is it range valid for port?
|
||||
bool valid_port(int32_t port) {
|
||||
return port >= 0 && port <= 65535;
|
||||
}
|
741
src/bgp_protocol_flow_spec.hpp
Normal file
741
src/bgp_protocol_flow_spec.hpp
Normal file
@ -0,0 +1,741 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "dynamic_binary_buffer.hpp"
|
||||
#include "fast_library.hpp"
|
||||
#include "fastnetmon_networks.hpp"
|
||||
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
#include "iana_ip_protocols.hpp"
|
||||
|
||||
#include "bgp_protocol.hpp"
|
||||
|
||||
class bgp_flow_spec_action_t;
|
||||
|
||||
|
||||
// This structure stores TCP flags in very human friendly way
|
||||
// It could store multiple enabled flags in same time
|
||||
class flow_spec_tcp_flagset_t {
|
||||
public:
|
||||
bool syn_flag = false;
|
||||
bool ack_flag = false;
|
||||
bool fin_flag = false;
|
||||
bool psh_flag = false;
|
||||
bool rst_flag = false;
|
||||
bool urg_flag = false;
|
||||
|
||||
// Do we have least one flag enabled?
|
||||
bool we_have_least_one_flag_enabled() const {
|
||||
return syn_flag || fin_flag || urg_flag || ack_flag || psh_flag || rst_flag;
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "syn: " << syn_flag << " "
|
||||
<< "ack: " << ack_flag << " "
|
||||
<< "fin: " << fin_flag << " "
|
||||
<< "psh: " << psh_flag << " "
|
||||
<< "rst: " << rst_flag << " "
|
||||
<< "urg: " << urg_flag;
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
template <class Archive> void serialize(Archive& ar, [[maybe_unused]] const unsigned int version) {
|
||||
ar& BOOST_SERIALIZATION_NVP(syn_flag);
|
||||
ar& BOOST_SERIALIZATION_NVP(ack_flag);
|
||||
ar& BOOST_SERIALIZATION_NVP(fin_flag);
|
||||
ar& BOOST_SERIALIZATION_NVP(psh_flag);
|
||||
ar& BOOST_SERIALIZATION_NVP(rst_flag);
|
||||
ar& BOOST_SERIALIZATION_NVP(urg_flag);
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==(const flow_spec_tcp_flagset_t& lhs, const flow_spec_tcp_flagset_t& rhs);
|
||||
bool operator!=(const flow_spec_tcp_flagset_t& lhs, const flow_spec_tcp_flagset_t& rhs);
|
||||
|
||||
// All possible values for BGP Flow Spec fragmentation field
|
||||
enum class flow_spec_fragmentation_types_t {
|
||||
FLOW_SPEC_DONT_FRAGMENT,
|
||||
FLOW_SPEC_IS_A_FRAGMENT,
|
||||
FLOW_SPEC_FIRST_FRAGMENT,
|
||||
FLOW_SPEC_LAST_FRAGMENT,
|
||||
// Well, this entity does not exist in RFC at all. It was addition from ExaBGP
|
||||
FLOW_SPEC_NOT_A_FRAGMENT,
|
||||
};
|
||||
|
||||
// Flow spec actions
|
||||
enum class bgp_flow_spec_action_types_t {
|
||||
FLOW_SPEC_ACTION_DISCARD,
|
||||
FLOW_SPEC_ACTION_ACCEPT,
|
||||
FLOW_SPEC_ACTION_RATE_LIMIT,
|
||||
FLOW_SPEC_ACTION_REDIRECT,
|
||||
FLOW_SPEC_ACTION_MARK
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool read_flow_spec_action_type_from_string(const std::string& string_form, bgp_flow_spec_action_types_t& action_type);
|
||||
std::string serialize_action_type(const bgp_flow_spec_action_types_t& action_type);
|
||||
|
||||
class bgp_flow_spec_action_t {
|
||||
public:
|
||||
void set_type(bgp_flow_spec_action_types_t action_type) {
|
||||
this->action_type = action_type;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action_types_t get_type() const {
|
||||
return this->action_type;
|
||||
}
|
||||
|
||||
void set_rate_limit(unsigned int rate_limit) {
|
||||
this->rate_limit = rate_limit;
|
||||
}
|
||||
|
||||
unsigned int get_rate_limit() const {
|
||||
return this->rate_limit;
|
||||
}
|
||||
|
||||
uint16_t get_redirect_as() const {
|
||||
return redirect_as;
|
||||
}
|
||||
|
||||
uint32_t get_redirect_value() const {
|
||||
return redirect_value;
|
||||
}
|
||||
|
||||
void set_redirect_as(uint16_t value) {
|
||||
redirect_as = value;
|
||||
}
|
||||
|
||||
void set_redirect_value(uint32_t value) {
|
||||
redirect_value = value;
|
||||
}
|
||||
|
||||
|
||||
template <class Archive> void serialize(Archive& ar, [[maybe_unused]] const unsigned int version) {
|
||||
ar& BOOST_SERIALIZATION_NVP(action_type);
|
||||
ar& BOOST_SERIALIZATION_NVP(rate_limit);
|
||||
ar& BOOST_SERIALIZATION_NVP(redirect_as);
|
||||
ar& BOOST_SERIALIZATION_NVP(redirect_value);
|
||||
}
|
||||
|
||||
private:
|
||||
bgp_flow_spec_action_types_t action_type = bgp_flow_spec_action_types_t::FLOW_SPEC_ACTION_ACCEPT;
|
||||
unsigned int rate_limit = 0;
|
||||
|
||||
// Values for redirect
|
||||
uint16_t redirect_as = 0;
|
||||
uint32_t redirect_value = 0;
|
||||
};
|
||||
|
||||
bool operator==(const bgp_flow_spec_action_t& lhs, const bgp_flow_spec_action_t& rhs);
|
||||
bool operator!=(const bgp_flow_spec_action_t& lhs, const bgp_flow_spec_action_t& rhs);
|
||||
|
||||
// We do not use < and > operators at all, sorry
|
||||
class flow_spec_rule_t {
|
||||
public:
|
||||
// This operation is very heavy, it may crash in case of entropy shortage and it actually happened to our customer
|
||||
// And we must not do them in constructors as it causes lots of side effects and slows down all things
|
||||
bool generate_uuid() {
|
||||
boost::uuids::random_generator gen;
|
||||
|
||||
try {
|
||||
announce_uuid = gen();
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_source_subnet_ipv4(const subnet_cidr_mask_t& source_subnet) {
|
||||
this->source_subnet_ipv4 = source_subnet;
|
||||
this->source_subnet_ipv4_used = true;
|
||||
}
|
||||
|
||||
void set_source_subnet_ipv6(const subnet_ipv6_cidr_mask_t& source_subnet) {
|
||||
this->source_subnet_ipv6 = source_subnet;
|
||||
this->source_subnet_ipv6_used = true;
|
||||
}
|
||||
|
||||
void set_destination_subnet_ipv4(const subnet_cidr_mask_t& destination_subnet) {
|
||||
this->destination_subnet_ipv4 = destination_subnet;
|
||||
this->destination_subnet_ipv4_used = true;
|
||||
}
|
||||
|
||||
void set_destination_subnet_ipv6(const subnet_ipv6_cidr_mask_t& destination_subnet) {
|
||||
this->destination_subnet_ipv6 = destination_subnet;
|
||||
this->destination_subnet_ipv6_used = true;
|
||||
}
|
||||
|
||||
void set_agent_subnet(subnet_cidr_mask_t subnet_param) {
|
||||
this->agent_subnet = subnet_param;
|
||||
this->agent_subnet_used = true;
|
||||
}
|
||||
|
||||
void add_source_port(uint16_t source_port) {
|
||||
this->source_ports.push_back(source_port);
|
||||
}
|
||||
|
||||
void add_destination_port(uint16_t destination_port) {
|
||||
this->destination_ports.push_back(destination_port);
|
||||
}
|
||||
|
||||
void add_packet_length(uint16_t packet_length) {
|
||||
this->packet_lengths.push_back(packet_length);
|
||||
}
|
||||
|
||||
void add_vlan(uint16_t vlan) {
|
||||
this->vlans.push_back(vlan);
|
||||
}
|
||||
|
||||
void add_ipv4_nexthop(uint32_t ip) {
|
||||
this->ipv4_nexthops.push_back(ip);
|
||||
}
|
||||
|
||||
void add_protocol(ip_protocol_t protocol) {
|
||||
this->protocols.push_back(protocol);
|
||||
}
|
||||
|
||||
void add_ttl(uint8_t ttl) {
|
||||
this->ttls.push_back(ttl);
|
||||
}
|
||||
|
||||
void add_fragmentation_flag(flow_spec_fragmentation_types_t flag) {
|
||||
this->fragmentation_flags.push_back(flag);
|
||||
}
|
||||
|
||||
void add_tcp_flagset(flow_spec_tcp_flagset_t flag) {
|
||||
this->tcp_flags.push_back(flag);
|
||||
}
|
||||
|
||||
void set_action(bgp_flow_spec_action_t action) {
|
||||
this->action = action;
|
||||
}
|
||||
|
||||
bgp_flow_spec_action_t get_action() const {
|
||||
return this->action;
|
||||
}
|
||||
|
||||
std::string get_announce_uuid_as_string() const {
|
||||
return boost::uuids::to_string(announce_uuid);
|
||||
}
|
||||
|
||||
template <class Archive> void serialize(Archive& ar, [[maybe_unused]] const unsigned int version) {
|
||||
ar& BOOST_SERIALIZATION_NVP(source_subnet_ipv4);
|
||||
ar& BOOST_SERIALIZATION_NVP(source_subnet_ipv4_used);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(source_subnet_ipv6);
|
||||
ar& BOOST_SERIALIZATION_NVP(source_subnet_ipv6_used);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(destination_subnet_ipv4);
|
||||
ar& BOOST_SERIALIZATION_NVP(destination_subnet_ipv4_used);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(destination_subnet_ipv6);
|
||||
ar& BOOST_SERIALIZATION_NVP(destination_subnet_ipv6_used);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(agent_subnet);
|
||||
ar& BOOST_SERIALIZATION_NVP(agent_subnet_used);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(source_ports);
|
||||
ar& BOOST_SERIALIZATION_NVP(destination_ports);
|
||||
ar& BOOST_SERIALIZATION_NVP(packet_lengths);
|
||||
ar& BOOST_SERIALIZATION_NVP(vlans);
|
||||
ar& BOOST_SERIALIZATION_NVP(ttls);
|
||||
ar& BOOST_SERIALIZATION_NVP(ipv4_nexthops);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(protocols);
|
||||
ar& BOOST_SERIALIZATION_NVP(fragmentation_flags);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(tcp_flags);
|
||||
ar& BOOST_SERIALIZATION_NVP(set_match_bit_for_tcp_flags);
|
||||
ar& BOOST_SERIALIZATION_NVP(set_match_bit_for_fragmentation_flags);
|
||||
|
||||
ar& BOOST_SERIALIZATION_NVP(action);
|
||||
ar& BOOST_SERIALIZATION_NVP(announce_uuid);
|
||||
}
|
||||
|
||||
// Source prefix
|
||||
subnet_cidr_mask_t source_subnet_ipv4;
|
||||
bool source_subnet_ipv4_used = false;
|
||||
|
||||
subnet_ipv6_cidr_mask_t source_subnet_ipv6;
|
||||
bool source_subnet_ipv6_used = false;
|
||||
|
||||
// Destination prefix
|
||||
subnet_cidr_mask_t destination_subnet_ipv4;
|
||||
bool destination_subnet_ipv4_used = false;
|
||||
|
||||
subnet_ipv6_cidr_mask_t destination_subnet_ipv6;
|
||||
bool destination_subnet_ipv6_used = false;
|
||||
|
||||
// Agent subnet
|
||||
subnet_cidr_mask_t agent_subnet;
|
||||
bool agent_subnet_used = false;
|
||||
|
||||
std::vector<uint16_t> source_ports;
|
||||
std::vector<uint16_t> destination_ports;
|
||||
|
||||
// It's total IP packet length (excluding Layer 2 but including IP header)
|
||||
// https://datatracker.ietf.org/doc/html/rfc5575#section-4
|
||||
std::vector<uint16_t> packet_lengths;
|
||||
|
||||
// This one is an non standard extension for our own purposes
|
||||
std::vector<uint16_t> vlans;
|
||||
|
||||
// This one is an non standard extension for our own purposes
|
||||
std::vector<uint8_t> ttls;
|
||||
|
||||
// IPv4 next hops for https://datatracker.ietf.org/doc/html/draft-ietf-idr-flowspec-redirect-ip-01
|
||||
std::vector<uint32_t> ipv4_nexthops ;
|
||||
|
||||
std::vector<ip_protocol_t> protocols;
|
||||
std::vector<flow_spec_fragmentation_types_t> fragmentation_flags;
|
||||
|
||||
std::vector<flow_spec_tcp_flagset_t> tcp_flags;
|
||||
|
||||
// By default we do not use match bit for TCP flags when encode them to Flow Spec NLRI
|
||||
// But in some cases it could be really useful
|
||||
bool set_match_bit_for_tcp_flags = false;
|
||||
|
||||
// By default we do not use match bit for fragmentation flags when encode them to Flow Spec NLRI
|
||||
// But in some cases (Huawei) it could be useful
|
||||
bool set_match_bit_for_fragmentation_flags = false;
|
||||
|
||||
bgp_flow_spec_action_t action;
|
||||
boost::uuids::uuid announce_uuid{};
|
||||
};
|
||||
|
||||
bool operator==(const flow_spec_rule_t& lhs, const flow_spec_rule_t& rhs);
|
||||
bool operator!=(const flow_spec_rule_t& lhs, const flow_spec_rule_t& rhs);
|
||||
|
||||
bool read_flow_spec_from_json_to_native_format(const std::string& json_encoded_flow_spec, flow_spec_rule_t& flow_spec_rule, bool require_action);
|
||||
bool encode_flow_spec_to_json(const flow_spec_rule_t& flow_spec_rule, std::string& json_encoded_flow_spec, bool add_uuid);
|
||||
bool decode_native_flow_spec_announce_from_binary_encoded_atributes(std::vector<dynamic_binary_buffer_t> binary_attributes,
|
||||
flow_spec_rule_t& flow_spec_rule);
|
||||
|
||||
bool encode_bgp_flow_spec_action_as_extended_attribute(const bgp_flow_spec_action_t& bgp_flow_spec_action,
|
||||
dynamic_binary_buffer_t& extended_attributes_as_binary_array);
|
||||
|
||||
// It's format of redirect target. So called route target community. Official spec RFC5575 is pretty vague about it:
|
||||
// https://datatracker.ietf.org/doc/html/rfc4360#section-4
|
||||
// But new BGP Flow Spec clarifies it as https://datatracker.ietf.org/doc/html/rfc8955#name-rt-redirect-rt-redirect-sub
|
||||
class __attribute__((__packed__)) redirect_2_octet_as_4_octet_value_t {
|
||||
// We must not access these fields directly as it requires explicit byte order conversion
|
||||
private:
|
||||
uint16_t as = 0;
|
||||
uint32_t value = 0;
|
||||
|
||||
public:
|
||||
uint16_t get_as_host_byte_order() const {
|
||||
return fast_ntoh(as);
|
||||
}
|
||||
|
||||
uint32_t get_value_host_byte_order() const {
|
||||
return fast_ntoh(value);
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "as: " << get_as_host_byte_order() << " "
|
||||
<< "value: " << get_value_host_byte_order() << " ";
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static_assert(sizeof(redirect_2_octet_as_4_octet_value_t) == 6,
|
||||
"Bad size for redirect_2_octet_as_4_octet_value_t");
|
||||
|
||||
// More details at https://tools.ietf.org/html/rfc5575 page 6
|
||||
class __attribute__((__packed__)) bgp_flow_spec_operator_byte_t {
|
||||
public:
|
||||
uint8_t equal : 1 = 0, greater_than : 1 = 0, less_than : 1 = 0, reserved : 1 = 0, bit_shift_len : 2 = 0,
|
||||
and_bit : 1 = 0, end_of_list : 1 = 0;
|
||||
|
||||
void set_equal_bit() {
|
||||
equal = 1;
|
||||
}
|
||||
|
||||
void set_greater_than_bit() {
|
||||
greater_than = 1;
|
||||
}
|
||||
|
||||
void set_less_than_bit() {
|
||||
less_than = 1;
|
||||
}
|
||||
|
||||
void set_and_bit() {
|
||||
and_bit = 1;
|
||||
}
|
||||
|
||||
void set_end_of_list_bit() {
|
||||
end_of_list = 1;
|
||||
}
|
||||
|
||||
bool set_length_in_bytes(uint32_t byte_length) {
|
||||
// We could set only for numbers which are pow of 2
|
||||
if (byte_length == 1) {
|
||||
bit_shift_len = 0;
|
||||
} else if (byte_length == 2) {
|
||||
bit_shift_len = 1;
|
||||
} else if (byte_length == 4) {
|
||||
bit_shift_len = 2;
|
||||
} else {
|
||||
logger << log4cpp::Priority::ERROR << "Could not calculate log2 for " << byte_length;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "end of list: " << uint32_t(end_of_list) << " "
|
||||
<< "and_bit: " << uint32_t(and_bit) << " "
|
||||
<< "bit_shift_len: " << uint32_t(bit_shift_len) << " "
|
||||
<< "reserved: " << uint32_t(reserved) << " "
|
||||
<< "less_than: " << uint32_t(less_than) << " "
|
||||
<< "greater_than: " << uint32_t(greater_than) << " "
|
||||
<< "equal: " << uint32_t(equal);
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
// Real value evaluated as 1 << bit_shift_len
|
||||
uint32_t get_value_length() {
|
||||
return 1 << bit_shift_len;
|
||||
}
|
||||
};
|
||||
|
||||
// Here we store multiple enumerable values for flow spec protocol (ports,
|
||||
// protocols and other)
|
||||
class flow_spec_enumerable_lement {
|
||||
public:
|
||||
uint8_t one_byte_value = 0;
|
||||
uint16_t two_byte_value = 0;
|
||||
|
||||
// Could be only 1 or 2 bytes
|
||||
uint32_t value_length = 0;
|
||||
bgp_flow_spec_operator_byte_t operator_byte{};
|
||||
};
|
||||
|
||||
typedef std::vector<flow_spec_enumerable_lement> multiple_flow_spec_enumerable_items_t;
|
||||
|
||||
bool read_one_or_more_values_encoded_with_operator_byte(uint8_t* start,
|
||||
uint8_t* global_end,
|
||||
uint32_t& readed_bytes,
|
||||
multiple_flow_spec_enumerable_items_t& multiple_flow_spec_enumerable_items);
|
||||
std::string get_flow_spec_type_name_by_number(uint8_t flow_spec_type);
|
||||
std::string get_bgp_attribute_name_by_number(uint8_t bgp_attribute_type);
|
||||
bool flow_spec_decode_nlri_value(uint8_t* data_ptr, uint32_t data_length, flow_spec_rule_t& flow_spec_rule);
|
||||
|
||||
class __attribute__((__packed__)) bgp_flow_spec_fragmentation_entity_t {
|
||||
public:
|
||||
uint8_t dont_fragment : 1 = 0, is_fragment : 1 = 0, first_fragment : 1 = 0, last_fragment : 1 = 0, reserved : 4 = 0;
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "reserved: " << uint32_t(reserved) << " "
|
||||
<< "last_fragment: " << uint32_t(last_fragment) << " "
|
||||
<< "first_fragment: " << uint32_t(first_fragment) << " "
|
||||
<< "is_fragment: " << uint32_t(is_fragment) << " "
|
||||
<< "dont_fragment: " << uint32_t(dont_fragment);
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(bgp_flow_spec_fragmentation_entity_t) == 1, "Broken size for bgp_flow_spec_fragmentation_entity_t");
|
||||
|
||||
// More details at https://tools.ietf.org/html/rfc5575#page-9
|
||||
// We use this version of operator byte for TCP flags and for fragmentation flags
|
||||
class __attribute__((__packed__)) bgp_flow_spec_bitmask_operator_byte_t {
|
||||
public:
|
||||
uint8_t match_bit : 1 = 0, not_bit : 1 = 0, reserved2 : 1 = 0, reserved1 : 1 = 0, bit_shift_len : 2 = 0,
|
||||
and_bit : 1 = 0, end_of_list : 1 = 0;
|
||||
|
||||
bgp_flow_spec_bitmask_operator_byte_t() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "end of list: " << uint32_t(end_of_list) << " "
|
||||
<< "and_bit: " << uint32_t(and_bit) << " "
|
||||
<< "bit_shift_len: " << uint32_t(bit_shift_len) << " "
|
||||
<< "reserved1: " << uint32_t(reserved1) << " "
|
||||
<< "reserved2: " << uint32_t(reserved2) << " "
|
||||
<< "not_bit: " << uint32_t(not_bit) << " "
|
||||
<< "match_bit: " << uint32_t(match_bit);
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
void set_not_bit() {
|
||||
not_bit = 1;
|
||||
}
|
||||
|
||||
void set_and_bit() {
|
||||
and_bit = 1;
|
||||
}
|
||||
|
||||
void set_match_bit() {
|
||||
match_bit = 1;
|
||||
}
|
||||
|
||||
void set_end_of_list_bit() {
|
||||
end_of_list = 1;
|
||||
}
|
||||
|
||||
bool set_length_in_bytes(uint32_t byte_length) {
|
||||
// We could set only for numbers which are pow of 2
|
||||
if (byte_length == 1) {
|
||||
bit_shift_len = 0;
|
||||
} else if (byte_length == 2) {
|
||||
bit_shift_len = 1;
|
||||
} else if (byte_length == 4) {
|
||||
bit_shift_len = 2;
|
||||
} else {
|
||||
logger << log4cpp::Priority::WARN << "Could not calculate log2 for " << byte_length;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Real value evaluated as 1 << bit_shift_len
|
||||
uint32_t get_value_length() {
|
||||
return 1 << bit_shift_len;
|
||||
}
|
||||
};
|
||||
|
||||
// We have two ways to encode TCP flags - one byte and two byte
|
||||
|
||||
// This is extracted some piece of code from: tcp_header_t /
|
||||
// network_data_structures
|
||||
class __attribute__((__packed__)) bgp_flowspec_two_byte_encoded_tcp_flags_t {
|
||||
public:
|
||||
uint16_t fin : 1 = 0, syn : 1 = 0, rst : 1 = 0, psh : 1 = 0, ack : 1 = 0, urg : 1 = 0, ece : 1 = 0, cwr : 1 = 0,
|
||||
ns : 1 = 0, reserved : 3 = 0, data_offset : 4 = 0;
|
||||
};
|
||||
|
||||
static_assert(sizeof(bgp_flowspec_two_byte_encoded_tcp_flags_t) == 2, "Bad size for bgp_flowspec_two_byte_encoded_tcp_flags_t");
|
||||
|
||||
class __attribute__((__packed__)) bgp_flowspec_one_byte_byte_encoded_tcp_flags_t {
|
||||
public:
|
||||
// Just drop 8 bytes from bgp_flowspec_two_byte_encoded_tcp_flags
|
||||
uint8_t fin : 1 = 0, syn : 1 = 0, rst : 1 = 0, psh : 1 = 0, ack : 1 = 0, urg : 1 = 0, ece : 1 = 0, cwr : 1 = 0;
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "cwr: " << uint32_t(cwr) << " "
|
||||
<< "ece: " << uint32_t(ece) << " "
|
||||
<< "urg: " << uint32_t(urg) << " "
|
||||
<< "ack: " << uint32_t(ack) << " "
|
||||
<< "psh: " << uint32_t(psh) << " "
|
||||
<< "rst: " << uint32_t(rst) << " "
|
||||
<< "syn: " << uint32_t(syn) << " "
|
||||
<< "fin: " << uint32_t(fin);
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(bgp_flowspec_one_byte_byte_encoded_tcp_flags_t) == 1, "Bad size for ");
|
||||
|
||||
// BGP flow spec entity numbers
|
||||
enum FLOW_SPEC_ENTITY_TYPES : uint8_t {
|
||||
FLOW_SPEC_ENTITY_DESTINATION_PREFIX = 1,
|
||||
FLOW_SPEC_ENTITY_SOURCE_PREFIX = 2,
|
||||
FLOW_SPEC_ENTITY_IP_PROTOCOL = 3,
|
||||
FLOW_SPEC_ENTITY_PORT = 4,
|
||||
FLOW_SPEC_ENTITY_DESTINATION_PORT = 5,
|
||||
FLOW_SPEC_ENTITY_SOURCE_PORT = 6,
|
||||
FLOW_SPEC_ENTITY_ICMP_TYPE = 7,
|
||||
FLOW_SPEC_ENTITY_ICMP_CODE = 8,
|
||||
FLOW_SPEC_ENTITY_TCP_FLAGS = 9,
|
||||
FLOW_SPEC_ENTITY_PACKET_LENGTH = 10,
|
||||
FLOW_SPEC_ENTITY_DSCP = 11,
|
||||
FLOW_SPEC_ENTITY_FRAGMENT = 12,
|
||||
};
|
||||
|
||||
/*
|
||||
Here we have custom NLRI encoding
|
||||
(https://tools.ietf.org/html/rfc4760#section-5.1.3):
|
||||
+---------------------------------------------------------+
|
||||
| Address Family Identifier (2 octets) |
|
||||
+---------------------------------------------------------+
|
||||
| Subsequent Address Family Identifier (1 octet) |
|
||||
+---------------------------------------------------------+
|
||||
| Length of Next Hop Network Address (1 octet) |
|
||||
+---------------------------------------------------------+
|
||||
| Network Address of Next Hop (variable) |
|
||||
+---------------------------------------------------------+
|
||||
| Reserved (1 octet) |
|
||||
+---------------------------------------------------------+
|
||||
| Network Layer Reachability Information (variable) |
|
||||
+---------------------------------------------------------+
|
||||
*/
|
||||
class __attribute__((__packed__)) bgp_mp_ext_flow_spec_header_t {
|
||||
public:
|
||||
uint16_t afi_identifier = AFI_IP;
|
||||
uint8_t safi_identifier = SAFI_FLOW_SPEC_UNICAST;
|
||||
// For BGP Flow spec we are using blank next hop because it's useless for us
|
||||
// now
|
||||
uint8_t length_of_next_hop = 0;
|
||||
// Here we have blank next hop. Or haven't ... :)
|
||||
uint8_t reserved = 0;
|
||||
// Here we have NLRI information
|
||||
|
||||
void network_to_host_byte_order() {
|
||||
afi_identifier = ntohs(afi_identifier);
|
||||
}
|
||||
|
||||
void host_byte_order_to_network_byte_order() {
|
||||
afi_identifier = htons(afi_identifier);
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "afi_identifier: " << uint32_t(afi_identifier) << " "
|
||||
<< "safi_identifier: " << uint32_t(safi_identifier) << " "
|
||||
<< "length_of_next_hop: " << uint32_t(length_of_next_hop) << " "
|
||||
<< "reserved: " << uint32_t(reserved);
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
class __attribute__((__packed__)) bgp_extended_community_element_flow_spec_rate_t {
|
||||
public:
|
||||
uint8_t type_hight = EXTENDED_COMMUNITY_TRANSITIVE_EXPEREMENTAL;
|
||||
uint8_t type_low = FLOW_SPEC_EXTENDED_COMMUNITY_SUBTYPE_TRAFFIC_RATE;
|
||||
|
||||
// This bytes are meaningless and should not processed at all by receiver side
|
||||
uint8_t value[2] = { 0, 0 };
|
||||
float rate_limit = 0;
|
||||
|
||||
void host_byte_order_to_network_byte_order() {
|
||||
// Have you ever do little endian to big endian conversion for float? We do!
|
||||
float rate_limit_copy = rate_limit;
|
||||
|
||||
logger << log4cpp::Priority::DEBUG << "Original rate: " << rate_limit;
|
||||
|
||||
// We do not use pointer to field structure here because it may cause alignment issues and gcc yells on it:
|
||||
// warning: taking address of packed member of ... may result in an unaligned pointer value [-Waddress-of-packed-member]
|
||||
uint32_t* integer_pointer = (uint32_t*)&rate_limit_copy;
|
||||
|
||||
logger << log4cpp::Priority::DEBUG << "Integer part of rate: " << *integer_pointer;
|
||||
|
||||
*integer_pointer = htonl(*integer_pointer);
|
||||
|
||||
// Overwrite original value
|
||||
this->rate_limit = rate_limit_copy;
|
||||
|
||||
logger << log4cpp::Priority::DEBUG << "Network byte order encoded rate limit: " << rate_limit;
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "type hight: " << uint32_t(type_hight) << " "
|
||||
<< "type low: " << uint32_t(type_low) << " "
|
||||
<< "value raw: " << print_binary_string_as_hex_with_leading_0x(value, sizeof(value));
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(bgp_extended_community_element_flow_spec_rate_t) == 8,
|
||||
"Bad size for bgp_extended_community_element_flow_spec_rate_t");
|
||||
|
||||
class __attribute__((__packed__)) bgp_extended_community_element_flow_spec_redirect_2_octet_as_4_octet_value_t_t {
|
||||
public:
|
||||
uint8_t type_hight = EXTENDED_COMMUNITY_TRANSITIVE_EXPEREMENTAL;
|
||||
uint8_t type_low = FLOW_SPEC_EXTENDED_COMMUNITY_SUBTYPE_REDIRECT_AS_TWO_BYTE;
|
||||
|
||||
// 6 octet value
|
||||
uint16_t redirect_as = 0;
|
||||
uint32_t redirect_value = 0;
|
||||
|
||||
void set_redirect_as(uint16_t value) {
|
||||
redirect_as = fast_hton(value);
|
||||
}
|
||||
|
||||
void set_redirect_value(uint32_t value) {
|
||||
redirect_value = fast_hton(value);
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "type hight: " << uint32_t(type_hight) << " "
|
||||
<< "type low: " << uint32_t(type_low) << " "
|
||||
<< "redirect_as: " << redirect_as << " "
|
||||
<< "redirect_value: " << redirect_value;
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(bgp_extended_community_element_flow_spec_redirect_2_octet_as_4_octet_value_t_t) == 8,
|
||||
"Bad size for bgp_extended_community_element_flow_spec_redirect_2_octet_as_4_octet_value_t_t");
|
||||
|
||||
// This structure encodes Flow Spec next hop IPv4
|
||||
class __attribute__((__packed__)) bgp_extended_community_element_flow_spec_ipv4_next_hop_t {
|
||||
public:
|
||||
uint8_t type_hight = EXTENDED_COMMUNITY_TRANSITIVE_IPV4_ADDRESS_SPECIFIC;
|
||||
uint8_t type_low = BGP_IPV4_EXTENDED_COMMUNITY_SUBTYPE_FLOW_SPEC_REDIRECT_IPv4;
|
||||
|
||||
// Actual value of IPv4 next hop
|
||||
uint32_t next_hop_ipv4 = 0;
|
||||
|
||||
// In this field we can set mirror flag to make packet copies
|
||||
uint16_t local_administrator = 0;
|
||||
|
||||
void host_byte_order_to_network_byte_order() {
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << "type hight: " << uint32_t(type_hight) << " "
|
||||
<< "type low: " << uint32_t(type_low) << " "
|
||||
<< "nexthop: " << next_hop_ipv4 << " "
|
||||
<< "local administrator: " << local_administrator;
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static_assert(sizeof(bgp_extended_community_element_flow_spec_ipv4_next_hop_t) == 8,
|
||||
"Bad size for bgp_extended_community_element_flow_spec_ipv4_next_hop_t");
|
||||
|
||||
static_assert(sizeof(bgp_flow_spec_bitmask_operator_byte_t) == 1, "Bad size for bgp_flow_spec_bitmask_operator_byte_t");
|
||||
static_assert(sizeof(bgp_flow_spec_operator_byte_t) == 1, "Bad size for bgp_flow_spec_operator_byte_t");
|
||||
|
||||
bool read_flow_spec_tcp_flags_from_strig(const std::string& string_form, flow_spec_tcp_flagset_t& tcp_flagset);
|
||||
bool read_flow_spec_fragmentation_types_from_string(const std::string& string_form, flow_spec_fragmentation_types_t& fragment_flag);
|
||||
bool valid_port(int32_t port);
|
||||
bool encode_flow_spec_to_json_raw(const flow_spec_rule_t& flow_spec_rule, bool add_uuid, nlohmann::json& flow_json);
|
||||
std::string flow_spec_fragmentation_flags_to_string(flow_spec_fragmentation_types_t const& fragment_flag);
|
||||
std::string flow_spec_tcp_flagset_to_string(flow_spec_tcp_flagset_t const& tcp_flagset);
|
Loading…
Reference in New Issue
Block a user