1
0
Fork 0
mirror of https://github.com/pavel-odintsov/fastnetmon synced 2024-05-03 22:26:30 +02:00
fastnetmon-rewritten/src/bgp_protocol.hpp

690 lines
24 KiB
C++

#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 "all_logcpp_libraries.hpp"
// Get log4cpp logger from main programme
extern log4cpp::Category& logger;
class bgp_attribute_origin;
class bgp_attribute_next_hop_ipv4;
class IPv4UnicastAnnounce;
class IPv6UnicastAnnounce;
bool decode_bgp_subnet_encoding_ipv4_raw(uint8_t* value, subnet_cidr_mask_t& extracted_prefix);
bool decode_bgp_subnet_encoding_ipv4(int len, uint8_t* value, subnet_cidr_mask_t& extracted_prefix, uint32_t& parsed_nlri_length);
uint32_t how_much_bytes_we_need_for_storing_certain_subnet_mask(uint8_t prefix_bit_length);
std::string get_bgp_attribute_name_by_number(uint8_t bgp_attribute_type);
enum BGP_PROTOCOL_MESSAGE_TYPES_UNTYPED : uint8_t {
BGP_PROTOCOL_MESSAGE_OPEN = 1,
BGP_PROTOCOL_MESSAGE_UPDATE = 2,
BGP_PROTOCOL_MESSAGE_NOTIFICATION = 3,
BGP_PROTOCOL_MESSAGE_KEEPALIVE = 4,
};
// More details here https://tools.ietf.org/html/rfc4271#page-12
class __attribute__((__packed__)) bgp_message_header_t {
public:
uint8_t marker[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
uint16_t length = 0;
uint8_t type = 0;
void host_byte_order_to_network_byte_order() {
length = htons(length);
}
};
static_assert(sizeof(bgp_message_header_t) == 19, "Broken size for bgp_message_header_t");
// TODO: move to
//
// https://www.ietf.org/rfc/rfc4271.txt pages 15 and 16
// https://github.com/osrg/gobgp/blob/d6148c75a30d87c3f8c1d0f68725127e4c5f3a65/packet/bgp.go#L3012
struct __attribute__((__packed__)) bgp_attribute_flags {
uint8_t : 4, extended_length_bit : 1 = 0, partial_bit : 1 = 0, transitive_bit : 1 = 0, optional_bit : 1 = 0;
bgp_attribute_flags(uint8_t flag_as_integer) {
memcpy(this, &flag_as_integer, sizeof(flag_as_integer));
}
bgp_attribute_flags() {
memset(this, 0, sizeof(*this));
}
void set_optional_bit(bool bit_value) {
optional_bit = (int)bit_value;
}
void set_transitive_bit(bool bit_value) {
transitive_bit = (int)bit_value;
}
void set_partial_bit(bool bit_value) {
partial_bit = (int)bit_value;
}
void set_extended_length_bit(bool bit_value) {
extended_length_bit = (int)bit_value;
}
bool get_optional_bit() {
return optional_bit == 1 ? true : false;
}
bool get_transitive_bit() {
return transitive_bit == 1 ? true : false;
}
bool get_partial_bit() {
return partial_bit == 1 ? true : false;
}
bool get_extended_length_bit() {
return extended_length_bit == 1 ? true : false;
}
std::string print() const {
std::stringstream buf;
buf << "optional: " << int(optional_bit) << " transitive: " << int(transitive_bit)
<< " partial_bit: " << int(partial_bit) << " extended_length_bit: " << int(extended_length_bit);
return buf.str();
}
};
class bgp_attibute_common_header_t {
public:
uint8_t attribute_flags = 0;
uint8_t attribute_type = 0;
uint32_t length_of_length_field = 0;
uint32_t attribute_value_length = 0;
uint32_t attribute_body_shift = 0;
// Just const value
uint32_t attribute_flag_and_type_length = 2;
std::string print() const {
std::stringstream buffer;
buffer << "attribute_flags: " << uint32_t(attribute_flags)
<< " "
// << "attribute_pretty_flags: " <<
// bgp_attribute_flags(attribute_flags).print() << " "
<< "attribute_type: " << uint32_t(attribute_type) << " "
<< "attribute_name: " << get_bgp_attribute_name_by_number(attribute_type) << " "
<< "length_of_length_field: " << length_of_length_field << " "
<< "attribute_value_length: " << attribute_value_length;
return buffer.str();
}
// More user friendly form
bool parse_raw_bgp_attribute_binary_buffer(dynamic_binary_buffer_t dynamic_binary_buffer) {
return parse_raw_bgp_attribute((uint8_t*)dynamic_binary_buffer.get_pointer(), dynamic_binary_buffer.get_used_size());
}
bool parse_raw_bgp_attribute(uint8_t* value, size_t len) {
if (len < attribute_flag_and_type_length or value == NULL) {
logger << log4cpp::Priority::WARN << "Too short attribute. We need least two bytes here but get " << len << " bytes";
return false;
}
// https://www.ietf.org/rfc/rfc4271.txt page 15
attribute_flags = value[0];
attribute_type = value[1];
bgp_attribute_flags attr_flags(attribute_flags);
// attr_flags.print();
length_of_length_field = 1;
if (attr_flags.extended_length_bit == 1) {
// When we have extended_length_bit we have two bytes for length
// information
// TODO: add support for this type of attributes
// logger << log4cpp::Priority::WARN << "We haven't support for extended
// length attributes.
// Sorry!" <<
// std::endl;
length_of_length_field = 2;
}
if (len < attribute_flag_and_type_length + length_of_length_field) {
logger << log4cpp::Priority::WARN << "Too short attribute because we need least "
<< attribute_flag_and_type_length + length_of_length_field << " bytes";
return false;
}
attribute_value_length = value[2];
// logger << log4cpp::Priority::WARN << "Attribute type: " <<
// int(attribute_type) ;
// logger << log4cpp::Priority::WARN << "Raw attribute length: " << len ;
// logger << log4cpp::Priority::WARN << "Attribute internal length: " <<
// int(attribute_value_length)
// ;
uint32_t total_attribute_length = attribute_flag_and_type_length + length_of_length_field + attribute_value_length;
// logger << log4cpp::Priority::WARN << "attribute_flag_and_type_length: "
// <<
// attribute_flag_and_type_length <<
// std::endl
// << "length_of_length_field: " << length_of_length_field
// << "attribute_value_length: " << attribute_value_length ;
if (len < total_attribute_length) {
logger << log4cpp::Priority::WARN << "Atrribute value length: " << total_attribute_length
<< " length exceed whole packet length " << len;
return false;
}
// Store shift to attribute payload
attribute_body_shift = attribute_flag_and_type_length + length_of_length_field;
return true;
}
};
bool decode_nlri_ipv4(int len, uint8_t* value, subnet_cidr_mask_t& extracted_prefix);
bool decode_attribute(int len, char* value, IPv4UnicastAnnounce& unicast_ipv4_announce);
bool encode_bgp_subnet_encoding(const subnet_cidr_mask_t& prefix, dynamic_binary_buffer_t& buffer_result);
std::string get_origin_name_by_value(uint8_t origin_value);
const unsigned int AFI_IP = 1;
const unsigned int AFI_IP6 = 2;
const unsigned int SAFI_UNICAST = 1;
const unsigned int SAFI_FLOW_SPEC_UNICAST = 133;
const unsigned int ipv4_unicast_route_family = AFI_IP << 16 | SAFI_UNICAST;
const unsigned int ipv4_flow_spec_route_family = AFI_IP << 16 | SAFI_FLOW_SPEC_UNICAST;
const unsigned int ipv6_unicast_route_family = AFI_IP6 << 16 | SAFI_UNICAST;
const unsigned int ipv6_flow_spec_route_family = AFI_IP6 << 16 | SAFI_FLOW_SPEC_UNICAST;
// http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml
// https://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#transitive
enum EXTENDED_COMMUNITY_TYPES_HIGHT_UNTYPED : uint8_t {
// Transitive IPv4-Address-Specific Extended Community (Sub-Types are defined in the "Transitive IPv4-Address-Specific Extended Community Sub-Types" registry)
EXTENDED_COMMUNITY_TRANSITIVE_IPV4_ADDRESS_SPECIFIC = 1, // 0x01
// We are encoding attributes for BGP flow spec this way
// Generic Transitive Experimental Use Extended Community
EXTENDED_COMMUNITY_TRANSITIVE_EXPEREMENTAL = 128, // 0x80
};
// Subtypes for EXTENDED_COMMUNITY_TRANSITIVE_EXPEREMENTAL
// http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml
// https://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#transitive
enum EXTENDED_COMMUNITY_TYPES_LOW_FOR_COMMUNITY_TRANSITIVE_EXPEREMENTAL : uint8_t {
FLOW_SPEC_EXTENDED_COMMUNITY_SUBTYPE_TRAFFIC_RATE = 6,
FLOW_SPEC_EXTENDED_COMMUNITY_SUBTYPE_TRAFFIC_ACTION = 7,
FLOW_SPEC_EXTENDED_COMMUNITY_SUBTYPE_REDIRECT_AS_TWO_BYTE = 8,
FLOW_SPEC_EXTENDED_COMMUNITY_SUBTYPE_TRAFFIC_REMARKING = 9,
};
enum BGP_ATTRIBUTES_TYPES : uint8_t {
// https://www.ietf.org/rfc/rfc4271.txt from page 17
BGP_ATTRIBUTE_ORIGIN = 1,
BGP_ATTRIBUTE_AS_PATH = 2,
BGP_ATTRIBUTE_NEXT_HOP = 3,
BGP_ATTRIBUTE_MULTI_EXIT_DISC = 4,
BGP_ATTRIBUTE_LOCAL_PREF = 5,
BGP_ATTRIBUTE_ATOMIC_AGGREGATE = 6,
BGP_ATTRIBUTE_AGGREGATOR = 7,
// https://tools.ietf.org/html/rfc1997 from page 1
BGP_ATTRIBUTE_COMMUNITY = 8,
// https://tools.ietf.org/html/rfc4760 from page 2
BGP_ATTRIBUTE_MP_REACH_NLRI = 14,
// https://tools.ietf.org/html/rfc4360 from page 1
BGP_ATTRIBUTE_EXTENDED_COMMUNITY = 16,
};
// https://www.ietf.org/rfc/rfc4271.txt from page 17
enum BGP_ORIGIN_TYPES : uint8_t { BGP_ORIGIN_IGP = 0, BGP_ORIGIN_EGP = 1, BGP_ORIGIN_INCOMPLETE = 2 };
// https://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#trans-ipv4
enum BGP_IPV4_EXTENDED_COMMUNITY_SUBTYPES_TRANSITIVE : uint8_t {
BGP_IPV4_EXTENDED_COMMUNITY_SUBTYPE_FLOW_SPEC_REDIRECT_IPv4 = 0x0c,
};
// It's short MP reach NLRI header. We use it in case when we have non zero length for length_of_next_hop
// I use it only to decode/encode IPv6 annnounces
class __attribute__((__packed__)) bgp_mp_reach_short_header_t {
public:
uint16_t afi_identifier = AFI_IP6;
uint8_t safi_identifier = SAFI_UNICAST;
// According to https://www.ietf.org/rfc/rfc2545.txt we should set it to 16 for global IP addresses
uint8_t length_of_next_hop = 16;
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);
return buffer.str();
}
};
class __attribute__((__packed__)) bgp_attribute_community_t {
public:
bgp_attribute_community_t() {
attribute_type = BGP_ATTRIBUTE_COMMUNITY;
// Set attribute flags
attribute_flags.set_transitive_bit(true);
attribute_flags.set_partial_bit(false);
attribute_flags.set_optional_bit(true);
attribute_flags.set_extended_length_bit(false);
}
bgp_attribute_flags attribute_flags;
uint8_t attribute_type = 0;
// This variable store total size in bytes of all community elements (each
// element should has
// size 4 bytes)
uint8_t attribute_length = 0;
// Here we have multiple elements of type: bgp_community_attribute_element_t
};
// This is new extended communities:
// More details: https://tools.ietf.org/html/rfc4360
class __attribute__((__packed__)) bgp_extended_community_attribute_t {
public:
bgp_extended_community_attribute_t() {
// Set attribute flags
attribute_flags.set_transitive_bit(true);
attribute_flags.set_partial_bit(false);
attribute_flags.set_optional_bit(true);
attribute_flags.set_extended_length_bit(false);
}
bgp_attribute_flags attribute_flags;
uint8_t attribute_type = BGP_ATTRIBUTE_EXTENDED_COMMUNITY;
// This variable store total size in bytes of all community elements. Each
// community element has
// size 8 bytes
uint8_t attribute_length = 0;
// Here we have multiple elements of type: bgp_extended_community_element_t
};
// BGP extended community attribute
class __attribute__((__packed__)) bgp_extended_community_element_t {
public:
uint8_t type_hight = 0;
uint8_t type_low = 0;
// This data depends on implementation and values in type_* variables
uint8_t value[6] = { 0, 0, 0, 0, 0, 0 };
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_t) == 8, "Bad size for bgp_extended_community_element_t");
// This is class for storing old style BGP communities which support only 16
// bit AS numbers
class __attribute__((__packed__)) bgp_community_attribute_element_t {
public:
uint16_t asn_number = 0;
uint16_t community_number = 0;
void host_byte_order_to_network_byte_order() {
asn_number = htons(asn_number);
community_number = htons(community_number);
}
};
bool read_bgp_community_from_string(std::string community_as_string, bgp_community_attribute_element_t& bgp_community_attribute_element);
static_assert(sizeof(bgp_community_attribute_element_t) == 4, "Broken size of bgp_community_attribute_element_t");
// With this attribute we are encoding different things (flow spec for example)
class __attribute__((__packed__)) bgp_attribute_multiprotocol_extensions_t {
public:
bgp_attribute_multiprotocol_extensions_t() {
attribute_flags.set_transitive_bit(false);
attribute_flags.set_optional_bit(true);
attribute_flags.set_partial_bit(false);
attribute_flags.set_extended_length_bit(false);
}
std::string print() const {
std::stringstream buf;
buf << attribute_flags.print() << "attribute type: " << int(attribute_type)
<< " attribute_length: " << int(attribute_length);
return buf.str();
}
bgp_attribute_flags attribute_flags;
uint8_t attribute_type = BGP_ATTRIBUTE_MP_REACH_NLRI;
uint8_t attribute_length = 0;
// value has very complex format and we are encoding it with external code
};
class __attribute__((__packed__)) bgp_attribute_origin {
public:
bgp_attribute_origin() {
// Set attribute flags
attribute_flags.set_transitive_bit(true);
attribute_flags.set_partial_bit(false);
attribute_flags.set_optional_bit(false);
attribute_flags.set_extended_length_bit(false);
}
std::string print() const {
std::stringstream buf;
buf << attribute_flags.print() << "attribute type: " << int(attribute_type)
<< " attribute_length: " << int(attribute_length) << " attribute_value: " << attribute_value;
return buf.str();
}
bgp_attribute_flags attribute_flags;
uint8_t attribute_type = BGP_ATTRIBUTE_ORIGIN;
uint8_t attribute_length = 1;
uint8_t attribute_value = (uint8_t)BGP_ORIGIN_INCOMPLETE;
};
class __attribute__((__packed__)) bgp_attribute_next_hop_ipv4 {
public:
bgp_attribute_next_hop_ipv4() {
// Set attribute flags
attribute_flags.set_transitive_bit(true);
attribute_flags.set_partial_bit(false);
attribute_flags.set_optional_bit(false);
attribute_flags.set_extended_length_bit(false);
}
bgp_attribute_next_hop_ipv4(uint32_t next_hop) : bgp_attribute_next_hop_ipv4() {
attribute_value = next_hop;
}
bgp_attribute_flags attribute_flags;
uint8_t attribute_type = BGP_ATTRIBUTE_NEXT_HOP;
uint8_t attribute_length = 4;
uint32_t attribute_value = 0;
std::string print() const {
std::stringstream buf;
buf << attribute_flags.print() << "attribute type: " << int(attribute_type)
<< " attribute_length: " << int(attribute_length) << " attribute_value: " << attribute_value;
return buf.str();
}
};
class IPv4UnicastAnnounce {
public:
void set_withdraw(bool withdraw) {
this->is_withdraw = withdraw;
}
bool get_withdraw() {
return this->is_withdraw;
}
void set_next_hop(uint32_t next_hop) {
this->next_hop = next_hop;
}
void set_prefix(subnet_cidr_mask_t prefix) {
this->prefix = prefix;
}
void set_origin(BGP_ORIGIN_TYPES origin) {
this->origin = origin;
}
uint32_t get_next_hop() {
return next_hop;
}
subnet_cidr_mask_t get_prefix() {
return this->prefix;
}
BGP_ORIGIN_TYPES get_origin() {
return this->origin;
}
// Returns prefix in text form
std::string get_prefix_in_cidr_form() {
return convert_ip_as_uint_to_string(prefix.subnet_address) + "/" + std::to_string(prefix.cidr_prefix_length);
}
bool generate_nlri(dynamic_binary_buffer_t& buffer_result) const {
return encode_bgp_subnet_encoding(this->prefix, buffer_result);
}
std::vector<dynamic_binary_buffer_t> get_attributes() const {
/*
* The sender of an UPDATE message SHOULD order path attributes within
* the UPDATE message in ascending order of attribute type. The
* receiver of an UPDATE message MUST be prepared to handle path
* attributes within UPDATE messages that are out of order.
*/
bgp_attribute_origin origin_attr;
// logger << log4cpp::Priority::WARN << "origin_attr: " <<
// origin_attr.print() <<
// std::endl;
bgp_attribute_next_hop_ipv4 next_hop_attr(next_hop);
// logger << log4cpp::Priority::WARN << "next_hop_attr: " <<
// next_hop_attr.print() <<
// std::endl;
dynamic_binary_buffer_t origin_as_binary_array;
origin_as_binary_array.set_maximum_buffer_size_in_bytes(sizeof(origin_attr));
origin_as_binary_array.append_data_as_object_ptr(&origin_attr);
dynamic_binary_buffer_t next_hop_as_binary_array;
next_hop_as_binary_array.set_maximum_buffer_size_in_bytes(sizeof(next_hop_attr));
next_hop_as_binary_array.append_data_as_object_ptr(&next_hop_attr);
// TODO: remove ugly code with custom build of each vector
if (community_list.empty()) {
// Vector should be ordered in ascending order of attribute types
return std::vector<dynamic_binary_buffer_t>{ origin_as_binary_array, next_hop_as_binary_array };
} else {
// We have communities
bgp_attribute_community_t bgp_attribute_community;
// Each record has this of 4 bytes
bgp_attribute_community.attribute_length = community_list.size() * sizeof(bgp_community_attribute_element_t);
uint32_t community_attribute_full_length = sizeof(bgp_attribute_community_t) + bgp_attribute_community.attribute_length;
dynamic_binary_buffer_t communities_list_as_binary_array;
communities_list_as_binary_array.set_maximum_buffer_size_in_bytes(community_attribute_full_length);
communities_list_as_binary_array.append_data_as_object_ptr(&bgp_attribute_community);
for (auto bgp_community_element : community_list) {
// Encode they in network byte order
bgp_community_element.host_byte_order_to_network_byte_order();
communities_list_as_binary_array.append_data_as_object_ptr(&bgp_community_element);
}
return std::vector<dynamic_binary_buffer_t>{ origin_as_binary_array, next_hop_as_binary_array,
communities_list_as_binary_array };
}
}
std::string print() const {
std::stringstream buf;
buf << "Prefix: " << convert_ip_as_uint_to_string(prefix.subnet_address) << "/" << prefix.cidr_prefix_length << " "
<< "Origin: " << get_origin_name_by_value(origin) << " "
<< "Next hop: " << convert_ip_as_uint_to_string(next_hop) + "/32";
// TODO: print pretty communities!!!
return buf.str();
}
// Add multiple communities in single step
bool add_multiple_communities(std::vector<bgp_community_attribute_element_t> bgp_communities) {
for (auto bgp_community : bgp_communities) {
community_list.push_back(bgp_community);
}
return true;
}
bool add_community(bgp_community_attribute_element_t bgp_community) {
community_list.push_back(bgp_community);
return true;
}
private:
uint32_t next_hop = 0;
subnet_cidr_mask_t prefix;
BGP_ORIGIN_TYPES origin = BGP_ORIGIN_INCOMPLETE;
bool is_withdraw = false;
std::vector<bgp_community_attribute_element_t> community_list;
};
class IPv6UnicastAnnounce {
public:
void set_withdraw(bool withdraw) {
this->is_withdraw = withdraw;
}
bool get_withdraw() {
return this->is_withdraw;
}
void set_next_hop(subnet_ipv6_cidr_mask_t next_hop) {
this->next_hop = next_hop;
}
void set_prefix(subnet_ipv6_cidr_mask_t prefix) {
this->prefix = prefix;
}
void set_origin(BGP_ORIGIN_TYPES origin) {
this->origin = origin;
}
subnet_ipv6_cidr_mask_t get_next_hop() const {
return next_hop;
}
subnet_ipv6_cidr_mask_t get_prefix() const {
return this->prefix;
}
BGP_ORIGIN_TYPES get_origin() const {
return this->origin;
}
std::string print() const {
std::stringstream buf;
buf << "Prefix: " << convert_ipv6_subnet_to_string(prefix) << " "
<< "Origin: " << get_origin_name_by_value(origin) << " "
<< "Next hop: " << convert_ipv6_subnet_to_string(next_hop);
return buf.str();
}
// Returns prefix in text form
std::string get_prefix_in_cidr_form() const {
return convert_ipv6_subnet_to_string(prefix);
}
// Add multiple communities in single step
bool add_multiple_communities(std::vector<bgp_community_attribute_element_t> bgp_communities) {
for (auto bgp_community : bgp_communities) {
community_list.push_back(bgp_community);
}
return true;
}
bool add_community(bgp_community_attribute_element_t bgp_community) {
community_list.push_back(bgp_community);
return true;
}
std::vector<bgp_community_attribute_element_t> get_communities() const {
return community_list;
}
private:
subnet_ipv6_cidr_mask_t next_hop{};
subnet_ipv6_cidr_mask_t prefix{};
BGP_ORIGIN_TYPES origin = BGP_ORIGIN_INCOMPLETE;
bool is_withdraw = false;
std::vector<bgp_community_attribute_element_t> community_list;
};
static_assert(sizeof(bgp_attribute_flags) == 1, "broken size for bgp_attribute_flags");
static_assert(sizeof(bgp_attribute_origin) == 4, "Bad size for bgp_attribute_origin");
static_assert(sizeof(bgp_attribute_next_hop_ipv4) == 7, "Bad size for bgp_attribute_next_hop_ipv4");
bool is_bgp_community_valid(std::string community_as_string);
bool decode_ipv6_announce_from_binary_encoded_atributes(std::vector<dynamic_binary_buffer_t> binary_attributes,
IPv6UnicastAnnounce& ipv6_announce);
bool decode_mp_reach_ipv6(int len, uint8_t* value, bgp_attibute_common_header_t bgp_attibute_common_header, IPv6UnicastAnnounce& ipv6_announce);
bool encode_ipv6_announces_into_bgp_mp_reach_attribute_internal(const IPv6UnicastAnnounce& ipv6_announce,
dynamic_binary_buffer_t& bgp_mp_reach_ipv6_attribute);
bool encode_ipv6_announces_into_bgp_mp_reach_attribute(const IPv6UnicastAnnounce& ipv6_announce,
dynamic_binary_buffer_t& bgp_mp_reach_ipv6_attribute);
bool encode_ipv6_prefix(const subnet_ipv6_cidr_mask_t& prefix, dynamic_binary_buffer_t& dynamic_buffer);