diff --git a/docs/manual/config/config_file_reference.rst b/docs/manual/config/config_file_reference.rst index a9c4280258..8f9b08018c 100644 --- a/docs/manual/config/config_file_reference.rst +++ b/docs/manual/config/config_file_reference.rst @@ -202,12 +202,13 @@ Text 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 less than 120, 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. + * 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] @@ -773,7 +818,7 @@ The default value is: ``default`` //CycloneDDS/Domain/Internal ============================ -Children: :ref:`AccelerateRexmitBlockSize`, :ref:`AckDelay`, :ref:`AutoReschedNackDelay`, :ref:`BuiltinEndpointSet`, :ref:`BurstSize`, :ref:`ControlTopic`, :ref:`DefragReliableMaxSamples`, :ref:`DefragUnreliableMaxSamples`, :ref:`DeliveryQueueMaxSamples`, :ref:`EnableExpensiveChecks`, :ref:`GenerateKeyhash`, :ref:`HeartbeatInterval`, :ref:`LateAckMode`, :ref:`LivelinessMonitoring`, :ref:`MaxParticipants`, :ref:`MaxQueuedRexmitBytes`, :ref:`MaxQueuedRexmitMessages`, :ref:`MaxSampleSize`, :ref:`MeasureHbToAckLatency`, :ref:`MonitorPort`, :ref:`MultipleReceiveThreads`, :ref:`NackDelay`, :ref:`PreEmptiveAckDelay`, :ref:`PrimaryReorderMaxSamples`, :ref:`PrioritizeRetransmit`, :ref:`RediscoveryBlacklistDuration`, :ref:`RetransmitMerging`, :ref:`RetransmitMergingPeriod`, :ref:`RetryOnRejectBestEffort`, :ref:`SPDPResponseMaxDelay`, :ref:`SecondaryReorderMaxSamples`, :ref:`SocketReceiveBufferSize`, :ref:`SocketSendBufferSize`, :ref:`SquashParticipants`, :ref:`SynchronousDeliveryLatencyBound`, :ref:`SynchronousDeliveryPriorityThreshold`, :ref:`Test`, :ref:`UnicastResponseToSPDPMessages`, :ref:`UseMulticastIfMreqn`, :ref:`Watermarks`, :ref:`WriterLingerDuration` +Children: :ref:`AccelerateRexmitBlockSize`, :ref:`AckDelay`, :ref:`AutoReschedNackDelay`, :ref:`BuiltinEndpointSet`, :ref:`BurstSize`, :ref:`ControlTopic`, :ref:`DefragReliableMaxSamples`, :ref:`DefragUnreliableMaxSamples`, :ref:`DeliveryQueueMaxSamples`, :ref:`EnableExpensiveChecks`, :ref:`ExtendedPacketInfo`, :ref:`GenerateKeyhash`, :ref:`HeartbeatInterval`, :ref:`LateAckMode`, :ref:`LivelinessMonitoring`, :ref:`MaxParticipants`, :ref:`MaxQueuedRexmitBytes`, :ref:`MaxQueuedRexmitMessages`, :ref:`MaxSampleSize`, :ref:`MeasureHbToAckLatency`, :ref:`MonitorPort`, :ref:`MultipleReceiveThreads`, :ref:`NackDelay`, :ref:`PreEmptiveAckDelay`, :ref:`PrimaryReorderMaxSamples`, :ref:`PrioritizeRetransmit`, :ref:`RediscoveryBlacklistDuration`, :ref:`RetransmitMerging`, :ref:`RetransmitMergingPeriod`, :ref:`RetryOnRejectBestEffort`, :ref:`SPDPResponseMaxDelay`, :ref:`SecondaryReorderMaxSamples`, :ref:`SocketReceiveBufferSize`, :ref:`SocketSendBufferSize`, :ref:`SquashParticipants`, :ref:`SynchronousDeliveryLatencyBound`, :ref:`SynchronousDeliveryPriorityThreshold`, :ref:`Test`, :ref:`UnicastResponseToSPDPMessages`, :ref:`UseMulticastIfMreqn`, :ref:`Watermarks`, :ref:`WriterLingerDuration` The Internal elements deal with a variety of settings that are evolving and that are not necessarily fully supported. For the majority of the Internal settings the functionality is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options. @@ -954,6 +999,18 @@ In addition, there is the keyword all that enables all checks. The default value is: ```` +.. _`//CycloneDDS/Domain/Internal/ExtendedPacketInfo`: + +//CycloneDDS/Domain/Internal/ExtendedPacketInfo +----------------------------------------------- + +Boolean + +Whether to enable the IP\_PKTINFO on UDP sockets to get hold of the packet destination address and interface on which it was received. This allows for better filtering on discovery packets, but comes at a small performance penalty. + +The default value is: ``true`` + + .. _`//CycloneDDS/Domain/Internal/GenerateKeyhash`: //CycloneDDS/Domain/Internal/GenerateKeyhash @@ -2642,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[570f67bd3080674a4bad53d9580a8bb7ad1e6e4d] + generated from ddsi_config.h[9f834d377bdea61bea6507feed2fc4a8924dc02e] generated from ddsi__cfgunits.h[bd22f0c0ed210501d0ecd3b07c992eca549ef5aa] - generated from ddsi__cfgelems.h[d4d0b8c7cf61f0a1cfa4b62e02458cf7b8962536] - generated from ddsi_config.c[efeae198a5e12ca8977a655216470564b5c44b64] + 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 3227bcdbe2..1c0571abd7 100644 --- a/docs/manual/options.md +++ b/docs/manual/options.md @@ -129,19 +129,33 @@ Text 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 less than 120, 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. + * 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 @@ -517,7 +552,7 @@ The default value is: `default` ### //CycloneDDS/Domain/Internal -Children: [AccelerateRexmitBlockSize](#cycloneddsdomaininternalacceleraterexmitblocksize), [AckDelay](#cycloneddsdomaininternalackdelay), [AutoReschedNackDelay](#cycloneddsdomaininternalautoreschednackdelay), [BuiltinEndpointSet](#cycloneddsdomaininternalbuiltinendpointset), [BurstSize](#cycloneddsdomaininternalburstsize), [ControlTopic](#cycloneddsdomaininternalcontroltopic), [DefragReliableMaxSamples](#cycloneddsdomaininternaldefragreliablemaxsamples), [DefragUnreliableMaxSamples](#cycloneddsdomaininternaldefragunreliablemaxsamples), [DeliveryQueueMaxSamples](#cycloneddsdomaininternaldeliveryqueuemaxsamples), [EnableExpensiveChecks](#cycloneddsdomaininternalenableexpensivechecks), [GenerateKeyhash](#cycloneddsdomaininternalgeneratekeyhash), [HeartbeatInterval](#cycloneddsdomaininternalheartbeatinterval), [LateAckMode](#cycloneddsdomaininternallateackmode), [LivelinessMonitoring](#cycloneddsdomaininternallivelinessmonitoring), [MaxParticipants](#cycloneddsdomaininternalmaxparticipants), [MaxQueuedRexmitBytes](#cycloneddsdomaininternalmaxqueuedrexmitbytes), [MaxQueuedRexmitMessages](#cycloneddsdomaininternalmaxqueuedrexmitmessages), [MaxSampleSize](#cycloneddsdomaininternalmaxsamplesize), [MeasureHbToAckLatency](#cycloneddsdomaininternalmeasurehbtoacklatency), [MonitorPort](#cycloneddsdomaininternalmonitorport), [MultipleReceiveThreads](#cycloneddsdomaininternalmultiplereceivethreads), [NackDelay](#cycloneddsdomaininternalnackdelay), [PreEmptiveAckDelay](#cycloneddsdomaininternalpreemptiveackdelay), [PrimaryReorderMaxSamples](#cycloneddsdomaininternalprimaryreordermaxsamples), [PrioritizeRetransmit](#cycloneddsdomaininternalprioritizeretransmit), [RediscoveryBlacklistDuration](#cycloneddsdomaininternalrediscoveryblacklistduration), [RetransmitMerging](#cycloneddsdomaininternalretransmitmerging), [RetransmitMergingPeriod](#cycloneddsdomaininternalretransmitmergingperiod), [RetryOnRejectBestEffort](#cycloneddsdomaininternalretryonrejectbesteffort), [SPDPResponseMaxDelay](#cycloneddsdomaininternalspdpresponsemaxdelay), [SecondaryReorderMaxSamples](#cycloneddsdomaininternalsecondaryreordermaxsamples), [SocketReceiveBufferSize](#cycloneddsdomaininternalsocketreceivebuffersize), [SocketSendBufferSize](#cycloneddsdomaininternalsocketsendbuffersize), [SquashParticipants](#cycloneddsdomaininternalsquashparticipants), [SynchronousDeliveryLatencyBound](#cycloneddsdomaininternalsynchronousdeliverylatencybound), [SynchronousDeliveryPriorityThreshold](#cycloneddsdomaininternalsynchronousdeliveryprioritythreshold), [Test](#cycloneddsdomaininternaltest), [UnicastResponseToSPDPMessages](#cycloneddsdomaininternalunicastresponsetospdpmessages), [UseMulticastIfMreqn](#cycloneddsdomaininternalusemulticastifmreqn), [Watermarks](#cycloneddsdomaininternalwatermarks), [WriterLingerDuration](#cycloneddsdomaininternalwriterlingerduration) +Children: [AccelerateRexmitBlockSize](#cycloneddsdomaininternalacceleraterexmitblocksize), [AckDelay](#cycloneddsdomaininternalackdelay), [AutoReschedNackDelay](#cycloneddsdomaininternalautoreschednackdelay), [BuiltinEndpointSet](#cycloneddsdomaininternalbuiltinendpointset), [BurstSize](#cycloneddsdomaininternalburstsize), [ControlTopic](#cycloneddsdomaininternalcontroltopic), [DefragReliableMaxSamples](#cycloneddsdomaininternaldefragreliablemaxsamples), [DefragUnreliableMaxSamples](#cycloneddsdomaininternaldefragunreliablemaxsamples), [DeliveryQueueMaxSamples](#cycloneddsdomaininternaldeliveryqueuemaxsamples), [EnableExpensiveChecks](#cycloneddsdomaininternalenableexpensivechecks), [ExtendedPacketInfo](#cycloneddsdomaininternalextendedpacketinfo), [GenerateKeyhash](#cycloneddsdomaininternalgeneratekeyhash), [HeartbeatInterval](#cycloneddsdomaininternalheartbeatinterval), [LateAckMode](#cycloneddsdomaininternallateackmode), [LivelinessMonitoring](#cycloneddsdomaininternallivelinessmonitoring), [MaxParticipants](#cycloneddsdomaininternalmaxparticipants), [MaxQueuedRexmitBytes](#cycloneddsdomaininternalmaxqueuedrexmitbytes), [MaxQueuedRexmitMessages](#cycloneddsdomaininternalmaxqueuedrexmitmessages), [MaxSampleSize](#cycloneddsdomaininternalmaxsamplesize), [MeasureHbToAckLatency](#cycloneddsdomaininternalmeasurehbtoacklatency), [MonitorPort](#cycloneddsdomaininternalmonitorport), [MultipleReceiveThreads](#cycloneddsdomaininternalmultiplereceivethreads), [NackDelay](#cycloneddsdomaininternalnackdelay), [PreEmptiveAckDelay](#cycloneddsdomaininternalpreemptiveackdelay), [PrimaryReorderMaxSamples](#cycloneddsdomaininternalprimaryreordermaxsamples), [PrioritizeRetransmit](#cycloneddsdomaininternalprioritizeretransmit), [RediscoveryBlacklistDuration](#cycloneddsdomaininternalrediscoveryblacklistduration), [RetransmitMerging](#cycloneddsdomaininternalretransmitmerging), [RetransmitMergingPeriod](#cycloneddsdomaininternalretransmitmergingperiod), [RetryOnRejectBestEffort](#cycloneddsdomaininternalretryonrejectbesteffort), [SPDPResponseMaxDelay](#cycloneddsdomaininternalspdpresponsemaxdelay), [SecondaryReorderMaxSamples](#cycloneddsdomaininternalsecondaryreordermaxsamples), [SocketReceiveBufferSize](#cycloneddsdomaininternalsocketreceivebuffersize), [SocketSendBufferSize](#cycloneddsdomaininternalsocketsendbuffersize), [SquashParticipants](#cycloneddsdomaininternalsquashparticipants), [SynchronousDeliveryLatencyBound](#cycloneddsdomaininternalsynchronousdeliverylatencybound), [SynchronousDeliveryPriorityThreshold](#cycloneddsdomaininternalsynchronousdeliveryprioritythreshold), [Test](#cycloneddsdomaininternaltest), [UnicastResponseToSPDPMessages](#cycloneddsdomaininternalunicastresponsetospdpmessages), [UseMulticastIfMreqn](#cycloneddsdomaininternalusemulticastifmreqn), [Watermarks](#cycloneddsdomaininternalwatermarks), [WriterLingerDuration](#cycloneddsdomaininternalwriterlingerduration) The Internal elements deal with a variety of settings that are evolving and that are not necessarily fully supported. For the majority of the Internal settings the functionality is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options. @@ -645,6 +680,14 @@ In addition, there is the keyword all that enables all checks. The default value is: `` +#### //CycloneDDS/Domain/Internal/ExtendedPacketInfo +Boolean + +Whether to enable the IP\_PKTINFO on UDP sockets to get hold of the packet destination address and interface on which it was received. This allows for better filtering on discovery packets, but comes at a small performance penalty. + +The default value is: `true` + + #### //CycloneDDS/Domain/Internal/GenerateKeyhash Boolean @@ -1846,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 02ded851fa..e8cc343f05 100644 --- a/etc/cyclonedds.rnc +++ b/etc/cyclonedds.rnc @@ -91,9 +91,9 @@ CycloneDDS configuration""" ] ] & [ a:documentation [ xml:lang="en" """

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 less than 120, 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

""" ] ] +
  • 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, 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 { @@ -462,6 +483,12 @@ CycloneDDS configuration""" ] ] xsd:token { pattern = "((whc|rhc|xevent|all)(,(whc|rhc|xevent|all))*)|" } }? & [ a:documentation [ xml:lang="en" """ +

    Whether to enable the IP_PKTINFO on UDP sockets to get hold of the packet destination address and interface on which it was received. This allows for better filtering on discovery packets, but comes at a small performance penalty.

    +

    The default value is: true

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

    When true, include keyhashes in outgoing data for topics with keys.

    The default value is: false

    """ ] ] element GenerateKeyhash { @@ -1283,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[570f67bd3080674a4bad53d9580a8bb7ad1e6e4d] +# generated from ddsi_config.h[9f834d377bdea61bea6507feed2fc4a8924dc02e] # generated from ddsi__cfgunits.h[bd22f0c0ed210501d0ecd3b07c992eca549ef5aa] -# generated from ddsi__cfgelems.h[d4d0b8c7cf61f0a1cfa4b62e02458cf7b8962536] -# generated from ddsi_config.c[efeae198a5e12ca8977a655216470564b5c44b64] +# 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 4ab6d375b9..893b41d8a7 100644 --- a/etc/cyclonedds.xsd +++ b/etc/cyclonedds.xsd @@ -174,9 +174,9 @@ 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 less than 120, 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>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, 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> + + @@ -600,6 +623,7 @@ CycloneDDS configuration + @@ -756,6 +780,13 @@ CycloneDDS configuration + + + +<p>Whether to enable the IP_PKTINFO on UDP sockets to get hold of the packet destination address and interface on which it was received. This allows for better filtering on discovery packets, but comes at a small performance penalty.</p> +<p>The default value is: <code>true</code></p> + + @@ -1939,10 +1970,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br> - + - - + + diff --git a/fuzz/fuzz_handle_rtps_message/fuzz_handle_rtps_message.c b/fuzz/fuzz_handle_rtps_message/fuzz_handle_rtps_message.c index a85763f173..ab332f9fae 100644 --- a/fuzz/fuzz_handle_rtps_message/fuzz_handle_rtps_message.c +++ b/fuzz/fuzz_handle_rtps_message/fuzz_handle_rtps_message.c @@ -87,12 +87,12 @@ int LLVMFuzzerTestOneInput( ddsi_rbufpool_setowner(rbpool, ddsrt_thread_self()); ddsi_guid_prefix_t guidprefix = { 0 }; - ddsi_locator_t srcloc = { 0 }; + struct ddsi_network_packet_info pktinfo = { 0 }; struct ddsi_rmsg *rmsg = ddsi_rmsg_new (rbpool); unsigned char *buff = (unsigned char *) DDSI_RMSG_PAYLOAD (rmsg); memcpy (buff, data, size); ddsi_rmsg_setsize (rmsg, (uint32_t) size); - ddsi_handle_rtps_message (thrst, &gv, fakeconn, &guidprefix, rbpool, rmsg, size, buff, &srcloc); + ddsi_handle_rtps_message (thrst, &gv, fakeconn, &guidprefix, rbpool, rmsg, size, buff, &pktinfo); ddsi_rmsg_commit (rmsg); ddsi_fini(&gv); diff --git a/src/core/ddsc/tests/nwpart.c b/src/core/ddsc/tests/nwpart.c index 625d860a01..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); @@ -608,7 +609,11 @@ CU_Theory ((bool same_machine, bool proxypp_has_defmc, int n_ep_uc, int n_ep_mc, printf ("advertised plist: %s\n", buf); } - struct ddsi_addrset *as = ddsi_get_endpoint_addrset (&gv, &plist, as_default, NULL); + struct ddsi_network_packet_info pktinfo; + ddsi_set_unspec_locator (&pktinfo.src); + pktinfo.if_index = 0; + pktinfo.dst.kind = DDSI_LOCATOR_KIND_INVALID; + struct ddsi_addrset *as = ddsi_get_endpoint_addrset (&gv, &plist, as_default, &pktinfo, false, false); int n = 0; while (expected[n]) diff --git a/src/core/ddsi/defconfig.c b/src/core/ddsi/defconfig.c index 814d0d5ef5..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; @@ -84,6 +84,7 @@ void ddsi_config_init_default (struct ddsi_config *cfg) cfg->max_rexmit_burst_size = UINT32_C (1048576); cfg->init_transmit_extra_pct = UINT32_C (4294967295); cfg->max_frags_in_rexmit_of_sample = UINT32_C (1); + cfg->extended_packet_info = INT32_C (1); cfg->tcp_nodelay = INT32_C (1); cfg->tcp_port = INT32_C (-1); cfg->tcp_read_timeout = INT64_C (2000000000); @@ -99,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[570f67bd3080674a4bad53d9580a8bb7ad1e6e4d] */ +/* generated from ddsi_config.h[9f834d377bdea61bea6507feed2fc4a8924dc02e] */ /* generated from ddsi__cfgunits.h[bd22f0c0ed210501d0ecd3b07c992eca549ef5aa] */ -/* generated from ddsi__cfgelems.h[d4d0b8c7cf61f0a1cfa4b62e02458cf7b8962536] */ -/* generated from ddsi_config.c[efeae198a5e12ca8977a655216470564b5c44b64] */ +/* 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 f3be421a94..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; @@ -398,6 +401,7 @@ struct ddsi_config int retry_on_reject_besteffort; int generate_keyhash; uint32_t max_sample_size; + bool extended_packet_info; /* compability options */ enum ddsi_standards_conformance standards_conformance; 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 a0ccf089fa..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), @@ -1551,6 +1582,14 @@ static struct cfgelem internal_cfgelems[] = { NOMEMBER, NOFUNCTIONS, DESCRIPTION("

    Setting for controlling the size of transmitting bursts.

    ")), + BOOL("ExtendedPacketInfo", NULL, 1, "true", + MEMBER(extended_packet_info), + FUNCTIONS(0, uf_boolean, 0, pf_boolean), + DESCRIPTION( + "

    Whether to enable the IP_PKTINFO on UDP sockets to get hold of the packet " + "destination address and interface on which it was received. This allows " + "for better filtering on discovery packets, but comes at a small performance " + "penalty.

    ")), LIST("EnableExpensiveChecks", NULL, 1, "", MEMBER(enabled_xchecks), FUNCTIONS(0, uf_xcheck, 0, pf_xcheck), @@ -1827,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), @@ -1853,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( @@ -1870,10 +1923,12 @@ static struct cfgelem discovery_cfgelems[] = { "
    • auto: which will attempt to automatically determine an " "available participant index " "(see also Discovery/MaxAutoParticipantIndex), or
    • \n" - "
    • a non-negative integer less than 120, or
    • \n" - "
    • none:, which causes it to use arbitrary port numbers for " + "
    • 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__discovery_addrset.h b/src/core/ddsi/src/ddsi__discovery_addrset.h index edfb01ce55..47b81b61e4 100644 --- a/src/core/ddsi/src/ddsi__discovery_addrset.h +++ b/src/core/ddsi/src/ddsi__discovery_addrset.h @@ -17,6 +17,8 @@ extern "C" { #endif +struct ddsi_network_packet_info; + typedef struct ddsi_interface_set { bool xs[MAX_XMIT_CONNS]; } ddsi_interface_set_t; @@ -60,11 +62,12 @@ bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv * @param[in] gv domain state, needed for interfaces, transports, tracing * @param[in] uc list of advertised unicast locators * @param[in] mc list of advertised multicast locators - * @param[in] srcloc source address for discovery packet, or "invalid" + * @param[in] pktinfo packet information for discovery packet + * @param[in] allow_srcloc whether pktinfo.src is allowed as a fallback address * @param[in,out] inherited_intfs set of applicable interfaces, may be NULL * * @return new addrset, possibly empty */ -struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const ddsi_interface_set_t *inherited_intfs) +struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const struct ddsi_network_packet_info *pktinfo, bool allow_srcloc, const ddsi_interface_set_t *inherited_intfs) ddsrt_attribute_warn_unused_result ddsrt_nonnull((1,2,3,4)); #if defined (__cplusplus) diff --git a/src/core/ddsi/src/ddsi__discovery_endpoint.h b/src/core/ddsi/src/ddsi__discovery_endpoint.h index c1303a47af..b0d9281b7c 100644 --- a/src/core/ddsi/src/ddsi__discovery_endpoint.h +++ b/src/core/ddsi/src/ddsi__discovery_endpoint.h @@ -30,10 +30,11 @@ struct ddsi_xevent; struct ddsi_xpack; struct ddsi_domaingv; struct ddsi_receiver_state; +struct ddsi_network_packet_info; /** @component discovery */ -struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) - ddsrt_attribute_warn_unused_result ddsrt_nonnull((1,2,3)); +struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const struct ddsi_network_packet_info *pktinfo, bool allow_srcloc, bool force_srcloc) + ddsrt_attribute_warn_unused_result ddsrt_nonnull_all; /** @component discovery */ int ddsi_sedp_write_writer (struct ddsi_writer *wr) ddsrt_nonnull_all; diff --git a/src/core/ddsi/src/ddsi__radmin.h b/src/core/ddsi/src/ddsi__radmin.h index 1cc458d2f5..ba12c3317b 100644 --- a/src/core/ddsi/src/ddsi__radmin.h +++ b/src/core/ddsi/src/ddsi__radmin.h @@ -21,6 +21,7 @@ #include "dds/ddsi/ddsi_locator.h" #include "dds/ddsi/ddsi_protocol.h" #include "dds/ddsi/ddsi_radmin.h" +#include "ddsi__tran.h" #if defined (__cplusplus) extern "C" { @@ -57,7 +58,7 @@ struct ddsi_receiver_state { ddsi_vendorid_t vendor; /* 2 */ ddsi_protocol_version_t protocol_version; /* 2 => 44/48 */ struct ddsi_tran_conn *conn; /* Connection for request */ - ddsi_locator_t srcloc; + struct ddsi_network_packet_info pktinfo; struct ddsi_domaingv *gv; }; diff --git a/src/core/ddsi/src/ddsi__receive.h b/src/core/ddsi/src/ddsi__receive.h index 113407c39c..109b558098 100644 --- a/src/core/ddsi/src/ddsi__receive.h +++ b/src/core/ddsi/src/ddsi__receive.h @@ -23,6 +23,7 @@ struct ddsi_tran_listener; struct ddsi_recv_thread_arg; struct ddsi_writer; struct ddsi_proxy_reader; +struct ddsi_network_packet_info; struct ddsi_gap_info { ddsi_seqno_t gapstart; // == 0 on init, indicating no gap recorded yet @@ -56,7 +57,7 @@ int ddsi_user_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, const int ddsi_add_gap (struct ddsi_xmsg *msg, struct ddsi_writer *wr, struct ddsi_proxy_reader *prd, ddsi_seqno_t start, ddsi_seqno_t base, uint32_t numbits, const uint32_t *bits); /** @component incoming_rtps */ -void ddsi_handle_rtps_message (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool, struct ddsi_rmsg *rmsg, size_t sz, unsigned char *msg, const ddsi_locator_t *srcloc); +void ddsi_handle_rtps_message (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool, struct ddsi_rmsg *rmsg, size_t sz, unsigned char *msg, const struct ddsi_network_packet_info *pktinfo); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__tran.h b/src/core/ddsi/src/ddsi__tran.h index cef8837cd3..ea0890af74 100644 --- a/src/core/ddsi/src/ddsi__tran.h +++ b/src/core/ddsi/src/ddsi__tran.h @@ -86,15 +86,23 @@ DDSRT_STATIC_ASSERT (offsetof (ddsi_tran_write_msgfrags_t, tran_reserved) + DDSI #define DDSI_DECL_CONST_TRAN_WRITE_MSGFRAGS_PTR(name_, ...) \ DDSI_DECL_CONST_TRAN_WRITE_MSGFRAGS_MSVC_WORKAROUND(DDSI_DECL_CONST_TRAN_WRITE_MSGFRAGS_PTR1(name_, DDSRT_COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)) +/// @brief Type for describing what the intended use of a connection being created is enum ddsi_tran_qos_purpose { - DDSI_TRAN_QOS_XMIT_UC, // will send unicast only - DDSI_TRAN_QOS_XMIT_MC, // may send unicast or multicast - DDSI_TRAN_QOS_RECV_UC, // will be used for receiving unicast - DDSI_TRAN_QOS_RECV_MC // will be used for receiving multicast + DDSI_TRAN_QOS_XMIT_UC, ///< will send unicast only + DDSI_TRAN_QOS_XMIT_MC, ///< may send unicast or multicast + DDSI_TRAN_QOS_RECV_UC, ///< will be used for receiving unicast + DDSI_TRAN_QOS_RECV_MC ///< will be used for receiving multicast +}; + +/// @brief Network packet info +struct ddsi_network_packet_info { + ddsi_locator_t src; ///< Source address + ddsi_locator_t dst; ///< Actual destination address in packet, pkt_dst.kind = INVALID if unknown (other fields undefined) + uint32_t if_index; ///< Interface over which packet was received, 0 if unknown }; /* Function pointer types */ -typedef ssize_t (*ddsi_tran_read_fn_t) (struct ddsi_tran_conn *, unsigned char *, size_t, bool, ddsi_locator_t *); +typedef ssize_t (*ddsi_tran_read_fn_t) (struct ddsi_tran_conn *, unsigned char *, size_t, bool, struct ddsi_network_packet_info *pktinfo); typedef ssize_t (*ddsi_tran_write_fn_t) (struct ddsi_tran_conn *, const ddsi_locator_t *, const ddsi_tran_write_msgfrags_t *, uint32_t); typedef int (*ddsi_tran_locator_fn_t) (struct ddsi_tran_factory *, struct ddsi_tran_base *, ddsi_locator_t *); typedef bool (*ddsi_tran_supports_fn_t) (const struct ddsi_tran_factory *, int32_t); @@ -385,8 +393,8 @@ inline ssize_t ddsi_conn_write (struct ddsi_tran_conn * conn, const ddsi_locator } /** @component transport */ -inline ssize_t ddsi_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, ddsi_locator_t *srcloc) { - return conn->m_closed ? -1 : conn->m_read_fn (conn, buf, len, allow_spurious, srcloc); +inline ssize_t ddsi_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, struct ddsi_network_packet_info *pktinfo) { + return conn->m_closed ? -1 : conn->m_read_fn (conn, buf, len, allow_spurious, pktinfo); } /** @component transport */ diff --git a/src/core/ddsi/src/ddsi_config.c b/src/core/ddsi/src/ddsi_config.c index 52c9d624b9..18617573ef 100644 --- a/src/core/ddsi/src/ddsi_config.c +++ b/src/core/ddsi/src/ddsi_config.c @@ -1559,14 +1559,17 @@ 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) { *elem = DDSI_PARTICIPANT_INDEX_NONE; return URES_SUCCESS; } else { - return uf_int_min_max (cfgst, parent, cfgelem, first, value, 0, 120); + return uf_natint (cfgst, parent, cfgelem, first, value); } } diff --git a/src/core/ddsi/src/ddsi_discovery_addrset.c b/src/core/ddsi/src/ddsi_discovery_addrset.c index 035dc69e92..da43d45b17 100644 --- a/src/core/ddsi/src/ddsi_discovery_addrset.c +++ b/src/core/ddsi/src/ddsi_discovery_addrset.c @@ -28,43 +28,31 @@ 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 ddsi_locator_t *loc, struct ddsi_addrset *as, ddsi_interface_set_t *intfs, bool *direct) +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) { size_t interf_idx; switch (ddsi_is_nearby_address (gv, loc, (size_t) gv->n_interfaces, gv->interfaces, &interf_idx)) @@ -86,21 +74,39 @@ static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * cons // one would not be able to reach this address. if (!gv->config.dontRoute) { - // Pick the first selected interface that isn't link-local or loopback + // If we know on what interface we received the packet that we are now + // processing and it matches one that we use, pick that. + // + // Else pick the first selected interface that isn't link-local or loopback // (maybe it matters, maybe not, but it doesn't make sense to assign // a transmit socket for a local interface to a distant host). If none // exists, skip the address. - for (int i = 0; i < gv->n_interfaces; i++) + int i = gv->n_interfaces; + if (pktinfo->if_index != 0) + { + for (i = 0; i < gv->n_interfaces; i++) + { + if (gv->interfaces[i].if_index == pktinfo->if_index && + gv->interfaces[i].loc.kind == loc->kind) + break; + } + } + if (i == gv->n_interfaces) { + // no obvious choice based on what we know of the packet ingress ... // do not use link-local or loopback interfaces transmit conn for distant nodes - if (gv->interfaces[i].link_local || gv->interfaces[i].loopback) - continue; - if (gv->interfaces[i].loc.kind != loc->kind) - continue; - ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { + for (i = 0; i < gv->n_interfaces; i++) + { + if (!gv->interfaces[i].link_local && !gv->interfaces[i].loopback && + gv->interfaces[i].loc.kind == loc->kind) + break; + } + } + if (i < gv->n_interfaces) + { + ddsi_add_xlocator_to_addrset (gv, routed_as ? routed_as : as, &(const ddsi_xlocator_t) { .conn = gv->xmit_conns[i], .c = *loc }); - break; } } break; @@ -109,13 +115,8 @@ static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * cons } } -struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const ddsi_interface_set_t *inherited_intfs) +static bool ddsi_addrset_from_locatorlist_allow_loopback (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc) { - struct ddsi_addrset *as = ddsi_new_addrset (); - ddsi_interface_set_t intfs; - ddsi_interface_set_init (&intfs); - - // if all interfaces are loopback, or all locators in uc are loopback, we're cool with loopback addresses bool allow_loopback; { bool a = true; @@ -137,9 +138,20 @@ struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv continue; allow_loopback = (ddsi_is_nearby_address (gv, &l->loc, (size_t) gv->n_interfaces, gv->interfaces, NULL) == DNAR_SELF); } - //GVTRACE(" allow_loopback=%d\n", allow_loopback); + return allow_loopback; +} + +static struct ddsi_addrset *ddsi_addrset_from_locatorlists_handle_uc (const struct ddsi_domaingv *gv, const struct ddsi_network_packet_info *pktinfo, bool allow_loopback, const ddsi_locators_t *uc, ddsi_interface_set_t *intfs, bool *direct) +{ + struct ddsi_addrset *as = ddsi_new_addrset (); + + // When we encounter addresses that do not match an interface we are using but that routable, + // they temporarily get added to this second address set, to be used only if there are no + // addresses that do match an interface + struct ddsi_addrset *routed_as = ddsi_new_addrset (); - bool direct = false; + *direct = false; + ddsi_interface_set_init (intfs); for (struct ddsi_locators_one *l = uc->first; l != NULL; l = l->next) { #if 0 @@ -189,26 +201,26 @@ struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv } } - addrset_from_locatorlists_add_one (gv, &loc, as, &intfs, &direct); + addrset_from_locatorlists_add_one (gv, pktinfo, &loc, as, intfs, direct, routed_as); } - if (ddsi_addrset_empty (as) && !ddsi_is_unspec_locator (srcloc)) - { - //GVTRACE("add srcloc\n"); - // FIXME: conn_read should provide interface information in source address - //GVTRACE (" add-srcloc"); - addrset_from_locatorlists_add_one (gv, srcloc, as, &intfs, &direct); - } + if (ddsi_addrset_empty (as)) + ddsi_copy_addrset_into_addrset (gv, as, routed_as); + ddsi_unref_addrset (routed_as); + return as; +} +static void ddsi_addrset_from_locatorlists_intfs_fallback (const struct ddsi_domaingv *gv, const struct ddsi_addrset *as, const ddsi_interface_set_t *inherited_intfs, bool direct, ddsi_interface_set_t *intfs) +{ if (ddsi_addrset_empty (as) && inherited_intfs) { // implies no interfaces enabled in "intfs" yet -- just use whatever // we inherited for the purposes of selecting multicast addresses assert (!direct); for (int i = 0; i < gv->n_interfaces; i++) - assert (!intfs.xs[i]); + assert (!intfs->xs[i]); //GVTRACE (" using-inherited-intfs"); - intfs = *inherited_intfs; + *intfs = *inherited_intfs; } else if (!direct && gv->config.multicast_ttl > 1) { @@ -218,16 +230,19 @@ struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv //GVTRACE (" enabling-non-loopback/link-local"); for (int i = 0; i < gv->n_interfaces; i++) { - assert (!intfs.xs[i]); - intfs.xs[i] = !(gv->interfaces[i].link_local || gv->interfaces[i].loopback); + assert (!intfs->xs[i]); + intfs->xs[i] = !(gv->interfaces[i].link_local || gv->interfaces[i].loopback); } } +} +static void ddsi_addrset_from_locatorlist_handle_mc (const struct ddsi_domaingv *gv, const ddsi_locators_t *mc, const ddsi_interface_set_t *intfs, struct ddsi_addrset *as) +{ #if 0 GVTRACE("enabled interfaces for multicast:"); for (int i = 0; i < gv->n_interfaces; i++) { - if (intfs[i]) + if (intfs->xs[i]) GVTRACE(" %s(%d)", gv->interfaces[i].name, gv->interfaces[i].mc_capable); } GVTRACE("\n"); @@ -235,19 +250,48 @@ struct ddsi_addrset *ddsi_addrset_from_locatorlists (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); } } } - return as; } +struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const struct ddsi_network_packet_info *pktinfo, bool allow_srcloc, const ddsi_interface_set_t *inherited_intfs) +{ + // if all interfaces are loopback, or all locators in uc are loopback, we're cool with loopback addresses + const bool allow_loopback = ddsi_addrset_from_locatorlist_allow_loopback (gv, uc); + //GVTRACE(" allow_loopback=%d\n", allow_loopback); + + // choose unicast locators looking at interfaces, keeping track of which interfaces seem to connect directly + // the interfaces are needed to decide on which interfaces the multicast addresses are considered meaningful + bool direct; + ddsi_interface_set_t intfs; + struct ddsi_addrset *as = ddsi_addrset_from_locatorlists_handle_uc (gv, pktinfo, allow_loopback, uc, &intfs, &direct); + + // if no addresses were picked yet but we have a suitable source locator, use that source locator + if (ddsi_addrset_empty (as) && allow_srcloc && !ddsi_is_unspec_locator (&pktinfo->src)) + { + // FIXME: conn_read should provide interface information in source address + //GVTRACE (" add-srcloc"); + addrset_from_locatorlists_add_one (gv, pktinfo, &pktinfo->src, as, &intfs, &direct, NULL); + } + + // if no decisions yet on suitable interfaces, fall back on inherited interfaces (if any), or else whatever + // seems reasonable for the purposes of multicastinf + ddsi_addrset_from_locatorlists_intfs_fallback (gv, as, inherited_intfs, direct, &intfs); + + // now that we have decided on interfaces, add multicast addresses if we think we can do something with them + ddsi_addrset_from_locatorlist_handle_mc (gv, mc, &intfs, as); + return as; +} diff --git a/src/core/ddsi/src/ddsi_discovery_endpoint.c b/src/core/ddsi/src/ddsi_discovery_endpoint.c index 85e46ad6b2..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) @@ -389,19 +400,15 @@ static void addrset_from_locatorlists_collect_interfaces (const ddsi_xlocator_t } } -struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) +struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const struct ddsi_network_packet_info *pktinfo, bool allow_srcloc, bool force_srcloc) { const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; const ddsi_locators_t *uc = (datap->present & PP_UNICAST_LOCATOR) ? &datap->unicast_locators : ∅ const ddsi_locators_t *mc = (datap->present & PP_MULTICAST_LOCATOR) ? &datap->multicast_locators : ∅ - ddsi_locator_t srcloc; - if (rst_srcloc == NULL) - ddsi_set_unspec_locator (&srcloc); - else // force use of source locator - { + assert (!allow_srcloc || !ddsi_is_unspec_locator (&pktinfo->src)); + assert (!force_srcloc || allow_srcloc); + if (force_srcloc) uc = ∅ - srcloc = *rst_srcloc; - } // any interface that works for the participant is presumed ok ddsi_interface_set_t intfs; @@ -410,7 +417,7 @@ struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, .gv = gv, .intfs = &intfs }); //GVTRACE(" {%d%d%d%d}", intfs.xs[0], intfs.xs[1], intfs.xs[2], intfs.xs[3]); - struct ddsi_addrset *as = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + struct ddsi_addrset *as = ddsi_addrset_from_locatorlists (gv, uc, mc, pktinfo, allow_srcloc, &intfs); // if SEDP gives: // - no addresses, use ppant uni- and multicast addresses // - only multicast, use those for multicast and use ppant address for unicast @@ -511,8 +518,11 @@ void ddsi_handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, dds GVLOGDISC (" NEW"); } - as = ddsi_get_endpoint_addrset (gv, datap, proxypp->as_default, gv->config.tcp_use_peeraddr_for_unicast ? &rst->srcloc : NULL); - ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " (as", as); + // allow, force this way: original behaviour; allow_srcloc = true means SEDP quite quickly ends up using it + // in favour of inheriting addresses from participant + const bool allow_srcloc = gv->config.tcp_use_peeraddr_for_unicast && !ddsi_is_unspec_locator (&rst->pktinfo.src); + const bool force_srcloc = allow_srcloc; + as = ddsi_get_endpoint_addrset (gv, datap, proxypp->as_default, &rst->pktinfo, allow_srcloc, force_srcloc); if (ddsi_addrset_empty (as) || !ddsi_addrset_contains_non_psmx_uc (as)) { ddsi_unref_addrset (as); diff --git a/src/core/ddsi/src/ddsi_discovery_spdp.c b/src/core/ddsi/src/ddsi_discovery_spdp.c index 83bd4c53ce..699974e596 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); @@ -579,6 +586,53 @@ static void make_participants_dependent_on_ddsi2 (struct ddsi_domaingv *gv, cons } } +enum find_internal_interface_index_result { + FIIIR_NO_INFO, + FIIIR_NO_MATCH, + FIIIR_MATCH +}; + +static enum find_internal_interface_index_result find_internal_interface_index (const struct ddsi_domaingv *gv, uint32_t nwstack_if_index, int *internal_if_index) ddsrt_nonnull_all; + +static enum find_internal_interface_index_result find_internal_interface_index (const struct ddsi_domaingv *gv, uint32_t nwstack_if_index, int *internal_if_index) +{ + if (nwstack_if_index == 0) + return FIIIR_NO_INFO; + for (int i = 0; i < gv->n_interfaces; i++) + { + if (gv->interfaces[i].if_index == nwstack_if_index) + { + *internal_if_index = i; + return FIIIR_MATCH; + } + } + return FIIIR_NO_MATCH; +} + +static bool accept_packet_from_interface (const struct ddsi_domaingv *gv, const struct ddsi_receiver_state *rst) +{ + int internal_if_index; + switch (find_internal_interface_index (gv, rst->pktinfo.if_index, &internal_if_index)) + { + case FIIIR_NO_MATCH: + // Don't accept SPDP packets received on a interface outside the enabled ones + break; + case FIIIR_MATCH: + // Accept all unicast packets (except those manifestly received over an interface we are not using) + // and multicast packets if we chose to do multicast discovery on the interface over we received it + if (!ddsi_is_mcaddr (gv, &rst->pktinfo.dst) || gv->interfaces[internal_if_index].allow_multicast & DDSI_AMC_SPDP) + return true; + break; + case FIIIR_NO_INFO: + // We could try to match the source address with an interface. Perhaps the destination address + // is available even though the interface index is not, allowing some tricks. On Linux, Windows + // and macOS this shouldn't happen, so rather than complicate things unnecessarily, just accept + // the packet like we always used to do. + return true; + } + return false; +} + static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap) { struct ddsi_domaingv * const gv = rst->gv; @@ -591,6 +645,13 @@ static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_ dds_duration_t lease_duration; unsigned custom_flags = 0; + // Don't just process any SPDP packet but look at the network interface and uni/multicast + // One could refine this even further by also looking at the locators advertised in the + // packet, but this should suffice to drop unwanted multicast packets, which is the only + // use case I am currently aware of. + if (!accept_packet_from_interface (gv, rst)) + return 0; + /* If advertised domain id or domain tag doesn't match, ignore the message. Do this first to minimize the impact such messages have. */ { @@ -602,7 +663,7 @@ static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_ return 0; } } - + if (!(datap->present & PP_PARTICIPANT_GUID) || !(datap->present & PP_BUILTIN_ENDPOINT_SET)) { GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); @@ -753,29 +814,24 @@ static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_ const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; const ddsi_locators_t *uc; const ddsi_locators_t *mc; - ddsi_locator_t srcloc; - ddsi_interface_set_t intfs; + bool allow_srcloc; + ddsi_interface_set_t inherited_intfs; - srcloc = rst->srcloc; uc = (datap->present & PP_DEFAULT_UNICAST_LOCATOR) ? &datap->default_unicast_locators : ∅ mc = (datap->present & PP_DEFAULT_MULTICAST_LOCATOR) ? &datap->default_multicast_locators : ∅ if (gv->config.tcp_use_peeraddr_for_unicast) uc = ∅ // force use of source locator - else if (uc != &emptyset) - ddsi_set_unspec_locator (&srcloc); // can't always use the source address - - ddsi_interface_set_init (&intfs); - as_default = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + allow_srcloc = (uc == &emptyset) && !ddsi_is_unspec_locator (&rst->pktinfo.src); + ddsi_interface_set_init (&inherited_intfs); + as_default = ddsi_addrset_from_locatorlists (gv, uc, mc, &rst->pktinfo, allow_srcloc, &inherited_intfs); - srcloc = rst->srcloc; uc = (datap->present & PP_METATRAFFIC_UNICAST_LOCATOR) ? &datap->metatraffic_unicast_locators : ∅ mc = (datap->present & PP_METATRAFFIC_MULTICAST_LOCATOR) ? &datap->metatraffic_multicast_locators : ∅ if (gv->config.tcp_use_peeraddr_for_unicast) uc = ∅ // force use of source locator - else if (uc != &emptyset) - ddsi_set_unspec_locator (&srcloc); // can't always use the source address - ddsi_interface_set_init (&intfs); - as_meta = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + allow_srcloc = (uc == &emptyset) && !ddsi_is_unspec_locator (&rst->pktinfo.src); + ddsi_interface_set_init (&inherited_intfs); + as_meta = ddsi_addrset_from_locatorlists (gv, uc, mc, &rst->pktinfo, allow_srcloc, &inherited_intfs); ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " (data", as_default); ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " meta", as_meta); 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; diff --git a/src/core/ddsi/src/ddsi_raweth.c b/src/core/ddsi/src/ddsi_raweth.c index 8e5446b5fe..8c28348566 100644 --- a/src/core/ddsi/src/ddsi_raweth.c +++ b/src/core/ddsi/src/ddsi_raweth.c @@ -46,13 +46,13 @@ #define DDSI_BPF_IS_CLONING_DEV (1) #include #include -#endif +#endif #include #include #define DDSI_ETHERTYPE_VLAN ETHERTYPE_VLAN #endif -#if defined(__linux) +#if defined(__linux) #define DDSI_ETH_ADDR_LEN ETH_ALEN #define DDSI_LINK_FAMILY AF_PACKET @@ -68,7 +68,7 @@ union ddsi_cmessage { typedef struct ddsi_raweth_conn { struct ddsi_tran_conn m_base; - ddsrt_socket_t m_sock; + ddsrt_socket_ext_t m_sockext; int m_ifindex; #if DDSI_USE_BSD ddsrt_mutex_t lock; @@ -111,12 +111,14 @@ static char *ddsi_raweth_to_string (char *dst, size_t sizeof_dst, const ddsi_loc return dst; } -static void set_locator(ddsi_locator_t *srcloc, const uint8_t * addr, uint16_t port, uint16_t vtag) +static void set_pktinfo(struct ddsi_network_packet_info *pktinfo, const uint8_t * addr, uint16_t port, uint16_t vtag) { - srcloc->kind = DDSI_LOCATOR_KIND_RAWETH; - srcloc->port = (uint32_t)port + (((uint32_t)vtag & 0xfff) << 20) + (((uint32_t)vtag & 0xf000) << 4); - memset(srcloc->address, 0, 10); - memcpy(srcloc->address + 10, addr, 6); + pktinfo->src.kind = DDSI_LOCATOR_KIND_RAWETH; + pktinfo->src.port = (uint32_t)port + (((uint32_t)vtag & 0xfff) << 20) + (((uint32_t)vtag & 0xf000) << 4); + memset(pktinfo->src.address, 0, 10); + memcpy(pktinfo->src.address + 10, addr, 6); + pktinfo->if_index = 0; + pktinfo->dst.kind = DDSI_LOCATOR_KIND_INVALID; } static size_t set_ethernet_header(struct ddsi_vlan_header *hdr, uint16_t proto, const ddsi_locator_t * dst, const ddsi_locator_t * src) @@ -140,7 +142,7 @@ static size_t set_ethernet_header(struct ddsi_vlan_header *hdr, uint16_t proto, } #if defined(__linux) -static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, ddsi_locator_t *srcloc) +static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, struct ddsi_network_packet_info *pktinfo) { dds_return_t rc; ssize_t ret = 0; @@ -170,7 +172,7 @@ static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned cha msghdr.msg_controllen = sizeof(cmessage); do { - rc = ddsrt_recvmsg(((ddsi_raweth_conn_t) conn)->m_sock, &msghdr, 0, &ret); + rc = ddsrt_recvmsg(&((ddsi_raweth_conn_t) conn)->m_sockext, &msghdr, 0, &ret); } while (rc == DDS_RETCODE_INTERRUPTED); if (ret > (ssize_t) sizeof (ehdr)) @@ -186,8 +188,8 @@ static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned cha break; } - if (srcloc) - set_locator(srcloc, src.sll_addr, ntohs (src.sll_protocol), vtag); + if (pktinfo) + set_pktinfo(pktinfo, src.sll_addr, ntohs (src.sll_protocol), vtag); /* Check for udp packet truncation */ if ((((size_t) ret) > len) @@ -207,7 +209,7 @@ static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned cha rc != DDS_RETCODE_BAD_PARAMETER && rc != DDS_RETCODE_NO_CONNECTION) { - DDS_CERROR(&conn->m_base.gv->logconfig, "UDP recvmsg sock %d: ret %d retcode %d\n", (int) ((ddsi_raweth_conn_t) conn)->m_sock, (int) ret, rc); + DDS_CERROR(&conn->m_base.gv->logconfig, "UDP recvmsg sock %d: ret %d retcode %d\n", (int) ((ddsi_raweth_conn_t) conn)->m_sockext.sock, (int) ret, rc); } return ret; } @@ -251,7 +253,7 @@ static ssize_t ddsi_raweth_conn_write (struct ddsi_tran_conn * conn, const ddsi_ #endif do { - rc = ddsrt_sendmsg (uc->m_sock, &msg, sendflags, &ret); + rc = ddsrt_sendmsg (uc->m_sockext.sock, &msg, sendflags, &ret); } while ((rc == DDS_RETCODE_INTERRUPTED) || (rc == DDS_RETCODE_TRY_AGAIN) || (rc == DDS_RETCODE_NOT_ALLOWED && retry-- > 0)); @@ -374,7 +376,7 @@ static dds_return_t ddsi_raweth_create_conn (struct ddsi_tran_conn **conn_out, s } memset (uc, 0, sizeof (*uc)); - uc->m_sock = sock; + ddsrt_socket_ext_init (&uc->m_sockext, sock); uc->m_ifindex = addr.sll_ifindex; ddsi_factory_conn_init (fact, intf, &uc->m_base); uc->m_base.m_base.m_port = port; @@ -386,7 +388,7 @@ static dds_return_t ddsi_raweth_create_conn (struct ddsi_tran_conn **conn_out, s uc->m_base.m_write_fn = ddsi_raweth_conn_write; uc->m_base.m_disable_multiplexing_fn = 0; - DDS_CTRACE (&fact->gv->logconfig, "ddsi_raweth_create_conn %s socket %d port %u\n", mcast ? "multicast" : "unicast", uc->m_sock, uc->m_base.m_base.m_port); + DDS_CTRACE (&fact->gv->logconfig, "ddsi_raweth_create_conn %s socket %d port %u\n", mcast ? "multicast" : "unicast", uc->m_sockext.sock, uc->m_base.m_base.m_port); *conn_out = &uc->m_base; return DDS_RETCODE_OK; } @@ -410,7 +412,7 @@ static int joinleave_asm_mcgroup (ddsrt_socket_t socket, int join, const ddsi_lo struct ddsi_vlan_tag { unsigned short tag; - unsigned short proto; + unsigned short proto; }; /* The ddsi_raweth_conn_read reads from the bpf file descriptor. @@ -427,7 +429,7 @@ struct ddsi_vlan_tag { * the manipulations using the field to obtain the next packet in the buffer can be safely done. */ -static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, ddsi_locator_t *srcloc) +static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, struct ddsi_network_packet_info *pktinfo) { ssize_t ret = 0; dds_return_t rc = DDS_RETCODE_OK; @@ -442,7 +444,7 @@ static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned cha if (uc->avail == 0) { - if ((ret = read(uc->m_sock, uc->buffer, uc->buflen)) <= 0) + if ((ret = read(uc->m_sockext.sock, uc->buffer, uc->buflen)) <= 0) { DDS_CERROR (&conn->m_base.gv->logconfig, "ddsi_raweth_create_conn read failed ... retcode = %"PRIdSIZE"\n", ret); rc = DDS_RETCODE_ERROR; @@ -457,11 +459,11 @@ static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned cha ptr = uc->bptr; bpf_hdr = (struct bpf_hdr *) ptr; ptr += bpf_hdr->bh_hdrlen; - + eth_hdr = (struct ddsi_ethernet_header *)ptr; ptr += sizeof(*eth_hdr); - if (bpf_hdr->bh_datalen == bpf_hdr->bh_caplen) + if (bpf_hdr->bh_datalen == bpf_hdr->bh_caplen) { ret = (ssize_t)(bpf_hdr->bh_datalen - sizeof(struct ddsi_ethernet_header)); if (ntohs(eth_hdr->proto) == ETHERTYPE_VLAN) @@ -473,8 +475,8 @@ static ssize_t ddsi_raweth_conn_read (struct ddsi_tran_conn * conn, unsigned cha if ((size_t)ret <= len) { memcpy(buf, ptr, (size_t)ret); - if (srcloc) - set_locator(srcloc, eth_hdr->smac, ntohs (eth_hdr->proto), (vtag ? ntohs(vtag->tag) : 0)); + if (pktinfo) + set_pktinfo(pktinfo, eth_hdr->smac, ntohs (eth_hdr->proto), (vtag ? ntohs(vtag->tag) : 0)); } else { @@ -520,7 +522,7 @@ static ssize_t ddsi_raweth_conn_write (struct ddsi_tran_conn * conn, const ddsi_ iovs[0].iov_base = &vhdr; iovs[0].iov_len = hdrlen; - if ((ret = writev (uc->m_sock, iovs, (int)(msgfrags->niov + 1))) < 0) + if ((ret = writev (uc->m_sockext.sock, iovs, (int)(msgfrags->niov + 1))) < 0) { DDS_CERROR(&conn->m_base.gv->logconfig, "ddsi_raweth_conn_write failed with retcode %"PRIdSIZE, ret); rc = DDS_RETCODE_ERROR; @@ -577,7 +579,7 @@ static dds_return_t ddsi_raweth_create_conn (struct ddsi_tran_conn **conn_out, s #else for (i = 0; i < 100; ++i) { char name[11] = {0}; - sprintf (name, "/dev/bpf%d", i); + snprintf (name, sizeof (name), "/dev/bpf%d", i); sock = open (name, O_RDWR); if (sock >=0) break; @@ -588,8 +590,8 @@ static dds_return_t ddsi_raweth_create_conn (struct ddsi_tran_conn **conn_out, s { DDS_CERROR (&fact->gv->logconfig, "ddsi_raweth_create_conn %s port %u failed ... retcode = %d\n", mcast ? "multicast" : "unicast", port, sock); return DDS_RETCODE_ERROR; - } - + } + // activate immediate mode (therefore, buf_len is initially set to "1") int mode = 1; if ((r = ioctl (sock, BIOCIMMEDIATE, &mode)) == -1 ) { @@ -651,7 +653,7 @@ static dds_return_t ddsi_raweth_create_conn (struct ddsi_tran_conn **conn_out, s } memset (uc, 0, sizeof (*uc)); - uc->m_sock = sock; + uc->m_sockext.sock = sock; uc->m_ifindex = addr.sdl_index; ddsi_factory_conn_init (fact, intf, &uc->m_base); uc->m_base.m_base.m_port = port; @@ -668,7 +670,7 @@ static dds_return_t ddsi_raweth_create_conn (struct ddsi_tran_conn **conn_out, s uc->avail = 0; ddsrt_mutex_init (&uc->lock); - DDS_CTRACE (&fact->gv->logconfig, "ddsi_raweth_create_conn %s socket %d port %u\n", mcast ? "multicast" : "unicast", uc->m_sock, uc->m_base.m_base.m_port); + DDS_CTRACE (&fact->gv->logconfig, "ddsi_raweth_create_conn %s socket %d port %u\n", mcast ? "multicast" : "unicast", uc->m_sockext.sock, uc->m_base.m_base.m_port); *conn_out = &uc->m_base; return DDS_RETCODE_OK; @@ -688,7 +690,7 @@ static int joinleave_asm_mcgroup (ddsrt_socket_t socket, int join, const ddsi_lo static ddsrt_socket_t ddsi_raweth_conn_handle (struct ddsi_tran_base * base) { - return ((ddsi_raweth_conn_t) base)->m_sock; + return ((ddsi_raweth_conn_t) base)->m_sockext.sock; } static bool ddsi_raweth_supports (const struct ddsi_tran_factory *fact, int32_t kind) @@ -702,7 +704,7 @@ static int ddsi_raweth_conn_locator (struct ddsi_tran_factory * fact, struct dds ddsi_raweth_conn_t uc = (ddsi_raweth_conn_t) base; int ret = -1; (void) fact; - if (uc->m_sock != DDSRT_INVALID_SOCKET) + if (uc->m_sockext.sock != DDSRT_INVALID_SOCKET) { loc->kind = DDSI_LOCATOR_KIND_RAWETH; loc->port = uc->m_base.m_base.m_port; @@ -729,7 +731,7 @@ static int ddsi_raweth_join_mc (struct ddsi_tran_conn * conn, const ddsi_locator { ddsi_raweth_conn_t uc = (ddsi_raweth_conn_t) conn; (void)srcloc; - return joinleave_asm_mcgroup(uc->m_sock, 1, mcloc, interf); + return joinleave_asm_mcgroup(uc->m_sockext.sock, 1, mcloc, interf); } } @@ -741,7 +743,7 @@ static int ddsi_raweth_leave_mc (struct ddsi_tran_conn * conn, const ddsi_locato { ddsi_raweth_conn_t uc = (ddsi_raweth_conn_t) conn; (void)srcloc; - return joinleave_asm_mcgroup(uc->m_sock, 0, mcloc, interf); + return joinleave_asm_mcgroup(uc->m_sockext.sock, 0, mcloc, interf); } } @@ -751,9 +753,10 @@ static void ddsi_raweth_release_conn (struct ddsi_tran_conn * conn) DDS_CTRACE (&conn->m_base.gv->logconfig, "ddsi_raweth_release_conn %s socket %d port %d\n", conn->m_base.m_multicast ? "multicast" : "unicast", - uc->m_sock, + uc->m_sockext.sock, uc->m_base.m_base.m_port); - ddsrt_close (uc->m_sock); + ddsrt_socket_ext_fini (&uc->m_sockext); + ddsrt_close (uc->m_sockext.sock); ddsrt_free (conn); } diff --git a/src/core/ddsi/src/ddsi_receive.c b/src/core/ddsi/src/ddsi_receive.c index 9429ce0730..c4206c381f 100644 --- a/src/core/ddsi/src/ddsi_receive.c +++ b/src/core/ddsi/src/ddsi_receive.c @@ -2920,7 +2920,7 @@ static int handle_submsg_sequence struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, - const ddsi_locator_t *srcloc, + const struct ddsi_network_packet_info *pktinfo, ddsrt_wctime_t tnowWC, ddsrt_etime_t tnowE, const ddsi_guid_prefix_t * const src_prefix, @@ -2965,7 +2965,7 @@ static int handle_submsg_sequence rst->rtps_encoded = rtps_encoded; rst->vendor = hdr->vendorid; rst->protocol_version = hdr->version; - rst->srcloc = *srcloc; + rst->pktinfo = *pktinfo; rst->gv = gv; rst_live = 0; ts_for_latmeas = 0; @@ -3190,7 +3190,7 @@ static int handle_submsg_sequence } } -static void handle_rtps_message (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool, struct ddsi_rmsg *rmsg, size_t sz, unsigned char *msg, const ddsi_locator_t *srcloc) +static void handle_rtps_message (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool, struct ddsi_rmsg *rmsg, size_t sz, unsigned char *msg, const struct ddsi_network_packet_info *pktinfo) { ddsi_rtps_header_t *hdr = (ddsi_rtps_header_t *) msg; assert (ddsi_thread_is_asleep ()); @@ -3212,22 +3212,26 @@ static void handle_rtps_message (struct ddsi_thread_state * const thrst, struct if (gv->logconfig.c.mask & DDS_LC_TRACE) { - char addrstr[DDSI_LOCSTRLEN]; - ddsi_locator_to_string(addrstr, sizeof(addrstr), srcloc); - GVTRACE ("HDR(%"PRIx32":%"PRIx32":%"PRIx32" vendor %d.%d) len %lu from %s\n", - PGUIDPREFIX (hdr->guid_prefix), hdr->vendorid.id[0], hdr->vendorid.id[1], (unsigned long) sz, addrstr); + char srcaddrstr[DDSI_LOCSTRLEN]; + char dstaddrstr[DDSI_LOCSTRLEN] = "unknown"; + ddsi_locator_to_string (srcaddrstr, sizeof(srcaddrstr), &pktinfo->src); + if (pktinfo->dst.kind != DDSI_LOCATOR_KIND_INVALID) + ddsi_locator_to_string (dstaddrstr, sizeof(dstaddrstr), &pktinfo->dst); + GVTRACE ("HDR(%"PRIx32":%"PRIx32":%"PRIx32" vendor %d.%d) len %lu from %s to %s@%"PRIu32"\n", + PGUIDPREFIX (hdr->guid_prefix), hdr->vendorid.id[0], hdr->vendorid.id[1], (unsigned long) sz, + srcaddrstr, dstaddrstr, pktinfo->if_index); } ddsi_rtps_msg_state_t res = ddsi_security_decode_rtps_message (thrst, gv, &rmsg, &hdr, &msg, &sz, rbpool, conn->m_stream); if (res != DDSI_RTPS_MSG_STATE_ERROR) { - handle_submsg_sequence (thrst, gv, conn, srcloc, ddsrt_time_wallclock (), ddsrt_time_elapsed (), &hdr->guid_prefix, guidprefix, msg, (size_t) sz, msg + DDSI_RTPS_MESSAGE_HEADER_SIZE, rmsg, res == DDSI_RTPS_MSG_STATE_ENCODED); + handle_submsg_sequence (thrst, gv, conn, pktinfo, ddsrt_time_wallclock (), ddsrt_time_elapsed (), &hdr->guid_prefix, guidprefix, msg, (size_t) sz, msg + DDSI_RTPS_MESSAGE_HEADER_SIZE, rmsg, res == DDSI_RTPS_MSG_STATE_ENCODED); } } } -void ddsi_handle_rtps_message (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool, struct ddsi_rmsg *rmsg, size_t sz, unsigned char *msg, const ddsi_locator_t *srcloc) +void ddsi_handle_rtps_message (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool, struct ddsi_rmsg *rmsg, size_t sz, unsigned char *msg, const struct ddsi_network_packet_info *pktinfo) { - handle_rtps_message (thrst, gv, conn, guidprefix, rbpool, rmsg, sz, msg, srcloc); + handle_rtps_message (thrst, gv, conn, guidprefix, rbpool, rmsg, sz, msg, pktinfo); } static bool do_packet (struct ddsi_thread_state * const thrst, struct ddsi_domaingv *gv, struct ddsi_tran_conn * conn, const ddsi_guid_prefix_t *guidprefix, struct ddsi_rbufpool *rbpool) @@ -3242,7 +3246,7 @@ static bool do_packet (struct ddsi_thread_state * const thrst, struct ddsi_domai unsigned char * buff; size_t buff_len = maxsz; ddsi_rtps_header_t * hdr; - ddsi_locator_t srcloc; + struct ddsi_network_packet_info pktinfo; if (rmsg == NULL) { @@ -3264,7 +3268,7 @@ static bool do_packet (struct ddsi_thread_state * const thrst, struct ddsi_domai /* Read in DDSI header plus MSG_LEN sub message that follows it */ - sz = ddsi_conn_read (conn, buff, stream_hdr_size, true, &srcloc); + sz = ddsi_conn_read (conn, buff, stream_hdr_size, true, &pktinfo); if (sz == 0) { /* Spurious read -- which at this point is still ok */ @@ -3312,13 +3316,13 @@ static bool do_packet (struct ddsi_thread_state * const thrst, struct ddsi_domai { /* Get next packet */ - sz = ddsi_conn_read (conn, buff, buff_len, true, &srcloc); + sz = ddsi_conn_read (conn, buff, buff_len, true, &pktinfo); } if (sz > 0 && !gv->deaf) { ddsi_rmsg_setsize (rmsg, (uint32_t) sz); - handle_rtps_message(thrst, gv, conn, guidprefix, rbpool, rmsg, (size_t) sz, buff, &srcloc); + handle_rtps_message(thrst, gv, conn, guidprefix, rbpool, rmsg, (size_t) sz, buff, &pktinfo); } ddsi_rmsg_commit (rmsg); return (sz > 0); diff --git a/src/core/ddsi/src/ddsi_tcp.c b/src/core/ddsi/src/ddsi_tcp.c index 37e28408d9..1f7d90d373 100644 --- a/src/core/ddsi/src/ddsi_tcp.c +++ b/src/core/ddsi/src/ddsi_tcp.c @@ -464,7 +464,7 @@ static int32_t addrfam_to_locator_kind (int af) return (af == AF_INET) ? DDSI_LOCATOR_KIND_TCPv4 : DDSI_LOCATOR_KIND_TCPv6; } -static ssize_t ddsi_tcp_conn_read (struct ddsi_tran_conn * conn, unsigned char *buf, size_t len, bool allow_spurious, ddsi_locator_t *srcloc) +static ssize_t ddsi_tcp_conn_read (struct ddsi_tran_conn * conn, unsigned char *buf, size_t len, bool allow_spurious, struct ddsi_network_packet_info *pktinfo) { struct ddsi_tran_factory_tcp * const fact = (struct ddsi_tran_factory_tcp *) conn->m_factory; struct ddsi_domaingv const * const gv = fact->fact.gv; @@ -489,10 +489,12 @@ static ssize_t ddsi_tcp_conn_read (struct ddsi_tran_conn * conn, unsigned char * pos += (size_t) n; if (pos == len) { - if (srcloc) + if (pktinfo) { const int32_t kind = addrfam_to_locator_kind (tcp->m_peer_addr.a.sa_family); - ddsi_ipaddr_to_loc(srcloc, &tcp->m_peer_addr.a, kind); + ddsi_ipaddr_to_loc (&pktinfo->src, &tcp->m_peer_addr.a, kind); + pktinfo->if_index = 0; + pktinfo->dst.kind = DDSI_LOCATOR_KIND_INVALID; } return (ssize_t) pos; } diff --git a/src/core/ddsi/src/ddsi_tran.c b/src/core/ddsi/src/ddsi_tran.c index 52de6b88e6..f42132c807 100644 --- a/src/core/ddsi/src/ddsi_tran.c +++ b/src/core/ddsi/src/ddsi_tran.c @@ -35,7 +35,7 @@ extern inline dds_return_t ddsi_factory_create_conn (struct ddsi_tran_conn **con extern inline int ddsi_listener_locator (struct ddsi_tran_listener * listener, ddsi_locator_t * loc); extern inline int ddsi_listener_listen (struct ddsi_tran_listener * listener); extern inline struct ddsi_tran_conn * ddsi_listener_accept (struct ddsi_tran_listener * listener); -extern inline ssize_t ddsi_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, ddsi_locator_t *srcloc); +extern inline ssize_t ddsi_conn_read (struct ddsi_tran_conn * conn, unsigned char * buf, size_t len, bool allow_spurious, struct ddsi_network_packet_info *pktinfo); extern inline ssize_t ddsi_conn_write (struct ddsi_tran_conn * conn, const ddsi_locator_t *dst, const ddsi_tran_write_msgfrags_t *msgfrags, uint32_t flags); extern inline uint32_t ddsi_tran_get_locator_port (const struct ddsi_tran_factory *factory, const ddsi_locator_t *loc); extern inline void ddsi_tran_set_locator_port (const struct ddsi_tran_factory *factory, ddsi_locator_t *loc, uint32_t port); diff --git a/src/core/ddsi/src/ddsi_udp.c b/src/core/ddsi/src/ddsi_udp.c index ced1444252..236cf08d2c 100644 --- a/src/core/ddsi/src/ddsi_udp.c +++ b/src/core/ddsi/src/ddsi_udp.c @@ -8,6 +8,10 @@ // // SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause +// The in6_pktinfo is somewhat fussy, but these seem to do the trick ... +#define __APPLE_USE_RFC_3542 +#define _GNU_SOURCE + #include #include #include "dds/ddsrt/atomics.h" @@ -43,7 +47,7 @@ union addr { typedef struct ddsi_udp_conn { struct ddsi_tran_conn m_base; - ddsrt_socket_t m_sock; + ddsrt_socket_ext_t m_sockext; #if defined _WIN32 WSAEVENT m_sockEvent; #endif @@ -65,11 +69,75 @@ static void addr_to_loc (const struct ddsi_tran_factory *tran, ddsi_locator_t *d ddsi_ipaddr_to_loc (dst, &src->a, (src->a.sa_family == AF_INET) ? DDSI_LOCATOR_KIND_UDPv4 : DDSI_LOCATOR_KIND_UDPv6); } -static ssize_t ddsi_udp_conn_read (struct ddsi_tran_conn * conn_cmn, unsigned char * buf, size_t len, bool allow_spurious, ddsi_locator_t *srcloc) +static void translate_pktinfo (struct ddsi_network_packet_info *pktinfo, ddsrt_msghdr_t *msghdr_in, uint32_t port, bool ipv6) +{ + // msghdr_in is not const .... because ... Linux ... + // for the rest: I hate Windows, with good reason. +#ifndef _WIN32 + ddsrt_msghdr_t *msghdr = msghdr_in; +#else + WSAMSG msghdr_tmp = { .Control = { .len = (DWORD) msghdr_in->msg_controllen, .buf = msghdr_in->msg_control } }; + WSAMSG *msghdr = &msghdr_tmp; +#ifndef CMSG_DATA +#define CMSG_DATA WSA_CMSG_DATA +#endif +#endif + // in_addr / in6_addr, so the conversion functions that deal with sockaddr are not applicable +#ifdef CMSG_SPACE // skip everything if control message supported not defined +#if DDSRT_HAVE_IPV6 && defined IPV6_PKTINFO + if (ipv6) + { + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (msghdr); cmsg != NULL; cmsg = CMSG_NXTHDR (msghdr, cmsg)) + { + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo const * const pkt6 = (const struct in6_pktinfo *) CMSG_DATA (cmsg); + pktinfo->dst.kind = DDSI_LOCATOR_KIND_UDPv6; + pktinfo->dst.port = port; + memcpy (pktinfo->dst.address, &pkt6->ipi6_addr, 16); + pktinfo->if_index = pkt6->ipi6_ifindex; + return; + } + } + } +#else + (void) ipv6; +#endif // HAVE_IPV6 && IPV6_PKTINFO +#if defined IP_PKTINFO + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (msghdr); cmsg != NULL; cmsg = CMSG_NXTHDR (msghdr, cmsg)) + { + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) + { + struct in_pktinfo const * const pkt4 = (const struct in_pktinfo *) CMSG_DATA (cmsg); + pktinfo->dst.kind = DDSI_LOCATOR_KIND_UDPv4; + pktinfo->dst.port = port; + memset (pktinfo->dst.address, 0, 12); + memcpy (pktinfo->dst.address + 12, &pkt4->ipi_addr, 4); + pktinfo->if_index = (uint32_t) pkt4->ipi_ifindex; + return; + } + } +#endif +#endif // CMSG_SPACE + // early outs as soon as packet info has been set, so if we get here, we don't know anything + pktinfo->dst.kind = DDSI_LOCATOR_KIND_INVALID; + pktinfo->if_index = 0; +} + +static ssize_t ddsi_udp_conn_read (struct ddsi_tran_conn * conn_cmn, unsigned char * buf, size_t len, bool allow_spurious, struct ddsi_network_packet_info *pktinfo) { ddsi_udp_conn_t conn = (ddsi_udp_conn_t) conn_cmn; struct ddsi_domaingv * const gv = conn->m_base.m_base.gv; union addr src; +#ifdef CMSG_SPACE + union in_pktinfo_4_6 { + struct in_pktinfo ip4; +#if DDSRT_HAVE_IPV6 + struct in6_pktinfo ip6; +#endif + }; + char incmsg[CMSG_SPACE (sizeof (union in_pktinfo_4_6))]; +#endif ddsrt_iovec_t msg_iov = { .iov_base = (void *) buf, .iov_len = (ddsrt_iov_len_t) len /* Windows uses unsigned, POSIX (except Linux) int */ @@ -79,6 +147,11 @@ static ssize_t ddsi_udp_conn_read (struct ddsi_tran_conn * conn_cmn, unsigned ch .msg_namelen = (socklen_t) sizeof (src), .msg_iov = &msg_iov, .msg_iovlen = 1 +#ifdef CMSG_SPACE + , + .msg_controllen = sizeof (incmsg), + .msg_control = incmsg +#endif // accrights/control implicitly initialised to 0 // msg_flags is an out parameter anyway }; @@ -87,28 +160,33 @@ static ssize_t ddsi_udp_conn_read (struct ddsi_tran_conn * conn_cmn, unsigned ch dds_return_t rc; ssize_t nrecv = 0; do { - rc = ddsrt_recvmsg (conn->m_sock, &msghdr, 0, &nrecv); + rc = ddsrt_recvmsg (&conn->m_sockext, &msghdr, 0, &nrecv); } while (rc == DDS_RETCODE_INTERRUPTED); if (nrecv > 0) { - if (srcloc) - addr_to_loc (conn->m_base.m_factory, srcloc, &src); + if (pktinfo) + { + addr_to_loc (conn->m_base.m_factory, &pktinfo->src, &src); + translate_pktinfo (pktinfo, &msghdr, conn->m_base.m_base.m_port, src.a.sa_family == AF_INET6); + } if (gv->pcap_fp) { union addr dest; socklen_t dest_len = sizeof (dest); - if (ddsrt_getsockname (conn->m_sock, &dest.a, &dest_len) != DDS_RETCODE_OK) + if (ddsrt_getsockname (conn->m_sockext.sock, &dest.a, &dest_len) != DDS_RETCODE_OK) memset (&dest, 0, sizeof (dest)); ddsi_write_pcap_received (gv, ddsrt_time_wallclock (), &src.x, &dest.x, buf, (size_t) nrecv); } /* Check for udp packet truncation */ -#if DDSRT_MSGHDR_FLAGS - const bool trunc_flag = (msghdr.msg_flags & MSG_TRUNC) != 0; -#else +#if ! DDSRT_MSGHDR_FLAGS const bool trunc_flag = false; +#elif defined MSG_CTRUNC + const bool trunc_flag = (msghdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) != 0; +#else + const bool trunc_flag = (msghdr.msg_flags & MSG_TRUNC) != 0; #endif if ((size_t) nrecv > len || trunc_flag) { @@ -121,7 +199,7 @@ static ssize_t ddsi_udp_conn_read (struct ddsi_tran_conn * conn_cmn, unsigned ch } else if (rc != DDS_RETCODE_BAD_PARAMETER && rc != DDS_RETCODE_NO_CONNECTION) { - GVERROR ("UDP recvmsg sock %d: ret %d retcode %"PRId32"\n", (int) conn->m_sock, (int) nrecv, rc); + GVERROR ("UDP recvmsg sock %d: ret %d retcode %"PRId32"\n", (int) conn->m_sockext.sock, (int) nrecv, rc); nrecv = -1; } return nrecv; @@ -157,7 +235,7 @@ static ssize_t ddsi_udp_conn_write (struct ddsi_tran_conn * conn_cmn, const ddsi #if MSG_NOSIGNAL && !LWIP_SOCKET sendflags |= MSG_NOSIGNAL; #endif - rc = ddsrt_sendmsg (conn->m_sock, &msg, sendflags, &nsent); + rc = ddsrt_sendmsg (conn->m_sockext.sock, &msg, sendflags, &nsent); if (rc != DDS_RETCODE_OK) { // IIRC, NOT_ALLOWED is something that spuriously happens on some old versions of Linux i.c.w. firewalls @@ -184,10 +262,10 @@ static ssize_t ddsi_udp_conn_write (struct ddsi_tran_conn * conn_cmn, const ddsi break; WSANETWORKEVENTS ev; WaitForSingleObject (conn->m_sockEvent, 5); - WSAEnumNetworkEvents (conn->m_sock, conn->m_sockEvent, &ev); + WSAEnumNetworkEvents (conn->m_sockext.sock, conn->m_sockEvent, &ev); } #endif - rc = ddsrt_sendmsg (conn->m_sock, &msg, sendflags, &nsent); + rc = ddsrt_sendmsg (conn->m_sockext.sock, &msg, sendflags, &nsent); } } @@ -195,7 +273,7 @@ static ssize_t ddsi_udp_conn_write (struct ddsi_tran_conn * conn_cmn, const ddsi { union addr sa; socklen_t alen = sizeof (sa); - if (ddsrt_getsockname (conn->m_sock, &sa.a, &alen) != DDS_RETCODE_OK) + if (ddsrt_getsockname (conn->m_sockext.sock, &sa.a, &alen) != DDS_RETCODE_OK) memset(&sa, 0, sizeof(sa)); ddsi_write_pcap_sent (gv, ddsrt_time_wallclock (), &sa.x, &msg, (size_t) nsent); } @@ -213,8 +291,8 @@ static void ddsi_udp_disable_multiplexing (struct ddsi_tran_conn * conn_cmn) ddsi_udp_conn_t conn = (ddsi_udp_conn_t) conn_cmn; uint32_t zero = 0; DWORD dummy; - WSAEventSelect (conn->m_sock, 0, 0); - WSAIoctl (conn->m_sock, FIONBIO, &zero,sizeof(zero), NULL,0, &dummy, NULL,NULL); + WSAEventSelect (conn->m_sockext.sock, 0, 0); + WSAIoctl (conn->m_sockext.sock, FIONBIO, &zero,sizeof(zero), NULL,0, &dummy, NULL,NULL); #else (void) conn_cmn; #endif @@ -223,7 +301,7 @@ static void ddsi_udp_disable_multiplexing (struct ddsi_tran_conn * conn_cmn) static ddsrt_socket_t ddsi_udp_conn_handle (struct ddsi_tran_base * conn_cmn) { ddsi_udp_conn_t conn = (ddsi_udp_conn_t) conn_cmn; - return conn->m_sock; + return conn->m_sockext.sock; } static bool ddsi_udp_supports (const struct ddsi_tran_factory *fact_cmn, int32_t kind) @@ -237,7 +315,7 @@ static int ddsi_udp_conn_locator (struct ddsi_tran_factory * fact_cmn, struct dd struct ddsi_udp_tran_factory const * const fact = (const struct ddsi_udp_tran_factory *) fact_cmn; ddsi_udp_conn_t conn = (ddsi_udp_conn_t) conn_cmn; int ret = -1; - if (conn->m_sock != DDSRT_INVALID_SOCKET) + if (conn->m_sockext.sock != DDSRT_INVALID_SOCKET) { loc->kind = fact->m_kind; loc->port = conn->m_base.m_base.m_port; @@ -281,6 +359,39 @@ static dds_return_t set_dont_route (struct ddsi_domaingv const * const gv, ddsrt return rc; } + // Perhaps not all platforms support this, so only log errors on setting in the trace +static dds_return_t setsockopt_pktinfo (struct ddsi_domaingv const * const gv, ddsrt_socket_t socket, bool ipv6) +{ + dds_return_t rc = DDS_RETCODE_UNSUPPORTED; +#if DDSRT_HAVE_IPV6 + if (ipv6) + { + const int yes = 1; +#if defined IPV6_RECVPKTINFO // must come first for macOS + if ((rc = ddsrt_setsockopt (socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof (yes))) != DDS_RETCODE_OK) + GVTRACE ("ddsi_udp_create_conn: set IPV6_RECVPKTINFO = 1 failed: %s\n", dds_strretcode (rc)); +#elif defined IPV6_PKTINFO + if ((rc = ddsrt_setsockopt (socket, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof (yes))) != DDS_RETCODE_OK) + GVTRACE ("ddsi_udp_create_conn: set IPV6_PKTINFO = 1 failed: %s\n", dds_strretcode (rc)); +#else + (void)yes; + GVTRACE ("ddsi_udp_create_conn: IPV6_RECVPKTINFO, IPV6_PKTINFO not defined\n"); +#endif + return rc; + } +#else + (void) ipv6; +#endif +#ifdef IP_PKTINFO + const int yes = 1; + if ((rc = ddsrt_setsockopt (socket, IPPROTO_IP, IP_PKTINFO, &yes, sizeof (yes))) != DDS_RETCODE_OK) + GVTRACE ("ddsi_udp_create_conn: set IP_PKTINFO = 1 failed: %s\n", dds_strretcode (rc)); +#else + GVTRACE ("ddsi_udp_create_conn: set IP_PKTINFO not defined\n"); +#endif + return rc; +} + static dds_return_t set_socket_buffer (struct ddsi_domaingv const * const gv, ddsrt_socket_t sock, int32_t socket_option, const char *socket_option_name, const char *name, const struct ddsi_config_socket_buf_size *config, uint32_t default_min_size) { // if (min, max)= and initbuf= then request= and result= @@ -530,6 +641,13 @@ static dds_return_t ddsi_udp_create_conn (struct ddsi_tran_conn **conn_out, stru if (gv->config.dontRoute && set_dont_route (gv, sock, ipv6) != DDS_RETCODE_OK) goto fail_w_socket; + // IP_PKTINFO on socket so we get to know the destination address and the interface + // on which the packet was received. If it doesn't work, we don't mind: it simply + // means there is slightly less information available for making sense of addresses + // or deciding whether multicast SPDP packet really is to be processed + if (gv->config.extended_packet_info) + (void) setsockopt_pktinfo (gv, sock, ipv6); + if ((rc = ddsrt_bind (sock, &socketname.a, ddsrt_sockaddr_get_size (&socketname.a))) != DDS_RETCODE_OK) { /* PRECONDITION_NOT_MET (= EADDRINUSE) is expected if reuse_addr isn't set, should be handled at @@ -557,11 +675,11 @@ static dds_return_t ddsi_udp_create_conn (struct ddsi_tran_conn **conn_out, stru ddsi_udp_conn_t conn = ddsrt_malloc (sizeof (*conn)); memset (conn, 0, sizeof (*conn)); - conn->m_sock = sock; + ddsrt_socket_ext_init (&conn->m_sockext, sock); conn->m_diffserv = qos->m_diffserv; #if defined _WIN32 && !defined WINCE conn->m_sockEvent = WSACreateEvent (); - WSAEventSelect (conn->m_sock, conn->m_sockEvent, FD_WRITE); + WSAEventSelect (conn->m_sockext.sock, conn->m_sockEvent, FD_WRITE); #endif ddsi_factory_conn_init (&fact->fact, intf, &conn->m_base); @@ -575,7 +693,7 @@ static dds_return_t ddsi_udp_create_conn (struct ddsi_tran_conn **conn_out, stru conn->m_base.m_disable_multiplexing_fn = ddsi_udp_disable_multiplexing; conn->m_base.m_locator_fn = ddsi_udp_conn_locator; - GVTRACE ("ddsi_udp_create_conn %s socket %"PRIdSOCK" port %"PRIu32"\n", purpose_str, conn->m_sock, conn->m_base.m_base.m_port); + GVTRACE ("ddsi_udp_create_conn %s socket %"PRIdSOCK" port %"PRIu32"\n", purpose_str, conn->m_sockext.sock, conn->m_base.m_base.m_port); *conn_out = &conn->m_base; return DDS_RETCODE_OK; @@ -657,10 +775,10 @@ static int ddsi_udp_join_mc (struct ddsi_tran_conn * conn_cmn, const ddsi_locato (void) srcloc; #ifdef DDS_HAS_SSM if (srcloc) - return joinleave_ssm_mcgroup (conn->m_sock, 1, srcloc, mcloc, interf); + return joinleave_ssm_mcgroup (conn->m_sockext.sock, 1, srcloc, mcloc, interf); else #endif - return joinleave_asm_mcgroup (conn->m_sock, 1, mcloc, interf); + return joinleave_asm_mcgroup (conn->m_sockext.sock, 1, mcloc, interf); } static int ddsi_udp_leave_mc (struct ddsi_tran_conn * conn_cmn, const ddsi_locator_t *srcloc, const ddsi_locator_t *mcloc, const struct ddsi_network_interface *interf) @@ -669,10 +787,10 @@ static int ddsi_udp_leave_mc (struct ddsi_tran_conn * conn_cmn, const ddsi_locat (void) srcloc; #ifdef DDS_HAS_SSM if (srcloc) - return joinleave_ssm_mcgroup (conn->m_sock, 0, srcloc, mcloc, interf); + return joinleave_ssm_mcgroup (conn->m_sockext.sock, 0, srcloc, mcloc, interf); else #endif - return joinleave_asm_mcgroup (conn->m_sock, 0, mcloc, interf); + return joinleave_asm_mcgroup (conn->m_sockext.sock, 0, mcloc, interf); } static void ddsi_udp_release_conn (struct ddsi_tran_conn * conn_cmn) @@ -681,8 +799,9 @@ static void ddsi_udp_release_conn (struct ddsi_tran_conn * conn_cmn) struct ddsi_domaingv const * const gv = conn->m_base.m_base.gv; GVTRACE ("ddsi_udp_release_conn %s socket %"PRIdSOCK" port %"PRIu32"\n", conn_cmn->m_base.m_multicast ? "multicast" : "unicast", - conn->m_sock, conn->m_base.m_base.m_port); - ddsrt_close (conn->m_sock); + conn->m_sockext.sock, conn->m_base.m_base.m_port); + ddsrt_socket_ext_fini (&conn->m_sockext); + ddsrt_close (conn->m_sockext.sock); #if defined _WIN32 && !defined WINCE WSACloseEvent (conn->m_sockEvent); #endif diff --git a/src/core/ddsi/tests/plist_leasedur.c b/src/core/ddsi/tests/plist_leasedur.c index 9bd18152e9..e970efec70 100644 --- a/src/core/ddsi/tests/plist_leasedur.c +++ b/src/core/ddsi/tests/plist_leasedur.c @@ -290,8 +290,10 @@ static void ddsi_plist_leasedur_new_proxypp_impl (bool include_lease_duration) unsigned char pkt_trailer[] = { SENTINEL }; - ddsi_locator_t srcloc; - ddsi_conn_locator (gv.xmit_conns[0], &srcloc); + struct ddsi_network_packet_info pktinfo; + ddsi_conn_locator (gv.xmit_conns[0], &pktinfo.src); + pktinfo.dst.kind = DDSI_LOCATOR_KIND_INVALID; + pktinfo.if_index = 0; const ddsi_guid_t proxypp_guid = { .prefix = ddsi_ntoh_guid_prefix ((ddsi_guid_prefix_t){ .s = { TEST_GUIDPREFIX_BYTES } }), .entityid = { .u = DDSI_ENTITYID_PARTICIPANT } @@ -313,7 +315,7 @@ static void ddsi_plist_leasedur_new_proxypp_impl (bool include_lease_duration) memcpy (buf + size, pkt_trailer, sizeof (pkt_trailer)); size += sizeof (pkt_trailer); ddsi_rmsg_setsize (rmsg, (uint32_t) size); - ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &srcloc); + ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &pktinfo); ddsi_rmsg_commit (rmsg); // Discovery data processing is done by the dq.builtin thread, so we can't be @@ -414,8 +416,10 @@ static void ddsi_plist_leasedur_new_proxyrd_impl (bool include_lease_duration) unsigned char pkt_p4[] = { SENTINEL }; - ddsi_locator_t srcloc; - ddsi_conn_locator (gv.xmit_conns[0], &srcloc); + struct ddsi_network_packet_info pktinfo; + ddsi_conn_locator (gv.xmit_conns[0], &pktinfo.src); + pktinfo.dst.kind = DDSI_LOCATOR_KIND_INVALID; + pktinfo.if_index = 0; const ddsi_guid_t prd_guid = { .prefix = ddsi_ntoh_guid_prefix ((ddsi_guid_prefix_t){ .s = { TEST_GUIDPREFIX_BYTES } }), .entityid = { .u = 0x107 } @@ -444,7 +448,7 @@ static void ddsi_plist_leasedur_new_proxyrd_impl (bool include_lease_duration) memcpy (buf + size, pkt_p4, sizeof (pkt_p4)); size += sizeof (pkt_p4); ddsi_rmsg_setsize (rmsg, (uint32_t) size); - ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &srcloc); + ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &pktinfo); ddsi_rmsg_commit (rmsg); // Discovery data processing is done by the dq.builtin thread, so we can't be diff --git a/src/core/ddsi/tests/pmd_message.c b/src/core/ddsi/tests/pmd_message.c index d91a1dff83..6460dfc799 100644 --- a/src/core/ddsi/tests/pmd_message.c +++ b/src/core/ddsi/tests/pmd_message.c @@ -190,8 +190,10 @@ static void create_fake_proxy_participant (void) HDR (DDSI_PID_PARTICIPANT_LEASE_DURATION, 8), SER32BE (100), SER32BE (0), SENTINEL }; - ddsi_locator_t srcloc; - ddsi_conn_locator (gv.xmit_conns[0], &srcloc); + struct ddsi_network_packet_info pktinfo; + ddsi_conn_locator (gv.xmit_conns[0], &pktinfo.src); + pktinfo.dst.kind = DDSI_LOCATOR_KIND_INVALID; + pktinfo.if_index = 0; const ddsi_guid_t proxypp_guid = { .prefix = ddsi_ntoh_guid_prefix ((ddsi_guid_prefix_t){ .s = { TEST_GUIDPREFIX_BYTES } }), .entityid = { .u = DDSI_ENTITYID_PARTICIPANT } @@ -206,7 +208,7 @@ static void create_fake_proxy_participant (void) memcpy (buf, spdp_pkt, sizeof (spdp_pkt)); size += sizeof (spdp_pkt); ddsi_rmsg_setsize (rmsg, (uint32_t) size); - ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &srcloc); + ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &pktinfo); ddsi_rmsg_commit (rmsg); // wait until SPDP message has been processed wait_for_dqueue (); @@ -279,15 +281,17 @@ static void send_pmd_message (uint32_t seqlo, uint16_t encoding, uint16_t option // Process the packet we so carefully constructed above as if it was received // over the network. Stack is deaf (and mute), so there is no risk that the // message gets dropped because some buffer is full - ddsi_locator_t srcloc; - ddsi_conn_locator (gv.xmit_conns[0], &srcloc); + struct ddsi_network_packet_info pktinfo; + ddsi_conn_locator (gv.xmit_conns[0], &pktinfo.src); + pktinfo.dst.kind = DDSI_LOCATOR_KIND_INVALID; + pktinfo.if_index = 0; struct ddsi_rmsg *rmsg = ddsi_rmsg_new (rbufpool); unsigned char *buf = (unsigned char *) DDSI_RMSG_PAYLOAD (rmsg); size_t size = 0; memcpy (buf, pmd_pkt, sizeof (pmd_pkt)); size += sizeof (pmd_pkt) - 24 + act_payload_size; ddsi_rmsg_setsize (rmsg, (uint32_t) size); - ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &srcloc); + ddsi_handle_rtps_message (thrst, &gv, gv.data_conn_uc, NULL, rbufpool, rmsg, size, buf, &pktinfo); ddsi_rmsg_commit (rmsg); // wait until PMD message has been processed wait_for_dqueue (); diff --git a/src/ddsrt/include/dds/ddsrt/sockets.h b/src/ddsrt/include/dds/ddsrt/sockets.h index e003c0dd4a..8e885d3f6e 100644 --- a/src/ddsrt/include/dds/ddsrt/sockets.h +++ b/src/ddsrt/include/dds/ddsrt/sockets.h @@ -17,7 +17,7 @@ #endif /** @file sockets.h - * + * * This provides the interface for sockets. Most of the functions declared here, map directly onto the corresponding * OS functions. Therefore the nitty gritty details are omitted as they should be available in the OS documentation. */ @@ -39,10 +39,10 @@ extern const struct in6_addr ddsrt_in6addr_loopback; #if DDSRT_HAVE_GETHOSTNAME /** * @brief Get the hostname - * + * * The buffer needs to be large enough to hold the name including null terminator. * On success, the buffer will include the null terminator. - * + * * @param[out] hostname a buffer to copy the name into * @param[in] buffersize the space (in bytes) that may be used * @return a DDS_RETCODE (OK, ERROR, NOT_ENOUGH_SPACE) @@ -55,7 +55,7 @@ ddsrt_gethostname( /** * @brief Creates a socket - * + * * @param[out] sockptr a pointer to the socket (file descriptor) created * @param[in] domain the communication domain, selects the protocol family e.g. PF_INET, PF_PACKET * @param[in] type specifies communication semantics e.g. SOCK_STREAM, SOCK_RAW @@ -63,7 +63,7 @@ ddsrt_gethostname( * Normally only a single protocol exists to support a particular socket type within a given protocol family, * in which case protocol can be specified as 0. * @return a DDS_RETCODE (OK, ERROR, NOT_ALLOWED, BAD_PARAMETER, OUT_OF_RESOURCES) - * + * * See @ddsrt_bind, @ddsrt_close */ dds_return_t @@ -73,9 +73,35 @@ ddsrt_socket( int type, int protocol); +/** + * @brief Initialize an "extended socket" based on a normal socket + * + * @note The "extended socket" currently only serves as a holder for the WSARecvMsg + * pointer on Windows. + * + * @param[out] sockext the extended socket + * @param[in] sock the socket (file descriptor) created by @ref ddsrt_socket + */ +void +ddsrt_socket_ext_init( + ddsrt_socket_ext_t *sockext, + ddsrt_socket_t sock); + +/** + * @brief Undo the work of @ref ddsrt_socket_ext_init + * + * @note Does not close the socket (it is not opened by @ref ddsrt_socket_ext_init, so it + * is not closed here) + * + * @param[in] sockext the extended socket + */ +void +ddsrt_socket_ext_fini( + ddsrt_socket_ext_t *sockext); + /** * @brief Close the socket - * + * * @param[in,out] sock the socket (file descriptor) created by @ref ddsrt_socket * @return a DDS_RETCODE (OK, ERROR, INTERRUPTED, BAD_PARAMETER) */ @@ -85,12 +111,12 @@ ddsrt_close( /** * @brief Connects the socket to the address specified by 'addr'. - * + * * @param[in,out] sock the socket * @param[in] addr a socket address * @param[in] addrlen the size of 'addr' * @return a DDS_RETCODE (OK, ERROR, TIMEOUT, and more) - * + * * See @ref ddsrt_accept */ dds_return_t @@ -101,18 +127,18 @@ ddsrt_connect( /** * @brief Accept a connect (@ref ddsrt_connect) request and create a new connected socket for it. - * + * * Is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). * - 'addrlen' must contain the size of the structure pointed to by 'addr' before the operation, * and afterwards will contain the the size of the peer address. * - when not used, 'addr' and 'addrlen' must be NULL - * + * * @param[in,out] sock socket with which to wait for a connection * @param[out] addr address of the connecting peer * @param[in,out] addrlen the size (in bytes) of the structure pointed to by 'addr' * @param[out] connptr pointer to the new connected socket. * @return a DDS_RETCODE (OK, ERROR, and more) - * + * * See @ref ddsrt_bind, ddsrt_listen */ dds_return_t @@ -124,9 +150,9 @@ ddsrt_accept( /** * @brief Marks the socket referred to by 'sock' as a passive socket. - * + * * A passive socket will be used to accept incoming connection requests using @ref ddsrt_accept. - * + * * @param[in,out] sock file descriptor of the socket * @param[in] backlog maximum number of pending connections * @return a DDS_RETCODE (OK, ERROR, PRECONDITION_NOT_MET, BAD_PARAMETER, ILLEGAL_OPERATION) @@ -138,10 +164,10 @@ ddsrt_listen( /** * @brief Assign an address to the socket. - * + * * When a socket is created with @ref ddsrt_socket, it exists in a name space (address family) but has no address assigned to it. * @ref ddsrt_bind assigns the address specified by 'addr' to the socket referred to by the file descriptor 'sock'. - * + * * @param[in,out] sock the socket * @param[in] addr address to assign to the socket * @param[in] addrlen specifies the size, in bytes, of the address structure pointed to by 'addr' @@ -155,11 +181,11 @@ ddsrt_bind( /** * @brief Get the current address to which the socket is bound (@ref ddsrt_bind). - * + * * The 'addrlen' argument should be initialized to indicate the amount of space (in bytes) * pointed to by 'addr'. On return it contains the actual size of the socket address. * The returned address is truncated if the buffer provided is too small; in this case, 'addrlen' will return a value greater than was supplied to the call. - * + * * @param[in] sock the socket * @param[out] addr the address of the socket * @param[in,out] addrlen specifies the size, in bytes, of the address structure pointed to by 'addr' @@ -173,18 +199,18 @@ ddsrt_getsockname( /** * @brief Send data from a buffer - * + * * - Sends 'len' bytes of 'buf' * - The 'flags' can be 0, or the bitwise OR of one or more of: * {MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_MORE, MSG_NOSIGNAL, MSG_OOB} - * + * * @param[in] sock the socket * @param[in] buf buffer containing the data to send * @param[in] len size (in bytes) of 'buf' * @param[in] flags flags for special options * @param[out] sent the number of bytes sent * @return a DDS_RETCODE (OK, ERROR, and more) - * + * * See @ref ddsrt_recv */ dds_return_t @@ -197,16 +223,16 @@ ddsrt_send( /** * @brief Send a message - * + * * - The 'flags' can be 0, or the bitwise OR of one or more of: * {MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_MORE, MSG_NOSIGNAL, MSG_OOB} - * + * * @param[in] sock the socket * @param[in] msg the message to send * @param[in] flags flags for special options * @param[out] sent the number of bytes sent * @return a DDS_RETCODE (OK, ERROR, and more) - * + * * See @ref ddsrt_recvmsg */ dds_return_t @@ -218,20 +244,20 @@ ddsrt_sendmsg( /** * @brief Receive data into a buffer - * + * * - If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on * the type of socket the message is received from. * - If no data is available at the socket, the call waits for a message to arrive, unless the socket is nonblocking (MSG_DONTWAIT). * - The 'flags' can be 0, or the bitwise OR of one or more of: * {MSG_CMSG_CLOEXEC, MSG_DONTWAIT, MSG_ERRQUEUE, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL} - * + * * @param[in] sock the socket * @param[out] buf buffer in which to receive the data * @param[in] len the size available in the buffer * @param[in] flags flags for special options * @param[out] rcvd number of bytes received * @return a DDS_RETCODE (OK, ERROR, TRY_AGAIN, BAD_PARAMETER, NO_CONNECTION, INTERRUPTED, OUT_OF_RESOURCES, ILLEGAL_OPERATION) - * + * * See @ref ddsrt_send */ dds_return_t @@ -244,42 +270,42 @@ ddsrt_recv( /** * @brief Receive a message - * + * * - If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on * the type of socket the message is received from. * - If no data is available at the socket, the call waits for a message to arrive, unless the socket is nonblocking (MSG_DONTWAIT). * - The 'flags' can be 0, or the bitwise OR of one or more of: * {MSG_CMSG_CLOEXEC, MSG_DONTWAIT, MSG_ERRQUEUE, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL} - * - * @param[in] sock the socket + * + * @param[in] sockext the socket * @param[out] msg the message received * @param[in] flags flags for special options * @param[out] rcvd number of bytes received * @return a DDS_RETCODE (OK, ERROR, TRY_AGAIN, BAD_PARAMETER, NO_CONNECTION, INTERRUPTED, OUT_OF_RESOURCES, ILLEGAL_OPERATION) - * + * * See @ref ddsrt_sendmsg */ dds_return_t ddsrt_recvmsg( - ddsrt_socket_t sock, + const ddsrt_socket_ext_t *sockext, ddsrt_msghdr_t *msg, int flags, ssize_t *rcvd); /** * @brief Get options from the socket. - * + * * Argument 'optlen' is a value-result argument, initially containing the size - * of the buffer pointed to by 'optval', and modified on return to indicate the + * of the buffer pointed to by 'optval', and modified on return to indicate the * actual size of the value returned. - * + * * @param[in] sock the socket * @param[in] level the level at which the option resides. For socket API use SOL_SOCKET * @param[in] optname the name of the option (SO_REUSEADDR, SO_DONTROUTE, SO_BROADCAST, SO_SNDBUF, SO_RCVBUF, ...) * @param[out] optval buffer into which to receive the option value * @param[in,out] optlen size of buffer 'optval' * @return a DDS_RETCODE (OK, ERROR, BAD_PARAMETER, UNSUPPORTED) - * + * * See @ref ddsrt_setsockopt */ dds_return_t @@ -292,18 +318,18 @@ ddsrt_getsockopt( /** * @brief Set options on the socket - * + * * Most socket-level options utilize an int argument for 'optval'. * The argument should be nonzero to enable a boolean option, * or zero if the option is to be disabled. - * + * * @param[in,out] sock the socket * @param[in] level the level at which the option resides. For socket API use SOL_SOCKET * @param[in] optname the name of the option (SO_REUSEADDR, SO_DONTROUTE, SO_BROADCAST, SO_SNDBUF, SO_RCVBUF, ...) * @param[in] optval buffer containing the option value * @param[in] optlen size of buffer 'optval' * @return a DDS_RETCODE (OK, ERROR, BAD_PARAMETER, UNSUPPORTED) - * + * * See @ref ddsrt_getsockopt */ dds_return_t @@ -457,16 +483,16 @@ ddsrt_nonnull_all; /** * @brief Convert a string to a socket address - * + * * The socket address 'sa' can be any of type: * (struct sockaddr_in*, struct sockaddr_in6*, struct sockaddr_storage*) * Note that the data is copied into the existing socket address (does not allocate memory). - * + * * @param[in] af the address family (AF_INET, AF_INET6) * @param[in] str the string input e.g. "192.0.2.0" * @param[out] sa the socket address to overwrite * @return a DDS_RETCODE (OK, BAD_PARAMETER) - * + * * See @ref ddsrt_sockaddrtostr */ dds_return_t @@ -475,15 +501,15 @@ ddsrt_sockaddrfromstr( /** * @brief Convert a socket address to a string - * + * * The socket address 'sa' can be any of type: * (const struct sockaddr_in*, const struct sockaddr_in6*, const struct sockaddr_storage*) - * + * * @param[in] sa the socket address to convert into a string * @param[out] buf a string buffer for the output * @param[in] size the size (in bytes) of 'buf' * @return a DDS_RETCODE (OK, BAD_PARAMETER, NOT_ENOUGH_SPACE) - * + * * See @ref ddsrt_sockaddrfromstr */ dds_return_t diff --git a/src/ddsrt/include/dds/ddsrt/sockets/posix.h b/src/ddsrt/include/dds/ddsrt/sockets/posix.h index 910f115942..fa3cd261a6 100644 --- a/src/ddsrt/include/dds/ddsrt/sockets/posix.h +++ b/src/ddsrt/include/dds/ddsrt/sockets/posix.h @@ -37,6 +37,10 @@ typedef int ddsrt_socket_t; #define DDSRT_INVALID_SOCKET (-1) #define PRIdSOCK "d" +typedef struct ddsrt_socket_ext { + ddsrt_socket_t sock; +} ddsrt_socket_ext_t; + #if LWIP_SOCKET # define DDSRT_HAVE_SSM 0 # define IFF_UP 0x1 @@ -65,7 +69,7 @@ typedef int ddsrt_socket_t; # define IP_MULTICAST_LOOP 34 struct ip_mreq { - struct in_addr imr_multiaddr; + struct in_addr imr_multiaddr; struct in_addr imr_interface; }; diff --git a/src/ddsrt/include/dds/ddsrt/sockets/windows.h b/src/ddsrt/include/dds/ddsrt/sockets/windows.h index 2298cb83ec..218a2ef44f 100644 --- a/src/ddsrt/include/dds/ddsrt/sockets/windows.h +++ b/src/ddsrt/include/dds/ddsrt/sockets/windows.h @@ -14,6 +14,11 @@ typedef SOCKET ddsrt_socket_t; #define DDSRT_INVALID_SOCKET (INVALID_SOCKET) #define PRIdSOCK PRIuPTR +typedef struct ddsrt_socket_ext_t { + ddsrt_socket_t sock; + LPFN_WSARECVMSG wsarecvmsg; +} ddsrt_socket_ext_t; + #if defined(NTDDI_VERSION) && \ defined(_WIN32_WINNT_WS03) && \ (NTDDI_VERSION >= _WIN32_WINNT_WS03) diff --git a/src/ddsrt/src/ifaddrs/posix/ifaddrs.c b/src/ddsrt/src/ifaddrs/posix/ifaddrs.c index 23f8691053..6c057a0746 100644 --- a/src/ddsrt/src/ifaddrs/posix/ifaddrs.c +++ b/src/ddsrt/src/ifaddrs/posix/ifaddrs.c @@ -12,11 +12,16 @@ #include #include #include +#include +#include +#include +#include #include "dds/ddsrt/heap.h" #include "dds/ddsrt/ifaddrs.h" #include "dds/ddsrt/retcode.h" #include "dds/ddsrt/string.h" +#include "dds/ddsrt/random.h" #if __APPLE__ #include @@ -132,6 +137,116 @@ static enum ddsrt_iftype guess_iftype (const struct ifaddrs *sys_ifa) } #endif +static bool is_the_kernel_likely_lying_about_multicast (const ddsrt_ifaddrs_t *ifa) +{ + assert (ifa->addr->sa_family == AF_INET || ifa->addr->sa_family == AF_INET6); + bool multicast_works = false; + const bool ipv6 = (ifa->addr->sa_family == AF_INET6); + socklen_t addrsz = ipv6 ? sizeof (struct sockaddr_in6) : sizeof (struct sockaddr_in); + // multicast over link local address works in macOS, but the default firewall rule is not happy with this + // so let us simply assume the "normal" loopback interface address like ::1 exists as well + if (ipv6 && IN6_IS_ADDR_LINKLOCAL (&((const struct sockaddr_in6 *) ifa->addr)->sin6_addr)) + return false; + int sock = socket (ifa->addr->sa_family, SOCK_DGRAM, 0); + if (sock < 0) + return false; +#ifdef __APPLE__ + // macOS needs a short timeout, curiously enough! (but we don't need this code on macOS + // because the kernel tells it as it is + const struct timeval recvtimeo = { .tv_sec = 0, .tv_usec = 10000 }; + if (setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, &recvtimeo, sizeof (recvtimeo)) != 0) + goto out; +#endif + union ipsockaddr { + struct sockaddr gen; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr, mcaddr; + memset (&addr, 0, sizeof (addr)); + memset (&mcaddr, 0, sizeof (mcaddr)); + // Multicast address: abuse DDSI's default address because we need to pick something + if (ipv6) { + addr.ipv6 = *((struct sockaddr_in6 *) ifa->addr); + addr.ipv6.sin6_port = 0; + mcaddr = addr; + if (inet_pton (mcaddr.gen.sa_family, "ff02::ffff:239.255.0.1", &mcaddr.ipv6.sin6_addr) != 1) + goto out; + } else { + addr.ipv4 = *((struct sockaddr_in *) ifa->addr); + addr.ipv4.sin_addr.s_addr = htonl (INADDR_ANY); // because we can't receive multicasts otherwise + addr.ipv4.sin_port = 0; + mcaddr = addr; + if (inet_pton (mcaddr.gen.sa_family, "239.255.0.1", &mcaddr.ipv4.sin_addr) != 1) + goto out; + } + if (bind (sock, &addr.gen, addrsz) < 0) + goto out; + if (getsockname (sock, &addr.gen, &addrsz) < 0) + goto out; + if (ipv6) + { + const unsigned hops = 0; + struct ipv6_mreq ipv6mreq; + mcaddr.ipv6.sin6_port = addr.ipv6.sin6_port; + memset (&ipv6mreq, 0, sizeof (ipv6mreq)); + ipv6mreq.ipv6mr_multiaddr = mcaddr.ipv6.sin6_addr; + ipv6mreq.ipv6mr_interface = ifa->index; + if (setsockopt (sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mreq, sizeof (ipv6mreq)) != 0 || + setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifa->index, sizeof (ifa->index)) != 0 || + setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof (hops)) != 0) + goto out; + } + else + { + const unsigned char ttl = 0; + struct ip_mreq mreq; + mcaddr.ipv4.sin_port = addr.ipv4.sin_port; + mreq.imr_multiaddr = mcaddr.ipv4.sin_addr; + mreq.imr_interface = addr.ipv4.sin_addr; + if (setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) != 0 || + setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, &addr.ipv4.sin_addr, sizeof (addr.ipv4.sin_addr)) != 0 || + setsockopt (sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof (ttl)) != 0) + goto out; + } + // Use a 128 bit random payload to make it unlikely that we conclude multicast works + // because of some other packet that just happens to reach our socket in the (very) + // short time it exists + const uint32_t contents[4] = { + ddsrt_random (), ddsrt_random (), ddsrt_random (), ddsrt_random () + }; + ddsrt_msghdr_t msg = { + .msg_name = &mcaddr.gen, + .msg_namelen = addrsz, + .msg_iov = &(struct iovec) { .iov_len = sizeof (contents), .iov_base = (void *) &contents }, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + if (sendmsg (sock, &msg, 0) != (ssize_t) sizeof (contents)) + goto out; +#ifndef __APPLE__ // because we do a short timeout instead + if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1) + goto out; +#endif + unsigned char recvbuf[sizeof (contents)]; + msg.msg_iov = &(struct iovec) { .iov_len = sizeof (recvbuf), .iov_base = recvbuf }; + ssize_t nrecv; + while ((nrecv = recvmsg (sock, &msg, 0)) > 0) + { + if (nrecv == sizeof (recvbuf) && + memcmp (recvbuf, contents, sizeof (recvbuf)) == 0 && + !(msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC))) + { + multicast_works = true; + break; + } + } +out: + close (sock); + return multicast_works; +} + static dds_return_t copyaddr(ddsrt_ifaddrs_t **ifap, const struct ifaddrs *sys_ifa, enum ddsrt_iftype type) { @@ -165,6 +280,16 @@ copyaddr(ddsrt_ifaddrs_t **ifap, const struct ifaddrs *sys_ifa, enum ddsrt_iftyp if (ifa->addr && ifa->netmask && ifa->netmask->sa_family == 0) { ifa->netmask->sa_family = ifa->addr->sa_family; } + /* Common on Linux: a loopback interface that does not have the MULTICAST + flag but that does support multicast in reality, at least on IPv4. Do + a trial run if we're doing something with INET */ + if (ifa->addr && + (ifa->flags & IFF_LOOPBACK) && !(ifa->flags & IFF_MULTICAST) && + (ifa->addr->sa_family == AF_INET || ifa->addr->sa_family == AF_INET6)) + { + if (is_the_kernel_likely_lying_about_multicast (ifa)) + ifa->flags |= IFF_MULTICAST; + } } if (err == 0) { diff --git a/src/ddsrt/src/sockets/posix/socket.c b/src/ddsrt/src/sockets/posix/socket.c index 6e9cd6780f..3c291ebcc6 100644 --- a/src/ddsrt/src/sockets/posix/socket.c +++ b/src/ddsrt/src/sockets/posix/socket.c @@ -71,6 +71,21 @@ ddsrt_socket(ddsrt_socket_t *sockptr, int domain, int type, int protocol) return DDS_RETCODE_ERROR; } +void +ddsrt_socket_ext_init( + ddsrt_socket_ext_t *sockext, + ddsrt_socket_t sock) +{ + sockext->sock = sock; +} + +void +ddsrt_socket_ext_fini( + ddsrt_socket_ext_t *sockext) +{ + (void)sockext; +} + dds_return_t ddsrt_close( ddsrt_socket_t sock) @@ -350,6 +365,7 @@ ddsrt_setsockopt( return DDS_RETCODE_ERROR; } #endif /* DDSRT_HAVE_IPV6 */ + case IP_PKTINFO: case IP_MULTICAST_IF: case IP_MULTICAST_TTL: case IP_MULTICAST_LOOP: @@ -386,7 +402,7 @@ ddsrt_setsockopt( } } } else if (optname == IP_DROP_MEMBERSHIP) { - if (maddr) { + if (maddr) { if (net_if_ipv4_maddr_rm(iface, &(mreq->imr_multiaddr))) { net_if_ipv4_maddr_leave(iface, maddr); net_if_mcast_monitor(iface, &(maddr->address), false); @@ -524,14 +540,14 @@ static ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) dds_return_t ddsrt_recvmsg( - ddsrt_socket_t sock, + const ddsrt_socket_ext_t *sockext, ddsrt_msghdr_t *msg, int flags, ssize_t *rcvd) { ssize_t n; - if ((n = recvmsg(sock, msg, flags)) != -1) { + if ((n = recvmsg(sockext->sock, msg, flags)) != -1) { assert(n >= 0); *rcvd = n; return DDS_RETCODE_OK; diff --git a/src/ddsrt/src/sockets/windows/socket.c b/src/ddsrt/src/sockets/windows/socket.c index c106b6397b..a875d18f03 100644 --- a/src/ddsrt/src/sockets/windows/socket.c +++ b/src/ddsrt/src/sockets/windows/socket.c @@ -19,6 +19,9 @@ #include "dds/ddsrt/retcode.h" #include "dds/ddsrt/time.h" +// Has to be included after most of windows has been included, it seems +#include + #ifdef ddsrt_select #undef ddsrt_select /* See sockets.h for details. */ #endif @@ -97,6 +100,43 @@ ddsrt_socket(ddsrt_socket_t *sockptr, int domain, int type, int protocol) return DDS_RETCODE_ERROR; } +void +ddsrt_socket_ext_init( + ddsrt_socket_ext_t *sockext, + ddsrt_socket_t sock) +{ + sockext->sock = sock; + // It is not clear to me whether each socket gets the same WSARecvMsg function pointer + // so we have to request it for every socket. It is clear to me that one mustn't try it + // on a SOCK_STREAM socket + int type; + int length = sizeof (int); + getsockopt (sock, SOL_SOCKET, SO_TYPE, (char *) &type, &length); + if (type == SOCK_STREAM) + { + sockext->wsarecvmsg = 0; + } + else + { + GUID wsarecvmsg_guid = WSAID_WSARECVMSG; + DWORD dwBytesReturned = 0; + if (WSAIoctl (sockext->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, + &wsarecvmsg_guid, sizeof (wsarecvmsg_guid), + &sockext->wsarecvmsg, sizeof (sockext->wsarecvmsg), + &dwBytesReturned, NULL, NULL) != 0) + { + sockext->wsarecvmsg = 0; + } + } +} + +void +ddsrt_socket_ext_fini( + ddsrt_socket_ext_t *sockext) +{ + (void)sockext; +} + dds_return_t ddsrt_close(ddsrt_socket_t sock) { @@ -496,46 +536,82 @@ ddsrt_recv( return recv_error_to_retcode(WSAGetLastError()); } +/* Compile time check to ensure iovec matches WSABUF. */ +struct iovec_matches_WSABUF { + char sizeof_matches[sizeof(ddsrt_iovec_t) == sizeof(WSABUF) ? 1 : -1]; + char base_off_matches[offsetof(ddsrt_iovec_t, iov_base) == offsetof(WSABUF, buf) ? 1 : -1]; + char base_size_matches[sizeof(((ddsrt_iovec_t *)8)->iov_base) == sizeof(((WSABUF *)8)->buf) ? 1 : -1]; + char len_off_matches[offsetof(ddsrt_iovec_t, iov_len) == offsetof(WSABUF, len) ? 1 : -1]; + char len_size_matches[sizeof(((ddsrt_iovec_t *)8)->iov_len) == sizeof(((WSABUF *)8)->len) ? 1 : -1]; +}; + dds_return_t ddsrt_recvmsg( - ddsrt_socket_t sock, + const ddsrt_socket_ext_t *sockext, ddsrt_msghdr_t *msg, int flags, ssize_t *rcvd) { - int err, n; - assert(msg != NULL); - assert(msg->msg_iovlen == 1); - assert(msg->msg_controllen == 0); - assert(msg->msg_iov[0].iov_len < INT_MAX); - - msg->msg_flags = 0; - n = recvfrom( - sock, - msg->msg_iov[0].iov_base, - (int)msg->msg_iov[0].iov_len, - flags, - msg->msg_name, - &msg->msg_namelen); - - if (n != -1) { - *rcvd = n; - return DDS_RETCODE_OK; + if (sockext->wsarecvmsg) + { + WSAMSG wsamsg = { + .name = (LPSOCKADDR) msg->msg_name, + .namelen = (INT) msg->msg_namelen, + .lpBuffers = (LPWSABUF) msg->msg_iov, + .dwBufferCount = (DWORD) msg->msg_iovlen, + .Control = { + .len = (ULONG) msg->msg_controllen, + .buf = (CHAR *) msg->msg_control, + }, + .dwFlags = 0 + }; + DWORD n; + if (sockext->wsarecvmsg (sockext->sock, &wsamsg, &n, NULL, 0) != 0) + { + int err = WSAGetLastError(); + return recv_error_to_retcode(err); + } + else + { + msg->msg_flags = wsamsg.dwFlags; + msg->msg_controllen = wsamsg.Control.len; + *rcvd = (ssize_t) n; + return DDS_RETCODE_OK; + } } + else + { + assert(msg->msg_iovlen == 1); + assert(msg->msg_iov[0].iov_len < INT_MAX); + msg->msg_flags = 0; + int n = recvfrom( + sockext->sock, + msg->msg_iov[0].iov_base, + (int)msg->msg_iov[0].iov_len, + flags, + msg->msg_name, + &msg->msg_namelen); + msg->msg_controllen = 0; + + if (n != -1) { + *rcvd = n; + return DDS_RETCODE_OK; + } - err = WSAGetLastError(); - if (err == WSAEMSGSIZE) { - /* Windows returns an error for too-large messages, UNIX expects the - original size and the MSG_TRUNC flag. MSDN states it is truncated, which - presumably means it returned as much of the message as it could. Return - that the message was one byte larger than the available space and set - MSG_TRUNC. */ - *rcvd = msg->msg_iov[0].iov_len + 1; - msg->msg_flags |= MSG_TRUNC; - } + int err = WSAGetLastError(); + if (err == WSAEMSGSIZE) { + /* Windows returns an error for too-large messages, UNIX expects the + original size and the MSG_TRUNC flag. MSDN states it is truncated, which + presumably means it returned as much of the message as it could. Return + that the message was one byte larger than the available space and set + MSG_TRUNC. */ + *rcvd = msg->msg_iov[0].iov_len + 1; + msg->msg_flags |= MSG_TRUNC; + } - return recv_error_to_retcode(err); + return recv_error_to_retcode(err); + } } static dds_return_t @@ -606,15 +682,6 @@ ddsrt_send( return send_error_to_retcode(WSAGetLastError()); } -/* Compile time check to ensure iovec matches WSABUF. */ -struct iovec_matches_WSABUF { - char sizeof_matches[sizeof(ddsrt_iovec_t) == sizeof(WSABUF) ? 1 : -1]; - char base_off_matches[offsetof(ddsrt_iovec_t, iov_base) == offsetof(WSABUF, buf) ? 1 : -1]; - char base_size_matches[sizeof(((ddsrt_iovec_t *)8)->iov_base) == sizeof(((WSABUF *)8)->buf) ? 1 : -1]; - char len_off_matches[offsetof(ddsrt_iovec_t, iov_len) == offsetof(WSABUF, len) ? 1 : -1]; - char len_size_matches[sizeof(((ddsrt_iovec_t *)8)->iov_len) == sizeof(((WSABUF *)8)->len) ? 1 : -1]; -}; - dds_return_t ddsrt_sendmsg( ddsrt_socket_t sock, @@ -628,6 +695,8 @@ ddsrt_sendmsg( assert(msg != NULL); assert(msg->msg_controllen == 0); + // msg_iov -> WSABUF* cast validity is checked by compile-time + // checks defined above in "struct iovec_matches_WSABUF" ret = WSASendTo( sock, (WSABUF *)msg->msg_iov, diff --git a/src/ddsrt/tests/select.c b/src/ddsrt/tests/select.c index 46aab5e48e..8561ee15cd 100644 --- a/src/ddsrt/tests/select.c +++ b/src/ddsrt/tests/select.c @@ -107,7 +107,7 @@ CU_Test(ddsrt_select, duration_to_timeval) typedef struct { dds_duration_t delay; dds_duration_t skew; - ddsrt_socket_t sock; + ddsrt_socket_ext_t sockext; } thread_arg_t; static void @@ -161,13 +161,13 @@ static uint32_t select_timeout_routine(void *ptr) #if LWIP_SOCKET DDSRT_WARNING_GNUC_OFF(sign-conversion) #endif - FD_SET(arg->sock, &rdset); + FD_SET(arg->sockext.sock, &rdset); #if LWIP_SOCKET DDSRT_WARNING_GNUC_ON(sign-conversion) #endif before = dds_time(); - rc = ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay); + rc = ddsrt_select(arg->sockext.sock + 1, &rdset, NULL, NULL, arg->delay); after = dds_time(); delay = after - before; @@ -198,7 +198,7 @@ CU_Test(ddsrt_select, timeout) confidence that time calculation is not completely broken, it is by no means proof that time calculation is entirely correct! */ arg.skew = DDS_MSECS(1000); - arg.sock = socks[0]; + ddsrt_socket_ext_init (&arg.sockext, socks[0]); fprintf (stderr, "create thread\n"); ddsrt_threadattr_init(&attr); @@ -216,6 +216,7 @@ CU_Test(ddsrt_select, timeout) CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK); CU_ASSERT_EQUAL(res, 1); + ddsrt_socket_ext_fini (&arg.sockext); (void)ddsrt_close(socks[0]); (void)ddsrt_close(socks[1]); } @@ -232,14 +233,14 @@ static uint32_t recv_routine(void *ptr) #if LWIP_SOCKET DDSRT_WARNING_GNUC_OFF(sign-conversion) #endif - FD_SET(arg->sock, &rdset); + FD_SET(arg->sockext.sock, &rdset); #if LWIP_SOCKET DDSRT_WARNING_GNUC_ON(sign-conversion) #endif - (void)ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay); + (void)ddsrt_select(arg->sockext.sock + 1, &rdset, NULL, NULL, arg->delay); - if (ddsrt_recv(arg->sock, buf, sizeof(buf), 0, &rcvd) == DDS_RETCODE_OK) { + if (ddsrt_recv(arg->sockext.sock, buf, sizeof(buf), 0, &rcvd) == DDS_RETCODE_OK) { return (rcvd == sizeof(mesg) && memcmp(buf, mesg, sizeof(mesg)) == 0); } @@ -259,7 +260,7 @@ CU_Test(ddsrt_select, send_recv) arg.delay = DDS_SECS(1); arg.skew = 0; - arg.sock = socks[0]; + ddsrt_socket_ext_init (&arg.sockext, socks[0]); ddsrt_threadattr_init(&attr); rc = ddsrt_thread_create(&thr, "recv", &attr, &recv_routine, &arg); @@ -274,6 +275,7 @@ CU_Test(ddsrt_select, send_recv) CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK); CU_ASSERT_EQUAL(res, 1); + ddsrt_socket_ext_fini (&arg.sockext); (void)ddsrt_close(socks[0]); (void)ddsrt_close(socks[1]); } @@ -298,14 +300,14 @@ static uint32_t recvmsg_routine(void *ptr) #if LWIP_SOCKET DDSRT_WARNING_GNUC_OFF(sign-conversion) #endif - FD_SET(arg->sock, &rdset); + FD_SET(arg->sockext.sock, &rdset); #if LWIP_SOCKET DDSRT_WARNING_GNUC_ON(sign-conversion) #endif - (void)ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay); + (void)ddsrt_select(arg->sockext.sock + 1, &rdset, NULL, NULL, arg->delay); - if (ddsrt_recvmsg(arg->sock, &msg, 0, &rcvd) == DDS_RETCODE_OK) { + if (ddsrt_recvmsg(&arg->sockext, &msg, 0, &rcvd) == DDS_RETCODE_OK) { return (rcvd == sizeof(mesg) && memcmp(buf, mesg, sizeof(mesg)) == 0); } @@ -324,7 +326,7 @@ CU_Test(ddsrt_select, sendmsg_recvmsg) sockets_pipe(socks); memset(&arg, 0, sizeof(arg)); - arg.sock = socks[0]; + ddsrt_socket_ext_init (&arg.sockext, socks[0]); ddsrt_threadattr_init(&attr); rc = ddsrt_thread_create(&thr, "recvmsg", &attr, &recvmsg_routine, &arg); @@ -347,6 +349,7 @@ CU_Test(ddsrt_select, sendmsg_recvmsg) CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK); CU_ASSERT_EQUAL(res, 1); + ddsrt_socket_ext_fini (&arg.sockext); (void)ddsrt_close(socks[0]); (void)ddsrt_close(socks[1]); }