-
Notifications
You must be signed in to change notification settings - Fork 32
Feature/add support for nl80211 standard commands to BWL #900
Conversation
18e14ae
to
53dbca9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything is documented ✔️
Dynamic allocation uses smart pointers ✔️
It looks excellent!
I have a few comments you might want to read though so I'm approving, but also adding the "don't merge" tag to prevent Mergify from merging it automatically in cases someone else approves it in the meantime.
Overall I'm also not a big fan of having everything in a single commit, but I don't think it's a blocker.
std::unique_ptr<nl80211_client> nl80211_client_factory::create_instance() | ||
{ | ||
return std::make_unique<nl80211_client_dummy>(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This factory doesn't look very useful... I guess it's there for consistency with other implementations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said, this is the implementation of the factory in the Dummy flavor of the BWL library. The factory is defined to return an instance of a class that implements the interface nl80211_client
. This particular implementation returns an instance of nl80211_client_dummy
. Methods of this class do nothing but return dummy data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nitpick) the rest of the bwl uses a factory function instead of a factory class, e.g. ap_wlan_hal_create()
. It would be more consistent to use a factory function nl80211_client_create()
here as well.
@rmelotte asked me to review this change as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good job documenting everything.
I think this should have been split up to more commits, but I don't think its worth it at this point.
I don't really like the fact we need to treat BWL DUMMY mode differently in the CMAKE. The fact that we implement this in shared
leads to code duplication (that you probably mean to remove in future PR), but I think it should be handled as part of this PR.
The way I would do it, is by introducing another level of inheritance to the DWPAL implementation like @vitalybu suggested when we discussed this - have BWL DWPAL derive from BWL nl80211, and override everything except for the nl80211 standard commands.
The BWL Dummy should just implement stubs like you did in this PR.
I mark this as needs changes so @arnout and @vitalybu can comment on the direction in which this is going.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good, but I do have some comments, so not approving just yet
Fixed
Fixed
Fixed |
I evaluated @vitalybu's suggestion but decided to go with this solution because it's much simpler. Also, if BWL DWPAL derives from BWL nl80211, then it will take longer to compile and the binary will be bigger. BWL DWPAL, BWL nl80211 and whatever other part of the application can all be users of the Another goal of this design is that it can be easily extended to send NETLINK_ROUTE commands (probably with a |
Define a structure to hold collected link metrics. This structure will later be used to fill the TLVs of the Link Metric response message. Use uint32_t instead of uint16_t for max throughput capacity and bit rate fields to allow for bigger values. Spec document uses uint16_t so a saturation to the maximum value is required when copying values to the TLV. Link metrics have to be collected differently for wired and wireless links. Define an interface class for a link metrics collector. Derived implementation classes will override pure virtual methods. Create an implementation class for Ethernet links. This implementation reads network interface stats using a Netlink socket and the NETLINK_ROUTE protocol. TODO in future tasks: - Implement link metrics collector for WiFi links in ([TASK] Link metric collection for WiFi links #792) - Move all link metrics related classes to BPL ([TASK] Move link metric related classes to BPL #910) - Implement Ethernet link metrics collector using and extending Netlink classes defined in "Feature/add support for nl80211 standard commands to BWL #900" Signed-off-by: Mario Maz <[email protected]>
Note that this looks like implementation inheritance, and 25 years ago I was thought that this is evil.
Please include this reasoning (more or less copy&paste of the text above) in one of the commit messages. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of nitpicks but strong LGTM!
common/beerocks/bwl/shared/README.md
Outdated
@@ -0,0 +1,6 @@ | |||
# Shared files directory | |||
|
|||
The `shared` directory contains classes shared among all the implementations that use actual WiFi radios (i.e.: the BWPAL and NL80211 flavors but not the Dummy one). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth adding documentation to the README.md for the nl80211 support which is added in this PR.
It might be useful to read through the README.md which will serve as the DR for the implementation before browsing the code.
Anyway, this is one hell of a nitpick, so ignore it if you like :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent documentation in this PR. I wish all the code was like that.
As often, I have a truckload of comments, but nothing critical. Except for one thing: the negative error
number. It would have been great if there were a unit test that would validate such issues :-)
Given that there is nothing critical, I'm approving, so please do the following:
- apply the fixups that you agree with and are easy to do;
- for bigger changes that you think really should be done, make an issue;
- for the hints of the type "code could be refactored like this", just leave them as unresolved - we can refer back to this PR when development on the netlink stuff continues;
- squash and rebase everything, and mergify!
std::unique_ptr<nl80211_client> nl80211_client_factory::create_instance() | ||
{ | ||
return std::make_unique<nl80211_client_dummy>(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nitpick) the rest of the bwl uses a factory function instead of a factory class, e.g. ap_wlan_hal_create()
. It would be more consistent to use a factory function nl80211_client_create()
here as well.
* This interface lists the methods to read and write wireless hardware status and configuration | ||
* through a NL80211 socket. | ||
* | ||
* Programming to an interface allows dependent classes to remain unaware of the different | ||
* implementations of the interface as well as facilitates the creation of mock implementations for | ||
* unit testing. | ||
* | ||
* This is a C++ interface: an abstract class that is designed to be specifically used as a base | ||
* class and which derived classes (implementations) will override each pure virtual function. | ||
* | ||
* Known implementations: nl80211_client_impl (uses NL80211 socket), nl80211_client_dummy (fake | ||
* implementation that returns dummy data). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good documentation, thank you!
bool netlink_socket::connect() | ||
{ | ||
// Connect the socket | ||
if (nl_connect(m_nl_socket.get(), m_protocol) != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(For my understanding, not a change request:) Why is the explicit get() needed here? Is it because nl_connect is an extern C that there's no automatic conversion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I didn't know it was even possible to perform such implicit conversion from smart pointer to raw pointer type. But even if it was possible, in this case I'd rather use the explicit access.
If I remove the call to get()
, then I get this error when compiling for the RAX40:
../common/beerocks/bwl/shared/netlink_socket.cpp: In member function 'virtual bool bwl::netlink_socket::connect()':
../common/beerocks/bwl/shared/netlink_socket.cpp:29:43: error: cannot convert 'std::unique_ptr<nl_sock, void (*)(nl_sock*)>' to 'nl_sock*' for argument '1' to 'int nl_connect(nl_sock*, int)'
if (nl_connect(m_nl_socket, m_protocol) != 0) {
^
I found these quite interesting articles about the topic:
#include <easylogging++.h> | ||
|
||
/** | ||
* Receive and transmit socket buffer size in bytes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be useful to mention why this is changed from the default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I don't know. I've been investigating and found that the call to nl_socket_set_buffer_size
ends up calling setsockopt(SO_SNDBUF)
and setsockopt(SO_RCVBUF)
. Some people report that they were losing events with default values and had to increase the receive buffer size to fix it. Some tutorials I've found also change default sizes but don't explain why.
I borrowed this code from base_wlan_hal_nl80211::send_nl80211_msg
and thought that who initially wrote that code had considered it was necessary to call nl_socket_set_buffer_size
so I kept it. I didn't question myself why it was actually needed nor if values used were the most appropriate.
While investigating, I've realized that the call to nl_socket_set_buffer_size
requires the socket to be connected or otherwise it fails with NLE_BAD_SOCK. Thus I've removed the call from the constructor and put it after the socket connection has been made.
base_wlan_hal_nl80211
is also calling nl_socket_set_buffer_size
but before connecting the socket so I guess it must be failing too (no error code is being checked after the call) and buffer size is not being changed. Thus it looks like, for the commands being executed up to now in BWL, it's not necessary to increase buffer size.
const char *family_name = "nl80211"; | ||
|
||
m_family_id = genl_ctrl_resolve(m_nl_socket.get(), family_name); | ||
if (0 > m_family_id) { | ||
LOG(ERROR) << "'" << family_name << "' family not found!"; | ||
result = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in my proposal, this bit would go into post_connect()
and the error handling (i.e. calling close()) would go into the non-virtual connect()
function.
/** | ||
* @brief C++ wrapper class around the generic Netlink protocol library (libnl-genl). | ||
* | ||
* This is a very simple C++ wrapper class around the generic Netlink protocol library. Its aim is | ||
* to facilitate the use of the library from C++ applications. This class extends the | ||
* netlink_socket base class for the generic Netlink protocol (NETLINK_GENERIC). | ||
* | ||
* Known derived classes: nl80211_socket. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With no functionality other than setting the protocol, I'm not sure if it's worthwhile to introduce this additional class. IMO it makes the code harder to understand with no benefit at all.
This class is only useful if later on
- there is a second, non-nl80211 netlink socket (and there probably should be one, for nlrt, but that one will not be in bwl so code needs to be refactored anyway);
- and there is some common code between them that should be in this class and not in netlink_socket itself.
But in that case, we can just introduce the class at that time.
03a8a7e
to
7f93d9f
Compare
We need support for nl80211 standard commands for things such as obtaining link metrics or device capabilities. In "Send standard NL80211 commands using DWPAL #782" we firstly evaluated the convenience of adding such support in DWPAL and then in BWL. As a result of such evaluation we concluded that DWPAL was not the most suitable place to add such support. Reason is that NL80211 standard commands are not exclusive of devices where DWPAL is used and support is also required for other flavors of the BWL library. Then we decided to add support for standard commands into the BWL library, but shared to all flavors, not only for the DWPAL version. A proposal was made to include support for NL80211 standard commands in the NL80211 classes, then derive BWL DWPAL from BWL NL80211 and override everything except the standard commands. This proposal was not finally implemented to "favor composition instead of inheritance". Instead of inheritance, a nl8011_client interface and a factory for objects of classes implementing such interface have been defined. BWL DWPAL, BWL nl80211 and whatever other part of the application can all be users of the nl80211_client factory, create an instance of a class implementing the nl80211_client interface and use it to send NL80211 standard commands. Although I prefer to inject the dependencies to facilitate unit-testing (if we ever had to), so most probably BWL DWPAL and BWL nl80211 classes that send NL80211 standard commands will have a nl80211_client parameter in their constructor instead of calling the factory. Another goal of this design is that it can be easily extended to send NETLINK_ROUTE commands (probably with a netlink_route_client). Maybe we should move all these classes from BWL to BPL because they are not intended for WiFi only. Created C++ wrappers around libnl and libnl-genl to facilitate sending NL80211 commands through a NL80211 netlink socket. Classes have been created in bwl/shared so NL80211 standard commands will be available to all flavours of the BWL library (except the Dummy one). Public include files have been created in bwl/include. Created a C++ interface with the list of commands available. Create two implementations, one to actually send those commands to the WiFi driver and a fake one for the Dummy implementation of the BWL library. Created a factory class in each BWL implementation to create instances of the NL80211 client. This factory will be used both from within the BWL library or from outside. Add NL80211_CMD_GET_STATION command implementation as an example of how standard commands are to be implemented. Once this design had been validated, the rest of standard commands will be added. Also we will do a code refactoring to use the new classes and eliminate duplicated code. Signed-off-by: Mario Maz <[email protected]>
7f93d9f
to
40afb37
Compare
Define a structure to hold collected link metrics. This structure will later be used to fill the TLVs of the Link Metric response message. Use uint32_t instead of uint16_t for max throughput capacity and bit rate fields to allow for bigger values. Spec document uses uint16_t so a saturation to the maximum value is required when copying values to the TLV. Link metrics have to be collected differently for wired and wireless links. Define an interface class for a link metrics collector. Derived implementation classes will override pure virtual methods. Create an implementation class for Ethernet links. This implementation reads network interface stats using a Netlink socket and the NETLINK_ROUTE protocol. TODO in future tasks: - Implement link metrics collector for WiFi links in ([TASK] Link metric collection for WiFi links #792) - Move all link metrics related classes to BPL ([TASK] Move link metric related classes to BPL #910) - Implement Ethernet link metrics collector using and extending Netlink classes defined in "Feature/add support for nl80211 standard commands to BWL #900" Signed-off-by: Mario Maz <[email protected]>
Define a structure to hold collected link metrics. This structure will later be used to fill the TLVs of the Link Metric response message. Use uint32_t instead of uint16_t for max throughput capacity and bit rate fields to allow for bigger values. Spec document uses uint16_t so a saturation to the maximum value is required when copying values to the TLV. Link metrics have to be collected differently for wired and wireless links. Define an interface class for a link metrics collector. Derived implementation classes will override pure virtual methods. Create an implementation class for Ethernet links. This implementation reads network interface stats using a Netlink socket and the NETLINK_ROUTE protocol. TODO in future tasks: - Implement link metrics collector for WiFi links in ([TASK] Link metric collection for WiFi links #792) - Move all link metrics related classes to BPL ([TASK] Move link metric related classes to BPL #910) - Implement Ethernet link metrics collector using and extending Netlink classes defined in "Feature/add support for nl80211 standard commands to BWL #900" Signed-off-by: Mario Maz <[email protected]>
Define a structure to hold collected link metrics. This structure will later be used to fill the TLVs of the Link Metric response message. Use uint32_t instead of uint16_t for max throughput capacity and bit rate fields to allow for bigger values. Spec document uses uint16_t so a saturation to the maximum value is required when copying values to the TLV. Link metrics have to be collected differently for wired and wireless links. Define an interface class for a link metrics collector. Derived implementation classes will override pure virtual methods. Create an implementation class for Ethernet links. This implementation reads network interface stats using a Netlink socket and the NETLINK_ROUTE protocol. TODO in future tasks: - Implement link metrics collector for WiFi links in ([TASK] Link metric collection for WiFi links #792) - Move all link metrics related classes to BPL ([TASK] Move link metric related classes to BPL #910) - Implement Ethernet link metrics collector using and extending Netlink classes defined in "Feature/add support for nl80211 standard commands to BWL #900" Signed-off-by: Mario Maz <[email protected]>
Define a structure to hold collected link metrics. This structure will later be used to fill the TLVs of the Link Metric response message. Use uint32_t instead of uint16_t for max throughput capacity and bit rate fields to allow for bigger values. Spec document uses uint16_t so a saturation to the maximum value is required when copying values to the TLV. Link metrics have to be collected differently for wired and wireless links. Define an interface class for a link metrics collector. Derived implementation classes will override pure virtual methods. Create an implementation class for Ethernet links. This implementation reads network interface stats using a Netlink socket and the NETLINK_ROUTE protocol. TODO in future tasks: - Implement link metrics collector for WiFi links in ([TASK] Link metric collection for WiFi links #792) - Move all link metrics related classes to BPL ([TASK] Move link metric related classes to BPL #910) - Implement Ethernet link metrics collector using and extending Netlink classes defined in "Feature/add support for nl80211 standard commands to BWL #900" Signed-off-by: Mario Maz <[email protected]>
Define a structure to hold collected link metrics. This structure will later be used to fill the TLVs of the Link Metric response message. Use uint32_t instead of uint16_t for max throughput capacity and bit rate fields to allow for bigger values. Spec document uses uint16_t so a saturation to the maximum value is required when copying values to the TLV. Link metrics have to be collected differently for wired and wireless links. Define an interface class for a link metrics collector. Derived implementation classes will override pure virtual methods. Create an implementation class for Ethernet links. This implementation reads network interface stats using a Netlink socket and the NETLINK_ROUTE protocol. TODO in future tasks: - Implement link metrics collector for WiFi links in ([TASK] Link metric collection for WiFi links #792) - Move all link metrics related classes to BPL ([TASK] Move link metric related classes to BPL #910) - Implement Ethernet link metrics collector using and extending Netlink classes defined in "Feature/add support for nl80211 standard commands to BWL #900" Signed-off-by: Mario Maz <[email protected]>
Create C++ wrappers around libnl and libnl-genl to facilitate sending
NL80211 commands through a NL80211 netlink socket.
Classes have been created in bwl/shared so NL80211 standard commands
will be available to all flavours of the BWL library except the Dummy one.
Add NL80211_CMD_GET_STATION command implementation as an example of
how standard commands are to be implemented.
Once this design had been validated, the rest of standard commands will
be added. Also we will do a code refactoring to use the new classes and
eliminate duplicated code.