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: dropping packets on IP unnumbered point-to-{,multi}point or non-multiaccess emulated link #29788

Closed
tomkcook opened this issue Jan 17, 2019 · 1 comment

Comments

@tomkcook
Copy link

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

$ go version
go version go1.11.4 linux/amd64

Also present in 1.10.4

Does this issue reproduce with the latest release?

Yes.

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

Observed both on Ubuntu 18.04 amd64 and Ubuntu 16.04 arm6.

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/tkcook/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/tkcook/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/lib/go-1.11"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.11/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
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 -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build317929097=/tmp/go-build -gno-record-gcc-switches"

What did you do?

The program below is intended to ping 8.8.8.8 over a specific network interface, as a test of whether that interface is up regardless of whether it is the one with the highest-priority default route.

In certain situations, the packets sent by this program go out over the default route, not the named interface. The exact combination required to make this happen is unclear, but we have seen it where the highest-priority default route (ie the one with the numerically lowest priority) is a PPP interface and the program is used to ping over a different interface. This can be seen by running tcpdump on the two interfaces, eg sudo tcpdump -i wlan0 icmp.

package main

import (
  "encoding/binary"
	"errors"
  "time"
  "os"
	"syscall"
  
  "net"
  "fmt"
	"flag"
	
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"github.com/vishvananda/netlink"
)

func valid_ip_address(ping_iface string) (net.IP, error) {
	link, err := netlink.LinkByName(ping_iface)
	if err != nil {
		fmt.Println(err)
		return net.ParseIP("0.0.0.0"), err
	}
	addresses, err := netlink.AddrList(link, syscall.AF_INET)
	for _, address := range addresses {
		if address.Scope == 253 {
			// link-local address
			continue
		}
		if ms0, ms1 := address.Mask.Size(); ms0 == ms1 {
			// The address has a netmask implying there is only one host on this
			// "network"
			continue
		}
		
		return address.IP, nil
	}
	return net.ParseIP("0.0.0.0"), errors.New("No IP4 address found on interface")
}

func main() {
	ping_iface := ""
	flag.StringVar(&ping_iface, "p", "wlan0", "The interface over which to send ping packets")
	flag.Parse()

	local_address, err := valid_ip_address(ping_iface)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Pinging through interface address %s\n", local_address.String())
	
	c, err := icmp.ListenPacket("ip4:icmp", local_address.String())
	if err != nil {
		fmt.Println(err)
		return
	}

	
	defer c.Close()

	data := make([]byte, 16)
	binary.BigEndian.PutUint64(data[0:8], uint64(time.Now().Unix()))
	for ii := 8; ii < len(data); ii += 1 {
		data[ii] = byte('V')
	}
	
	m := icmp.Message {
		Type: ipv4.ICMPTypeEcho,
		Code: 0,
		Body: &icmp.Echo {
			ID: os.Getpid() & 0xffff,
			Seq: 1,
			Data: data,
		},
	}
	message, _ := m.Marshal(nil)
	remote := net.IPAddr {
		IP: net.ParseIP("8.8.8.8"),
	}
	fmt.Println(remote)
	n, err := c.WriteTo(message, &remote)
	if err != nil {
		fmt.Println(err)
	}
	rmessage := make([]byte, 2000)
	timeout := time.Now().Add(2 * time.Second)
	c.SetReadDeadline(timeout)
	n, _, _ = c.ReadFrom(rmessage)
	msg, err:= icmp.ParseMessage(1, rmessage[:n])
	if err != nil {
		fmt.Println(err)
		return
	}
	switch msg.Type {
	case ipv4.ICMPTypeEchoReply:
		fmt.Println("Received response to ping")
	}
	
}

What did you expect to see?

Expected the packets to go over the interface named on the command line (or wlan0 by default).

What did you see instead?

Packets are routed according to the routing table.

@gopherbot gopherbot added this to the Unreleased milestone Jan 17, 2019
@mikioh mikioh changed the title x/net net.icmp.ListenPacket doesn't bind socket to interface on Linux x/net/icmp: dropping packets on IP unnumbered point-to-{,multi)point or non-multiaccess emulated link Jan 17, 2019
@mikioh
Copy link
Contributor

mikioh commented Jan 17, 2019

I think this is not an issue to the Go language or its standard/external library. So next time, please use more appropriate forums described in https://github.com/golang/go/wiki/Questions.

You may have options as follows:

  1. use a technique known as "policy-based IP routing"; on Linux, the interface to the implementation is ip{,6}tables or iproute2,
  2. use an operating system-specific link-layer control knob (socket option); on Linux, BINDTODEVICE might be an option, though I'm not sure the option will work correctly in any condition,
  3. use an operating system-specific network-layer control knob (socket option); on Linux, IPv{4,6} ancillary data for the control message,
  4. replace the protocol stack inside the kernel with your own one; in general, many appliances running Linux kernel inject their purpose-specific kernel loadable modules.

For option (2), you may use a combination of net.ListenConfig.ListenPacket and ipv4.NewPacketConn.
For option (3), you may use the approach to option (2) or icmp.PacketConn.IPv{4,6}PacketConn.
For options (1) and (4), you may need to investigate the kernel code more carefully; actually I think that option (2) also requires this because there's no concrete/explicit documentation on the behavior of RIB/FIB what happens when the IP routing/forwarding information bases are tangled up, for example, a link/network-layer adjacency; an output interface is brought down and brought up (or flapping intermittently.)

@mikioh mikioh closed this as completed Jan 17, 2019
@mikioh mikioh changed the title x/net/icmp: dropping packets on IP unnumbered point-to-{,multi)point or non-multiaccess emulated link x/net/icmp: dropping packets on IP unnumbered point-to-{,multi}point or non-multiaccess emulated link Jan 23, 2019
@golang golang locked and limited conversation to collaborators Jan 23, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants