paho.mqtt.c
paho.mqtt.c copied to clipboard
Allow the network interface to be specified for connect
migrated from Bugzilla #460695 status NEW severity enhancement in component MQTT-C for 1.1 Reported in version 1.0 on platform PC Assigned to: Ian Craggs
On 2015-02-24 09:14:11 -0500, Ian Craggs wrote:
The serverURI on the MQTTClient_create call allows the specification of the destination TCP hostname or address, and destination port.
This request is to allow the network interface to be specified as well, for the case where there is more than one on the local machine.
On 2015-02-24 09:39:32 -0500, Ian Craggs wrote:
Requested by Sven Henze ([email protected]).
A quick read of "Unix Network Programming" seems to indicate we need to use bind() for this. But you can't bind directly to an interface, so we would have to query the address for an interface and bind to that, I assume.
The easiest way to allow this to be specified would be to add it to the serverURI. Something like tcp://<network_interface>hostname:port. I don't know if there is a standard notation for this already.
setsockopt with SO_BINDTODEVICE on Linux.
Bind to an address obtained from the network interface name elsewhere - I think.
Would be a valuable feature to have . Most of the hosts have multiple Network cards and this feature will allow to classify our Mqtt traffic
We are always open to PRs.
Hello, I would like to ask if this problem has been solved?
The easiest way to allow this to be specified would be to add it to the serverURI. Something like tcp://<network_interface>hostname:port. I don't know if there is a standard notation for this already.
It depends on if "serverURI" is an actual URI or just a string that looks like an URI. if it's an URI, the suggested syntax would be invalid. It it's just a string, it would be fine. AFAIK there is no standard notification to define an interface in an URI (see https://en.wikipedia.org/wiki/Uniform_Resource_Identifier). On the other hand, when I look at https://en.wikipedia.org/wiki/List_of_URI_schemes, "valid" is a flexible thing when talking about URIs ^^
Edit: tcp://hostname:port;ifname=eth0
would be a syntax that is readable and seems to comply with common uri schemes. Similar with tcp://hostname:port?ifname=eth0
.
“tcp://hostname:port;ifname=eth0” This method is great. Do you plan to add this tab.
I think it's better to bind to a IP Adress
I think it's better to bind to a IP Adress
Why? Both methods are similar but for IP you'd first need to figure out the ip address of a device (which might be dynamic). And SO_BINDTODEVICE should do it.
SO_BINDTODEVICE
needs CAP_NET_RAW
capabilities for which you need root rights in Linux Kernel based systems. A common aproche is to get the interface IP and bind the IP. This would also work on Windows.
I'm intending to look at this in the next couple of weeks.
I didn't write the suggestion of adding it to the serverURI. My first thought was to allow a callback to be set, so the application could choose between those available.
SO_BINDTODEVICE
needsCAP_NET_RAW
capabilities for which you need root rights in Linux Kernel based systems. A common aproche is to get the interface IP and bind the IP. This would also work on Windows.
That's a good point. So would it be better to tell the lib an ip address to bind to? Or a device and it fetches the IP address for it?
I just checked "curl" which takes an interface as parameter but works as user with no additional rights. Looks like the way to go for me.
I've added a draft Linux implementation into a 1.4 development branch. It does use SO_BINDTODEVICE, which works on my Ubuntu Linux machine without needing root privileges. I don't think I have given myself any extra capabilities.
To use it, you set a callback:
MQTTAsync_setInterfaceCallback(interface_callback);
#include <sys/socket.h>
struct MQTTAsync_interface interface_callback(int count, struct MQTTAsync_interface* interfaces)
{
int i = 0;
struct MQTTAsync_interface interface = {NULL, 0};
for (i = 0; i < count; ++i)
{
printf("paho_c_pub %d %s\n", interfaces[i].family, interfaces[i].name);
if (strcmp(interfaces[i].name, "enp3so") == 0)
{
interface.name = MQTTAsync_malloc(3);
strcpy(interface.name, interfaces[i].name);
}
}
return interface;
}
It also lets you choose whether you want to prefer IPv4 or IPv6 addresses - the default is still IPv4 as it was before.
I has tested, the api works normally, the api is very good, but like "LIBMQTT_API int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl, MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc)" Whether to consider adding a parameter,which like context.
@chenzl-123 thanks for trying it out. To have the context in there would mean having the callback specific to a client object rather than global. I think that makes sense - allows for more flexibility. I'll make that change.
I've made the changes for MacOS and to set the callback per client object as well. Just the Windows implementation to do.
thank you very much!
@icraggs This change is great, thank you. On linux, root permissions are required which is fine for me.
One issue is when the device does not exist or binding fails (e.g. due to missing root permissions). In this case, the client binds to the default device. IMO it should better return an error to the caller, so that the caller can device how to proceed.
Kind regards, Michael.
So I don't think I need root permission on Ubuntu (20.04.5 LTS). Does anyone know if this differs by Linux distribution and/or version?
A quick internet search shows me questions about root permissions needed from 2013 or older. I suspect this has been changed in recent Linux kernel versions.
I'm making some changes to pass in the interface addresses as well as the name, as I think in some cases we'll need to use bind().
@micw setsockopt doesn't seem to return an error if a non-existent interface name is used, I've added to code to report and handle an error if one is returned from the setsockopt call.
OK. There are now three return fields ftom the selectInterface callback as shown in the paho_c_pub and paho_c_sub samples (-A option) as well as the docs.
struct MQTTAsync_interface_choice {
char struct_id[4]; /** The eyecatcher for this structure. Must be MQIC. */
int struct_version; /** The version number of this structure. Must be 0. */
char* name; /**< the interface name to bind to, or NULL to ignore */
int family; /**< the address family AF_INET or AF_INET6, to bind to (MacOS) */
char* address; /**< the network address to bind to, or NULL ignore */
};
- interface name - the string name of the device interface (Linux and Mac only, Windows is a no-op)
- address family - AF_INET or AF_INET6. Needed on Mac for the interface name binding
- network address - IP4 or IP6 string network address to bind to. The address family should be set correctly.
Only one of interface name and network address should be set.
@icraggs We're currently still testing the interface binding, but it looks good so far since we finally got a proof-of-concept running with the synchronous API. However, for the final implementation we also need the async API but I try to incorporate everything together in our code base. We will make a final statement when everything we need is working - or not :P
Nevertheless, we faced some issues with missing PINGREQs when data is on the fly. The corresponding issues, i.e. https://github.com/eclipse/paho.mqtt.c/issues/1277 (?), are already solved in v1.3.12, but unfortunately not in the needed v1.4. So it would be great, if the fixes could be integrated into v1.4 so we can test everything together as whole in the future.
Thanks and regards, Frank
@comcrypto-frank-ullmann Hi. I was away when you made this comment. I checked and the fix for that issue is in the 1.4 branch. I think you should probably open a new issue and describe your problem ideally with a client library trace at the protocol level.
@icraggs Hey ho, I finally achived to create the corresponding bug report for the issue: https://github.com/eclipse/paho.mqtt.c/issues/1384 I think, the mentioned issue in the previous post was not correct, but I struggled to find a proper one. Sorry for that. However, I hope you find the problem with the information in the bug report. Thanks for looking into it.
Thanks and regards, Frank