From 15ea833fd4c1f3a386aa3c98d1d1ea27d5bcefec Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Tue, 11 Jul 2023 17:18:42 +0200 Subject: [PATCH] Make "allow multicast" per-interface This adds an "allow_multicast" attribute to the network interface configuration and uses the old General/AllowMulticast as the default value. This way, both backwards compatibility and easy global configuration are supported. This incidentally addresses a slew of bugs in multi-network configurations, but it does make things rather more complicated. Signed-off-by: Erik Boasson --- docs/manual/config/config_file_reference.rst | 67 ++++- docs/manual/options.md | 57 ++++- etc/cyclonedds.rnc | 43 +++- etc/cyclonedds.xsd | 45 +++- src/core/ddsc/tests/nwpart.c | 3 +- src/core/ddsi/defconfig.c | 8 +- src/core/ddsi/include/dds/ddsi/ddsi_config.h | 3 + .../ddsi/include/dds/ddsi/ddsi_nwinterfaces.h | 1 + src/core/ddsi/src/ddsi__cfgelems.h | 73 +++++- src/core/ddsi/src/ddsi_config.c | 5 +- src/core/ddsi/src/ddsi_discovery_addrset.c | 50 ++-- src/core/ddsi/src/ddsi_discovery_endpoint.c | 31 ++- src/core/ddsi/src/ddsi_discovery_spdp.c | 9 +- src/core/ddsi/src/ddsi_endpoint.c | 79 ++++-- src/core/ddsi/src/ddsi_init.c | 231 +++++++++++++----- src/core/ddsi/src/ddsi_mcgroup.c | 6 +- src/core/ddsi/src/ddsi_nwinterfaces.c | 90 +++++-- src/core/ddsi/src/ddsi_proxy_endpoint.c | 23 +- 18 files changed, 612 insertions(+), 212 deletions(-) diff --git a/docs/manual/config/config_file_reference.rst b/docs/manual/config/config_file_reference.rst index ed87fabfa6..8f9b08018c 100644 --- a/docs/manual/config/config_file_reference.rst +++ b/docs/manual/config/config_file_reference.rst @@ -204,10 +204,11 @@ This element specifies the DDSI participant index used by this instance of the C * a non-negative integer, or - * none: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible. + * none: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible, or + * default: use none if multicast discovery is used on all selected network interfaces, else auto. -The default value is: ``none`` +The default value is: ``default`` .. _`//CycloneDDS/Domain/Discovery/Peers`: @@ -215,11 +216,29 @@ The default value is: ``none`` //CycloneDDS/Domain/Discovery/Peers ----------------------------------- +Attributes: :ref:`AddLocalhost` Children: :ref:`Peer` This element statically configures addresses for discovery. +.. _`//CycloneDDS/Domain/Discovery/Peers[@AddLocalhost]`: + +//CycloneDDS/Domain/Discovery/Peers[@AddLocalhost] +-------------------------------------------------- + +Boolean + +This attribute determines controls the localhost will automatically be added to the list of peers:. + * false: never + + * true: always + + * default: if multicast discovery is unavailable * + +The default value is: ``default`` + + .. _`//CycloneDDS/Domain/Discovery/Peers/Peer`: //CycloneDDS/Domain/Discovery/Peers/Peer @@ -391,9 +410,9 @@ The General element specifies overall Cyclone DDS service settings. One of: * Keyword: default -* Comma-separated list of: false, spdp, asm, ssm, true +* Comma-separated list of: false, spdp, asm, ssm, true, default -This element controls whether Cyclone DDS uses multicasts for data traffic. +This element controls the default for the per-network interface setting whether Cyclone DDS uses multicasts for discovery and data traffic. It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default". @@ -404,10 +423,9 @@ It is a comma-separated list of some of the following keywords: "spdp", "asm", " * ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported) +When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses. -When set to "false" all multicasting is disabled. The default, "true" enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses. - -"default" maps on spdp if the network is a WiFi network, on true if it is a wired network +The special value "default" maps on spdp if the network is a WiFi network, on true if it is a wired network The default value is: ``default`` @@ -515,7 +533,7 @@ This element specifies the network interfaces for use by Cyclone DDS. Multiple i //CycloneDDS/Domain/General/Interfaces/NetworkInterface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Attributes: :ref:`address`, :ref:`autodetermine`, :ref:`multicast`, :ref:`name`, :ref:`prefer_multicast`, :ref:`presence_required`, :ref:`priority` +Attributes: :ref:`address`, :ref:`allow_multicast`, :ref:`autodetermine`, :ref:`multicast`, :ref:`name`, :ref:`prefer_multicast`, :ref:`presence_required`, :ref:`priority` This element defines a network interface. You can set autodetermine="true" to autoselect the interface CycloneDDS considers the highest quality. If autodetermine="false" (the default), you must specify the name and/or address attribute. If you specify both, they must match the same interface. @@ -532,6 +550,33 @@ This attribute specifies the address of the interface. With ipv4 allows matchin The default value is: ```` +.. _`//CycloneDDS/Domain/General/Interfaces/NetworkInterface[@allow_multicast]`: + +//CycloneDDS/Domain/General/Interfaces/NetworkInterface[@allow_multicast] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +One of: +* Keyword: default +* Comma-separated list of: false, spdp, asm, ssm, true, default + +This element controls whether Cyclone DDS uses multicasts for data traffic on this interface. + +It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default". + + * spdp: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data. + + * asm: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast + + * ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported) + + +When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses. + +The special value "default" takes the value from the globalGeneral/AllowMulticast setting. + +The default value is: ``default`` + + .. _`//CycloneDDS/Domain/General/Interfaces/NetworkInterface[@autodetermine]`: //CycloneDDS/Domain/General/Interfaces/NetworkInterface[@autodetermine] @@ -2654,10 +2699,10 @@ The categorisation of tracing output is incomplete and hence most of the verbosi The default value is: ``none`` .. - generated from ddsi_config.h[7a2ea305c6a2eab7fe285734641fe60da41874b6] + generated from ddsi_config.h[9f834d377bdea61bea6507feed2fc4a8924dc02e] generated from ddsi__cfgunits.h[bd22f0c0ed210501d0ecd3b07c992eca549ef5aa] - generated from ddsi__cfgelems.h[bf5f0d9b9265e4b6f3f61e7624325af68eb276bf] - generated from ddsi_config.c[2d8c4ee6633a21fb69c3f7cb0bf685c1ca9e5132] + generated from ddsi__cfgelems.h[f10059d775cf2e4961a2e9520bb1a4da6a124778] + generated from ddsi_config.c[0a59324bd889637ea7d04765da9b76bbe74997c1] generated from _confgen.h[e32eabfc35e9f3a7dcb63b19ed148c0d17c6e5fc] generated from _confgen.c[237308acd53897a34e8c643e16e05a61d73ffd65] generated from generate_rnc.c[b50e4b7ab1d04b2bc1d361a0811247c337b74934] diff --git a/docs/manual/options.md b/docs/manual/options.md index a7f188d813..1c0571abd7 100644 --- a/docs/manual/options.md +++ b/docs/manual/options.md @@ -131,17 +131,31 @@ This element specifies the DDSI participant index used by this instance of the C * a non-negative integer, or - * none: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible. + * none: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible, or + * default: use none if multicast discovery is used on all selected network interfaces, else auto. -The default value is: `none` +The default value is: `default` #### //CycloneDDS/Domain/Discovery/Peers +Attributes: [AddLocalhost](#cycloneddsdomaindiscoverypeersaddlocalhost) Children: [Peer](#cycloneddsdomaindiscoverypeerspeer) This element statically configures addresses for discovery. +#### //CycloneDDS/Domain/Discovery/Peers[@AddLocalhost] +Boolean + +This attribute determines controls the localhost will automatically be added to the list of peers:. + * false: never + + * true: always + + * default: if multicast discovery is unavailable * +The default value is: `default` + + ##### //CycloneDDS/Domain/Discovery/Peers/Peer Attributes: [Address](#cycloneddsdomaindiscoverypeerspeeraddress) @@ -253,9 +267,9 @@ The General element specifies overall Cyclone DDS service settings. #### //CycloneDDS/Domain/General/AllowMulticast One of: * Keyword: default -* Comma-separated list of: false, spdp, asm, ssm, true +* Comma-separated list of: false, spdp, asm, ssm, true, default -This element controls whether Cyclone DDS uses multicasts for data traffic. +This element controls the default for the per-network interface setting whether Cyclone DDS uses multicasts for discovery and data traffic. It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default". @@ -265,10 +279,9 @@ It is a comma-separated list of some of the following keywords: "spdp", "asm", " * ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported) +When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses. -When set to "false" all multicasting is disabled. The default, "true" enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses. - -"default" maps on spdp if the network is a WiFi network, on true if it is a wired network +The special value "default" maps on spdp if the network is a WiFi network, on true if it is a wired network The default value is: `default` @@ -340,7 +353,7 @@ This element specifies the network interfaces for use by Cyclone DDS. Multiple i ##### //CycloneDDS/Domain/General/Interfaces/NetworkInterface -Attributes: [address](#cycloneddsdomaingeneralinterfacesnetworkinterfaceaddress), [autodetermine](#cycloneddsdomaingeneralinterfacesnetworkinterfaceautodetermine), [multicast](#cycloneddsdomaingeneralinterfacesnetworkinterfacemulticast), [name](#cycloneddsdomaingeneralinterfacesnetworkinterfacename), [prefer_multicast](#cycloneddsdomaingeneralinterfacesnetworkinterfaceprefermulticast), [presence_required](#cycloneddsdomaingeneralinterfacesnetworkinterfacepresencerequired), [priority](#cycloneddsdomaingeneralinterfacesnetworkinterfacepriority) +Attributes: [address](#cycloneddsdomaingeneralinterfacesnetworkinterfaceaddress), [allow_multicast](#cycloneddsdomaingeneralinterfacesnetworkinterfaceallowmulticast), [autodetermine](#cycloneddsdomaingeneralinterfacesnetworkinterfaceautodetermine), [multicast](#cycloneddsdomaingeneralinterfacesnetworkinterfacemulticast), [name](#cycloneddsdomaingeneralinterfacesnetworkinterfacename), [prefer_multicast](#cycloneddsdomaingeneralinterfacesnetworkinterfaceprefermulticast), [presence_required](#cycloneddsdomaingeneralinterfacesnetworkinterfacepresencerequired), [priority](#cycloneddsdomaingeneralinterfacesnetworkinterfacepriority) This element defines a network interface. You can set autodetermine="true" to autoselect the interface CycloneDDS considers the highest quality. If autodetermine="false" (the default), you must specify the name and/or address attribute. If you specify both, they must match the same interface. @@ -353,6 +366,28 @@ This attribute specifies the address of the interface. With ipv4 allows matchin The default value is: `` +##### //CycloneDDS/Domain/General/Interfaces/NetworkInterface[@allow_multicast] +One of: +* Keyword: default +* Comma-separated list of: false, spdp, asm, ssm, true, default + +This element controls whether Cyclone DDS uses multicasts for data traffic on this interface. + +It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default". + + * spdp: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data. + + * asm: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast + + * ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported) + +When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses. + +The special value "default" takes the value from the globalGeneral/AllowMulticast setting. + +The default value is: `default` + + ##### //CycloneDDS/Domain/General/Interfaces/NetworkInterface[@autodetermine] Text @@ -1854,10 +1889,10 @@ While none prevents any message from being written to a DDSI2 log file. The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are config, fine and finest. The default value is: `none` - + - - + + diff --git a/etc/cyclonedds.rnc b/etc/cyclonedds.rnc index fb18706c45..e8cc343f05 100644 --- a/etc/cyclonedds.rnc +++ b/etc/cyclonedds.rnc @@ -92,8 +92,8 @@ CycloneDDS configuration""" ] ]

