Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VRF implementation for dataplane #242

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

VRF implementation for dataplane #242

wants to merge 9 commits into from

Conversation

stal76
Copy link
Collaborator

@stal76 stal76 commented Sep 8, 2024

Previously, it was possible to add routing rules for different VRFs, but it was not possible to set VRFs for packets and only 'default' was used for them.
In the current change, the ability to set VRF for the entire logical port has been added. Later it will be possible to set VRF by acl rules.
To solve the LPM problem, an algorithm like to 'a path-compressed trie' is used.

common/define.h Outdated
@@ -103,6 +103,7 @@ extern LogPriority logPriority;
#define YANET_RIB_PRIORITY_DEFAULT ((uint32_t)10000)
#define YANET_RIB_PRIORITY_ROUTE_TUNNEL_FALLBACK ((uint32_t)11000)
#define YANET_RIB_PRIORITY_ROUTE_REPEAT ((uint32_t)12000)
#define YANET_RIB_VRF_DEFAULT "default"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr auto YANET_RIB_VRF_DEFAULT = "default";

common/define.h Outdated
Comment on lines 105 to 121
#define YANET_RIB_PRIORITY_ROUTE_REPEAT ((uint32_t)12000)
#define YANET_RIB_VRF_DEFAULT "default"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should replace that in 97th line in librib/libyabird.cpp too:

		std::get<1>(request) = {peer_address,
		                        {"default", ///< @todo: vrf
		                         YANET_RIB_PRIORITY_DEFAULT}};

Comment on lines 172 to 180
bool is_ignored_table(const std::string& table_name) const
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const method returning value should be marked [[nodiscard]]

