dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.34k stars 4.74k forks source link

System.Net.NetworkInformation.Ping.Send not working on docker #37734

Open dynamicdeploy opened 4 years ago

dynamicdeploy commented 4 years ago

Environment

  1. Docker Desktop on Windows 10
  2. dotnet core 3.1 sdk docker image - mcr.microsoft.com/dotnet/core/sdk:3.1

I have a program that does traceroute. Program copied below. Docker file also copied below. Problem 1) Traceroute returns 0.0.0.0 as return ipaddress when run in docker and times out 2) Same program runs fine on the same desktop outside of docker

I am expecting the program to run fine on docker. Is this a defect?

Docker output traceroute to www.microsoft.com, 15 hops max, 25 byte packets 1 Request timed out. 2 Request timed out. 3 Request timed out. 4 Request timed out. 5 Request timed out. 6 Request timed out. 7 Request timed out. 8 Request timed out. 9 Request timed out. 10 Request timed out. 11 360 ms 23.73.130.110

Output from the same program on the same PC traceroute to www.microsoft.com, 15 hops max, 25 byte packets 1 47 ms 2601:644:8200:7b10:1256:11ff:feaf:67d4 2 49 ms 2001:558:4000:76::1 3 63 ms 2001:558:82:d004::1 4 63 ms 2001:558:80:105a::1 5 63 ms 2001:558:80:1d1::1 6 Request timed out. 7 Request timed out. 8 Request timed out. 9 Request timed out. 10 96 ms 2001:559::7fa 11 74 ms 2600:1488:a040:108::b 12 63 ms 2600:1406:3c:39b::356e

Dockerfile FROM mcr.microsoft.com/dotnet/core/sdk:3.1 COPY . App/ WORKDIR /App ENTRYPOINT ["dotnet", "xxxx.dll"]

Traceroute program

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Diagnostics.Contracts;

namespace OneRow.Utils.NetworkManager
{
    public class ParallelTraceroute
    {
        private const string DATA = "onerow - http://onerow.io"; //60 Bytes "
        private static readonly byte[] _buffer = Encoding.ASCII.GetBytes(DATA); 
        private const int MAX_HOPS = 15; 
        private const string STR_REQUEST_TIMEOUT = "Request timed out."; 
        private const string STR_REQUEST_TIME_NA = "*"; 
        private const int REQUEST_TIMEOUT = 4000;

        /// <summary>
        /// Runs traceroute and writes result to console.
        /// </summary>
        public static async Task TryTraceRouteAsync(string hostNameOrAddress)
        {
            EnsureCommonArguments(hostNameOrAddress);
            Contract.EndContractBlock();

            using (var console = Console.OpenStandardOutput())
            using (var sw = new StreamWriter(console))
            {
                await TryTraceRouteAsync(hostNameOrAddress, sw);
            }
        }

        /// <summary>
        /// Runs traceroute and writes result to provided stream.
        /// </summary>
        public static async Task TryTraceRouteAsync(string hostNameOrAddress, StreamWriter outputStreamWriter)
        {
            EnsureCommonArguments(hostNameOrAddress);
            if (outputStreamWriter == null)
            {
                throw new ArgumentNullException(nameof(outputStreamWriter));
            }
            Contract.EndContractBlock();

            await outputStreamWriter.WriteLineAsync($"traceroute to {hostNameOrAddress}, {MAX_HOPS} hops max, {_buffer.Length} byte packets");

            //dispatch parallel tasks for each hop
            var arrTraceRouteTasks = new Task<TraceRouteResult>[MAX_HOPS];
            for (int zeroBasedHop = 0; zeroBasedHop < MAX_HOPS; zeroBasedHop++)
            {
                arrTraceRouteTasks[zeroBasedHop] = TryTraceRouteInternalAsync(hostNameOrAddress, zeroBasedHop);
            }

            //and wait for them to finish
            await Task.WhenAll(arrTraceRouteTasks);

            //now just collect all results and write them to output stream
            for (int hop = 0; hop < MAX_HOPS; hop++)
            {
                var traceTask = arrTraceRouteTasks[hop];
                if (traceTask.Status == TaskStatus.RanToCompletion)
                {
                    var res = traceTask.Result;
                    await outputStreamWriter.WriteLineAsync(res.Message);

                    if (res.IsComplete)
                    {
                        //trace complete
                        break;
                    }
                }
                else
                {
                    await outputStreamWriter.WriteLineAsync($"Could not get result for hop #{hop + 1}");
                }
            }
        }

        private static void EnsureCommonArguments(string hostNameOrAddress)
        {
            if (hostNameOrAddress == null)
            {
                throw new ArgumentNullException(nameof(hostNameOrAddress));
            }

            if (string.IsNullOrWhiteSpace(hostNameOrAddress))
            {
                throw new ArgumentException("Hostname or address is required", nameof(hostNameOrAddress));
            }
        }

        public class TraceRouteResult
        {
            public TraceRouteResult(string message, bool isComplete)
            {
                Message = message;
                IsComplete = isComplete;
            }

            public string Message
            {
                get; private set;
            }

