Skip to content
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)-haiku;$(NetCoreAppCurrent)</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-openbsd;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)-haiku;$(NetCoreAppCurrent)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseCompilerGeneratedDocXmlFile>false</UseCompilerGeneratedDocXmlFile>
</PropertyGroup>
Expand Down Expand Up @@ -166,8 +166,8 @@
<Compile Include="System\Net\NetworkInformation\NetworkAddressChange.Android.cs" />
</ItemGroup>

<!-- OSX -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos' or '$(TargetPlatformIdentifier)' == 'freebsd'">
<!-- OSX/FreeBSD/OpenBSD -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos' or '$(TargetPlatformIdentifier)' == 'freebsd' or '$(TargetPlatformIdentifier)' == 'openbsd'">
<Compile Include="System\Net\NetworkInformation\IPGlobalPropertiesPal.Bsd.cs" />
<Compile Include="System\Net\NetworkInformation\NetworkInterfacePal.Bsd.cs" />
<Compile Include="System\Net\NetworkInformation\BsdIcmpV4Statistics.cs" />
Expand Down Expand Up @@ -201,7 +201,7 @@
<Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Libraries.cs" Link="Common\Interop\FreeBSD\Interop.Libraries.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'linux' or '$(TargetPlatformIdentifier)' == 'freebsd'">
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'linux' or '$(TargetPlatformIdentifier)' == 'freebsd' or '$(TargetPlatformIdentifier)' == 'openbsd'">
<Compile Include="System\Net\NetworkInformation\NetworkAddressChange.Unix.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.NetworkChange.cs" Link="Common\Interop\Unix\System.Native\Interop.NetworkChange.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ internal sealed class BsdIPGlobalProperties : UnixIPGlobalProperties
private unsafe TcpConnectionInformation[] GetTcpConnections(bool listeners)
{
int realCount = Interop.Sys.GetEstimatedTcpConnectionCount();
if (realCount == -1)
{
// The platform (e.g. OpenBSD) does not expose the TCP connection table.
throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform);
}

int infoCount = realCount * 2;
Interop.Sys.NativeTcpConnectionInformation[] infos = new Interop.Sys.NativeTcpConnectionInformation[infoCount];
fixed (Interop.Sys.NativeTcpConnectionInformation* infosPtr = infos)
Expand Down Expand Up @@ -70,6 +76,12 @@ public override IPEndPoint[] GetActiveTcpListeners()
public override unsafe IPEndPoint[] GetActiveUdpListeners()
{
int realCount = Interop.Sys.GetEstimatedUdpListenerCount();
if (realCount == -1)
{
// The platform (e.g. OpenBSD) does not expose the UDP listener table.
throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform);
}

int infoCount = realCount * 2;
Interop.Sys.IPEndPointInfo[] infos = new Interop.Sys.IPEndPointInfo[infoCount];
fixed (Interop.Sys.IPEndPointInfo* infosPtr = infos)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public IPGlobalPropertiesTest(ITestOutputHelper output)

