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

Comments

@lochv
Copy link

@lochv 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

@lochv
Copy link
Author

@lochv 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

Loading

@andybons
Copy link
Member

@andybons andybons commented Apr 15, 2020

Loading

@alexbrainman
Copy link
Member

@alexbrainman alexbrainman commented Apr 19, 2020

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

Alex

Loading

@lochv
Copy link
Author

@lochv 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);
			}
		}
	}
}

Loading

@alexbrainman
Copy link
Member

@alexbrainman alexbrainman commented May 3, 2020

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

Loading

@dhaavi
Copy link

@dhaavi 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.

Loading

@lochv
Copy link
Author

@lochv 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

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants