Skip to content

Multihoming initial release #9387

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

Merged
merged 4 commits into from
Feb 21, 2019
Merged

Conversation

tymoteuszblochmobica
Copy link
Contributor

@tymoteuszblochmobica tymoteuszblochmobica commented Jan 15, 2019

Description

Added Multihoming feature to LWIP (ability to use more than one network interfaces) for increasing networking reliability.
This involves:

  • LWIP interface
  • LWIP IP routing
  • DNS storage
  • Sockets (bind to interface name possibility)
  • possibility to add non default network interface
  • cellular middleware modifications if cellular connection is used

Release notes

Network Interface and Network Stack API is extended with new members and new parameter to specify name of network interface
LWIP IP layer IP_route function is extended for selecting interface by its name.
NSAPI DNS now has storage for each interface's DNS servers.

New members :
NetworkInterface::set_as_default
NetworkInterface::get_interface name
NetworkStack::get_ip_address_if

LWIP::setsockopt has new option NSAPI_BIND_TO_DEVICE for binding socket to interface name.

New parameter "interface_name" addded to:
NetworkInterface::add_dns_server
NetworkInterface::gethostbyname

If selection based on interface name is not needed an empty tring can be passed.
Then original functionality is preserved.

Pull request type

[ ] Fix
[ ] Refactor
[ ] Target update
[] Functionality change
[ ] Docs update
[ ] Test update
[x ] Breaking change

Reviewers

@SeppoTakalo
@mikaleppanen
@kjbracey-arm

@tymoteuszblochmobica
Copy link
Contributor Author

@SeppoTakalo please review
@mikaleppanen please review
@kjbracey-arm please review

@cmonr
Copy link
Contributor

cmonr commented Jan 15, 2019

@tymoteuszblochmobica Please add a description to this PR, as it's completely unclear what this is.

Also, please take a look at the travis-ci/astyle job.

@ciarmcom ciarmcom requested review from a team January 15, 2019 18:00
@ciarmcom
Copy link
Member

@tymoteuszblochmobica, thank you for your changes.
@ARMmbed/mbed-os-test @ARMmbed/mbed-os-ipcore @ARMmbed/mbed-os-wan @ARMmbed/mbed-os-maintainers please review.

@0xc0170
Copy link
Contributor

0xc0170 commented Jan 16, 2019

Functionality change

This needs description here in the first commit but more importantly in the commit message. Introducing a new functionality change .
Multihoming initial release commit msg is good for simple fix that is easy to understand from the change.

Copy link
Contributor

@SeppoTakalo SeppoTakalo left a comment

Choose a reason for hiding this comment

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

Thanks @tymoteuszblochmobica
Left couple of questions and suggestions...

@@ -127,10 +127,14 @@ typedef enum nsapi_security {
NSAPI_SECURITY_UNKNOWN = 0xFF, /*!< unknown/unsupported security in scan results */
} nsapi_security_t;

/** Maximum size of network interface name
/** Size of 2 char network interface name from driver
*/
#define NSAPI_INTERFACE_NAME_SIZE 2
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this actually a networkinterface name prefix instead of full name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes this is a 2 char prefix from network driver.LWIP netif_add appends 8bit sequence number so range 0-255 takes 3 chars plus '\0' gives 6 char total string length