[Fact]
[SkipOnPlatform(TestPlatforms.Android, "Expected behavior is different on Android")]
[SkipOnPlatform(TestPlatforms.OpenBSD, "TCP/UDP connection enumeration is unsupported on OpenBSD")]
public void IPGlobalProperties_AccessAllMethods_NoErrors()
{
IPGlobalProperties gp = IPGlobalProperties.GetIPGlobalProperties();
Expand Down Expand Up @@ -129,6 +130,7 @@ public void IPGlobalProperties_IPv4_IPv6_NoErrors_Android(int ipVersion)
[Theory]
[MemberData(nameof(Loopbacks))]
[SkipOnPlatform(TestPlatforms.Android, "Unsupported on Android")]
[SkipOnPlatform(TestPlatforms.OpenBSD, "TCP connection enumeration is unsupported on OpenBSD")]
public void IPGlobalProperties_TcpListeners_Succeed(IPAddress address)
{
using (var server = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
Expand All @@ -145,6 +147,7 @@ public void IPGlobalProperties_TcpListeners_Succeed(IPAddress address)
[Theory]
[MemberData(nameof(Loopbacks))]
[SkipOnPlatform(TestPlatforms.Android, "Unsupported on Android")]
[SkipOnPlatform(TestPlatforms.OpenBSD, "UDP listener enumeration is unsupported on OpenBSD")]
public void IPGlobalProperties_UdpListeners_Succeed(IPAddress address)
{
using (var server = new Socket(address.AddressFamily, SocketType.Dgram, ProtocolType.Udp))
Expand All @@ -158,7 +161,7 @@ public void IPGlobalProperties_UdpListeners_Succeed(IPAddress address)
}

[Theory]
[PlatformSpecific(~(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.Android))]
[PlatformSpecific(~(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.Android | TestPlatforms.OpenBSD))]
[MemberData(nameof(Loopbacks))]
public async Task IPGlobalProperties_TcpActiveConnections_Succeed(IPAddress address)
{
Expand Down Expand Up @@ -190,6 +193,7 @@ public async Task IPGlobalProperties_TcpActiveConnections_Succeed(IPAddress addr

[Fact]
[SkipOnPlatform(TestPlatforms.Android, "Unsupported on Android")]
[SkipOnPlatform(TestPlatforms.OpenBSD, "TCP connection enumeration is unsupported on OpenBSD")]
public void IPGlobalProperties_TcpActiveConnections_NotListening()
{
TcpConnectionInformation[] tcpCconnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ public void BasicTest_GetIsNetworkAvailable_Success()
}

[ConditionalTheory]
[SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Expected behavior is different on OSX or FreeBSD")]
[SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD | TestPlatforms.OpenBSD, "Expected behavior is different on OSX, FreeBSD, or OpenBSD")]
[SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")]
[InlineData(false)]
[InlineData(true)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ private static SafeSocketHandle ValidateHandle(SafeSocketHandle handle)
public static bool OSSupportsIPv4 => SocketProtocolSupportPal.OSSupportsIPv4;
public static bool OSSupportsIPv6 => SocketProtocolSupportPal.OSSupportsIPv6;
[UnsupportedOSPlatformGuard("wasi")]
internal static bool OSSupportsIPv6DualMode => !OperatingSystem.IsWasi() && OSSupportsIPv6;
// OpenBSD does not support dual-mode (IPv4-mapped IPv6) sockets: setting IPV6_V6ONLY to 0 fails with EINVAL.
internal static bool OSSupportsIPv6DualMode => !OperatingSystem.IsWasi() && OSSupportsIPv6 && !RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD"));
[UnsupportedOSPlatformGuard("wasi")]
internal static bool OSSupportsThreads => !OperatingSystem.IsWasi();
public static bool OSSupportsUnixDomainSockets => SocketProtocolSupportPal.OSSupportsUnixDomainSockets;
Expand Down
12 changes: 12 additions & 0 deletions src/native/libs/System.Native/pal_interfaceaddresses.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,18 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), sain6->sin6_addr.s6_addr, sizeof(sain6->sin6_addr.s6_addr));
uint32_t scopeId = sain6->sin6_scope_id;

#ifdef TARGET_OPENBSD
// OpenBSD's KAME-derived stack embeds the scope id (interface index) in the second
// 16-bit word of a link-local address and leaves sin6_scope_id set to 0. Recover the
// scope id from the address bytes and restore the canonical link-local form.
if (scopeId == 0 && iai.AddressBytes[0] == 0xFE && (iai.AddressBytes[1] & 0xC0) == 0x80)
{
scopeId = (uint32_t)((iai.AddressBytes[2] << 8) | iai.AddressBytes[3]);
iai.AddressBytes[2] = 0;
iai.AddressBytes[3] = 0;
}
#endif

struct sockaddr_in6* mask_sain6 = (struct sockaddr_in6*)current->ifa_netmask;
iai.PrefixLength = mask_sain6 != NULL ? mask2prefix((uint8_t*)&mask_sain6->sin6_addr.s6_addr, NUM_BYTES_IN_IPV6_ADDRESS) : NUM_BYTES_IN_IPV6_ADDRESS * 8;
onIpv6Found(context, actualName, &iai, &scopeId);
Expand Down
6 changes: 4 additions & 2 deletions src/native/libs/System.Native/pal_networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -3370,9 +3370,11 @@ static int32_t TryChangeSocketEventRegistrationInner(
0,
0,
GetKeventUdata(data));
#if defined(__FreeBSD__)
#if defined(__FreeBSD__) || defined(__OpenBSD__)
// Issue: #30698
// FreeBSD seems to have some issue when setting read/write events together.
// FreeBSD and OpenBSD have an issue when setting read/write events together
// in a single kevent() call: the second (write) filter is silently not armed,
// so connect-completion is never delivered and async connect hangs.
// As a workaround use separate kevent() calls.
if (writeChanged)
{
Expand Down
157 changes: 93 additions & 64 deletions src/native/libs/System.Native/pal_networkstatistics.c
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,97 @@ int32_t SystemNative_GetActiveUdpListeners(IPEndPointInfo* infos, int32_t* infoC
return 0;
}

#else
int32_t SystemNative_GetTcpGlobalStatistics(TcpGlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetIPv4GlobalStatistics(IPv4GlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetUdpGlobalStatistics(UdpGlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetIcmpv4GlobalStatistics(Icmpv4GlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetIcmpv6GlobalStatistics(Icmpv6GlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetEstimatedTcpConnectionCount(void)
{
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetActiveTcpConnectionInfos(NativeTcpConnectionInformation* infos, int32_t* infoCount)
{
(void)infos;
(void)infoCount;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetEstimatedUdpListenerCount(void)
{
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetActiveUdpListeners(IPEndPointInfo* infos, int32_t* infoCount)
{
(void)infos;
(void)infoCount;
errno = ENOTSUP;
return -1;
}
#endif // HAVE_NETINET_TCP_VAR_H

// SystemNative_GetNativeIPInterfaceStatistics and SystemNative_GetNumRoutes only require the
// route/interface sysctl APIs (rt_msghdr), not the protocol statistics that live in tcp_var.h.
// Gate them on HAVE_RT_MSGHDR so platforms that lack tcp_var.h but do have rt_msghdr (e.g. OpenBSD)
// still get the real implementation instead of the ENOTSUP stub.
#if HAVE_RT_MSGHDR
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#if HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#include <sys/ioctl.h>
#include <unistd.h>
#include <net/if.h>
#if HAVE_IOS_NET_ROUTE_H
#include "ios/net/route.h"
#else
#include <net/route.h>
#endif
#if HAVE_NET_IFMEDIA_H
#include <net/if_media.h>
#elif HAVE_IOS_NET_IFMEDIA_H
#include "ios/net/if_media.h"
#endif

int32_t SystemNative_GetNativeIPInterfaceStatistics(char* interfaceName, NativeIPInterfaceStatistics* retStats)
{
assert(interfaceName != NULL && retStats != NULL);
Expand Down Expand Up @@ -709,6 +800,7 @@ int32_t SystemNative_GetNativeIPInterfaceStatistics(char* interfaceName, NativeI
memset(retStats, 0, sizeof(NativeIPInterfaceStatistics));
return -1;
}

int32_t SystemNative_GetNumRoutes(void)
{
int32_t count = 0;
Expand Down Expand Up @@ -753,69 +845,6 @@ int32_t SystemNative_GetNumRoutes(void)
return count;
}
#else
int32_t SystemNative_GetTcpGlobalStatistics(TcpGlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetIPv4GlobalStatistics(IPv4GlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetUdpGlobalStatistics(UdpGlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetIcmpv4GlobalStatistics(Icmpv4GlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetIcmpv6GlobalStatistics(Icmpv6GlobalStatistics* retStats)
{
(void)retStats;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetEstimatedTcpConnectionCount(void)
{
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetActiveTcpConnectionInfos(NativeTcpConnectionInformation* infos, int32_t* infoCount)
{
(void)infos;
(void)infoCount;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetEstimatedUdpListenerCount(void)
{
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetActiveUdpListeners(IPEndPointInfo* infos, int32_t* infoCount)
{
(void)infos;
(void)infoCount;
errno = ENOTSUP;
return -1;
}

int32_t SystemNative_GetNativeIPInterfaceStatistics(char* interfaceName, NativeIPInterfaceStatistics* retStats)
{
(void)interfaceName;
Expand All @@ -829,4 +858,4 @@ int32_t SystemNative_GetNumRoutes(void)
errno = ENOTSUP;
return -1;
}
#endif // HAVE_NETINET_TCP_VAR_H
#endif // HAVE_RT_MSGHDR
Loading