Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions docs/PubSub.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,10 @@ a [Security Key Service](#security-key-service-sks). Two warnings are expected:
with the metadata configured on its DataSetReader instead.

The sample's default endpoint, `opc.udp://239.0.0.1:4840`, is a multicast
address. On one computer, the subscriber can miss multicast messages: the UDP
transport disables multicast loopback by default
(`UdpTransportOptions.MulticastLoopback`), and the publisher sends through the
operating system's default multicast interface, which can differ from the
interface that the subscriber joins. The unicast loopback address avoids both
limitations. See [UDP / UADP](#udp--uadp) for the multicast options.
address. On one computer, the subscriber can miss multicast messages because
the UDP transport disables multicast loopback by default
(`UdpTransportOptions.MulticastLoopback`). The unicast loopback address avoids
this limitation. See [UDP / UADP](#udp--uadp) for the multicast options.

A subscriber decodes a message only when it matches the publisher's settings:

Expand Down Expand Up @@ -587,12 +585,15 @@ defaults favor containment over reach:
| --- | --- | --- |
| `Ttl` | `1` | Multicast and unicast time-to-live; keeps multicast on the local subnet. |
| `MulticastLoopback` | `false` | Whether this host receives copies of its own multicast messages. |
| `PreferredNetworkInterface` | `null` | NIC name or local IP address used to join multicast groups when the connection address does not name one; otherwise the first operational interface for the address family. |

A receiving connection joins its multicast group on that interface. A
sending connection uses the operating system's default multicast interface.
For a publisher and subscriber on one computer, use a unicast loopback address,
as in [Run a first publisher and subscriber](#run-a-first-publisher-and-subscriber).
| `PreferredNetworkInterface` | `null` | NIC name or local IP address used for multicast when the connection address does not name one; otherwise the first operational interface for the address family, preferring a non-loopback interface. |

A receiving connection joins its multicast group on that interface. A sending
connection sends its multicast messages and discovery announcements through
the same interface. On a computer with several network interfaces, name the
interface that reaches the other PubSub applications, either in the
connection address (`NetworkInterface`) or in `PreferredNetworkInterface`. For
a publisher and subscriber on one computer, use a unicast loopback address, as
in [Run a first publisher and subscriber](#run-a-first-publisher-and-subscriber).

### Ethernet / UADP (`opc.eth://`)

Expand Down
62 changes: 62 additions & 0 deletions src/Opc.Ua.PubSub.Udp/UdpDatagramTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,21 @@ internal IPEndPoint? RemoteEndpoint
}
}

/// <summary>
/// The socket of the open transport, or <see langword="null"/> while the
/// transport is closed. Exposed for tests that inspect the socket options.
/// </summary>
internal Socket? SocketForTest
Comment thread
marcschier marked this conversation as resolved.
Outdated
{
get
{
lock (m_sync)
{
return m_socket;
}
}
}

internal void SetAuthenticatedRemoteEndpoint(IPEndPoint remoteEndpoint)
{
if (remoteEndpoint is null)
Expand Down Expand Up @@ -873,9 +888,49 @@ private void ConfigureSocket(Socket socket)
{
m_logger.SettingMulticastLoopbackFailed(ex, m_connection.Name);
}
SetMulticastSendInterface(socket);
ApplyQosCategory(socket);
}

/// <summary>
/// Sends multicast datagrams, including discovery announcements, on the network
/// interface that the transport joins multicast groups on. Without the option the
/// operating system picks its default multicast route, which can be another
/// interface than the one subscribers listen on (OPC 10000-14 7.3.2.2).
/// </summary>
private void SetMulticastSendInterface(Socket socket)
{
try
{
if (socket.AddressFamily == AddressFamily.InterNetwork)
{
IPAddress? localAddress = SelectLocalIPv4(m_networkInterface);
if (localAddress is not null)
{
socket.SetSocketOption(
SocketOptionLevel.IP,
SocketOptionName.MulticastInterface,
localAddress.GetAddressBytes());
}
}
else if (socket.AddressFamily == AddressFamily.InterNetworkV6)
{
int interfaceIndex = SelectIPv6InterfaceIndex(m_networkInterface);
if (interfaceIndex != 0)
{
socket.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.MulticastInterface,
interfaceIndex);
}
}
}
catch (SocketException ex)
{
m_logger.SettingMulticastInterfaceFailed(ex, m_connection.Name);
}
}

private void ApplyQosCategory(Socket socket)
{
if (string.IsNullOrEmpty(m_v2Settings.QosCategory))
Expand Down Expand Up @@ -1387,6 +1442,13 @@ public static partial void UdpUnicastAlreadyConnected(
Exception exception,
string? connection,
string endpoint);

[LoggerMessage(EventId = PubSubUdpEventIds.UdpDatagramTransport + 19, Level = LogLevel.Debug,
Message = "Setting IP_MULTICAST_IF failed for connection '{Connection}'.")]
Comment thread
marcschier marked this conversation as resolved.
Outdated
public static partial void SettingMulticastInterfaceFailed(
this ILogger logger,
Exception exception,
string? connection);
}

}
5 changes: 3 additions & 2 deletions src/Opc.Ua.PubSub.Udp/UdpNetworkInterfaceResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ namespace Opc.Ua.PubSub.Udp
/// <remarks>
/// Implements the NIC-selection guidance in
/// <see href="https://reference.opcfoundation.org/specs/OPC-10000-14/v1.05.06/7.3.2.2">
/// Part 14 §7.3.2.2 UDP multicast / broadcast</see> — multicast
/// joins must specify the interface to avoid the OS picking an
/// Part 14 §7.3.2.2 UDP multicast / broadcast</see>: the transport
/// joins multicast groups and sends multicast datagrams on the
/// resolved interface, so the operating system does not pick an
/// unrelated route.
/// </remarks>
public static class UdpNetworkInterfaceResolver
Expand Down
5 changes: 4 additions & 1 deletion src/Opc.Ua.PubSub.Udp/UdpTransportOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ public sealed class UdpTransportOptions
/// <c>NetworkInterface.Description</c>) or a literal IP
/// address bound to a local NIC. When <see langword="null"/>
/// or empty the transport picks the first up-and-running
/// interface that supports the target address family.
/// interface that supports the target address family. The
/// transport joins multicast groups and sends multicast
/// datagrams, including discovery announcements, on this
/// interface.
/// </summary>
public string? PreferredNetworkInterface { get; set; }
}
Expand Down
Loading
Loading