query->interface_name = NULL;
} else {
query->interface_name = interface_name;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This if-else is not needed.
Even if interface_name is NULL, you can just do query->interface_name = interface_name;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

{
_SocketAddress(nsapi_create_stack(stack), host, port);
_SocketAddress(nsapi_create_stack(stack), host, port, interface_name);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is deprecated function, can we just use NULL and not touch the API?

That way we would not need to touch the SocketAddress at all, just call gethostbyname() with NULL interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


call_in_callback_cb_t call_in_cb = get_call_in_callback();

return nsapi_dns_query_async(this, name, callback, call_in_cb, interface_name, version);
Copy link
Contributor

Choose a reason for hiding this comment

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

There is only one line difference to the function above, so can these two be combined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
}

return nsapi_dns_query(this, name, address, interface_name, version);
Copy link
Contributor

Choose a reason for hiding this comment

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

There is only one line difference to the function above, so can these two be combined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -340,6 +340,8 @@
#endif // MBED_CONF_LWIP_ETHERNET_ENABLED

#define LWIP_L3IP 0
//Maximum size of network interface name
#define NETWORK_INTERFACE_NAME_MAX_SIZE 4
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this is 4 and NSAPI_INTERFACE_NAME_MAX_SIZE is 6?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -136,6 +137,8 @@ enum lwip_internal_netif_client_data_index
#define NETIF_CHECKSUM_DISABLE_ALL 0x0000
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */

#define INTERFACE_NAME_SIZE 6
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use NSAPI_INTERFACE_NAME_MAX_SIZE here? is it the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

INTERFACE_NAME_SIZE renamed to INTERFACE_NAME_MAX_SIZE
Modified lwipopts.h
#define INTERFACE_NAME_MAX_SIZE NSAPI_INTERFACE_NAME_MAX_SIZE to avoid including nsapi_types.h in lwip dns and netif

@@ -49,6 +49,12 @@
extern "C" {
#endif

struct dns_server_interface {
char interface_name [NETWORK_INTERFACE_NAME_MAX_SIZE];
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the name is copied here instead of referring to const char *?

I'm assuming that interface names do not change in run time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a local storage for DNS servers for each interface name

if (numdns < DNS_MAX_SERVERS) {
return &dns_servers[numdns];
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the comparison wrong way?

Documentation says must be < DNS_MAX_SERVERS then you immediately return if that is true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mistake during whitespace correction. Fixed

@@ -47,6 +47,8 @@
*/
class ESP8266Interface : public NetworkStack, public WiFiInterface {
public:
using NetworkStack::get_ip_address;
using WiFiInterface::connect;
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we truly causing all external drivers to adapt to this new API?

We are not controlling all of those, so I would still consider whether this can be avoided.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -457,6 +474,30 @@ nsapi_error_t LWIP::add_l3ip_interface(L3IP &l3ip, bool default_if, OnboardNetwo
nsapi_error_t LWIP::remove_l3ip_interface(OnboardNetworkStack::Interface **interface_out)
{
#if LWIP_L3IP
if ((interface_out != NULL) && (*interface_out != NULL)) {

Interface *lwip = static_cast<Interface *>(*interface_out);

Choose a reason for hiding this comment

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

extra space

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

*
* @return netif name eg "en0"
*/
virtual char *get_interface_name(char *buf);

Choose a reason for hiding this comment

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

Should this have buffer length as a parameter, also is returning the buffer needed?

{
struct netif *netif;
s8_t i;

if(interface_name !=NULL) {

Choose a reason for hiding this comment

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

correct spaces

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

if (netif_default == netif) {
return true;
} else {
return false;

Choose a reason for hiding this comment

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

indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -1911,13 +1911,14 @@ tcp_eff_send_mss_impl(u16_t sendmss, const ip_addr_t *dest
#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING
, const ip_addr_t *src
#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */
, const char *interface_name

Choose a reason for hiding this comment

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

indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -49,6 +49,12 @@
extern "C" {
#endif

struct dns_server_interface {
char interface_name [INTERFACE_NAME_SIZE];
ip_addr_t dns_servers[DNS_MAX_SERVERS];

Choose a reason for hiding this comment

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

indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

#define LWIP_L3IP 0
#define LWIP_L3IP 0
//Maximum size of network interface name
#define INTERFACE_NAME_SIZE 6

Choose a reason for hiding this comment

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

spaces

Choose a reason for hiding this comment

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

should this be defined to be NSAPI_INTERFACE_NAME_MAX_SIZE

Copy link
Contributor Author

Choose a reason for hiding this comment

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

INTERFACE_NAME_SIZE renamed to INTERFACE_NAME_MAX_SIZE
Modified lwipopts.h
#define INTERFACE_NAME_MAX_SIZE NSAPI_INTERFACE_NAME_MAX_SIZE to avoid including nsapi_types.h in lwip dns and netif

* @param address Pointer to a SocketAddress to store the result.
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* @param interface_name Network interface name
* version is chosen by the stack (defaults to NSAPI_UNSPEC).

Choose a reason for hiding this comment

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

parameters are other way around in function prototype. Also move "version is chosen..."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

* @param address Pointer to a SocketAddress to store the result.
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* @param interface_name Network interface name
* version is chosen by the stack (defaults to NSAPI_UNSPEC).

Choose a reason for hiding this comment

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

Parameters are other way around, correct "version is..."

* @param callback Callback that is called for result.
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* @param interface_name Network interface_name
* version is chosen by the stack (defaults to NSAPI_UNSPEC).

Choose a reason for hiding this comment

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

same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -174,6 +174,9 @@ nsapi_error_t InternetSocket::setsockopt(int level, int optname, const void *opt
ret = NSAPI_ERROR_NO_SOCKET;
} else {
ret = _stack->setsockopt(_socket, level, optname, optval, optlen);
if (optname == NSAPI_BIND_TO_DEVICE && level == NSAPI_SOCKET) {
strncpy(_interface_name, static_cast<const char *>(optval), optlen);
}

Choose a reason for hiding this comment

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

We should get cloud client input on this as well. We make the interface binding (of client socket) with options, where as linux uses the bind operation. Now they need to adapt to both options

Copy link
Contributor

Choose a reason for hiding this comment

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

@JanneKiiskila Mind having someone chime in?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

This is now ready for integration, is there anything blocking ?

Copy link
Contributor

Choose a reason for hiding this comment

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

If it helps in any way, the mbed-client-testapp CI is passing fine with the most recent commit in this PR

@AriParkkila
Copy link

The current DNS implementation my return IPv4 address over IPv6 network interface, if version is not explicitly given for gethostbyname. This PR could also improve DNS query to prefer to the active IP version?

@SeppoTakalo
Copy link
Contributor

@yogpan01 Who is the correct person from Client team to review this work?

Basically we need opinion what do you think about Socket to use setsockopt() to bind it into one particular interface. This differs from Linux where bind() can already do that.

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 20, 2019

Too fast? I'll cancel the job (another force push)

@mbed-ci
Copy link

mbed-ci commented Feb 20, 2019

Test run: FAILED

Summary: 1 of 1 test jobs failed
Build number : 8
Build artifacts

Failed test jobs:

  • jenkins-ci/mbed-os-ci_unittests

@michalpasztamobica
Copy link
Contributor

@0xc0170 , let's wait for @SeppoTakalo to double check if wiced binaries were built correctly. We've seen some failures in our local CI...

@tymoteuszblochmobica
Copy link
Contributor Author

Unittests now build and pass after API change

@SeppoTakalo
Copy link
Contributor

Yesterday I made a mistake and pushed Wiced binaries build against master.
Now this branch should have correct ones. I just rebuilt those.

I'll verify in CI, so wait a minute.

@SeppoTakalo
Copy link
Contributor

image

Builds now.

@0xc0170 This can now be tested. Thanks.

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 20, 2019

CI restarted

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 20, 2019

While in CI

@mikaleppanen
@kjbracey-arm

Can you review please?

@mbed-ci
Copy link

mbed-ci commented Feb 20, 2019

Test run: SUCCESS

Summary: 12 of 12 test jobs passed
Build number : 9
Build artifacts

Copy link
Contributor

@kjbracey kjbracey left a comment

Choose a reason for hiding this comment

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

Could of things to follow up on, but seems fine.

}

if (NETCONNTYPE_GROUP(s->conn->type) == NETCONN_TCP) {
s->conn->pcb.tcp->interface_name = (const char *)optval;
Copy link
Contributor

Choose a reason for hiding this comment

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

This bends the setsockopt API because it's not actually copying the option data. Can get away with it though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will be refactored in LWIP 2.1.2.
New LWIP has new socket to name bind implementation so code must be modified.

@@ -160,6 +160,20 @@ ip4_route(const ip4_addr_t *dest)
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */

if(interface_name !=NULL) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems inefficient to be doing name matching inside the stack per-packet - I would have thought the setsockopt would do lookup at that time, and then you're just netif pointer matching per-packet. Still, just an efficiency nit.

Would mean adding a clean-up to strip stale pointers from sockets if you ever delete an interface.

On the other hand, would solve the "not copying/processing option data" issue on the setsockopt.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will be refactored in LWIP 2.1.2.
New LWIP has modified ipxx_route function so name/ip matching must be implemented again.

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 21, 2019

Still waiting for #9387 (comment) , if all resolved, let us know

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

Successfully merging this pull request may close these issues.