ICMP回显响应数据包在内核和用户空间之间消失

时间:2019-01-18 10:07:39

标签: linux networking ping icmp

我有一个Linux系统:

$ uname -a
Linux mybox 4.14.77-v7+ #1 SMP Mon Jan 7 10:09:57 GMT 2019 armv7l Linux

用户空间是Alpine Linux 3.8,而libc是musl-libc。

系统具有两个以太网接口eth0和eth2。默认路由的配置如下:

$ ip route
default via 100.119.124.45 dev eth2  metric 103 
default via 10.40.0.1 dev eth0  metric 201 

如果更改优先级,以使eth0路由成为优先级路由,则可以在其上路由流量。如图所示,我也可以使用路由表ping 8.8.8.8

但是,当我尝试通过8.8.8.8 ping eth0时,tcpdump -nni eth0 icmp显示我收到了响应数据包,但是ping报告没有收到数据包。这是ping的输出:

$ sudo ping -I eth0 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
^C
--- 8.8.8.8 ping statistics ---
7 packets transmitted, 0 packets received, 100% packet loss

这是相应的tcpdump输出:

$ tcpdump -nni eth0 icmp
10:02:42.312257 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 0, length 64
10:02:42.318189 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 0, length 64
10:02:43.312525 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 1, length 64
10:02:43.318321 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 1, length 64
10:02:44.312792 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 2, length 64
10:02:44.318632 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 2, length 64
10:02:45.313066 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 3, length 64
10:02:45.318802 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 3, length 64
10:02:46.313327 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 4, length 64
10:02:46.319310 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 4, length 64
10:02:47.313595 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 5, length 64
10:02:47.319366 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 5, length 64
10:02:48.313822 IP 10.40.16.110 > google-public-dns-a.google.com: ICMP echo request, id 3146, seq 6, length 64
10:02:48.319652 IP google-public-dns-a.google.com > 10.40.16.110: ICMP echo reply, id 3146, seq 6, length 64

10.40.16.110eth0的IP地址。

这是怎么回事?数据包如何到达内核,却在内核和用户空间之间丢失?

我已经用自己的ping实现对其进行了测试:

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 sockaddr(address string) (syscall.Sockaddr, error) {
    a, err := net.ResolveIPAddr("ip4", address)
    if err != nil { return nil, err }
    a.IP = a.IP.To4()
    sa := &syscall.SockaddrInet4{}
    copy(sa.Addr[:], a.IP)
    return sa, nil
}

func ListenPacket(address, iface string) (net.PacketConn, error) {
    s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
    fmt.Println("trace")
    if err != nil { fmt.Println("Failed to open socket."); return nil, err }
    fmt.Println("trace")
    err = syscall.SetsockoptString(s, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, iface)
    if err != nil { return nil, err }
    sa, err := sockaddr(address)
    if err != nil { return nil, err }
    err = syscall.Bind(s, sa)
    if err != nil { return nil, err }
    f := os.NewFile(uintptr(s), "datagram-oriented icmp")
    defer f.Close()
    c, _ := net.FilePacketConn(f)
    return c, nil
}

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 {
            continue
        }
        if ms0, ms1 := address.Mask.Size(); ms0 == ms1 {
            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 %s address %s\n", ping_iface, local_address.String())

    c, err := ListenPacket(local_address.String(), ping_iface)
    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(10 * time.Second)
    c.SetReadDeadline(timeout)
    n, _, err = c.ReadFrom(rmessage)
    if err != nil {
        fmt.Println(err)
        return
    }
    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")
    }

}

这给出了相同的结果; tcpdump看到了数据包,但它们再也没有回到用户空间客户端。

更新,我已将iptables默认策略设置为ACCEPT,并从iptables(iptables-save | grep '\(^*\)\|\(COMMIT\)' | iptables-restore)中删除了所有内容,并禁用了rp_filter(echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter),并得到了相同的结果。

0 个答案:

没有答案