This element specifies the DDSI participant index used by this instance of the Cyclone DDS service for discovery purposes. Only one such participant id is used, independent of the number of actual DomainParticipants on the node. It is either:

  • auto: which will attempt to automatically determine an available participant index (see also Discovery/MaxAutoParticipantIndex), or
  • a non-negative integer, or
  • -
  • none: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible.
-

The default value is: none

""" ] ] +
  • none: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible, or
  • default: use none if multicast discovery is used on all selected network interfaces, else auto.
  • +

    The default value is: default

    """ ] ] element ParticipantIndex { text }? @@ -101,6 +101,15 @@ CycloneDDS configuration""" ] ]

    This element statically configures addresses for discovery.

    """ ] ] element Peers { [ a:documentation [ xml:lang="en" """ +

    This attribute determines controls the localhost will automatically be added to the list of peers:.

    +
    • false: never
    • +
    • true: always
    • +
    • default: if multicast discovery is unavailable
    +

    The default value is: default

    """ ] ] + attribute AddLocalhost { + xsd:boolean + }? + & [ a:documentation [ xml:lang="en" """

    This element statically configures addresses for discovery.

    """ ] ] element Peer { [ a:documentation [ xml:lang="en" """ @@ -181,18 +190,17 @@ CycloneDDS configuration""" ] ]

    The General element specifies overall Cyclone DDS service settings.

    """ ] ] element General { [ a:documentation [ xml:lang="en" """ -

    This element controls whether Cyclone DDS uses multicasts for data traffic.

    +

    This element controls the default for the per-network interface setting whether Cyclone DDS uses multicasts for discovery and data traffic.

    It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default".

    • spdp: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data.
    • asm: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast
    • -
    • ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)
    • -
    -

    When set to "false" all multicasting is disabled. The default, "true" enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.

    -

    "default" maps on spdp if the network is a WiFi network, on true if it is a wired network

    +
  • ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)
  • +

    When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.

    +

    The special value "default" maps on spdp if the network is a WiFi network, on true if it is a wired network

    The default value is: default

    """ ] ] element AllowMulticast { - xsd:token { pattern = "default|((false|spdp|asm|ssm|true)(,(false|spdp|asm|ssm|true))*)" } + xsd:token { pattern = "default|((false|spdp|asm|ssm|true|default)(,(false|spdp|asm|ssm|true|default))*)" } }? & [ a:documentation [ xml:lang="en" """

    This element allows setting the SO_DONTROUTE option for outgoing packets to bypass the local routing tables. This is generally useful only when the routing tables cannot be trusted, which is highly unusual.

    @@ -250,6 +258,19 @@ CycloneDDS configuration""" ] ] text }? & [ a:documentation [ xml:lang="en" """ +

    This element controls whether Cyclone DDS uses multicasts for data traffic on this interface.

    +

    It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default".

    +
      +
    • spdp: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data.
    • +
    • asm: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast
    • +
    • ssm: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)
    +

    When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.

    +

    The special value "default" takes the value from the globalGeneral/AllowMulticast setting.

    +

    The default value is: default

    """ ] ] + attribute allow_multicast { + xsd:token { pattern = "default|((false|spdp|asm|ssm|true|default)(,(false|spdp|asm|ssm|true|default))*)" } + }? + & [ a:documentation [ xml:lang="en" """

    If set to "true" an interface is automatically selected. Specifying a name or an address when automatic is set is considered an error.

    The default value is: false

    """ ] ] attribute autodetermine { @@ -1289,10 +1310,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==
    duration_inf = xsd:token { pattern = "inf|0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" } memsize = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B" } } -# generated from ddsi_config.h[7a2ea305c6a2eab7fe285734641fe60da41874b6] +# generated from ddsi_config.h[9f834d377bdea61bea6507feed2fc4a8924dc02e] # generated from ddsi__cfgunits.h[bd22f0c0ed210501d0ecd3b07c992eca549ef5aa] -# generated from ddsi__cfgelems.h[bf5f0d9b9265e4b6f3f61e7624325af68eb276bf] -# generated from ddsi_config.c[2d8c4ee6633a21fb69c3f7cb0bf685c1ca9e5132] +# generated from ddsi__cfgelems.h[f10059d775cf2e4961a2e9520bb1a4da6a124778] +# generated from ddsi_config.c[0a59324bd889637ea7d04765da9b76bbe74997c1] # generated from _confgen.h[e32eabfc35e9f3a7dcb63b19ed148c0d17c6e5fc] # generated from _confgen.c[237308acd53897a34e8c643e16e05a61d73ffd65] # generated from generate_rnc.c[b50e4b7ab1d04b2bc1d361a0811247c337b74934] diff --git a/etc/cyclonedds.xsd b/etc/cyclonedds.xsd index f68074a076..893b41d8a7 100644 --- a/etc/cyclonedds.xsd +++ b/etc/cyclonedds.xsd @@ -175,8 +175,8 @@ CycloneDDS configuration <p>This element specifies the DDSI participant index used by this instance of the Cyclone DDS service for discovery purposes. Only one such participant id is used, independent of the number of actual DomainParticipants on the node. It is either:</p> <ul><li><i>auto</i>: which will attempt to automatically determine an available participant index (see also Discovery/MaxAutoParticipantIndex), or</li> <li>a non-negative integer, or</li> -<li><i>none</i>: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible.</li></ul> -<p>The default value is: <code>none</code></p> +<li><i>none</i>: which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible, or</li><li><i>default</i>: use <i>none</i> if multicast discovery is used on all selected network interfaces, else <i>auto</i>.</li></ul> +<p>The default value is: <code>default</code></p> @@ -188,6 +188,16 @@ CycloneDDS configuration + + + +<p>This attribute determines controls the localhost will automatically be added to the list of peers:.</p> +<ul><li><i>false</i>: never</li> +<li><i>true</i>: always</li> +<li><i>default</i>: if multicast discovery is unavailable<li></ul> +<p>The default value is: <code>default</code></p> + + @@ -321,20 +331,19 @@ CycloneDDS configuration -<p>This element controls whether Cyclone DDS uses multicasts for data traffic.</p> +<p>This element controls the default for the per-network interface setting whether Cyclone DDS uses multicasts for discovery and data traffic.</p> <p>It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default".</p> <ul> <li><i>spdp</i>: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data.</li> <li><i>asm</i>: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast</li> -<li><i>ssm</i>: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)</li> -</ul> -<p>When set to "false" all multicasting is disabled. The default, "true" enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.</p> -<p>"default" maps on spdp if the network is a WiFi network, on true if it is a wired network</p> +<li><i>ssm</i>: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)</li></ul> +<p>When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.</p> +<p>The special value "default" maps on spdp if the network is a WiFi network, on true if it is a wired network</p> <p>The default value is: <code>default</code></p> - + @@ -425,6 +434,20 @@ CycloneDDS configuration <p>The default value is: <code>&lt;empty&gt;</code></p> + + + +<p>This element controls whether Cyclone DDS uses multicasts for data traffic on this interface.</p> +<p>It is a comma-separated list of some of the following keywords: "spdp", "asm", "ssm", or either of "false" or "true", or "default".</p> +<ul> +<li><i>spdp</i>: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data.</li> +<li><i>asm</i>: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast</li> +<li><i>ssm</i>: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)</li></ul> +<p>When set to "false" all multicasting is disabled; "true"enables the full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.</p> +<p>The special value "default" takes the value from the globalGeneral/AllowMulticast setting.</p> +<p>The default value is: <code>default</code></p> + + @@ -1947,10 +1970,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br> - + - - + + diff --git a/src/core/ddsc/tests/nwpart.c b/src/core/ddsc/tests/nwpart.c index 6937140c88..22c639aab0 100644 --- a/src/core/ddsc/tests/nwpart.c +++ b/src/core/ddsc/tests/nwpart.c @@ -90,6 +90,7 @@ static void intf_init (struct ddsi_network_interface *intf, int index, bool allo intf->if_index = 1000u + (uint32_t) index; intf->mc_capable = (!allow_mc || index == 1) ? 0 : 1; intf->mc_flaky = 0; + intf->allow_multicast = (allow_mc && intf->mc_capable) ? DDSI_AMC_TRUE : DDSI_AMC_FALSE; intf->point_to_point = 0; intf->loopback = (index == 0) ? 1 : 0; intf->prefer_multicast = 0; @@ -508,7 +509,7 @@ CU_Theory ((bool same_machine, bool proxypp_has_defmc, int n_ep_uc, int n_ep_mc, struct ddsi_config config; ddsi_config_init_default (&config); config.transport_selector = DDSI_TRANS_UDP; - config.allowMulticast = DDSI_AMC_TRUE; + config.allowMulticast = DDSI_AMC_DEFAULT; errcount = 0; memset (&gv, 0, sizeof (gv)); // solves a spurious gcc-12 "uninitialized" warning setup (&gv, &config, true, false); diff --git a/src/core/ddsi/defconfig.c b/src/core/ddsi/defconfig.c index 677dfa4e7f..b1e7c1e663 100644 --- a/src/core/ddsi/defconfig.c +++ b/src/core/ddsi/defconfig.c @@ -30,7 +30,7 @@ void ddsi_config_init_default (struct ddsi_config *cfg) cfg->domainTag = ""; cfg->extDomainId.isdefault = 1; cfg->ds_grace_period = INT64_C (30000000000); - cfg->participantIndex = INT32_C (-2); + cfg->participantIndex = INT32_C (-3); cfg->maxAutoParticipantIndex = INT32_C (9); cfg->spdpMulticastAddressString = "239.255.0.1"; cfg->spdp_interval.isdefault = 1; @@ -100,10 +100,10 @@ void ddsi_config_init_default (struct ddsi_config *cfg) cfg->ssl_min_version.minor = 3; #endif /* DDS_HAS_SSL */ } -/* generated from ddsi_config.h[7a2ea305c6a2eab7fe285734641fe60da41874b6] */ +/* generated from ddsi_config.h[9f834d377bdea61bea6507feed2fc4a8924dc02e] */ /* generated from ddsi__cfgunits.h[bd22f0c0ed210501d0ecd3b07c992eca549ef5aa] */ -/* generated from ddsi__cfgelems.h[bf5f0d9b9265e4b6f3f61e7624325af68eb276bf] */ -/* generated from ddsi_config.c[2d8c4ee6633a21fb69c3f7cb0bf685c1ca9e5132] */ +/* generated from ddsi__cfgelems.h[f10059d775cf2e4961a2e9520bb1a4da6a124778] */ +/* generated from ddsi_config.c[0a59324bd889637ea7d04765da9b76bbe74997c1] */ /* generated from _confgen.h[e32eabfc35e9f3a7dcb63b19ed148c0d17c6e5fc] */ /* generated from _confgen.c[237308acd53897a34e8c643e16e05a61d73ffd65] */ /* generated from generate_rnc.c[b50e4b7ab1d04b2bc1d361a0811247c337b74934] */ diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_config.h b/src/core/ddsi/include/dds/ddsi/ddsi_config.h index 751fa8d6ef..334032089c 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_config.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_config.h @@ -67,6 +67,7 @@ enum ddsi_shm_loglevel { #define DDSI_PARTICIPANT_INDEX_AUTO -1 #define DDSI_PARTICIPANT_INDEX_NONE -2 +#define DDSI_PARTICIPANT_INDEX_DEFAULT -3 /* ddsi_config_listelem must be an overlay for all used listelem types */ struct ddsi_config_listelem { @@ -223,6 +224,7 @@ struct ddsi_config_network_interface { int presence_required; enum ddsi_boolean_default multicast; struct ddsi_config_maybe_int32 priority; + uint32_t allow_multicast; // no need for a "maybe" type: DDSI_AMC_DEFAULT takes care of that }; struct ddsi_config_network_interface_listelem { @@ -357,6 +359,7 @@ struct ddsi_config struct ddsi_config_ignoredpartition_listelem *ignoredPartitions; struct ddsi_config_partitionmapping_listelem *partitionMappings; #endif /* DDS_HAS_NETWORK_PARTITIONS */ + enum ddsi_boolean_default add_localhost_to_peers; struct ddsi_config_peer_listelem *peers; struct ddsi_config_thread_properties_listelem *thread_properties; diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_nwinterfaces.h b/src/core/ddsi/include/dds/ddsi/ddsi_nwinterfaces.h index 14884a13ea..0422c6dd0a 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_nwinterfaces.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_nwinterfaces.h @@ -33,6 +33,7 @@ struct ddsi_network_interface { unsigned link_local: 1; unsigned prefer_multicast: 1; unsigned is_psmx: 1; + uint32_t allow_multicast; int32_t priority; char *name; }; diff --git a/src/core/ddsi/src/ddsi__cfgelems.h b/src/core/ddsi/src/ddsi__cfgelems.h index 394f2c8bbb..8204f5541c 100644 --- a/src/core/ddsi/src/ddsi__cfgelems.h +++ b/src/core/ddsi/src/ddsi__cfgelems.h @@ -70,8 +70,39 @@ static struct cfgelem network_interface_attributes[] = { "system. If set to 'true', the interface will be assumed to be multicast capable " "even when the interface flags returned by the operating system state it is not " "(this provides a workaround for some platforms). If set to 'false', the interface " - "will never be used for multicast.

    ") - ), + "will never be used for multicast.

    " + )), + LIST("allow_multicast", NULL, 1, "default", + MEMBEROF(ddsi_config_network_interface_listelem, cfg.allow_multicast), + FUNCTIONS(0, uf_allow_multicast, 0, pf_allow_multicast), + DESCRIPTION( + "

    This element controls whether Cyclone DDS uses multicasts for data " + "traffic on this interface.

    \n" + "

    It is a comma-separated list of some of the following keywords: " + "\"spdp\", \"asm\", \"ssm\", or either of \"false\" or \"true\", or " + "\"default\".

    \n" + "
      \n" + "
    • spdp: " + "enables the use of ASM (any-source multicast) for participant " + "discovery, joining the multicast group on the discovery socket, " + "transmitting SPDP messages to this group, but never advertising nor " + "using any multicast address in any discovery message, thus forcing " + "unicast communications for all endpoint discovery and user data." + "
    • \n" + "
    • asm: " + "enables the use of ASM for all traffic, including receiving SPDP but " + "not transmitting SPDP messages via multicast" + "
    • \n" + "
    • ssm: " + "enables the use of SSM (source-specific multicast) for all non-SPDP " + "traffic (if supported)" + "
    \n" + "

    When set to \"false\" all multicasting is disabled; \"true\"" + "enables the full use of multicasts. Listening for multicasts can " + "be controlled by General/MulticastRecvNetworkInterfaceAddresses.

    \n" + "

    The special value \"default\" takes the value from the global" + "General/AllowMulticast setting.

    "), + VALUES("false","spdp","asm","ssm","true","default")), END_MARKER }; @@ -218,7 +249,8 @@ static struct cfgelem general_cfgelems[] = { MEMBER(allowMulticast), FUNCTIONS(0, uf_allow_multicast, 0, pf_allow_multicast), DESCRIPTION( - "

    This element controls whether Cyclone DDS uses multicasts for data " + "

    This element controls the default for the per-network interface " + "setting whether Cyclone DDS uses multicasts for discovery and data " "traffic.

    \n" "

    It is a comma-separated list of some of the following keywords: " "\"spdp\", \"asm\", \"ssm\", or either of \"false\" or \"true\", or " @@ -238,14 +270,13 @@ static struct cfgelem general_cfgelems[] = { "

  • ssm: " "enables the use of SSM (source-specific multicast) for all non-SPDP " "traffic (if supported)" - "
  • \n" - "\n" - "

    When set to \"false\" all multicasting is disabled. The default, " - "\"true\" enables the full use of multicasts. Listening for multicasts can " + "\n" + "

    When set to \"false\" all multicasting is disabled; \"true\"" + "enables the full use of multicasts. Listening for multicasts can " "be controlled by General/MulticastRecvNetworkInterfaceAddresses.

    \n" - "

    \"default\" maps on spdp if the network is a WiFi network, on true " - "if it is a wired network

    "), - VALUES("false","spdp","asm","ssm","true")), + "

    The special value \"default\" maps on spdp if the network is a " + "WiFi network, on true if it is a wired network

    "), + VALUES("false","spdp","asm","ssm","true","default")), BOOL(DEPRECATED("PreferMulticast"), NULL, 1, "false", MEMBER(depr_prefer_multicast), FUNCTIONS(0, uf_boolean, 0, pf_boolean), @@ -1835,6 +1866,20 @@ static struct cfgelem discovery_peers_cfgelems[] = { END_MARKER }; +static struct cfgelem discovery_peers_cfgattrs[] = { + BOOL("AddLocalhost", NULL, 1, "default", + MEMBER(add_localhost_to_peers), + FUNCTIONS(0, uf_boolean_default, 0, pf_boolean_default), + DESCRIPTION( + "

    This attribute determines controls the localhost will automatically " + "be added to the list of peers:.

    \n" + "
    • false: never
    • \n" + "
    • true: always
    • \n" + "
    • default: if multicast discovery is unavailable
    " + )), + END_MARKER +}; + static struct cfgelem discovery_cfgelems[] = { STRING("Tag", NULL, 1, "", MEMBER(domainTag), @@ -1861,13 +1906,13 @@ static struct cfgelem discovery_cfgelems[] = { "disappears, allowing reconnection without loss of data when the " "discovery service restarts (or another instance takes over).

    "), UNIT("duration_inf")), - GROUP("Peers", discovery_peers_cfgelems, NULL, 1, + GROUP("Peers", discovery_peers_cfgelems, discovery_peers_cfgattrs, 1, NOMEMBER, NOFUNCTIONS, DESCRIPTION( "

    This element statically configures addresses for discovery.

    " )), - STRING("ParticipantIndex", NULL, 1, "none", + STRING("ParticipantIndex", NULL, 1, "default", MEMBER(participantIndex), FUNCTIONS(0, uf_participantIndex, 0, pf_participantIndex), DESCRIPTION( @@ -1881,7 +1926,9 @@ static struct cfgelem discovery_cfgelems[] = { "
  • a non-negative integer, or
  • \n" "
  • none: which causes it to use arbitrary port numbers for " "unicast sockets which entirely removes the constraints on the " - "participant index but makes unicast discovery impossible.
  • " + "participant index but makes unicast discovery impossible, or" + "
  • default: use none if multicast discovery is used on all " + "selected network interfaces, else auto.
  • " )), INT("MaxAutoParticipantIndex", NULL, 1, "9", MEMBER(maxAutoParticipantIndex), diff --git a/src/core/ddsi/src/ddsi_config.c b/src/core/ddsi/src/ddsi_config.c index 023d9e1326..18617573ef 100644 --- a/src/core/ddsi/src/ddsi_config.c +++ b/src/core/ddsi/src/ddsi_config.c @@ -1559,7 +1559,10 @@ static void pf_domainId(struct ddsi_cfgst *cfgst, void *parent, struct cfgelem c static enum update_result uf_participantIndex (struct ddsi_cfgst *cfgst, void *parent, struct cfgelem const * const cfgelem, int first, const char *value) { int * const elem = cfg_address (cfgst, parent, cfgelem); - if (ddsrt_strcasecmp (value, "auto") == 0) { + if (ddsrt_strcasecmp (value, "default") == 0) { + *elem = DDSI_PARTICIPANT_INDEX_DEFAULT; + return URES_SUCCESS; + } else if (ddsrt_strcasecmp (value, "auto") == 0) { *elem = DDSI_PARTICIPANT_INDEX_AUTO; return URES_SUCCESS; } else if (ddsrt_strcasecmp (value, "none") == 0) { diff --git a/src/core/ddsi/src/ddsi_discovery_addrset.c b/src/core/ddsi/src/ddsi_discovery_addrset.c index 25fae90b39..da43d45b17 100644 --- a/src/core/ddsi/src/ddsi_discovery_addrset.c +++ b/src/core/ddsi/src/ddsi_discovery_addrset.c @@ -28,40 +28,28 @@ void ddsi_interface_set_init (ddsi_interface_set_t *intfs) intfs->xs[i] = false; } -bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv) +static uint32_t allow_multicast_mask_from_locator (const struct ddsi_domaingv *gv, const ddsi_locator_t *loc) { + uint32_t mask = 0; #ifdef DDS_HAS_SSM - /* Note that if the default multicast address is an SSM address, - we will simply advertise it. The recipients better understand - it means the writers will publish to address and the readers - favour SSM. */ - if (ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc)) - return (gv->config.allowMulticast & DDSI_AMC_SSM) != 0; - else - return (gv->config.allowMulticast & DDSI_AMC_ASM) != 0; + if (ddsi_is_ssm_mcaddr (gv, loc)) + mask |= DDSI_AMC_SSM; + else if (ddsi_is_mcaddr (gv, loc)) + mask |= DDSI_AMC_ASM; #else - return (gv->config.allowMulticast & DDSI_AMC_ASM) != 0; + if (ddsi_is_mcaddr (gv, loc)) + mask |= DDSI_AMC_ASM; #endif + return mask; } -static void allowmulticast_aware_add_to_addrset (const struct ddsi_domaingv *gv, uint32_t allow_multicast, struct ddsi_addrset *as, const ddsi_xlocator_t *loc) +bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv) { -#ifdef DDS_HAS_SSM - if (ddsi_is_ssm_mcaddr (gv, &loc->c)) - { - if (!(allow_multicast & DDSI_AMC_SSM)) - return; - } - else if (ddsi_is_mcaddr (gv, &loc->c)) - { - if (!(allow_multicast & DDSI_AMC_ASM)) - return; - } -#else - if (ddsi_is_mcaddr (gv, &loc->c) && !(allow_multicast & DDSI_AMC_ASM)) - return; -#endif - ddsi_add_xlocator_to_addrset (gv, as, loc); + const uint32_t mask = allow_multicast_mask_from_locator (gv, &gv->loc_default_mc); + for (int i = 0; i < gv->n_interfaces; i++) + if (gv->interfaces[i].allow_multicast & mask) + return true; + return false; } static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * const gv, const struct ddsi_network_packet_info *pktinfo, const ddsi_locator_t *loc, struct ddsi_addrset *as, ddsi_interface_set_t *intfs, bool *direct, struct ddsi_addrset *routed_as) @@ -262,16 +250,18 @@ static void ddsi_addrset_from_locatorlist_handle_mc (const struct ddsi_domaingv for (struct ddsi_locators_one *l = mc->first; l != NULL; l = l->next) { + const uint32_t mask = allow_multicast_mask_from_locator (gv, &l->loc); for (int i = 0; i < gv->n_interfaces; i++) { - if (intfs->xs[i] && gv->interfaces[i].mc_capable) + if (intfs->xs[i] && // interface must be enabled for this peer + (gv->interfaces[i].allow_multicast & mask) && // and must allow multicast + ddsi_factory_supports (gv->xmit_conns[i]->m_factory, l->loc.kind)) { const ddsi_xlocator_t loc = { .conn = gv->xmit_conns[i], .c = l->loc }; - if (ddsi_factory_supports (loc.conn->m_factory, loc.c.kind)) - allowmulticast_aware_add_to_addrset (gv, gv->config.allowMulticast, as, &loc); + ddsi_add_xlocator_to_addrset (gv, as, &loc); } } } diff --git a/src/core/ddsi/src/ddsi_discovery_endpoint.c b/src/core/ddsi/src/ddsi_discovery_endpoint.c index 516b5340b5..f2269b5d8e 100644 --- a/src/core/ddsi/src/ddsi_discovery_endpoint.c +++ b/src/core/ddsi/src/ddsi_discovery_endpoint.c @@ -44,13 +44,9 @@ struct add_locator_to_ps_arg { static void add_locator_to_ps (const ddsi_locator_t *loc, void *varg) { struct add_locator_to_ps_arg *arg = varg; - struct ddsi_locators_one *elem = ddsrt_malloc (sizeof (struct ddsi_locators_one)); struct ddsi_locators *locs; unsigned present_flag; - elem->loc = *loc; - elem->next = NULL; - if (ddsi_is_mcaddr (arg->gv, loc)) { locs = &arg->ps->multicast_locators; present_flag = PP_MULTICAST_LOCATOR; @@ -65,12 +61,27 @@ static void add_locator_to_ps (const ddsi_locator_t *loc, void *varg) locs->first = locs->last = NULL; arg->ps->present |= present_flag; } - locs->n++; - if (locs->first) - locs->last->next = elem; - else - locs->first = elem; - locs->last = elem; + + // We may come here because of a set of xlocators in which the same locator + // appears twice, so dedup. There's no harm in initializing the locator set + // in the parameter list, because we'll always add this if there was no list + // yet. + bool isnew = true; + for (const struct ddsi_locators_one *x = locs->first; x && isnew; x = x->next) + if (ddsi_compare_locators (&x->loc, loc) == 0) + isnew = false; + if (isnew) + { + struct ddsi_locators_one *elem = ddsrt_malloc (sizeof (struct ddsi_locators_one)); + elem->loc = *loc; + elem->next = NULL; + locs->n++; + if (locs->first) + locs->last->next = elem; + else + locs->first = elem; + locs->last = elem; + } } static void add_xlocator_to_ps (const ddsi_xlocator_t *loc, void *varg) diff --git a/src/core/ddsi/src/ddsi_discovery_spdp.c b/src/core/ddsi/src/ddsi_discovery_spdp.c index a5ff068bc5..fe79c3a547 100644 --- a/src/core/ddsi/src/ddsi_discovery_spdp.c +++ b/src/core/ddsi/src/ddsi_discovery_spdp.c @@ -36,7 +36,14 @@ static void maybe_add_pp_as_meta_to_as_disc (struct ddsi_domaingv *gv, const struct ddsi_addrset *as_meta) { - if (ddsi_addrset_empty_mc (as_meta) || !(gv->config.allowMulticast & DDSI_AMC_SPDP)) + // FIXME: this is mostly equivalent to the pre-per-interface "allow_multicast" setting, but we can do much better + // because we know the interface on which received it, whether it was a multicast, and, for Cyclone peers, whether + // it was spontaneous or in response to one we sent + bool allow_mc_spdp = false; + for (int i = 0; i < gv->n_interfaces && !allow_mc_spdp; i++) + if (gv->interfaces[i].allow_multicast & DDSI_AMC_SPDP) + allow_mc_spdp = true; + if (ddsi_addrset_empty_mc (as_meta) || !allow_mc_spdp) { ddsi_xlocator_t loc; ddsi_addrset_any_uc (as_meta, &loc); diff --git a/src/core/ddsi/src/ddsi_endpoint.c b/src/core/ddsi/src/ddsi_endpoint.c index c52e498168..cfd607f6c2 100644 --- a/src/core/ddsi/src/ddsi_endpoint.c +++ b/src/core/ddsi/src/ddsi_endpoint.c @@ -213,6 +213,35 @@ void ddsi_rebuild_writer_addrset (struct ddsi_writer *wr) ELOGDISC (wr, " (burst size %"PRIu32" rexmit %"PRIu32")\n", wr->init_burst_size_limit, wr->rexmit_burst_size_limit); } +static bool nwpart_includes_ssm_enabled_interfaces (const struct ddsi_domaingv *gv, const struct ddsi_config_networkpartition_listelem *np) + ddsrt_nonnull ((1)); + +static bool nwpart_includes_ssm_enabled_interfaces (const struct ddsi_domaingv *gv, const struct ddsi_config_networkpartition_listelem *np) +{ + if (np == NULL || np->uc_addresses == NULL) + { + // no network partition or one without unicast addresses -> any interface will do + for (int i = 0; i < gv->n_interfaces; i++) + if (gv->interfaces[i].allow_multicast & DDSI_AMC_SSM) + return true; + return false; + } + else + { + // only for those interfaces that are listed among the unicast addresses + // FIXME: this can be done more efficiently ... + for (const struct ddsi_networkpartition_address *a = np->uc_addresses; a; a = a->next) + { + for (int i = 0; i < gv->n_interfaces; i++) + if (a->loc.kind == gv->interfaces[i].loc.kind && + memcmp (a->loc.address, gv->interfaces[i].loc.address, sizeof (a->loc.address)) == 0 && + (gv->interfaces[i].allow_multicast & DDSI_AMC_SSM)) + return true; + } + return false; + } +} + static void writer_get_alive_state_locked (struct ddsi_writer *wr, struct ddsi_alive_state *st) { st->alive = wr->alive; @@ -745,6 +774,8 @@ int ddsi_writer_set_notalive (struct ddsi_writer *wr, bool notify) static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char *topic_name, const struct ddsi_sertype *type, const struct dds_qos *xqos, struct ddsi_whc *whc, ddsi_status_cb_t status_cb, void * status_entity) { + struct ddsi_domaingv * const gv = wr->e.gv; + ddsrt_cond_init (&wr->throttle_cond); wr->seq = 0; ddsrt_atomic_st64 (&wr->seq_xmit, (uint64_t) 0); @@ -795,7 +826,7 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char ddsi_set_topic_type_name (wr->xqos, topic_name, type->type_name); ELOGDISC (wr, "WRITER "PGUIDFMT" QOS={", PGUID (wr->e.guid)); - ddsi_xqos_log (DDS_LC_DISCOVERY, &wr->e.gv->logconfig, wr->xqos); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, wr->xqos); ELOGDISC (wr, "}\n"); assert (wr->xqos->present & DDSI_QP_RELIABILITY); @@ -817,7 +848,7 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char } wr->handle_as_transient_local = (wr->xqos->durability.kind == DDS_DURABILITY_TRANSIENT_LOCAL); wr->num_readers_requesting_keyhash += - wr->e.gv->config.generate_keyhash && + gv->config.generate_keyhash && ((wr->e.guid.entityid.u & DDSI_ENTITYID_KIND_MASK) == DDSI_ENTITYID_KIND_WRITER_WITH_KEY); wr->type = ddsi_sertype_ref (type); wr->as = ddsi_new_addrset (); @@ -827,7 +858,7 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char partitions that match multiple network partitions. From a safety point of view a wierd configuration. Here we chose the first one that we find */ - wr->network_partition = ddsi_get_nwpart_from_mapping (&wr->e.gv->logconfig, &wr->e.gv->config, wr->xqos, wr->xqos->topic_name); + wr->network_partition = ddsi_get_nwpart_from_mapping (&gv->logconfig, &gv->config, wr->xqos, wr->xqos->topic_name); #endif /* DDS_HAS_NETWORK_PARTITIONS */ #ifdef DDS_HAS_SSM @@ -837,42 +868,42 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char to advertise. */ wr->supports_ssm = 0; wr->ssm_as = NULL; - if (wr->e.gv->config.allowMulticast & DDSI_AMC_SSM) + if (nwpart_includes_ssm_enabled_interfaces (gv, wr->network_partition)) { - ddsi_xlocator_t loc; - int have_loc = 0; + const ddsi_locator_t *base_ssm_loc = NULL; if (wr->network_partition == NULL) { - if (ddsi_is_ssm_mcaddr (wr->e.gv, &wr->e.gv->loc_default_mc)) - { - loc.conn = wr->e.gv->xmit_conns[0]; // FIXME: hack - loc.c = wr->e.gv->loc_default_mc; - have_loc = 1; - } + if (ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc)) + base_ssm_loc = &gv->loc_default_mc; } else { if (wr->network_partition->ssm_addresses) { - assert (ddsi_is_ssm_mcaddr (wr->e.gv, &wr->network_partition->ssm_addresses->loc)); - loc.conn = wr->e.gv->xmit_conns[0]; // FIXME: hack - loc.c = wr->network_partition->ssm_addresses->loc; - have_loc = 1; + assert (ddsi_is_ssm_mcaddr (gv, &wr->network_partition->ssm_addresses->loc)); + base_ssm_loc = &wr->network_partition->ssm_addresses->loc; } } - if (have_loc) + if (base_ssm_loc != NULL) { wr->supports_ssm = 1; wr->ssm_as = ddsi_new_addrset (); - ddsi_add_xlocator_to_addrset (wr->e.gv, wr->ssm_as, &loc); + for (int i = 0; i < gv->n_interfaces; i++) + { + if ((gv->interfaces[i].allow_multicast & DDSI_AMC_SSM) && ddsi_factory_supports(gv->xmit_conns[i]->m_factory, base_ssm_loc->kind)) + { + ddsi_xlocator_t loc = { .conn = gv->xmit_conns[i], .c = *base_ssm_loc }; + ddsi_add_xlocator_to_addrset (gv, wr->ssm_as, &loc); + } + } ELOGDISC (wr, "writer "PGUIDFMT": ssm=%d", PGUID (wr->e.guid), wr->supports_ssm); - ddsi_log_addrset (wr->e.gv, DDS_LC_DISCOVERY, "", wr->ssm_as); + ddsi_log_addrset (gv, DDS_LC_DISCOVERY, "", wr->ssm_as); ELOGDISC (wr, "\n"); } } #endif - wr->evq = wr->e.gv->xevents; + wr->evq = gv->xevents; /* heartbeat event will be deleted when the handler can't find a writer for it in the hash table. NEVER => won't ever be @@ -908,8 +939,8 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char } else { - wr->whc_low = wr->e.gv->config.whc_lowwater_mark; - wr->whc_high = wr->e.gv->config.whc_init_highwater_mark.value; + wr->whc_low = gv->config.whc_lowwater_mark; + wr->whc_high = gv->config.whc_init_highwater_mark.value; } assert (!(ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE) && !ddsi_is_builtin_volatile_endpoint(wr->e.guid.entityid)) || (wr->whc_low == wr->whc_high && wr->whc_low == INT32_MAX)); @@ -1399,7 +1430,7 @@ static void reader_init_network_partition (struct ddsi_reader *rd) rd->uc_as = np->uc_addresses; rd->mc_as = np->asm_addresses; #ifdef DDS_HAS_SSM - if (np->ssm_addresses != NULL && (gv->config.allowMulticast & DDSI_AMC_SSM)) + if (np->ssm_addresses != NULL && nwpart_includes_ssm_enabled_interfaces (gv, np)) rd->favours_ssm = 1; #endif } @@ -1418,7 +1449,7 @@ static void reader_init_network_partition (struct ddsi_reader *rd) /* Note: SSM requires NETWORK_PARTITIONS; if network partitions do not override the default, we should check whether the default is an SSM address. */ - if (ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc) && (gv->config.allowMulticast & DDSI_AMC_SSM)) + if (ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc) && nwpart_includes_ssm_enabled_interfaces (gv, np)) rd->favours_ssm = 1; } #endif diff --git a/src/core/ddsi/src/ddsi_init.c b/src/core/ddsi/src/ddsi_init.c index 4c1820fad4..fcf9d42760 100644 --- a/src/core/ddsi/src/ddsi_init.c +++ b/src/core/ddsi/src/ddsi_init.c @@ -170,9 +170,13 @@ static enum make_uc_sockets_ret make_uc_sockets (struct ddsi_domaingv *gv, uint3 { assert (ppid == DDSI_PARTICIPANT_INDEX_NONE); *pdata = *pdisc = ddsi_get_port (&gv->config, DDSI_PORT_MULTI_DISC, ppid); - if (gv->config.allowMulticast) + if (gv->interfaces[0].allow_multicast) { /* FIXME: ugly hack - but we'll fix up after creating the multicast sockets */ +#ifndef NDEBUG // these are supposed to be consistent if DDSI_MSM_NO_UNICAST + for (int i = 1; i < gv->n_interfaces; i++) + assert (gv->interfaces[i].allow_multicast == gv->interfaces[0].allow_multicast); +#endif return MUSRET_SUCCESS; } } @@ -546,17 +550,31 @@ int ddsi_config_prep (struct ddsi_domaingv *gv, struct ddsi_cfgst *cfgst) gv->config.extDomainId.isdefault = 0; } + // Cater for some highly unusual configuration ... + if (gv->config.many_sockets_mode == DDSI_MSM_NO_UNICAST && gv->config.participantIndex == DDSI_PARTICIPANT_INDEX_DEFAULT) + { + gv->config.participantIndex = DDSI_PARTICIPANT_INDEX_NONE; + } + { char message[256]; int32_t ppidx; - if (gv->config.participantIndex >= 0 || gv->config.participantIndex == DDSI_PARTICIPANT_INDEX_NONE) - ppidx = gv->config.participantIndex; - else if (gv->config.participantIndex == DDSI_PARTICIPANT_INDEX_AUTO) - ppidx = gv->config.maxAutoParticipantIndex; - else + switch (gv->config.participantIndex) { - assert (0); - ppidx = 0; + case DDSI_PARTICIPANT_INDEX_NONE: + ppidx = gv->config.participantIndex; + break; + case DDSI_PARTICIPANT_INDEX_AUTO: + case DDSI_PARTICIPANT_INDEX_DEFAULT: + // check worst-case is valid, and for default this is the maximum value because we + // don't know yet whether it'll be "none" or "auto" + ppidx = gv->config.maxAutoParticipantIndex; + break; + default: + // configuration handling is supposed to ensure non-negative numbers + assert (gv->config.participantIndex >= 0); + ppidx = gv->config.participantIndex; + break; } if (!ddsi_valid_portmapping (&gv->config, ppidx, message, sizeof (message))) { @@ -669,15 +687,30 @@ static void joinleave_spdp_defmcip_helper (const ddsi_xlocator_t *loc, void *var static int joinleave_spdp_defmcip (struct ddsi_domaingv *gv, int dojoin) { + bool include_spdp = false, include_default = false; + // FIXME: should do this per-interface here, not iterate over interfaces again deeper down the callstack + // That means replacing the interfaces on which to receive multicasts + // Doing that is probably a good plan anyway ... + for (int i = 0; i < gv->n_interfaces; i++) + { + if (gv->interfaces[i].allow_multicast & DDSI_AMC_SPDP) + include_spdp = true; + if (gv->interfaces[i].allow_multicast & ~DDSI_AMC_SPDP) + include_default = true; + } + if (!(include_spdp || include_default)) + { + // No interest in multicasts at all, so no point in pretending we joined it either (which ddsi_join_mc + // will do) if we then know that it gets ignored deeper down. See FIXME above ... + return 0; + } + + struct joinleave_spdp_defmcip_helper_arg arg = { .gv = gv, .errcount = 0, .dojoin = dojoin }; /* Addrset provides an easy way to filter out duplicates */ - struct joinleave_spdp_defmcip_helper_arg arg; struct ddsi_addrset *as = ddsi_new_addrset (); - arg.gv = gv; - arg.errcount = 0; - arg.dojoin = dojoin; - if (gv->config.allowMulticast & DDSI_AMC_SPDP) + if (include_spdp) ddsi_add_locator_to_addrset (gv, as, &gv->loc_spdp_mc); - if (gv->config.allowMulticast & ~DDSI_AMC_SPDP) + if (include_default) ddsi_add_locator_to_addrset (gv, as, &gv->loc_default_mc); ddsi_addrset_forall (as, joinleave_spdp_defmcip_helper, &arg); ddsi_unref_addrset (as); @@ -943,7 +976,11 @@ static int setup_and_start_recv_threads (struct ddsi_domaingv *gv) gv->recv_threads[0].arg.mode = DDSI_RTM_MANY; if (gv->m_factory->m_connless && gv->config.many_sockets_mode != DDSI_MSM_NO_UNICAST && multi_recv_thr) { - if (ddsi_is_mcaddr (gv, &gv->loc_default_mc) && !ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc) && (gv->config.allowMulticast & DDSI_AMC_ASM)) + bool allow_asm_mc = false; + for (int i = 0; i < gv->n_interfaces && !allow_asm_mc; i++) + if (gv->interfaces[i].allow_multicast & DDSI_AMC_ASM) + allow_asm_mc = true; + if (ddsi_is_mcaddr (gv, &gv->loc_default_mc) && !ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc) && allow_asm_mc) { /* Multicast enabled, but it isn't an SSM address => handle data multicasts on a separate thread (the trouble with SSM addresses is that we only join matching writers, which our own sockets typically would not be) */ gv->recv_threads[gv->n_recv_threads].name = "recvMC"; @@ -1068,7 +1105,7 @@ static void free_conns (struct ddsi_domaingv *gv) } } -static int create_vnet_interface_for_psmx (struct ddsi_domaingv *gv, const char *psmx_instance_name, const ddsi_locator_t locator) +static int create_vnet_interface_for_psmx (struct ddsi_domaingv *gv, const char *psmx_instance_name, const ddsi_locator_t locator, bool mc_capable) { assert (gv); assert (psmx_instance_name); @@ -1094,11 +1131,12 @@ static int create_vnet_interface_for_psmx (struct ddsi_domaingv *gv, const char intf->loc = locator; intf->extloc = intf->loc; intf->loopback = false; - intf->mc_capable = true; // FIXME: matters most for discovery, this avoids auto-lack-of-multicast-mitigation + intf->mc_capable = mc_capable; // FIXME: matters most for discovery, this avoids auto-lack-of-multicast-mitigation intf->mc_flaky = false; intf->name = ddsrt_strdup (psmx_instance_name); intf->point_to_point = false; intf->is_psmx = true; + intf->allow_multicast = mc_capable ? DDSI_AMC_TRUE : DDSI_AMC_FALSE; // align with mc_capable intf->netmask.kind = DDSI_LOCATOR_KIND_INVALID; intf->netmask.port = DDSI_LOCATOR_PORT_INVALID; memset (intf->netmask.address, 0, sizeof (intf->netmask.address) - 6); @@ -1173,7 +1211,6 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm gv->config.enable_uc_locators = 1; /* TCP affects what features are supported/required */ gv->config.many_sockets_mode = DDSI_MSM_SINGLE_UNICAST; - gv->config.allowMulticast = DDSI_AMC_FALSE; if (ddsi_tcp_init (gv) < 0) goto err_udp_tcp_init; gv->m_factory = ddsi_factory_find (gv, gv->config.transport_selector == DDSI_TRANS_TCP ? "tcp" : "tcp6"); @@ -1207,48 +1244,105 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm goto err_gather_nwif; } + if (!gv->m_factory->m_connless) + { + // equivalent to all behaviour where global setting was simply forced to false + // FIXME: it'd perhaps be nicer to give an error if any of these is explicitly set to allow multicast when we cannot do that + gv->config.allowMulticast = DDSI_AMC_FALSE; + for (int i = 0; i < gv->n_interfaces; i++) + gv->interfaces[i].allow_multicast = DDSI_AMC_FALSE; + } + + if (gv->config.many_sockets_mode == DDSI_MSM_NO_UNICAST) + { + // only supported if there's at most a single real interface, otherwise it is too complicated for now + bool all_allow_mc = true, none_allow_mc = true; + for (int i = 0; i < gv->n_interfaces; i++) + { + if (gv->interfaces[i].allow_multicast) + none_allow_mc = false; + else + all_allow_mc = false; + } + if (!(all_allow_mc || none_allow_mc)) + { + GVERROR ("ManySocketsMode \"none\" is incompatible with multiple interfaces where multicast capability differs\n"); + goto err_gather_nwif; + } + } + if (psmx_locators != NULL) { + // set multicast flags to match the real interface; not quite right + // because it isn't a real interface, so + // FIXME: add a "fake"/"real" flag to interface + bool none_allow_mc = true; + for (int i = 0; i < gv->n_interfaces && none_allow_mc; i++) + if (gv->interfaces[i].allow_multicast) + none_allow_mc = false; for (uint32_t i = 0; i < psmx_locators->length; i++) { - if (create_vnet_interface_for_psmx (gv, psmx_locators->instances[i].psmx_instance_name, psmx_locators->instances[i].locator) < 0) + if (create_vnet_interface_for_psmx (gv, psmx_locators->instances[i].psmx_instance_name, psmx_locators->instances[i].locator, !none_allow_mc) < 0) goto err_psmx; } } - if (gv->config.allowMulticast) + // All interfaces allow SPDP multicast: + // - default ppidx = NONE if no peers else AUTO, default peers = {} + // + // Some interfaces allow SPDP multicast: + // - default ppidx = AUTO, default peers = {} + // + // No interfaces allow SPDP multicast: + // - default ppidx = AUTO, default peers = { localhost } + // + // MaxAutoParticipantIndex -> 100 -+ + // UnicastSPDPInterval -> 30s |_ perhaps adding something like this + // @silentports -> 5min | would make sense? + // @dropafter -> 30min -+ + bool add_self_to_as_disc = false; + if (gv->config.participantIndex == DDSI_PARTICIPANT_INDEX_DEFAULT) { +#ifndef NDEBUG for (int i = 0; i < gv->n_interfaces; i++) { - if (!gv->interfaces[i].mc_capable) - { - GVWARNING ("selected interface \"%s\" is not multicast-capable: disabling multicast\n", gv->interfaces[i].name); - gv->config.allowMulticast = DDSI_AMC_FALSE; - /* ensure discovery can work: firstly, that the process will be reachable on a "well-known" port - number, and secondly, that the local interface's IP address gets added to the discovery - address set */ - gv->config.participantIndex = DDSI_PARTICIPANT_INDEX_AUTO; - } - else if (gv->config.allowMulticast & DDSI_AMC_DEFAULT) - { - /* default is dependent on network interface type: if multicast is believed to be flaky, - use multicast only for SPDP packets */ - assert ((gv->config.allowMulticast & ~DDSI_AMC_DEFAULT) == 0); - if (gv->interfaces[i].mc_flaky) - { - gv->config.allowMulticast = DDSI_AMC_SPDP; - GVLOG (DDS_LC_CONFIG, "presumed flaky multicast, use for SPDP only\n"); - } - else - { - GVLOG (DDS_LC_CONFIG, "presumed robust multicast support, use for everything\n"); - gv->config.allowMulticast = DDSI_AMC_TRUE; - } - } + // sanity check that by now we have eliminated "default" from allow_multicast and + // that no bits in allow_multicast are set if the interface is not capable of + // handling multicast + assert ((gv->interfaces[i].allow_multicast & DDSI_AMC_DEFAULT) == 0); + assert (gv->interfaces[i].allow_multicast == 0 || gv->interfaces[i].mc_capable); + } +#endif + bool all_allow_spdp_mc = true, none_allow_spdp_mc = true; + for (int i = 0; i < gv->n_interfaces; i++) + { + if (gv->interfaces[i].allow_multicast & DDSI_AMC_SPDP) + none_allow_spdp_mc = false; + else + all_allow_spdp_mc = false; + } + if (all_allow_spdp_mc && gv->config.peers == NULL) + { + GVTRACE ("all interfaces allow spdp multicast, no peers defined: defaulting participant index to \"none\"\n"); + gv->config.participantIndex = DDSI_PARTICIPANT_INDEX_NONE; + } else if (all_allow_spdp_mc) + { + GVTRACE ("all interfaces allow spdp multicast, but peers defined: defaulting participant index to \"auto\"\n"); + gv->config.participantIndex = DDSI_PARTICIPANT_INDEX_AUTO; + } + else + { + GVTRACE ("some interfaces disallow spdp multicast: defaulting participant index to \"auto\"\n"); + gv->config.participantIndex = DDSI_PARTICIPANT_INDEX_AUTO; + } + if (gv->config.add_localhost_to_peers == DDSI_BOOLDEF_TRUE || + (none_allow_spdp_mc && gv->config.add_localhost_to_peers != DDSI_BOOLDEF_FALSE)) + { + // add self to as_disc, but only once we have everything set up to actually do that + add_self_to_as_disc = true; } } - assert ((gv->config.allowMulticast & DDSI_AMC_DEFAULT) == 0); if (set_recvips (gv) < 0) goto err_set_recvips; if (set_spdp_address (gv) < 0) @@ -1406,6 +1500,7 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm if (gv->m_factory->m_connless) { + assert (gv->config.participantIndex != DDSI_PARTICIPANT_INDEX_DEFAULT); if (gv->config.participantIndex >= 0 || gv->config.participantIndex == DDSI_PARTICIPANT_INDEX_NONE) { enum make_uc_sockets_ret musret = make_uc_sockets (gv, &port_disc_uc, &port_data_uc, gv->config.participantIndex); @@ -1480,10 +1575,15 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm gv->mship = ddsi_new_mcgroup_membership(); if (gv->m_factory->m_connless) { - if (!(gv->config.many_sockets_mode == DDSI_MSM_NO_UNICAST && gv->config.allowMulticast)) + bool allow_multicast = false; + for (int i = 0; i < gv->n_interfaces && !allow_multicast; i++) + if (gv->interfaces[i].allow_multicast) + allow_multicast = true; + + if (!(gv->config.many_sockets_mode == DDSI_MSM_NO_UNICAST && allow_multicast)) GVLOG (DDS_LC_CONFIG, "Unicast Ports: discovery %"PRIu32" data %"PRIu32"\n", ddsi_conn_port (gv->disc_conn_uc), ddsi_conn_port (gv->data_conn_uc)); - if (gv->config.allowMulticast) + if (allow_multicast) { if (!create_multicast_sockets (gv)) goto err_mc_conn; @@ -1548,7 +1648,7 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm for (int i = 0; i < gv->n_interfaces; i++) { const struct ddsi_tran_qos qos = { - .m_purpose = (gv->config.allowMulticast ? DDSI_TRAN_QOS_XMIT_MC : DDSI_TRAN_QOS_XMIT_UC), + .m_purpose = (gv->interfaces[i].allow_multicast ? DDSI_TRAN_QOS_XMIT_MC : DDSI_TRAN_QOS_XMIT_UC), .m_diffserv = 0, .m_interface = &gv->interfaces[i] }; @@ -1573,11 +1673,8 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm goto err_network_partition_config; // Join SPDP, default multicast addresses if enabled - if (gv->m_factory->m_connless && gv->config.allowMulticast) - { - if (joinleave_spdp_defmcip (gv, 1) < 0) - goto err_joinleave_spdp; - } + if (gv->m_factory->m_connless && joinleave_spdp_defmcip (gv, 1) < 0) + goto err_joinleave_spdp; /* Create event queues */ gv->xevents = ddsi_xeventq_new (gv, gv->config.max_queued_rexmit_bytes, gv->config.max_queued_rexmit_msgs); @@ -1587,23 +1684,25 @@ int ddsi_init (struct ddsi_domaingv *gv, struct ddsi_psmx_instance_locators *psm #endif gv->as_disc = ddsi_new_addrset (); - if (gv->config.allowMulticast & DDSI_AMC_SPDP) - ddsi_add_locator_to_addrset (gv, gv->as_disc, &gv->loc_spdp_mc); - /* If multicast was enabled but not available, always add the local interface to the discovery address set. - Conversion via string and add_peer_addresses has the benefit that the port number expansion happens - automatically. */ for (int i = 0; i < gv->n_interfaces; i++) { - if (!gv->interfaces[i].mc_capable && gv->config.peers == NULL) + if ((gv->interfaces[i].allow_multicast & DDSI_AMC_SPDP) && + ddsi_factory_supports (gv->xmit_conns[i]->m_factory, gv->loc_spdp_mc.kind)) { - struct ddsi_config_peer_listelem peer_local; - char local_addr[DDSI_LOCSTRLEN]; - ddsi_locator_to_string_no_port (local_addr, sizeof (local_addr), &gv->interfaces[i].loc); - peer_local.next = NULL; - peer_local.peer = local_addr; - add_peer_addresses (gv, gv->as_disc, &peer_local); + const ddsi_xlocator_t xloc = { .conn = gv->xmit_conns[i], .c = gv->loc_spdp_mc }; + ddsi_add_xlocator_to_addrset (gv, gv->as_disc, &xloc); } } + if (add_self_to_as_disc) + { + struct ddsi_config_peer_listelem peer_local; + char local_addr[DDSI_LOCSTRLEN]; + ddsi_locator_to_string_no_port (local_addr, sizeof (local_addr), &gv->interfaces[0].loc); + GVTRACE ("adding self (%s)\n", local_addr); + peer_local.next = NULL; + peer_local.peer = local_addr; + add_peer_addresses (gv, gv->as_disc, &peer_local); + } if (gv->config.peers) { add_peer_addresses (gv, gv->as_disc, gv->config.peers); diff --git a/src/core/ddsi/src/ddsi_mcgroup.c b/src/core/ddsi/src/ddsi_mcgroup.c index 9f9ae23e68..dd49c72ff9 100644 --- a/src/core/ddsi/src/ddsi_mcgroup.c +++ b/src/core/ddsi/src/ddsi_mcgroup.c @@ -207,7 +207,11 @@ static int joinleave_mcgroups (const struct ddsi_domaingv *gv, struct ddsi_tran_ int i, fails = 0, oks = 0; for (i = 0; i < gv->n_interfaces; i++) { - if (gv->interfaces[i].mc_capable) + // FIXME: not quite right to not take into account whether the address is for data or SPDP + // but it is still an improvement over only looking at whether it can handle multicast, and + // to do it right requires tracking multicast subscriptions per interface as well, so quite + // a bit of changing things around. Better to do that in a follow-up step + if (gv->interfaces[i].allow_multicast) { if (gv->recvips_mode == DDSI_RECVIPS_MODE_ALL || gv->recvips_mode == DDSI_RECVIPS_MODE_PREFERRED || interface_in_recvips_p (gv->recvips, &gv->interfaces[i])) { diff --git a/src/core/ddsi/src/ddsi_nwinterfaces.c b/src/core/ddsi/src/ddsi_nwinterfaces.c index 3a9cfaba36..b7bbb62fa7 100644 --- a/src/core/ddsi/src/ddsi_nwinterfaces.c +++ b/src/core/ddsi/src/ddsi_nwinterfaces.c @@ -224,6 +224,7 @@ static enum maybe_add_interface_result maybe_add_interface (struct ddsi_domaingv if ((dst->name = ddsrt_strdup (ifa->name)) == NULL) return MAI_OUT_OF_MEMORY; dst->priority = loopback ? 2 : 0; + dst->allow_multicast = DDSI_AMC_DEFAULT; dst->prefer_multicast = 0; *qout = q; return MAI_ADDED; @@ -369,6 +370,7 @@ static bool add_matching_interface (struct ddsi_domaingv *gv, struct interface_p } act_iface->prefer_multicast = ((unsigned) cfg_iface->cfg.prefer_multicast) & 1; + act_iface->allow_multicast = cfg_iface->cfg.allow_multicast; if (!cfg_iface->cfg.priority.isdefault) act_iface->priority = cfg_iface->cfg.priority.value; @@ -406,6 +408,62 @@ static void log_arbitrary_selection (struct ddsi_domaingv *gv, const struct ddsi GVLOG (DDS_LC_INFO, "\n"); } +static bool set_and_check_link_local (struct ddsi_domaingv *gv) +{ + gv->using_link_local_intf = false; + for (int i = 0; i < gv->n_interfaces; i++) + { + if (!gv->interfaces[i].link_local) + continue; + else if (!gv->using_link_local_intf) + gv->using_link_local_intf = true; + else + { + GVERROR ("multiple interfaces selected with at least one having a link-local address\n"); + return false; + } + } + return true; +} + +static bool set_and_check_allow_multicast (struct ddsi_domaingv *gv) +{ + for (int i = 0; i < gv->n_interfaces; i++) + { + struct ddsi_network_interface * const act_iface = &gv->interfaces[i]; + if (act_iface->allow_multicast == DDSI_AMC_DEFAULT) + { + if (!act_iface->mc_capable) + act_iface->allow_multicast = 0; + else if (gv->config.allowMulticast != DDSI_AMC_DEFAULT) + { + assert ((gv->config.allowMulticast & DDSI_AMC_DEFAULT) == 0); + act_iface->allow_multicast = gv->config.allowMulticast; + } + else if (act_iface->mc_flaky) + { + act_iface->allow_multicast = DDSI_AMC_SPDP; + GVLOG (DDS_LC_CONFIG, "%s: presumed flaky multicast, use for SPDP only\n", act_iface->name); + } + else + { + GVLOG (DDS_LC_CONFIG, "%s: presumed robust multicast support, use for everything\n", act_iface->name); + act_iface->allow_multicast = DDSI_AMC_TRUE; + } + } + assert ((act_iface->allow_multicast & DDSI_AMC_DEFAULT) == 0); + if (!act_iface->mc_capable) + { + if (act_iface->prefer_multicast || act_iface->allow_multicast) + { + GVERROR ("interface %s: not multicast capable but prefer_multicast and/or allow_multicast set\n", act_iface->name); + return false; + } + } + } + return true; +} + int ddsi_gather_network_interfaces (struct ddsi_domaingv *gv) { char addrbuf[DDSI_LOCSTRLEN]; @@ -483,19 +541,10 @@ int ddsi_gather_network_interfaces (struct ddsi_domaingv *gv) ddsrt_free(matches); } - gv->using_link_local_intf = false; - for (int i = 0; i < gv->n_interfaces && ok; i++) - { - if (!gv->interfaces[i].link_local) - continue; - else if (!gv->using_link_local_intf) - gv->using_link_local_intf = true; - else - { - GVERROR ("multiple interfaces selected with at least one having a link-local address\n"); - ok = false; - } - } + if (ok && !set_and_check_link_local (gv)) + ok = false; + if (ok && !set_and_check_allow_multicast (gv)) + ok = false; for (size_t i = 0; i < n_interfaces; i++) if (interfaces[i].name) @@ -517,7 +566,20 @@ int ddsi_gather_network_interfaces (struct ddsi_domaingv *gv) GVLOG (DDS_LC_CONFIG, "selected interfaces: "); for (int i = 0; i < gv->n_interfaces; i++) - GVLOG (DDS_LC_CONFIG, "%s%s (index %"PRIu32" priority %"PRId32")", (i == 0) ? "" : ", ", gv->interfaces[i].name, gv->interfaces[i].if_index, gv->interfaces[i].priority); + { + char flagstr[100] = ""; + int flagpos = 0; + if (gv->interfaces[i].allow_multicast & DDSI_AMC_SPDP) + flagpos += snprintf (flagstr + flagpos, sizeof (flagstr) - (size_t) flagpos, "%sspdp", (flagpos > 0) ? "," : ""); + if (gv->interfaces[i].allow_multicast & DDSI_AMC_ASM) + flagpos += snprintf (flagstr + flagpos, sizeof (flagstr) - (size_t) flagpos, "%sasm", (flagpos > 0) ? "," : ""); + if (gv->interfaces[i].allow_multicast & DDSI_AMC_SSM) + flagpos += snprintf (flagstr + flagpos, sizeof (flagstr) - (size_t) flagpos, "%sssm", (flagpos > 0) ? "," : ""); + (void) flagpos; + GVLOG (DDS_LC_CONFIG, "%s%s (index %"PRIu32" priority %"PRId32" mc {%s})", + (i == 0) ? "" : ", ", gv->interfaces[i].name, gv->interfaces[i].if_index, gv->interfaces[i].priority, + flagstr); + } GVLOG (DDS_LC_CONFIG, "\n"); return 1; } diff --git a/src/core/ddsi/src/ddsi_proxy_endpoint.c b/src/core/ddsi/src/ddsi_proxy_endpoint.c index 92021135f4..14ca192e41 100644 --- a/src/core/ddsi/src/ddsi_proxy_endpoint.c +++ b/src/core/ddsi/src/ddsi_proxy_endpoint.c @@ -160,6 +160,23 @@ static void proxy_endpoint_common_fini (struct ddsi_entity_common *e, struct dds ddsi_entity_common_fini (e); } +#ifdef DDS_HAS_SSM +static void addrset_interfaces_allow_ssm_helper (const ddsi_xlocator_t *xloc, void *vssm_allowed) +{ + bool *ssm_allowed = vssm_allowed; + if (xloc->conn->m_interf->allow_multicast & DDSI_AMC_SSM) + *ssm_allowed = true; +} + +static bool addrset_interfaces_allow_ssm (struct ddsi_addrset *as) +{ + // FIXME: const variant of addrset_forall would be nice here + bool ssm_allowed = false; + ddsi_addrset_forall (as, addrset_interfaces_allow_ssm_helper, &ssm_allowed); + return ssm_allowed; +} +#endif + #ifdef DDS_HAS_TYPELIB bool ddsi_is_proxy_endpoint (const struct ddsi_entity_common *e) { @@ -263,7 +280,7 @@ int ddsi_new_proxy_writer (struct ddsi_domaingv *gv, const struct ddsi_guid *ppg pwr->have_seen_heartbeat = !isreliable; pwr->local_matching_inprogress = 1; #ifdef DDS_HAS_SSM - pwr->supports_ssm = (ddsi_addrset_contains_ssm (gv, as) && gv->config.allowMulticast & DDSI_AMC_SSM) ? 1 : 0; + pwr->supports_ssm = (ddsi_addrset_contains_ssm (gv, as) && addrset_interfaces_allow_ssm (as)) ? 1 : 0; #endif pwr->local_psmx = proxy_is_local_psmx(gv, as); if (plist->present & PP_CYCLONE_REDUNDANT_NETWORKING) @@ -350,7 +367,7 @@ void ddsi_update_proxy_writer (struct ddsi_proxy_writer *pwr, ddsi_seqno_t seq, if (! ddsi_addrset_eq_onesidederr (pwr->c.as, as)) { #ifdef DDS_HAS_SSM - pwr->supports_ssm = (ddsi_addrset_contains_ssm (pwr->e.gv, as) && pwr->e.gv->config.allowMulticast & DDSI_AMC_SSM) ? 1 : 0; + pwr->supports_ssm = (ddsi_addrset_contains_ssm (pwr->e.gv, as) && addrset_interfaces_allow_ssm (as)) ? 1 : 0; #endif ddsi_unref_addrset (pwr->c.as); ddsi_ref_addrset (as); @@ -581,7 +598,7 @@ int ddsi_new_proxy_reader (struct ddsi_domaingv *gv, const struct ddsi_guid *ppg prd->deleting = 0; #ifdef DDS_HAS_SSM - prd->favours_ssm = (favours_ssm && gv->config.allowMulticast & DDSI_AMC_SSM) ? 1 : 0; + prd->favours_ssm = (favours_ssm && addrset_interfaces_allow_ssm (as)) ? 1 : 0; #endif prd->local_psmx = proxy_is_local_psmx (gv, as); prd->is_fict_trans_reader = 0;