func (self *HikarianIcmp) Run() { if self.mode == "decrypt" { clientConn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { log.Fatal(err) } defer clientConn.Close() for { buf := make([]byte, 1024) nr, caddr, err := clientConn.ReadFrom(buf) request, err := icmp.ParseMessage(ProtocolICMP, buf) if err != nil { log.Println("parse icmp request error: ", err.Error()) return } if request.Code == 0 { return } body, err := request.Body.Marshal(ProtocolICMP) if err != nil { log.Println("marshal body error: ", err.Error()) continue } hash := binary.BigEndian.Uint16(body[0:2]) + binary.BigEndian.Uint16(body[2:4]) dataChannel := self.DataChannelPool.Get(hash) if dataChannel == nil { dataChannel = make(chan []byte) self.DataChannelPool.Set(hash, dataChannel) go self.transportServer(clientConn, caddr, dataChannel) } if request.Code == MagicCode { log.Println("receive magic") dataChannel <- body[:nr-4] } } } else if self.mode == "encrypt" { client, err := net.ResolveTCPAddr("tcp", self.client) if err != nil { log.Fatalln("resolve client address error: ", err.Error()) } l, err := net.ListenTCP("tcp", client) if err != nil { log.Fatal(err) } defer l.Close() for { clientConn, err := l.AcceptTCP() if err != nil { log.Println("accept error: ", err.Error()) continue } defer clientConn.Close() go self.transportClient(clientConn) } } }
// Monitor clients going in/out of sleep mode via ICMP unreachable packets. func sleepMonitor() { c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { log.Printf("error listening for udp - sending data to all ports for all connected clients. err: %s", err) return } go icmpEchoSender(c) defer c.Close() for { buf := make([]byte, 1500) n, peer, err := c.ReadFrom(buf) if err != nil { log.Printf("%s\n", err.Error()) continue } msg, err := icmp.ParseMessage(1, buf[:n]) if err != nil { continue } ip := peer.String() // Look for echo replies, mark it as received. if msg.Type == ipv4.ICMPTypeEchoReply { pingResponse[ip] = stratuxClock.Time continue // No further processing needed. } // Only deal with ICMP Unreachable packets (since that's what iOS and Android seem to be sending whenever the apps are not available). if msg.Type != ipv4.ICMPTypeDestinationUnreachable { continue } // Packet parsing. mb, err := msg.Body.Marshal(1) if err != nil { continue } if len(mb) < 28 { continue } // The unreachable port. port := (uint16(mb[26]) << 8) | uint16(mb[27]) ipAndPort := ip + ":" + strconv.Itoa(int(port)) netMutex.Lock() p, ok := outSockets[ipAndPort] if !ok { // Can't do anything, the client isn't even technically connected. netMutex.Unlock() continue } p.LastUnreachable = stratuxClock.Time outSockets[ipAndPort] = p netMutex.Unlock() } }
func (p *Pinger) processPacket(recv *packet) error { var bytes []byte var proto int if p.ipv4 { if p.network == "ip" { bytes = ipv4Payload(recv.bytes) } else { bytes = recv.bytes } proto = protocolICMP } else { bytes = recv.bytes proto = protocolIPv6ICMP } var m *icmp.Message var err error if m, err = icmp.ParseMessage(proto, bytes[:recv.nbytes]); err != nil { return fmt.Errorf("Error parsing icmp message") } if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { // Not an echo reply, ignore it return nil } outPkt := &Packet{ Nbytes: recv.nbytes, IPAddr: p.ipaddr, } switch pkt := m.Body.(type) { case *icmp.Echo: outPkt.Rtt = time.Since(bytesToTime(pkt.Data[:timeSliceLength])) outPkt.Seq = pkt.Seq p.PacketsRecv += 1 default: // Very bad, not sure how this can happen return fmt.Errorf("Error, invalid ICMP echo reply. Body type: %T, %s", pkt, pkt) } p.rtts = append(p.rtts, outPkt.Rtt) handler := p.OnRecv if handler != nil { handler(outPkt) } return nil }
func main() { timeout := 5 t1 := time.Now() c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { checkError(err, "listen err, ") } c.SetDeadline(t1.Add(time.Second * time.Duration(timeout))) defer c.Close() wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: 1, Data: []byte("HELLO-R-U-THERE"), }, } wb, err := wm.Marshal(nil) if err != nil { log.Fatal(err) } // Send ICMP echo and start timer starttime := time.Now() if _, err := c.WriteTo(wb, &net.IPAddr{IP: net.ParseIP(targetIP)}); err != nil { log.Fatalf("WriteTo err, %s", err) } rb := make([]byte, 1500) // Look for ICMP response and stop time n, peer, err := c.ReadFrom(rb) stoptime := time.Now() if err != nil { log.Fatal(err) } //rm, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) rm, err := icmp.ParseMessage(1, rb[:n]) if err != nil { log.Fatal(err) } switch rm.Type { case ipv4.ICMPTypeEchoReply: log.Printf("got reflection from %v", peer) log.Printf("ICMP echo request sent on ", starttime) log.Printf("ICMP echo response received on ", stoptime) default: log.Printf("got %+v; want echo reply", rm) } }
func ping(listener *icmp.PacketConn, message icmp.Message, raddr net.Addr, timeout time.Duration) (Pong, error) { data, err := message.Marshal(nil) if err != nil { return Pong{}, err } _, err = listener.WriteTo(data, raddr) if err != nil { return Pong{}, err } now := time.Now() done := make(chan Pong) go func() { for { buf := make([]byte, 10000) // bufio n, peer, err := listener.ReadFrom(buf) if err != nil { return } since := time.Since(now) input, err := icmp.ParseMessage(protocolICMP, buf[:n]) if err != nil { return } if input.Type != ipv4.ICMPTypeEchoReply { continue } echo := input.Body.(*icmp.Echo) pong := Pong{ Peer: peer, ID: echo.ID, Seq: echo.Seq, Data: echo.Data, Size: n, RTT: since, } done <- pong return } }() select { case pong := <-done: return pong, nil case <-time.After(timeout): return Pong{}, errors.New("Timeout") } }
func ExamplePacketConn_nonPrivilegedPing() { switch runtime.GOOS { case "darwin": case "linux": log.Println("you may need to adjust the net.ipv4.ping_group_range kernel state") default: log.Println("not supported on", runtime.GOOS) return } c, err := icmp.ListenPacket("udp6", "fe80::1%en0") if err != nil { log.Fatal(err) } defer c.Close() wm := icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: 1, Data: []byte("HELLO-R-U-THERE"), }, } wb, err := wm.Marshal(nil) if err != nil { log.Fatal(err) } if _, err := c.WriteTo(wb, &net.UDPAddr{IP: net.ParseIP("ff02::1"), Zone: "en0"}); err != nil { log.Fatal(err) } rb := make([]byte, 1500) n, peer, err := c.ReadFrom(rb) if err != nil { log.Fatal(err) } rm, err := icmp.ParseMessage(58, rb[:n]) if err != nil { log.Fatal(err) } switch rm.Type { case ipv6.ICMPTypeEchoReply: log.Printf("got reflection from %v", peer) default: log.Printf("got %+v; want echo reply", rm) } }
// This checks if we can convert an URL to an IP func testDNS(pr *pingResponse, pi pingInfo, conn *icmp.PacketConn) { start := time.Now() msg := createMessage() msg_bytes, err := msg.Marshal(nil) if err != nil { emsg := "Could not marshal the message to []byte." logger.WriteString(emsg) pr.err = errors.New(emsg) return } ip, _ := net.LookupHost(pi.externalurl) if _, err := conn.WriteTo(msg_bytes, &net.UDPAddr{IP: net.ParseIP(ip[0]), Zone: "en0"}); err != nil { emsg := "Could not write to the internal ip address: " + ip[0] logger.WriteString(emsg) pr.external_url = false pr.err = errors.New(emsg) return } pr.external_url = true response := make([]byte, 1500) count, peer, err := conn.ReadFrom(response) if err != nil { emsg := "Could not read the response." logger.WriteString(emsg) pr.external_url = false pr.err = errors.New(emsg) return } _, err = icmp.ParseMessage(protocolICMP, response[:count]) if err != nil { emsg := "Could not parse the message received." logger.WriteString(emsg) pr.external_url = false pr.err = errors.New(emsg) return } logger.WriteString("Response " + strconv.Itoa(sequence) + " received from " + peer.String() + " after " + time.Now().Sub(start).String()) }
func TestMarshalAndParseMessageForIPv4(t *testing.T) { for i, tt := range marshalAndParseMessageForIPv4Tests { b, err := tt.Marshal(nil) if err != nil { t.Fatal(err) } m, err := icmp.ParseMessage(iana.ProtocolICMP, b) if err != nil { t.Fatal(err) } if m.Type != tt.Type || m.Code != tt.Code { t.Errorf("#%v: got %v; want %v", i, m, &tt) } if !reflect.DeepEqual(m.Body, tt.Body) { t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body) } } }
func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) { for i, tt := range marshalAndParseMultipartMessageForIPv4Tests { b, err := tt.Marshal(nil) if err != nil { t.Fatal(err) } if b[5] != 32 { t.Errorf("#%v: got %v; want 32", i, b[5]) } m, err := icmp.ParseMessage(iana.ProtocolICMP, b) if err != nil { t.Fatal(err) } if m.Type != tt.Type || m.Code != tt.Code { t.Errorf("#%v: got %v; want %v", i, m, &tt) } switch m.Type { case ipv4.ICMPTypeDestinationUnreachable: got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach) if !reflect.DeepEqual(got.Extensions, want.Extensions) { t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) } if len(got.Data) != 128 { t.Errorf("#%v: got %v; want 128", i, len(got.Data)) } case ipv4.ICMPTypeTimeExceeded: got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded) if !reflect.DeepEqual(got.Extensions, want.Extensions) { t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) } if len(got.Data) != 128 { t.Errorf("#%v: got %v; want 128", i, len(got.Data)) } case ipv4.ICMPTypeParameterProblem: got, want := m.Body.(*icmp.ParamProb), tt.Body.(*icmp.ParamProb) if !reflect.DeepEqual(got.Extensions, want.Extensions) { t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) } if len(got.Data) != 128 { t.Errorf("#%v: got %v; want 128", i, len(got.Data)) } } } }
func (i *ICMP) Read() ([]byte, error) { buf := make([]byte, 10000) n, _, err := i.listener.ReadFrom(buf) if err != nil { return nil, err } input, err := icmp.ParseMessage(_PROTOCOL_ICMP, buf[:n]) if err != nil { return nil, err } if input.Type != ipv4.ICMPTypeEchoReply { return nil, nil } echo, ok := input.Body.(*icmp.Echo) if !ok { return nil, nil } return echo.Data, nil }
func TestMarshalAndParseMessageForIPv6(t *testing.T) { pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")) for i, tt := range marshalAndParseMessageForIPv6Tests { for _, psh := range [][]byte{pshicmp, nil} { b, err := tt.Marshal(psh) if err != nil { t.Fatal(err) } m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b) if err != nil { t.Fatal(err) } if m.Type != tt.Type || m.Code != tt.Code { t.Errorf("#%v: got %v; want %v", i, m, &tt) } if !reflect.DeepEqual(m.Body, tt.Body) { t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body) } } } }
func TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) { pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")) for i, tt := range marshalAndParseMultipartMessageForIPv6Tests { for _, psh := range [][]byte{pshicmp, nil} { b, err := tt.Marshal(psh) if err != nil { t.Fatal(err) } if b[4] != 16 { t.Errorf("#%v: got %v; want 16", i, b[4]) } m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b) if err != nil { t.Fatal(err) } if m.Type != tt.Type || m.Code != tt.Code { t.Errorf("#%v: got %v; want %v", i, m, &tt) } switch m.Type { case ipv6.ICMPTypeDestinationUnreachable: got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach) if !reflect.DeepEqual(got.Extensions, want.Extensions) { t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) } if len(got.Data) != 128 { t.Errorf("#%v: got %v; want 128", i, len(got.Data)) } case ipv6.ICMPTypeTimeExceeded: got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded) if !reflect.DeepEqual(got.Extensions, want.Extensions) { t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) } if len(got.Data) != 128 { t.Errorf("#%v: got %v; want 128", i, len(got.Data)) } } } } }
func ExamplePacketConn_nonPrivilegedPing() { c, err := icmp.ListenPacket("udp6", "fe80::1%en0") if err != nil { log.Fatal(err) } defer c.Close() wm := icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: 1, Data: []byte("HELLO-R-U-THERE"), }, } wb, err := wm.Marshal(nil) if err != nil { log.Fatal(err) } if _, err := c.WriteTo(wb, &net.UDPAddr{IP: net.ParseIP("ff02::1"), Zone: "en0"}); err != nil { log.Fatal(err) } rb := make([]byte, 1500) n, peer, err := c.ReadFrom(rb) if err != nil { log.Fatal(err) } rm, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]) if err != nil { log.Fatal(err) } switch rm.Type { case ipv6.ICMPTypeEchoReply: log.Printf("got reflection from %v", peer) default: log.Printf("got %+v; want echo reply", rm) } }
func (p *Ping) parseMessage(r Reply, ssmap map[int]int64) bool { stop := time.Now().UnixNano() rep, err := icmp.ParseMessage(iana.ProtocolICMP, r.bytes) if err != nil { fmt.Printf("* Reply error from %s: icmp_seq=%d -> %s\n", r.addr, p.sends, r.bytes) return false } var seq int switch pkt := rep.Body.(type) { case *icmp.Echo: if !(pkt.ID == p.id && pkt.Seq <= p.sends && bytes.Equal(pkt.Data, p.data)) { fmt.Printf("* Wrong data %d bytes from %s (ID=[%d,%d], Seq=[%d,%d]): icmp_seq=%d\n", r.size, r.addr, pkt.ID, p.id, pkt.Seq, p.sends, p.sends) return false } seq = pkt.Seq default: fmt.Printf("* Not echo received from %s: icmp_seq=%d\n", r.size, r.addr, p.sends) return false } if v, ok := ssmap[seq]; !ok { fmt.Printf("* Duplicated packet received from %s: icmp_seq=%d\n", r.addr, p.sends) return false } else { rtt := stop - v delete(ssmap, seq) fmt.Printf("%d bytes from %s: icmp_seq=%d time=%.2f ms\n", r.size, r.addr, p.sends, float64(rtt)/float64(time.Millisecond)) } return true }
func (p *Pinger) procRecv(bytes []byte, ra net.Addr, ctx *context) { var ipaddr *net.IPAddr switch adr := ra.(type) { case *net.IPAddr: ipaddr = adr case *net.UDPAddr: ipaddr = &net.IPAddr{IP: adr.IP, Zone: adr.Zone} default: return } addr := ipaddr.String() p.mu.Lock() _, ok := p.addrs[addr] p.mu.Unlock() if !ok { return } var proto int if isIPv4(ipaddr.IP) { if p.network == "ip" { bytes = ipv4Payload(bytes) } proto = ProtocolICMP } else if isIPv6(ipaddr.IP) { proto = ProtocolIPv6ICMP } else { return } var m *icmp.Message var err error if m, err = icmp.ParseMessage(proto, bytes); err != nil { return } if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { return } var rtt time.Duration switch pkt := m.Body.(type) { case *icmp.Echo: if pkt.ID == p.id && pkt.Seq == p.seq { rtt = time.Since(bytesToTime(pkt.Data[:TimeSliceLength])) } default: return } if 0 == rtt { return } p.mu.Lock() delete(p.sent, addr) if len(p.sent) == 0 && !p.done { p.done = true close(ctx.done) } p.mu.Unlock() if p.OnRecv != nil { p.OnRecv(ipaddr, rtt) } }
// pingIcmp performs a ping to a destination. It select between ipv4 or ipv6 ping based // on the format of the destination ip. func (r Runner) pingIcmp() (err error) { var ( icmpType icmp.Type network string ) if strings.Contains(r.Parameters.Destination, ":") { network = "ip6:ipv6-icmp" icmpType = ipv6.ICMPTypeEchoRequest } else { network = "ip4:icmp" icmpType = ipv4.ICMPTypeEcho } c, err := net.Dial(network, r.Parameters.Destination) if err != nil { return fmt.Errorf("Dial failed: %v", err) } c.SetDeadline(time.Now().Add(time.Duration(r.Parameters.Timeout) * time.Second)) defer c.Close() // xid is the process ID. // Get process ID and make sure it fits in 16bits. xid := os.Getpid() & 0xffff // Sequence number of the packet. xseq := 0 packet := icmp.Message{ Type: icmpType, // Type of icmp message Code: 0, // icmp query messages use code 0 Body: &icmp.Echo{ ID: xid, // Packet id Seq: xseq, // Sequence number of the packet Data: bytes.Repeat([]byte("Ping!Ping!Ping!"), 3), }, } wb, err := packet.Marshal(nil) if err != nil { return fmt.Errorf("Connection failed: %v", err) } if _, err := c.Write(wb); err != nil { return fmt.Errorf("Conn.Write Error: %v", err) } rb := make([]byte, 1500) if _, err := c.Read(rb); err != nil { // If connection timed out, we return E_Timeout if e := err.(*net.OpError).Timeout(); e { return fmt.Errorf(E_Timeout) } if strings.Contains(err.Error(), "connection refused") { return fmt.Errorf(E_ConnRefused) } return fmt.Errorf("Conn.Read failed: %v", err) } _, err = icmp.ParseMessage(icmpType.Protocol(), rb) if err != nil { return fmt.Errorf("ParseICMPMessage failed: %v", err) } return }
func TestPacketConnReadWriteUnicastICMP(t *testing.T) { switch runtime.GOOS { case "nacl", "plan9", "windows": t.Skipf("not supported on %s", runtime.GOOS) } if m, ok := nettest.SupportsRawIPSocket(); !ok { t.Skip(m) } ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) if ifi == nil { t.Skipf("not available on %s", runtime.GOOS) } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatal(err) } defer c.Close() dst, err := net.ResolveIPAddr("ip4", "127.0.0.1") if err != nil { t.Fatal(err) } p := ipv4.NewPacketConn(c) defer p.Close() cf := ipv4.FlagDst | ipv4.FlagInterface if runtime.GOOS != "solaris" { // Solaris never allows to modify ICMP properties. cf |= ipv4.FlagTTL } for i, toggle := range []bool{true, false, true} { wb, err := (&icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal(nil) if err != nil { t.Fatal(err) } if err := p.SetControlMessage(cf, toggle); err != nil { if nettest.ProtocolNotSupported(err) { t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } p.SetTTL(i + 1) if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { t.Fatal(err) } if n, err := p.WriteTo(wb, nil, dst); err != nil { t.Fatal(err) } else if n != len(wb) { t.Fatalf("got %v; want %v", n, len(wb)) } rb := make([]byte, 128) loop: if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { t.Fatal(err) } if n, _, _, err := p.ReadFrom(rb); err != nil { switch runtime.GOOS { case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } else { m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) if err != nil { t.Fatal(err) } if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho { // On Linux we must handle own sent packets. goto loop } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } }
func TestRawConnReadWriteUnicastICMP(t *testing.T) { switch runtime.GOOS { case "nacl", "plan9", "windows": t.Skipf("not supported on %s", runtime.GOOS) } if m, ok := nettest.SupportsRawIPSocket(); !ok { t.Skip(m) } ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) if ifi == nil { t.Skipf("not available on %s", runtime.GOOS) } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatal(err) } defer c.Close() dst, err := net.ResolveIPAddr("ip4", "127.0.0.1") if err != nil { t.Fatal(err) } r, err := ipv4.NewRawConn(c) if err != nil { t.Fatal(err) } defer r.Close() cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { wb, err := (&icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal(nil) if err != nil { t.Fatal(err) } wh := &ipv4.Header{ Version: ipv4.Version, Len: ipv4.HeaderLen, TOS: i + 1, TotalLen: ipv4.HeaderLen + len(wb), TTL: i + 1, Protocol: 1, Dst: dst.IP, } if err := r.SetControlMessage(cf, toggle); err != nil { if nettest.ProtocolNotSupported(err) { t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } if err := r.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { t.Fatal(err) } if err := r.WriteTo(wh, wb, nil); err != nil { t.Fatal(err) } rb := make([]byte, ipv4.HeaderLen+128) loop: if err := r.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { t.Fatal(err) } if _, b, _, err := r.ReadFrom(rb); err != nil { switch runtime.GOOS { case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } else { m, err := icmp.ParseMessage(iana.ProtocolICMP, b) if err != nil { t.Fatal(err) } if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho { // On Linux we must handle own sent packets. goto loop } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } }
func ExamplePacketConn_tracingIPPacketRoute() { // Tracing an IP packet route to www.google.com. const host = "www.google.com" ips, err := net.LookupIP(host) if err != nil { log.Fatal(err) } var dst net.IPAddr for _, ip := range ips { if ip.To4() != nil { dst.IP = ip fmt.Printf("using %v for tracing an IP packet route to %s\n", dst.IP, host) break } } if dst.IP == nil { log.Fatal("no A record found") } c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolICMP), "0.0.0.0") // ICMP for IPv4 if err != nil { log.Fatal(err) } defer c.Close() p := ipv4.NewPacketConn(c) if err := p.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil { log.Fatal(err) } wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Data: []byte("HELLO-R-U-THERE"), }, } rb := make([]byte, 1500) for i := 1; i <= 64; i++ { // up to 64 hops wm.Body.(*icmp.Echo).Seq = i wb, err := wm.Marshal(nil) if err != nil { log.Fatal(err) } if err := p.SetTTL(i); err != nil { log.Fatal(err) } // In the real world usually there are several // multiple traffic-engineered paths for each hop. // You may need to probe a few times to each hop. begin := time.Now() if _, err := p.WriteTo(wb, nil, &dst); err != nil { log.Fatal(err) } if err := p.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil { log.Fatal(err) } n, cm, peer, err := p.ReadFrom(rb) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { fmt.Printf("%v\t*\n", i) continue } log.Fatal(err) } rm, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) if err != nil { log.Fatal(err) } rtt := time.Since(begin) // In the real world you need to determine whether the // received message is yours using ControlMessage.Src, // ControlMessage.Dst, icmp.Echo.ID and icmp.Echo.Seq. switch rm.Type { case ipv4.ICMPTypeTimeExceeded: names, _ := net.LookupAddr(peer.String()) fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, cm) case ipv4.ICMPTypeEchoReply: names, _ := net.LookupAddr(peer.String()) fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, cm) return default: log.Printf("unknown ICMP message: %+v\n", rm) } } }
func (p *Pinger) procRecv(recv *packet, queue map[string]*net.IPAddr) { var ipaddr *net.IPAddr switch adr := recv.addr.(type) { case *net.IPAddr: ipaddr = adr case *net.UDPAddr: ipaddr = &net.IPAddr{IP: adr.IP, Zone: adr.Zone} default: return } addr := ipaddr.String() p.mu.Lock() if _, ok := p.addrs[addr]; !ok { p.mu.Unlock() return } p.mu.Unlock() var bytes []byte var proto int if isIPv4(ipaddr.IP) { if p.network == "ip" { bytes = ipv4Payload(recv.bytes) } else { bytes = recv.bytes } proto = ProtocolICMP } else if isIPv6(ipaddr.IP) { bytes = recv.bytes proto = ProtocolIPv6ICMP } else { return } var m *icmp.Message var err error if m, err = icmp.ParseMessage(proto, bytes); err != nil { return } if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { return } var rtt time.Duration switch pkt := m.Body.(type) { case *icmp.Echo: p.mu.Lock() if pkt.ID == p.id && pkt.Seq == p.seq { rtt = time.Since(bytesToTime(pkt.Data[:TimeSliceLength])) } p.mu.Unlock() default: return } if _, ok := queue[addr]; ok { delete(queue, addr) p.mu.Lock() handler := p.OnRecv p.mu.Unlock() if handler != nil { handler(ipaddr, rtt) } } }
func TestPacketConnReadWriteMulticastICMP(t *testing.T) { switch runtime.GOOS { case "freebsd": // due to a bug on loopback marking // See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065. t.Skipf("not supported on %s", runtime.GOOS) case "nacl", "plan9", "windows": t.Skipf("not supported on %s", runtime.GOOS) } if !supportsIPv6 { t.Skip("ipv6 is not supported") } if m, ok := nettest.SupportsRawIPSocket(); !ok { t.Skip(m) } ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback) if ifi == nil { t.Skipf("not available on %s", runtime.GOOS) } for _, tt := range packetConnReadWriteMulticastICMPTests { c, err := net.ListenPacket("ip6:ipv6-icmp", "::") if err != nil { t.Fatal(err) } defer c.Close() pshicmp := icmp.IPv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, tt.grp.IP) p := ipv6.NewPacketConn(c) defer p.Close() if tt.src == nil { if err := p.JoinGroup(ifi, tt.grp); err != nil { t.Fatal(err) } defer p.LeaveGroup(ifi, tt.grp) } else { if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil { switch runtime.GOOS { case "freebsd", "linux": default: // platforms that don't support MLDv2 fail here t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } defer p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src) } if err := p.SetMulticastInterface(ifi); err != nil { t.Fatal(err) } if _, err := p.MulticastInterface(); err != nil { t.Fatal(err) } if err := p.SetMulticastLoopback(true); err != nil { t.Fatal(err) } if _, err := p.MulticastLoopback(); err != nil { t.Fatal(err) } cm := ipv6.ControlMessage{ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, Src: net.IPv6loopback, IfIndex: ifi.Index, } cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU var f ipv6.ICMPFilter f.SetAll(true) f.Accept(ipv6.ICMPTypeEchoReply) if err := p.SetICMPFilter(&f); err != nil { t.Fatal(err) } var psh []byte for i, toggle := range []bool{true, false, true} { if toggle { psh = nil if err := p.SetChecksum(true, 2); err != nil { // Solaris never allows to // modify ICMP properties. if runtime.GOOS != "solaris" { t.Fatal(err) } } } else { psh = pshicmp // Some platforms never allow to // disable the kernel checksum // processing. p.SetChecksum(false, -1) } wb, err := (&icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal(psh) if err != nil { t.Fatal(err) } if err := p.SetControlMessage(cf, toggle); err != nil { if nettest.ProtocolNotSupported(err) { t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { t.Fatal(err) } cm.HopLimit = i + 1 if n, err := p.WriteTo(wb, &cm, tt.grp); err != nil { t.Fatal(err) } else if n != len(wb) { t.Fatalf("got %v; want %v", n, len(wb)) } rb := make([]byte, 128) if n, _, _, err := p.ReadFrom(rb); err != nil { switch runtime.GOOS { case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } else { if m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]); err != nil { t.Fatal(err) } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0) } } } } }
func TestRawConnReadWriteMulticastICMP(t *testing.T) { if testing.Short() { t.Skip("to avoid external network") } if m, ok := nettest.SupportsRawIPSocket(); !ok { t.Skip(m) } ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) if ifi == nil { t.Skipf("not available on %s", runtime.GOOS) } for _, tt := range rawConnReadWriteMulticastICMPTests { c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatal(err) } defer c.Close() r, err := ipv4.NewRawConn(c) if err != nil { t.Fatal(err) } defer r.Close() if tt.src == nil { if err := r.JoinGroup(ifi, tt.grp); err != nil { t.Fatal(err) } defer r.LeaveGroup(ifi, tt.grp) } else { if err := r.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil { switch runtime.GOOS { case "freebsd", "linux": default: // platforms that don't support IGMPv2/3 fail here t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } defer r.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src) } if err := r.SetMulticastInterface(ifi); err != nil { t.Fatal(err) } if _, err := r.MulticastInterface(); err != nil { t.Fatal(err) } if err := r.SetMulticastLoopback(true); err != nil { t.Fatal(err) } if _, err := r.MulticastLoopback(); err != nil { t.Fatal(err) } cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { wb, err := (&icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal(nil) if err != nil { t.Fatal(err) } wh := &ipv4.Header{ Version: ipv4.Version, Len: ipv4.HeaderLen, TOS: i + 1, TotalLen: ipv4.HeaderLen + len(wb), Protocol: 1, Dst: tt.grp.IP, } if err := r.SetControlMessage(cf, toggle); err != nil { if nettest.ProtocolNotSupported(err) { t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } if err := r.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { t.Fatal(err) } r.SetMulticastTTL(i + 1) if err := r.WriteTo(wh, wb, nil); err != nil { t.Fatal(err) } rb := make([]byte, ipv4.HeaderLen+128) if rh, b, _, err := r.ReadFrom(rb); err != nil { t.Fatal(err) } else { m, err := icmp.ParseMessage(iana.ProtocolICMP, b) if err != nil { t.Fatal(err) } switch { case (rh.Dst.IsLoopback() || rh.Dst.IsLinkLocalUnicast() || rh.Dst.IsGlobalUnicast()) && m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1 case rh.Dst.IsMulticast() && m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0 default: t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } } }
func TestPacketConnReadWriteMulticastICMP(t *testing.T) { switch runtime.GOOS { case "nacl", "plan9", "solaris", "windows": t.Skipf("not supported on %s", runtime.GOOS) } if m, ok := nettest.SupportsRawIPSocket(); !ok { t.Skip(m) } ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) if ifi == nil { t.Skipf("not available on %s", runtime.GOOS) } for _, tt := range packetConnReadWriteMulticastICMPTests { c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatal(err) } defer c.Close() p := ipv4.NewPacketConn(c) defer p.Close() if tt.src == nil { if err := p.JoinGroup(ifi, tt.grp); err != nil { t.Fatal(err) } defer p.LeaveGroup(ifi, tt.grp) } else { if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil { switch runtime.GOOS { case "freebsd", "linux": default: // platforms that don't support IGMPv2/3 fail here t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } defer p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src) } if err := p.SetMulticastInterface(ifi); err != nil { t.Fatal(err) } if _, err := p.MulticastInterface(); err != nil { t.Fatal(err) } if err := p.SetMulticastLoopback(true); err != nil { t.Fatal(err) } if _, err := p.MulticastLoopback(); err != nil { t.Fatal(err) } cf := ipv4.FlagDst | ipv4.FlagInterface if runtime.GOOS != "solaris" { // Solaris never allows to modify ICMP properties. cf |= ipv4.FlagTTL } for i, toggle := range []bool{true, false, true} { wb, err := (&icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal(nil) if err != nil { t.Fatal(err) } if err := p.SetControlMessage(cf, toggle); err != nil { if nettest.ProtocolNotSupported(err) { t.Logf("not supported on %s", runtime.GOOS) continue } t.Fatal(err) } if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { t.Fatal(err) } p.SetMulticastTTL(i + 1) if n, err := p.WriteTo(wb, nil, tt.grp); err != nil { t.Fatal(err) } else if n != len(wb) { t.Fatalf("got %v; want %v", n, len(wb)) } rb := make([]byte, 128) if n, _, _, err := p.ReadFrom(rb); err != nil { t.Fatal(err) } else { m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) if err != nil { t.Fatal(err) } switch { case m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1 case m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0 default: t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } } }
func doPing(tt pingTest, seq int) error { c, err := icmp.ListenPacket(tt.network, tt.address) if err != nil { return err } defer c.Close() dst, err := googleAddr(c, tt.protocol) if err != nil { return err } if tt.protocol == iana.ProtocolIPv6ICMP { var f ipv6.ICMPFilter f.SetAll(true) f.Accept(ipv6.ICMPTypeDestinationUnreachable) f.Accept(ipv6.ICMPTypePacketTooBig) f.Accept(ipv6.ICMPTypeTimeExceeded) f.Accept(ipv6.ICMPTypeParameterProblem) f.Accept(ipv6.ICMPTypeEchoReply) if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil { return err } } wm := icmp.Message{ Type: tt.mtype, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq), Data: []byte("HELLO-R-U-THERE"), }, } wb, err := wm.Marshal(nil) if err != nil { return err } if n, err := c.WriteTo(wb, dst); err != nil { return err } else if n != len(wb) { return fmt.Errorf("got %v; want %v", n, len(wb)) } rb := make([]byte, 1500) if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil { return err } n, peer, err := c.ReadFrom(rb) if err != nil { return err } rm, err := icmp.ParseMessage(tt.protocol, rb[:n]) if err != nil { return err } switch rm.Type { case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply: return nil default: return fmt.Errorf("got %+v from %v; want echo reply", rm, peer) } }
func probeICMP(target string, w http.ResponseWriter, module Module) (success bool) { deadline := time.Now().Add(module.Timeout) socket, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { log.Errorf("Error listening to socket: %s", err) return } defer socket.Close() ip, err := net.ResolveIPAddr("ip4", target) if err != nil { log.Errorf("Error resolving address %s: %s", target, err) return } seq := getICMPSequence() pid := os.Getpid() & 0xffff wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("Prometheus Blackbox Exporter"), }, } wb, err := wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", target, err) return } if _, err := socket.WriteTo(wb, ip); err != nil { log.Errorf("Error writing to socker for %s: %s", target, err) return } rb := make([]byte, 1500) if err := socket.SetReadDeadline(deadline); err != nil { log.Errorf("Error setting socket deadline for %s: %s", target, err) return } for { n, peer, err := socket.ReadFrom(rb) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { log.Infof("Timeout reading from socket for %s: %s", target, err) return } log.Errorf("Error reading from socket for %s: %s", target, err) continue } if peer.String() != ip.String() { continue } rm, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) if err != nil { log.Warnf("Error parsing ICMP message for %s: %s", target, err) continue } if rm.Type == ipv4.ICMPTypeEchoReply { // The ICMP package does not support unmarshalling // messages, so assume this is the right sequence number. success = true return } } return }
func (this *Pinger) receiver() { defer func() { // Let's recover from panic if r := recover(); r != nil { glog.Errorf("Recovering from panic: %v", r) } this.wg.Done() close(this.reschan) this.Stop() }() rb := make([]byte, ipv4.HeaderLen+icmpHeaderSize+this.Size) var tempDelay time.Duration // how long to sleep on accept failure loop: for { rh, b, _, err := this.rconn.ReadFrom(rb) if err != nil { select { case <-this.done: break loop default: } // Borrowed from go1.3.3/src/pkg/net/http/server.go:1699 if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := this.MaxRTT; tempDelay > max { tempDelay = max } glog.Errorf("Accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } break loop } m, err := icmp.ParseMessage(ProtocolICMP, b) if err != nil { // Hm...bad message? continue } if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho { // On Linux we must handle own sent packets. continue } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { glog.Errorf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) continue } // Echo Reply er, ok := m.Body.(*icmp.Echo) if !ok { glog.Errorf("Error type requireing m.Body to *icmp.Echo") continue } if er.ID != int(this.pid) || er.Seq > int(this.seqnum) { glog.Debugf("%d != %d, %d != %d", er.ID, this.pid, er.Seq, this.seqnum) continue } rtt := time.Since(time.Unix(0, int64(binary.BigEndian.Uint64(er.Data)))) src := make(net.IP, len(rh.Src)) copy(src, rh.Src) dst := make(net.IP, len(rh.Dst)) copy(dst, rh.Dst) pr := &PingResult{ TOS: rh.TOS, TTL: rh.TTL, Type: m.Type, Code: m.Code, ID: er.ID, Seq: er.Seq, RTT: rtt, Src: src, Dst: dst, Size: len(er.Data), } this.mu.Lock() this.results[src.String()] = pr this.mu.Unlock() this.reschan <- pr } for ip, pr := range this.results { if pr == nil { this.reschan <- &PingResult{ Src: net.ParseIP(ip), ID: int(this.pid), Seq: int(this.seqnum), Err: fmt.Errorf("%s: Request timed out for seq %d", ip, this.seqnum), } } } }
func doPing(host string, tt *Ping, seq int) error { c, err := icmp.ListenPacket(tt.network, tt.address) if err != nil { return err } defer c.Close() dst, err := getAddr(host, c, tt.protocol) if err != nil { return err } now := time.Now() today := now.Truncate(24*time.Hour).UnixNano() / 1000000 transmitTime := uint32(now.UnixNano()/1000000 - today) wm := icmp.Message{ Type: tt.mtype, Code: 0, Body: &Timestamp{ ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq), OriginTimestamp: transmitTime, }, } wb, err := wm.Marshal(nil) if err != nil { return err } if n, err := c.WriteTo(wb, dst); err != nil { return err } else if n != len(wb) { return fmt.Errorf("got %v; want %v", n, len(wb)) } rb := make([]byte, 1500) if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil { return err } n, peer, err := c.ReadFrom(rb) if err != nil { return err } receivedTime := time.Now().UnixNano()/1000000 - today rm, err := icmp.ParseMessage(tt.protocol, rb[:n]) if err != nil { return err } switch rm.Type { case ipv4.ICMPTypeTimestampReply: b, _ := rm.Body.Marshal(iana.ProtocolICMP) ts, err := ParseTimestamp(b) if err != nil { fmt.Errorf("ParseTimestamp error: %s", err) } remoteReceiveTime := int64(ts.ReceiveTimestamp) rtt := int64(math.Abs(float64(remoteReceiveTime - int64(transmitTime) + receivedTime - int64(ts.TransmitTimestamp)))) delta := rtt/2 + int64(transmitTime) - remoteReceiveTime w := new(tabwriter.Writer) w.Init(os.Stdout, 0, 4, 0, '\t', 0) fmt.Fprintf(w, "ICMP timestamp:\tOriginate=%d Receive=%d Transmit=%d\n", ts.OriginTimestamp, ts.ReceiveTimestamp, ts.TransmitTimestamp) fmt.Fprintf(w, "ICMP timestamp RTT:\ttsrtt=%d\n", rtt) fmt.Fprintf(w, "Time difference:\tdelta=%d\n", delta) w.Flush() return nil default: return fmt.Errorf("got %+v from %v; want echo reply", rm, peer) } }
func TestPacketConnReadWriteUnicastICMP(t *testing.T) { switch runtime.GOOS { case "nacl", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { t.Skip("ipv6 is not supported") } if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip6:ipv6-icmp", "::1") if err != nil { t.Fatal(err) } defer c.Close() p := ipv6.NewPacketConn(c) defer p.Close() dst, err := net.ResolveIPAddr("ip6", "::1") if err != nil { t.Fatal(err) } pshicmp := icmp.IPv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP) cm := ipv6.ControlMessage{ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, Src: net.IPv6loopback, } cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) if ifi != nil { cm.IfIndex = ifi.Index } var f ipv6.ICMPFilter f.SetAll(true) f.Accept(ipv6.ICMPTypeEchoReply) if err := p.SetICMPFilter(&f); err != nil { t.Fatal(err) } var psh []byte for i, toggle := range []bool{true, false, true} { if toggle { psh = nil if err := p.SetChecksum(true, 2); err != nil { t.Fatal(err) } } else { psh = pshicmp // Some platforms never allow to disable the // kernel checksum processing. p.SetChecksum(false, -1) } wb, err := (&icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal(psh) if err != nil { t.Fatal(err) } if err := p.SetControlMessage(cf, toggle); err != nil { if nettest.ProtocolNotSupported(err) { t.Skipf("not supported on %q", runtime.GOOS) } t.Fatal(err) } cm.HopLimit = i + 1 if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { t.Fatal(err) } if n, err := p.WriteTo(wb, &cm, dst); err != nil { t.Fatal(err) } else if n != len(wb) { t.Fatalf("got %v; want %v", n, len(wb)) } rb := make([]byte, 128) if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { t.Fatal(err) } if n, cm, _, err := p.ReadFrom(rb); err != nil { switch runtime.GOOS { case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket t.Logf("not supported on %q", runtime.GOOS) continue } t.Fatal(err) } else { t.Logf("rcvd cmsg: %v", cm) if m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]); err != nil { t.Fatal(err) } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0) } } } }
func TestPingGoogle(t *testing.T) { if testing.Short() { t.Skip("to avoid external network") } switch runtime.GOOS { case "darwin": case "linux": t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state") default: t.Skipf("not supported on %q", runtime.GOOS) } for i, tt := range pingGoogleTests { if tt.network[:2] == "ip" && os.Getuid() != 0 { continue } c, err := icmp.ListenPacket(tt.network, tt.address) if err != nil { t.Error(err) continue } defer c.Close() dst, err := googleAddr(c, tt.protocol) if err != nil { t.Error(err) continue } wm := icmp.Message{ Type: tt.mtype, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: 1 << uint(i), Data: []byte("HELLO-R-U-THERE"), }, } wb, err := wm.Marshal(nil) if err != nil { t.Error(err) continue } if n, err := c.WriteTo(wb, dst); err != nil { t.Error(err, dst) continue } else if n != len(wb) { t.Errorf("got %v; want %v", n, len(wb)) continue } rb := make([]byte, 1500) n, peer, err := c.ReadFrom(rb) if err != nil { t.Error(err) continue } rm, err := icmp.ParseMessage(tt.protocol, rb[:n]) if err != nil { t.Error(err) continue } switch rm.Type { case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply: t.Logf("got reflection from %v", peer) default: t.Errorf("got %+v; want echo reply", rm) } } }
func (p *Pinger) listenIpv4() { if p.conn == nil { panic("conn doesnt exist") } log.Printf("starting rawSocket listener\n") rb := make([]byte, 1500) pkt := EchoResponse{} var readErr error var data []byte ipconn, ok := p.conn.(*net.IPConn) if !ok { panic("connection is not IPConn") } file, err := ipconn.File() if err != nil { panic(err.Error()) } defer file.Close() fd := file.Fd() var pktTime time.Time recvTime := syscall.Timeval{} for { if p.conn == nil { break } n, peer, err := p.conn.ReadFrom(rb) if err != nil { readErr = err break } _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.SIOCGSTAMP), uintptr(unsafe.Pointer(&recvTime))) err = nil if errno != 0 { err = errno } if err == nil { pktTime = time.Unix(0, recvTime.Nano()) } else { pktTime = time.Now() } rm, err := icmp.ParseMessage(ProtocolICMP, rb[:n]) if err != nil { fmt.Println(err.Error()) continue } if rm.Type == ipv4.ICMPTypeEchoReply { data = rm.Body.(*icmp.Echo).Data if len(data) < 9 { log.Printf("go-pinger: invalid data payload from %s. Expected at least 9bytes got %d", peer.String(), len(data)) continue } pkt = EchoResponse{ Peer: peer.String(), Seq: rm.Body.(*icmp.Echo).Seq, Id: rm.Body.(*icmp.Echo).ID, Received: pktTime, } if p.Debug { log.Printf("go-pinger: recieved pkt. %s\n", pkt.String()) } select { case p.packetChan <- pkt: default: log.Printf("go-pinger: droped echo response due to blocked packetChan. %s\n", pkt.String()) } } } if p.Debug { log.Printf("listen loop ended.") } p.m.Lock() if p.running { log.Println(readErr.Error()) p.Stop() p.start() } p.m.Unlock() }