diff --git a/src/libraries/Common/src/System/Net/NetworkInformation/UnixCommandLinePing.cs b/src/libraries/Common/src/System/Net/NetworkInformation/UnixCommandLinePing.cs index 579e69a153f04..c2fcca169d21d 100644 --- a/src/libraries/Common/src/System/Net/NetworkInformation/UnixCommandLinePing.cs +++ b/src/libraries/Common/src/System/Net/NetworkInformation/UnixCommandLinePing.cs @@ -90,7 +90,8 @@ public static string ConstructCommandLine(int packetSize, int timeout, string ad // OSX: ping requires -W flag which accepts timeout in MILLISECONDS; ping6 doesn't support timeout if (OperatingSystem.IsFreeBSD()) { - if (ipv4) + // Syntax changed in FreeBSD 13.0 and options are not common for both address families + if (ipv4 || Environment.OSVersion.Version.Major > 12) { sb.Append(" -W "); } @@ -135,7 +136,8 @@ public static string ConstructCommandLine(int packetSize, int timeout, string ad if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsMacOS()) { // OSX and FreeBSD use -h to set hop limit for IPv6 and -m ttl for IPv4 - if (ipv4) + // Syntax changed in FreeBSD 13.0 and options are not common for both address families + if (ipv4 || (OperatingSystem.IsFreeBSD() && Environment.OSVersion.Version.Major > 12)) { sb.Append(" -m "); } diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs index 0d81532f2bc14..94cdeb2fe890c 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs @@ -19,6 +19,7 @@ public partial class Ping private const int MinIpHeaderLengthInBytes = 20; private const int MaxIpHeaderLengthInBytes = 60; private const int IpV6HeaderLengthInBytes = 40; + private static ushort DontFragment = OperatingSystem.IsFreeBSD() ? (ushort)IPAddress.HostToNetworkOrder((short)0x4000) : (ushort)0x4000; private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, int timeout, PingOptions? options) { @@ -29,15 +30,17 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in bool ipv4 = address.AddressFamily == AddressFamily.InterNetwork; bool sendIpHeader = ipv4 && options != null && SendIpHeader; + int totalLength = 0; if (sendIpHeader) { iph.VersionAndLength = 0x45; + totalLength = sizeof(IpHeader) + checked(sizeof(IcmpHeader) + buffer.Length); // On OSX this strangely must be host byte order. - iph.TotalLength = (ushort)(sizeof(IpHeader) + checked(sizeof(IcmpHeader) + buffer.Length)); + iph.TotalLength = OperatingSystem.IsFreeBSD() ? (ushort)IPAddress.HostToNetworkOrder((short)totalLength) : (ushort)totalLength; iph.Protocol = 1; // ICMP iph.Ttl = (byte)options!.Ttl; - iph.Flags = (ushort)(options.DontFragment ? 0x4000 : 0); + iph.Flags = (ushort)(options.DontFragment ? DontFragment : 0); #pragma warning disable 618 iph.DestinationAddress = (uint)address.Address; #pragma warning restore 618 @@ -52,7 +55,7 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in { Type = ipv4 ? (byte)IcmpV4MessageType.EchoRequest : (byte)IcmpV6MessageType.EchoRequest, Identifier = id, - }, buffer)); + }, buffer, totalLength)); } private Socket GetRawSocket(SocketConfig socketConfig) @@ -405,14 +408,14 @@ public SocketConfig(EndPoint endPoint, int timeout, PingOptions? options, bool i public readonly byte[] SendBuffer; } - private static unsafe byte[] CreateSendMessageBuffer(IpHeader ipHeader, IcmpHeader icmpHeader, byte[] payload) + private static unsafe byte[] CreateSendMessageBuffer(IpHeader ipHeader, IcmpHeader icmpHeader, byte[] payload, int totalLength = 0) { int icmpHeaderSize = sizeof(IcmpHeader); int offset = 0; - int packetSize = ipHeader.TotalLength != 0 ? ipHeader.TotalLength : checked(icmpHeaderSize + payload.Length); + int packetSize = totalLength != 0 ? totalLength : checked(icmpHeaderSize + payload.Length); byte[] result = new byte[packetSize]; - if (ipHeader.TotalLength != 0) + if (totalLength != 0) { int ipHeaderSize = sizeof(IpHeader); new Span(&ipHeader, sizeof(IpHeader)).CopyTo(result); diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs index 3897853f6d856..987d0462d70ca 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs @@ -15,7 +15,7 @@ namespace System.Net.NetworkInformation { public partial class Ping { - private static bool SendIpHeader => false; + private static bool SendIpHeader => OperatingSystem.IsFreeBSD(); private static bool NeedsConnect => OperatingSystem.IsLinux(); private static bool SupportsDualMode => true; diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/TestSettings.cs b/src/libraries/System.Net.Ping/tests/FunctionalTests/TestSettings.cs index a36d4f93deb02..8a87d2f0111e6 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/TestSettings.cs +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/TestSettings.cs @@ -14,7 +14,9 @@ internal static class TestSettings public const int PingTimeout = 10 * 1000; public const string PayloadAsString = "'Post hoc ergo propter hoc'. 'After it, therefore because of it'. It means one thing follows the other, therefore it was caused by the other. But it's not always true. In fact it's hardly ever true."; - public static readonly byte[] PayloadAsBytes = Encoding.UTF8.GetBytes(TestSettings.PayloadAsString); + + // By default, FreeBSD supports buffer only up to 56 bytes + public static readonly byte[] PayloadAsBytes = Encoding.UTF8.GetBytes(OperatingSystem.IsFreeBSD() ? TestSettings.PayloadAsString.Substring(0, 55) : TestSettings.PayloadAsString); public static readonly byte[] PayloadAsBytesShort = Encoding.UTF8.GetBytes("ABCDEF0123456789");