            public bool IsComplete
            {
                get; private set;
            }
        }

        public static async Task<TraceRouteResult> TryTraceRouteInternalAsync(string hostNameOrAddress, int zeroBasedHop)
        {
            using (System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping())
            {
                var hop = zeroBasedHop + 1;

                PingOptions pingOptions = new PingOptions();
                Stopwatch stopWatch = new Stopwatch();
                pingOptions.DontFragment = true;
                pingOptions.Ttl = hop;

                stopWatch.Start();

                PingReply pingReply = await pingSender.SendPingAsync(
                    hostNameOrAddress,
                    REQUEST_TIMEOUT,
                    _buffer,
                    pingOptions
                );

                stopWatch.Stop();

                var elapsedMilliseconds = stopWatch.ElapsedMilliseconds;

                string pingReplyAddress;
                string strElapsedMilliseconds;

                if (pingReply.Status == IPStatus.TimedOut)
                {
                    pingReplyAddress = STR_REQUEST_TIMEOUT;
                    strElapsedMilliseconds = STR_REQUEST_TIME_NA;
                }
                else
                {
                    pingReplyAddress = pingReply.Address.ToString();
                    strElapsedMilliseconds = $"{elapsedMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture)} ms";
                }

                var traceResults = new StringBuilder();
                traceResults.Append(hop.ToString(System.Globalization.CultureInfo.InvariantCulture).PadRight(4, ' '));
                traceResults.Append(strElapsedMilliseconds.PadRight(10, ' '));
                traceResults.Append(pingReplyAddress);

                return new TraceRouteResult(traceResults.ToString(), pingReply.Status == IPStatus.Success);
            }
        }
    }
}
ghost commented 4 years ago

Tagging subscribers to this area: @dotnet/ncl Notify danmosemsft if you want to be subscribed.

wfurt commented 4 years ago

do you use windows or Linux containers? Also did you verify general network connectivity and name resolution in the container? You can run Wireshark on on the host to see if you see ICMP message going out and back. This can possibly be also caused by your firewall.

dynamicdeploy commented 4 years ago

I am using Linux containers. Actually if you see the dockerfile in the message, I am not specifying any container but downloading the base image from Microsoft. I check network connectivity to other containers and outside world before starting the application. dotnet core System.Net.Dns functions fail so I had to use an open source resolver which works. I feel the dns resolution problem and this one seems to be somewhat related because both are pointing to the dotnet core network library. Here are the things that work 1) Calling url using httpclient to https://www.microsoft.com and more 2) Database connection to other container and database running in the cloud 3) Calling Named url to a web service running on Azure container using HttpClient Things that don’t work 1) System.Net.Dns resolution functions 2) System.Net.NetworkInformation.Ping functions as copied in the original message. The ipaddress returned here is 0.0.0.0 Hope this helps Thanks Tej

Get Outlook for iOShttps://aka.ms/o0ukef


