Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/net/icmp: listen icmp in Windows not work properly #38427

Open
lochv opened this issue Apr 14, 2020 · 7 comments
Open

x/net/icmp: listen icmp in Windows not work properly #38427

lochv opened this issue Apr 14, 2020 · 7 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. OS-Windows
Milestone

Comments

@lochv
Copy link

lochv commented Apr 14, 2020

What version of Go are you using (go version)?

$ go version
go version go1.13.8 darwin/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/censored/Library/Caches/go-build"
GOENV="/Users/censored/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/censored/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/Cellar/go/1.13.8/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.13.8/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/censored/go/src/srt/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/4s/1bq_2censoredw0000gn/T/go-build488770590=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

I write a func to detect who ping to my computer

conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		log.Fatalf("listen err, %s", err)
	}
	defer conn.Close()

	if err != nil {
		 return
	}
	bytes := make([]byte, 512)
	for {
		fmt.Println("recv")
		n,_, err := conn.ReadFrom(bytes)
		if err != nil {
			fmt.Println(err.Error())
			continue
		}
		fmt.Println(n)
	}

What did you expect to see?

fmt.Println(n) every ping.

What did you see instead?

sometime it work, sometime it didn't work.
PoC video: https://youtu.be/AyQDH9AQSRc

@gopherbot gopherbot added this to the Unreleased milestone Apr 14, 2020
@andybons andybons added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. OS-Windows labels Apr 14, 2020
@lochv
Copy link
Author

lochv commented Apr 15, 2020

@andybons hi,

it seems SIO_RCVALL flag wasn't set, so only recv when network adapter just re-enabled.
https://github.com/golang/net/search?q=RCVALL&unscoped_q=RCVALL

@andybons
Copy link
Member

@alexbrainman

@alexbrainman
Copy link
Member

@lochv I don't know anything about icmp. I don't want to confuse you.

Alex

@lochv
Copy link
Author

lochv commented Apr 20, 2020

@alexbrainman the problem isn't icmp,
problem 1: in windows, socket need set SIO_RCVALL flag to monitor raw sock (linux didn't).
problem 2: in windows, socket bind on 0.0.0.0 canot set SIO_RCVALL flag.
so

icmp.ListenPacket("ip4:icmp", "0.0.0.0")

will not work in windows.

Need to port this example code from C to Go for icmp.ListenPacket work in windows

int F() {
	WSADATA            wsd;
	char hostname[100];
	struct hostent* local;
	struct in_addr addr;

	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		return 0;
	}

	SOCKET RecvSockets[20];
	ZeroMemory(&RecvSockets, sizeof(RecvSockets));

	gethostname(hostname, sizeof(hostname));
	local = gethostbyname(hostname);
	struct sockaddr_in RecvAddr;

	fd_set readfds;

	char *RecvBuf;
	RecvBuf = (char*)malloc(1024*sizeof(char));
	int BufLen = 1024;
	int iResult;
	struct sockaddr_in SenderAddr;
	int SenderAddrSize = sizeof(SenderAddr);
        // need listen on all interfaces one by one
	for (int i = 0; local->h_addr_list[i] != 0; i++)
	{
		DWORD  flag = RCVALL_ON;
		memcpy(&addr, local->h_addr_list[i], sizeof(struct in_addr));
		memcpy(&RecvAddr.sin_addr.s_addr, local->h_addr_list[i], sizeof(RecvAddr.sin_addr.s_addr));
		RecvAddr.sin_family = AF_INET;
		RecvAddr.sin_port = htons(0);
		RecvSockets[i] = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
		bind(RecvSockets[i], (SOCKADDR*)&RecvAddr, sizeof(RecvAddr));
		ioctlsocket(RecvSockets[i], SIO_RCVALL, &flag);
	}
	int activity;
	WCHAR szAddr[20] = { 0 };
	while (1) {
		FD_ZERO(&readfds);
		for (int i = 0; local->h_addr_list[i] != 0; i++)
		{
			FD_SET(RecvSockets[i], &readfds);
		}
		printf("read active");
		activity = select(0, &readfds, NULL, NULL, NULL);
		if (activity == SOCKET_ERROR)
		{
			printf("activity");
			wprintf(L"recvfrom failed with error %d\n", WSAGetLastError());
			
		}
		for (int i = 0; local->h_addr_list[i] != 0; i++)
		{
			SOCKET s = RecvSockets[i];
			if (FD_ISSET(s, &readfds))
			{
				ZeroMemory(RecvBuf, BufLen);
				iResult = recv(s, RecvBuf, BufLen, 0);
				if (iResult == SOCKET_ERROR)
					continue;
				if (iResult == 0)
					continue;
				printf("%d", iResult);
			}
		}
	}
}

@alexbrainman
Copy link
Member

the problem isn't icmp,
problem 1: in windows, socket need set SIO_RCVALL flag to monitor raw sock (linux didn't).
problem 2: in windows, socket bind on 0.0.0.0 canot set SIO_RCVALL flag.

@lochv I know nothing about these things. If you want to fix the code, here is how to contribute

https://golang.org/doc/contribute.html

Maybe altogether we can fix the code.

Thank you.

Alex

@dhaavi
Copy link

dhaavi commented Aug 27, 2020

If anyone is still interesting in this issue, I managed to somewhat find a fix.
Substantial caveat: Receiving ICMP packets from any source only seems to work if the Windows firewall is disabled. 🤷‍♂️
If someone has more insight into this, I'd appreciate a pointer to more information.

Here is the function that creates the listener that can send and receive ICMP packets from any source.
Here is our special (partial) traceroute function using it.

@lochv
Copy link
Author

lochv commented Aug 27, 2020

@dhaavi dhaa
i think the problem is how to modify "net" package: can listen icmp in windows + keep design pattern

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. OS-Windows
Projects
None yet
Development

No branches or pull requests

5 participants