obgm / libcoap

A CoAP (RFC 7252) implementation in C
Other
796 stars 424 forks source link

IP_MULTICAST_LOOP / IPV6_MULTICAST_LOOP option configuration #1497

Open BitFis opened 2 months ago

BitFis commented 2 months ago

Is your feature request related to a problem? Please describe.

Unable to configure IP_MULTICAST_LOOP which is important to define the correct behavior for sending multicast ipv6 packages.

Describe the solution you would like

Probably would be best to provide a function to retrieve the socket file descriptor.

Describe alternatives you have considered

Trying to retrieve the fid from coap_context_get_coap_fd. Not possible since the fid is well hidden in the epoll context. Alternatively directly accessing the socket fid, thought I prefer not to make libcoap a compile time depedency by using the internal includes.

Alternatively an api for the IP_MULTICAST_LOOP behavior could be provided, Thought providing access to the fid would also support other socket option configurations without requiring new functions and therefore may be more future proof.

Example API:

coap_fd_t coap_context_get_endpoint_sock_fd(const coap_context_t *context);

Additional context

Happy to alternatives. Also would provide an PR if support is welcome.

mrdeep1 commented 2 months ago

Several things here.

IP_MULTICAST_LOOP is for IPv4 sockets. With it enabled, an application sending a multicast packet, this multicast packet will be received by any other application listening on a different socket on the same host. I do not think this is supported for IPv6.

With IPV6_MULTICAST_LOOP enabled, this allows the transmitting socket to also receive the multicast packet. As I read it, this has not control over other sockets on the same host added to the same multicast group.

It probably is best to add in a new function (like coap_mcast_set_hops()) that can enable / disable IPV6_MULTICAST_LOOP / IPV_MULTICAST_LOOP on hte correct socket for the CoAP session - e.g. coap_mcast_set_ipv4_loop() and coap_mcast_set_ipv6_loop()

BitFis commented 2 months ago

@mrdeep1 thank you for the fast response.

Thanks for the hint just realized the mixup. I also found the multicast binding coap_join_mcast_group_intf opens a new socket so I am happy with your suggestion:

coap_mcast_set_ipv6_loop(coap_session_t *session, bool enable);
coap_mcast_set_ipv4_loop(coap_session_t *session, bool enable);

Would you like some help with the implementation?

mrdeep1 commented 2 months ago

For historical code compatibility reasons, bool is not currently supported, so these parameters would have to be an int with value 0 or 1.

For IPv6, using coap_join_mcast_group_intf() just updates the server listening socket with listening on the appropriate multicast address on the appropriate interface. This however does not work for a client.

It is unclear to me as to what is your actual use case here.

BitFis commented 2 months ago

The use case; I have multiple servers listening on the same multicast address + port and sending to the same multicast address + port. A very basic event based system with ipv6 multicast. To prevent the servers receiving their own messages (which leads to issues), I need to filter by source. But since there is this handy option IPV6_MULTICAST_LOOP I would prefer to use this.

mrdeep1 commented 2 months ago