@stal76 stal76 force-pushed the vrf branch 2 times, most recently from 79639b4 to c0399dd Compare December 22, 2024 16:47
Comment on lines 110 to 114
config_t() :
vrf(YANET_RIB_VRF_DEFAULT),
tunnel_enabled(false)
{
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is redundant, since that class' fields are already defined with default values:

	std::string vrf{"default"};
	bool tunnel_enabled{};

We need to change that "default" to YANET_RIB_VRF_DEFAULT though, and that's it

@@ -118,6 +118,8 @@ extern LogPriority logPriority;
#define YANET_RIB_PRIORITY_DEFAULT ((uint32_t)10000)
#define YANET_RIB_PRIORITY_ROUTE_TUNNEL_FALLBACK ((uint32_t)11000)
#define YANET_RIB_PRIORITY_ROUTE_REPEAT ((uint32_t)12000)
#define YANET_RIB_VRF_DEFAULT "default"
#define YANET_RIB_VRF_MAX_NUMBER 64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define YANET_RIB_VRF_MAX_NUMBER 64
inline constexpr auto YANET_RIB_VRF_MAX_NUMBER = 64;

or replace auto with int, whichever you prefer

Comment on lines 1069 to 1079
tVrfId VrfIdStorage::GetOrCreateOrException(const std::string& vrfName, const std::string& message)
{
std::optional<tVrfId> vrfId = GetOrCreate(vrfName);
if (!vrfId.has_value())
{
throw error_result_t(eResult::invalidVrfId, message + vrfName);
}
return *vrfId;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass only string literals as message to this function. Since we're taking const ref to a string, we will first require to construct that string from a string literal, and then we will construct another string by concatenating both when throwing an exception.

The better approach is to take message as a string_view, which is specifically designed as non-owning reference to a string.

Next, when calling this function, we expect user to add additional symbols ((": ") to message to make the error message pretty:

logicalPort.vrfId = controlplane_ptr->getVrfIdsStorage().GetOrCreateOrException(logicalPort.vrf, "Can't get id logicalPort.vrf: ");

The cleaner approach is to use std::ostringstream to construct the error message (which is far more efficient that concatenation with operator+).
So I suggest:

Suggested change
tVrfId VrfIdStorage::GetOrCreateOrException(const std::string& vrfName, const std::string& message)
{
std::optional<tVrfId> vrfId = GetOrCreate(vrfName);
if (!vrfId.has_value())
{
throw error_result_t(eResult::invalidVrfId, message + vrfName);
}
return *vrfId;
}
tVrfId VrfIdStorage::GetOrCreateOrException(const std::string& vrfName, std::string_view message)
{
std::optional<tVrfId> vrfId = GetOrCreate(vrfName);
if (!vrfId.has_value())
{
std::ostringstream oss;
oss << message <<": " << vrfName;
throw error_result_t(eResult::invalidVrfId, oss.str());
}
return *vrfId;
}

Comment on lines +1057 to +1059
if (vrf_ids.size() + 1 >= YANET_RIB_VRF_MAX_NUMBER)
{
vrf_ids[vrfName] = std::nullopt;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to store std::nullopt in a map if we exceed the number of different id's?
I think we can just don't put it there.
This way we can use plain tVrfId instead of std::optional<tVrfId> in the map. If the ID can’t be assigned, we simply don’t put an entry in the map at all

Comment on lines 1023 to 1039
std::optional<tVrfId> VrfIdStorage::Get(const std::string& vrfName)
{
if (vrfName.empty() || vrfName == YANET_RIB_VRF_DEFAULT)
{
return 0;
}

std::shared_lock lock(mutex);

auto iter = vrf_ids.find(vrfName);
if (iter != vrf_ids.end())
{
return iter->second;
}

return std::nullopt;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO Get method should be marked const. It makes sense that in order to get something, we don't actually change the state of the class.
Now, we do capture a mutex here, so we need to mark it mutable (mutable mutexes is a good idea):

private:
    mutable std::shared_mutex mutex;

Comment on lines 706 to 726
updaters_[vrf] = std::make_unique<UpdaterType>(name.c_str(), memory_manager_, socket_id_);
if (updaters_[vrf] == nullptr)
{
return eResult::errorAllocatingMemory;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work. std::make_unique never returns nullptr - either it succeeds, or it throws std::bad_alloc.

If we want to return an appropriate eResult, we need to use one of the new operator overloads that takes std::nothrow object:

auto new_updater = std::unique_ptr<UpdaterType>(
    new(std::nothrow) UpdaterType(uniqueName.c_str(), memory_manager_, socket_id_)
);

Or you can define helper function in the util namespace:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique_nothrow(Args&&... args) {
    return std::unique_ptr<T>(new(std::nothrow) T(std::forward<Args>(args)...));
}

Comment on lines +738 to +760
for (size_t index = 0; index < YANET_RIB_VRF_MAX_NUMBER; index++)
{
if (updaters_[index])
{
updaters_[index]->limits(limits);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (size_t index = 0; index < YANET_RIB_VRF_MAX_NUMBER; index++)
{
if (updaters_[index])
{
updaters_[index]->limits(limits);
}
}
for (const auto& updater : updaters_)
{
if (updater)
{
updater->limits(limits);
}
}

Comment on lines +749 to +771
for (size_t index = 0; index < YANET_RIB_VRF_MAX_NUMBER; index++)
{
if (updaters_[index])
{
updaters_[index]->report(report);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (size_t index = 0; index < YANET_RIB_VRF_MAX_NUMBER; index++)
{
if (updaters_[index])
{
updaters_[index]->report(report);
}
}
for (const auto& updater : updaters_)
{
if (updater)
{
updater->report(report);
}
}

Comment on lines +760 to +783
for (size_t index = 0; index < YANET_RIB_VRF_MAX_NUMBER; index++)
{
if (updaters_[index])
{
updaters_[index]->clear();
updaters_[index] = nullptr;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (size_t index = 0; index < YANET_RIB_VRF_MAX_NUMBER; index++)
{
if (updaters_[index])
{
updaters_[index]->clear();
updaters_[index] = nullptr;
}
}
for (const auto& updater : updaters_)
{
if (updater)
{
updater->clear();
updater.reset();
}
}

}

private:
std::array<InnerLpmType*, YANET_RIB_VRF_MAX_NUMBER> lpms_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::array<InnerLpmType*, YANET_RIB_VRF_MAX_NUMBER> is a common type, as I can see. I suggest to add an alias for it.
We will be able to utilize it GetLpms method of updater_vrf_lpm class too

Comment on lines +23 to +34
void Lookup(const Address* ipAddresses, const tVrfId* vrfIds, uint32_t* valueIds, const unsigned int& count) const
{
for (unsigned int index = 0; index < count; index++)
{
valueIds[index] = lpmValueIdInvalid;
tVrfId vrf = vrfIds[index];
if ((vrf < YANET_RIB_VRF_MAX_NUMBER) && (lpms_[vrf] != nullptr))
{
lpms_[vrf]->lookup(ipAddresses + index, valueIds + index, 1);
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct approach would be to use iterators to std::array instead of pointer arithmetics, but we're so elbow deep in c-style arrays and pointer arithmetics in worker.h and in underlying lpm that such change would seem unnecessary.
Let's hope that in YANET2 we will be able to make things right (:

(that's not an issue, ignore it)

@@ -51,6 +51,11 @@ class tStack
mbufsCount = 0;
}

inline void copy_from(tStack& other)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inline void copy_from(tStack& other)
void copy_from(tStack& other)

inline is redundant.

A function defined entirely inside a class/struct/union definition, whether it's a member function or a non-member friend function, is implicitly an inline function

link

- Fixed filling in lpm stats.
- Checking that the route table is ignored has been moved.
- VRF storage has been added in some structures, and vrf is also passed in calls to some functions.
- In controlplane.conf one can set the vrf in logicalPort. In the metadata of the network packet
  the vrfId is filled in according to the value from logicalPort.
- A VrfIdStorage object has been added to the cControlPlane class, which issues an id named vrf.
In controlplane.conf, one can set the vrf for lan and wan in the nat64statefull
and nat46clat sections, these values will be set in the packet metadata.
@ol-imorozko
Copy link
Collaborator

ol-imorozko commented Feb 18, 2025

I see that you've pushed Fix based on review comments commit.
Could you, please, also mark related conversations as "resolved"? Thanks :)

@ol-imorozko
Copy link
Collaborator

ol-imorozko commented Feb 19, 2025

I see that you've fixed the most important one among my review requests -- about std::make_unique never returning nullptr - either it succeeds, or it throws std::bad_alloc.
You can fix other things, most notably, https://github.com/yanet-platform/yanet/pull/242/files#r1934297957 about passing 8-bit and 32-bit value instead of two 64-bit pointers, but I'm going to approve it now anyway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants