Skip to content

Commit

Permalink
issue-1444: move netlink helpers to core/libs/netlink
Browse files Browse the repository at this point in the history
  • Loading branch information
antonmyagkov committed Nov 11, 2024
1 parent ba8f76a commit 8b360b4
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 179 deletions.
199 changes: 20 additions & 179 deletions cloud/blockstore/libs/nbd/netlink_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "utils.h"

#include <cloud/storage/core/libs/diagnostics/logging.h>
#include <cloud/storage/core/libs/netlink/netlink_socket.h>

#include <linux/nbd-netlink.h>

Expand All @@ -19,177 +20,17 @@ namespace {

using namespace NThreading;

using TResponseHandler = std::function<int(genlmsghdr*)>;

////////////////////////////////////////////////////////////////////////////////

constexpr TStringBuf NBD_DEVICE_SUFFIX = "/dev/nbd";

////////////////////////////////////////////////////////////////////////////////

class TNetlinkSocket
{
private:
nl_sock* Socket;
int Family;

public:
TNetlinkSocket()
{
Socket = nl_socket_alloc();

if (Socket == nullptr) {
throw TServiceError(E_FAIL) << "unable to allocate netlink socket";
}

if (int err = genl_connect(Socket)) {
nl_socket_free(Socket);
throw TServiceError(E_FAIL)
<< "unable to connect to generic netlink socket: "
<< nl_geterror(err);
}

Family = genl_ctrl_resolve(Socket, "nbd");

if (Family < 0) {
nl_socket_free(Socket);
throw TServiceError(E_FAIL)
<< "unable to resolve nbd netlink family: "
<< nl_geterror(Family);
}
}

~TNetlinkSocket()
{
nl_socket_free(Socket);
}

int GetFamily() const
{
return Family;
}

template <typename F>
void SetCallback(nl_cb_type type, F func)
{
auto arg = std::make_unique<TResponseHandler>(std::move(func));

if (int err = nl_socket_modify_cb(
Socket,
type,
NL_CB_CUSTOM,
TNetlinkSocket::ResponseHandler,
arg.get()))
{
throw TServiceError(E_FAIL)
<< "unable to set socket callback: " << nl_geterror(err);
}
arg.release();
}

static int ResponseHandler(nl_msg* msg, void* arg)
{
auto func = std::unique_ptr<TResponseHandler>(
static_cast<TResponseHandler*>(arg));

return (*func)(static_cast<genlmsghdr*>(nlmsg_data(nlmsg_hdr(msg))));
}

void Send(nl_msg* message)
{
if (int err = nl_send_auto(Socket, message); err < 0) {
throw TServiceError(E_FAIL)
<< "send error: " << nl_geterror(err);
}
if (int err = nl_wait_for_ack(Socket)) {
// this is either recv error, or an actual error message received
// from the kernel
throw TServiceError(E_FAIL)
<< "recv error: " << nl_geterror(err);
}
}
};

////////////////////////////////////////////////////////////////////////////////

class TNestedAttribute
{
private:
nl_msg* Message;
nlattr* Attribute;

public:
TNestedAttribute(nl_msg* message, int attribute)
: Message(message)
{
Attribute = nla_nest_start(message, attribute);
if (!Attribute) {
throw TServiceError(E_FAIL) << "unable to nest attribute";
}
}

~TNestedAttribute()
{
nla_nest_end(Message, Attribute);
}
};

////////////////////////////////////////////////////////////////////////////////

class TNetlinkMessage
{
private:
nl_msg* Message;

public:
TNetlinkMessage(int family, int command)
{
Message = nlmsg_alloc();
if (Message == nullptr) {
throw TServiceError(E_FAIL) << "unable to allocate message";
}
genlmsg_put(
Message,
NL_AUTO_PORT,
NL_AUTO_SEQ,
family,
0, // hdrlen
0, // flags
command,
0); // version
}

~TNetlinkMessage()
{
nlmsg_free(Message);
}

operator nl_msg*() const
{
return Message;
}

template <typename T>
void Put(int attribute, T data)
{
if (int err = nla_put(Message, attribute, sizeof(T), &data)) {
throw TServiceError(E_FAIL) << "unable to put attribute "
<< attribute << ": " << nl_geterror(err);
}
}

TNestedAttribute Nest(int attribute)
{
return TNestedAttribute(Message, attribute);
}
};

////////////////////////////////////////////////////////////////////////////////

class TNetlinkDevice final
: public IDevice
, public std::enable_shared_from_this<TNetlinkDevice>
{
using TNetlinkMessage = NCloud::NNetlink::TNetlinkMessage;
private:
const ILoggingServicePtr Logging;
const TNetworkAddress ConnectAddress;
Expand Down Expand Up @@ -231,7 +72,7 @@ class TNetlinkDevice final
void Disconnect();
void DoConnect(bool connected);

int StatusHandler(genlmsghdr* header);
int StatusHandler(nl_msg* nlmsg);
};

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -309,8 +150,8 @@ TFuture<NProto::TError> TNetlinkDevice::Stop(bool deleteDevice)
TFuture<NProto::TError> TNetlinkDevice::Resize(ui64 deviceSizeInBytes)
{
try {
TNetlinkSocket socket;
TNetlinkMessage message(socket.GetFamily(), NBD_CMD_RECONFIGURE);
auto socket = NCloud::NNetlink::CreateNetlinkSocket("nbd");
TNetlinkMessage message(socket->GetFamily(), NBD_CMD_RECONFIGURE);

message.Put(NBD_ATTR_INDEX, DeviceIndex);
message.Put(NBD_ATTR_SIZE_BYTES, deviceSizeInBytes);
Expand All @@ -321,7 +162,7 @@ TFuture<NProto::TError> TNetlinkDevice::Resize(ui64 deviceSizeInBytes)
message.Put(NBD_SOCK_FD, static_cast<ui32>(Socket));
}

socket.Send(message);
socket->Send(message);

} catch (const TServiceError& e) {
return MakeFuture(MakeError(
Expand Down Expand Up @@ -373,26 +214,25 @@ void TNetlinkDevice::DisconnectSocket()
// or reconfigure (if Reconfigure == true) specified device
void TNetlinkDevice::Connect()
{
TNetlinkSocket socket;
socket.SetCallback(
auto socket = NCloud::NNetlink::CreateNetlinkSocket("nbd");
socket->SetCallback(
NL_CB_VALID,
[device = shared_from_this()] (auto* header) {
return device->StatusHandler(header);
});
[device = shared_from_this()](nl_msg* nlmsg)
{ return device->StatusHandler(nlmsg); });

TNetlinkMessage message(socket.GetFamily(), NBD_CMD_STATUS);
TNetlinkMessage message(socket->GetFamily(), NBD_CMD_RECONFIGURE);
message.Put(NBD_ATTR_INDEX, DeviceIndex);
socket.Send(message);
socket->Send(message);
}

void TNetlinkDevice::Disconnect()
{
STORAGE_INFO("disconnect " << DeviceName);

TNetlinkSocket socket;
TNetlinkMessage message(socket.GetFamily(), NBD_CMD_DISCONNECT);
auto socket = NCloud::NNetlink::CreateNetlinkSocket("nbd");
TNetlinkMessage message(socket->GetFamily(), NBD_CMD_DISCONNECT);
message.Put(NBD_ATTR_INDEX, DeviceIndex);
socket.Send(message);
socket->Send(message);
StopResult.SetValue(MakeError(S_OK));
}

Expand All @@ -410,8 +250,8 @@ void TNetlinkDevice::DoConnect(bool connected)
STORAGE_INFO("connect " << DeviceName);
}

TNetlinkSocket socket;
TNetlinkMessage message(socket.GetFamily(), command);
auto socket = NCloud::NNetlink::CreateNetlinkSocket("nbd");
TNetlinkMessage message(socket->GetFamily(), command);

const auto& info = Handler->GetExportInfo();
message.Put(NBD_ATTR_INDEX, DeviceIndex);
Expand All @@ -437,7 +277,7 @@ void TNetlinkDevice::DoConnect(bool connected)
message.Put(NBD_SOCK_FD, static_cast<ui32>(Socket));
}

socket.Send(message);
socket->Send(message);
StartResult.SetValue(MakeError(S_OK));

} catch (const TServiceError& e) {
Expand All @@ -448,8 +288,9 @@ void TNetlinkDevice::DoConnect(bool connected)
}
}

int TNetlinkDevice::StatusHandler(genlmsghdr* header)
int TNetlinkDevice::StatusHandler(nl_msg* nlmsg)
{
auto header = static_cast<genlmsghdr*>(nlmsg_data(nlmsg_hdr(nlmsg)));
nlattr* attr[NBD_ATTR_MAX + 1] = {};
nlattr* deviceItem[NBD_DEVICE_ITEM_MAX + 1] = {};
nlattr* device[NBD_DEVICE_ATTR_MAX + 1] = {};
Expand Down
1 change: 1 addition & 0 deletions cloud/blockstore/libs/nbd/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ PEERDIR(
cloud/blockstore/libs/diagnostics
cloud/blockstore/libs/service
cloud/storage/core/libs/coroutine
cloud/storage/core/libs/netlink
contrib/libs/linux-headers
contrib/restricted/libnl/lib/nl-3
contrib/restricted/libnl/lib/nl-genl-3
Expand Down
83 changes: 83 additions & 0 deletions cloud/storage/core/libs/netlink/netlink_message.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <cloud/storage/core/libs/common/error.h>

#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>

namespace NCloud::NNetlink {

////////////////////////////////////////////////////////////////////////////////

class TNestedAttribute
{
private:
nl_msg* Message;
nlattr* Attribute;

public:
TNestedAttribute(nl_msg* message, int attribute)
: Message(message)
{
Attribute = nla_nest_start(message, attribute);
if (!Attribute) {
throw TServiceError(E_FAIL) << "unable to nest attribute";
}
}

~TNestedAttribute()
{
nla_nest_end(Message, Attribute);
}
};

////////////////////////////////////////////////////////////////////////////////

class TNetlinkMessage
{
private:
nl_msg* Message;

public:
TNetlinkMessage(int family, int command, int flags = 0, int version = 0)
{
Message = nlmsg_alloc();
if (Message == nullptr) {
throw TServiceError(E_FAIL) << "unable to allocate message";
}
genlmsg_put(
Message,
NL_AUTO_PORT,
NL_AUTO_SEQ,
family,
0, // hdrlen
flags,
command,
version);
}

~TNetlinkMessage()
{
nlmsg_free(Message);
}

operator nl_msg*() const
{
return Message;
}

template <typename T>
void Put(int attribute, T data)
{
if (int err = nla_put(Message, attribute, sizeof(T), &data)) {
throw TServiceError(E_FAIL)
<< "unable to put attribute " << attribute << ": "
<< nl_geterror(err);
}
}

TNestedAttribute Nest(int attribute)
{
return TNestedAttribute(Message, attribute);
}
};
} // namespace NCloud::NNetlink

Loading

0 comments on commit 8b360b4

Please sign in to comment.