Skip to content

Commit

Permalink
fix ping with SendIpHeader on FreeBSD (#63531)
Browse files Browse the repository at this point in the history
* fix ping with SendIpHeader on FreeBSD

* update to fix command line and DF

* add comment
  • Loading branch information
wfurt committed Jan 28, 2022
1 parent 4705207 commit 953fd35
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 ");
}
Expand Down Expand Up @@ -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 ");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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<byte>(&ipHeader, sizeof(IpHeader)).CopyTo(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down

0 comments on commit 953fd35

Please sign in to comment.