OK, so if I understand you correctly, you have a server A (well more than one, named B, C, D etc.) listening on a multicast address (group) + port. Then someone sends a multicast packet X, received by server A, and this server then decides to send out a multicast packet Y to the same multicast IP (group) and port. So, server B (and C, D etc) receive the original multicast packet X (but don't send out a multicast packet otherwise you will likely get a packet storm) as well as the multicast packet Y transmitted by A.

So, your issue is that Server A gets confused when it also sees the multicast packet Y it just sent. If that is the case, then IPV6_MULTICAST_LOOP simply needs to be disabled when coap_join_mcast_group_intf() is called - or am I missing something?

BitFis commented 2 months ago

Exactly the issue, From the example, A is a router with two interfaces and the function coap_join_mcast_group_intf() would elegantly prevent that 😇 .

mrdeep1 commented 2 months ago

OK - then I think that all is needed is to disable IPV6_MULTICAST_LOOPin coap_join_mcast_group_intf() and there is no need for any additional functions. You are welcome to just raise a PR for that, as I consider that to be a bug.

BitFis commented 2 months ago

Let me verify, disabling IPV6_MULTICAST_LOOP on the sending socket worked, thought i am not sure disabling it on the receiving socket will work, the ibm docs say:

... The API uses IPV6_MULTICAST_LOOP socket option to enable or disable the loopback of OUTGOING multicast datagrams ... https://www.ibm.com/docs/en/zos/2.1.0?topic=options-ipv6-multicast-loop

mrdeep1 commented 2 months ago

Yes, it will only disable the sending socket receiving the multicast packet it just transmitted.

BitFis commented 2 months ago

So disabling via coap_join_mcast_group_intf() wont work, since if I use coap_session_create_client() to send the udp multicast message, it will open a new socket which does not configure IPV6_MULTICAST_LOOP, aka. IPV6_MULTICAST_LOOP is enabled and I still receive the multicast message. Or am I missing something and I should not be using coap_session_create_client or have to ensure coap_session_create_client is already created for sending before calling coap_join_mcast_group_intf?

BitFis commented 2 months ago

You probably have understood it fully, but just to ensure we are on the same page, here a test implementation to verify: https://github.com/BitFis/example-multicast , If i have two sockets on the same linux/posix device. Socket A is the server listening socket and Socket B is the sending session socket. Setting IPV6_MULTICAST_LOOP (IPMUL) to disable on socket A, i will still receive the multicast messages from socket B. But having IPMUL on socket A enabled and disabled on socket B, I wont receive the multicast messages. So to solve this, either we add a coap_mcast_set_ipv4/6_loop function or/and disable IPV6_MULTICAST_LOOP by default. Thought all processes on the same machine will not receive the mutlicast messages, which may be desired by some for testing etc.

At least thats my current understanding of this configuration.

mrdeep1 commented 2 months ago

Ok. So one application is listening on socket that has multicast enabled as a server. It then opens new connection, sends multicast packet on new connection which is then picked up on the server side listening socket.

Not sure how to fix this for IPv6. Need to research this.

mrdeep1 commented 2 months ago

I think we simply need to disable IPV6_MULTICAST_LOOP when creating client type sockets.

BitFis commented 2 months ago

Works for me, thought do you want to provide an option to enable loopback if desired? Aka. provide the new api anyways?

BitFis commented 2 months ago

After checking further and having a few discussions, I would recommend not disabling IPV6_MULTICAST_LOOP when creating the client type sockets.

In case its disabled no other local process will be able to receive the multicast message of the coap_client which might lead to issues for some setups on only one host. By example if a client and a server (router) are on the same machine and the client wants to send/publish an mcast message to all devices and the server (router). Also for testing and debugging this could confuse developers and may lead to unnecessary questions / bug reports.

I would recommend the coap_mcast_set_ipv4/6_loop function and use it like the hop function. And only deviate from the standard linux configuation with functions.

May I open an PR to create a first draft for this new api functions?

mrdeep1 commented 2 months ago

Sure - go ahead and create a PR.

I do think that the client socket created by coap_proxy_get_ongoing_session() should by default have IPV6_MULTICAST_LOOP disabled if the listening endpoint of the proxy/relay application has invoked coap_join_mcast_group_intf() to safely protect against a multicast packet loop.

mrdeep1 commented 2 months ago

See #1502 which should fix your loop back issue for the client part of your application sending a mcast packet which is picked up by the server part of your application.

BitFis commented 2 months ago

Will verify this, thanks. A side question, there is currently no functionality which detects duplicate messages via message id?

mrdeep1 commented 2 months ago

A side question, there is currently no functionality which detects duplicate messages via message id?

Not sure what you are trying to ask here - client or server side? There is some code that does this checking but possibly not in the areas you are thinking about. Please give a Use Case.

BitFis commented 1 month ago

Hello, its a bit off topic of this issue, but I am thinking of server side in context of RFC7252 - 4.5. Message Deduplication.

   The recipient SHOULD acknowledge each
   duplicate copy of a Confirmable message using the same
   Acknowledgement or Reset message but SHOULD process any request or
   response in the message only once.

I am aware there some relaxation options, but just curious on if and how this is implemented. Maybe enabling ignoring a sent multicast message as duplicate.

mrdeep1 commented 1 month ago

Confirmable (CON) messages are explicitly not allowed for Multicast packets, so this is definitely off topic and should be raised in another issue.