From: Tomas Weinfurt notifications@github.com Sent: Wednesday, June 10, 2020 10:43:28 PM To: dotnet/runtime runtime@noreply.github.com Cc: Tejaswi Redkar dynamicdeploy@live.com; Author author@noreply.github.com Subject: Re: [dotnet/runtime] System.Net.NetworkInformation.Ping.Send not working on docker (#37734)

do you use windows or Linux containers? Also did you verify general network connectivity and name resolution in the container? You can run Wireshark on on the host to see if you see ICMP message going out and back. This can possibly be also caused by your firewall.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fdotnet%2Fruntime%2Fissues%2F37734%23issuecomment-642421403&data=02%7C01%7C%7C7229dd25bb8640319c9a08d80dca5f72%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637274510115673788&sdata=YnxxoMd%2Bo07PCvEKmZapkFzFJrzlElDGfdGivNkVtzc%3D&reserved=0, or unsubscribehttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAAKUGTPU66ZVJQVFQ35LQ63RWBVIBANCNFSM4N3CSJNA&data=02%7C01%7C%7C7229dd25bb8640319c9a08d80dca5f72%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637274510115683780&sdata=zGwUxPoe8ZlnfXUMUD2WwXdmRFdJLHTPWbhjad5llWs%3D&reserved=0.

wfurt commented 4 years ago

There is something wrong without your docker setup. I did run ping from your code as well as tried name resolution and that both work with the sdk:3.1 image.

dynamicdeploy commented 4 years ago

I am using all default and there are other services like Postgres, Neo4j, etc all work well without a glitch. Did you run the traceroute code I sent? Anything you want me to try?

Get Outlook for iOShttps://aka.ms/o0ukef


From: Tomas Weinfurt notifications@github.com Sent: Thursday, June 11, 2020 11:52:29 AM To: dotnet/runtime runtime@noreply.github.com Cc: Tejaswi Redkar dynamicdeploy@live.com; Author author@noreply.github.com Subject: Re: [dotnet/runtime] System.Net.NetworkInformation.Ping.Send not working on docker (#37734)

There is something wrong without your docker setup. I did run ping from your code as well as tried name resolution and that both work with the sdk:3.1 image.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://eur06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fdotnet%2Fruntime%2Fissues%2F37734%23issuecomment-642867410&data=02%7C01%7C%7C6e5f5a03f65446aca12308d80e3897ad%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637274983502923838&sdata=NG5M2Uee4ceYMcUOUTivlN%2Fv%2BMyJVZZMPYkb5yMGJms%3D&reserved=0, or unsubscribehttps://eur06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAAKUGTID4YTSYRSSOYJ7CP3RWERW3ANCNFSM4N3CSJNA&data=02%7C01%7C%7C6e5f5a03f65446aca12308d80e3897ad%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637274983502933831&sdata=Xe%2BJDqbgb%2FYe9BKuHja%2F2pNuBNXF5r81UMC5mrV3N7s%3D&reserved=0.

wfurt commented 4 years ago

no, since that is not complete app. I put the ping fragment to simple console app. You can drop to the shell and try ping/traceroute from there. You can check if /etc/resolv.conf looks reasonable and nslookup works. You can run Wireshark on the host to see what traffic comes out and back.

dynamicdeploy commented 4 years ago

Here is a simple test source code with the docker file https://github.com/dynamicdeploy/dotnetcoredocker It does the following: 1) Dns Resolution - Working 2) Ping - Working 3) Traceroute - Not working The same Traceroute should work fine on the desktop without any change. Output from running the program on the PC dotnet onerowcollector.dll www.microsoft.com

Resolving host.... Resolved www.microsoft.com to e13678.dspb.akamaiedge.net and ipaddress 2600:1406:3c:4af::356e Running ping... Address: 2600:1406:3c:4af::356e RoundTrip time: 16 Address: 2600:1406:3c:4af::356e RoundTrip time: 15 Address: 2600:1406:3c:4af::356e RoundTrip time: 13 Address: 2600:1406:3c:4af::356e RoundTrip time: 13 Address: 2600:1406:3c:4af::356e RoundTrip time: 13 Address: 2600:1406:3c:4af::356e RoundTrip time: 14 Address: 2600:1406:3c:4af::356e RoundTrip time: 13 Address: 2600:1406:3c:4af::356e RoundTrip time: 13 Address: 2600:1406:3c:4af::356e RoundTrip time: 12 Running traceroute... traceroute to www.microsoft.com, 15 hops max, 25 byte packets 1 64 ms 2001:558:4000:76::1 2 48 ms 2001:558:4000:76::1 3 35 ms 2001:558:82:d004::1 4 47 ms 2001:558:80:105a::1 5 61 ms 2001:558:80:1d1::1 6 68 ms 2001:558:80:197::1 7 68 ms 2001:558:0:f697::1 8 Request timed out. 9 Request timed out. 10 67 ms 2001:559:0:2d::26 11 50 ms 2600:1406:3c:4af::356e

Output from the container - Observe the traceroute results at the bottom.

C:\src\datarobo\Xintel\Xintel\onerowcollector\bin\Release\netcoreapp3.1\publish>docker run -it --rm tredkar/onerowcollector:1.0 Resolving host.... Resolved www.microsoft.com to e13678.dspb.akamaiedge.net and ipaddress 184.26.130.117 Running ping... Address: 184.26.130.117 RoundTrip time: 44 Address: 184.26.130.117 RoundTrip time: 103 Address: 184.26.130.117 RoundTrip time: 30 Address: 184.26.130.117 RoundTrip time: 46 Address: 184.26.130.117 RoundTrip time: 31 Address: 184.26.130.117 RoundTrip time: 18 Address: 184.26.130.117 RoundTrip time: 17 Address: 184.26.130.117 RoundTrip time: 30 Address: 184.26.130.117 RoundTrip time: 53 Running traceroute... *traceroute to www.microsoft.com, 15 hops max, 25 byte packets 1 Request timed out. 2 Request timed out. 3 Request timed out. 4 Request timed out. 5 Request timed out. 6 Request timed out. 7 Request timed out. 8 Request timed out. 9 Request timed out. 10 Request timed out. 11 Request timed out. 12 359 ms 184.26.130.117**

wfurt commented 4 years ago

did you try system traceroute? (apt install traceroute)

dynamicdeploy commented 4 years ago

I found someone else facing a similar issue. I could reproduce the same on my container https://stackoverflow.com/questions/60960067/cant-traceroute-from-linux-docker-in-windows-host Also, seems like there is a fix on Docker for Mac but still fails on Windows https://github.com/docker/for-mac/issues/1381 I just ran the following and even that didn't work on Docker for Windows docker run -it alpine traceroute -n www.google.com Unable to find image 'alpine:latest' locally latest: Pulling from library/alpine df20fa9351a1: Pull complete Digest: sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321 Status: Downloaded newer image for alpine:latest traceroute to www.google.com (172.217.5.100), 30 hops max, 46 byte packets 1 172.17.0.1 0.013 ms 0.006 ms 0.006 ms 2 3 4 5 6 7 8 *

goes on..

karelz commented 4 years ago

Triage: Does not seem to be critical for 5.0.

wfurt commented 4 years ago

for clarity, this seems to be specific to Docker on Windows and may not be .NET issue. I think Triage: is right.