cauto 2 år sedan
incheckning
3f980e8d33
14 ändrade filer med 631 tillägg och 0 borttagningar
  1. 8 0
      .idea/.gitignore
  2. 8 0
      .idea/modules.xml
  3. 9 0
      .idea/node_monitor.iml
  4. 21 0
      conf/seelog.xml
  5. 11 0
      g/struct.go
  6. 10 0
      go.mod
  7. 6 0
      go.sum
  8. 70 0
      main.go
  9. 1 0
      nettools/icmp_other.go
  10. 9 0
      nettools/icmp_unix.go
  11. 303 0
      nettools/icmpping.go
  12. 53 0
      nettools/pingI.go
  13. 34 0
      nettools/resolver.go
  14. 88 0
      nettools/tcpping.go

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/node_monitor.iml" filepath="$PROJECT_DIR$/.idea/node_monitor.iml" />
+    </modules>
+  </component>
+</project>

+ 9 - 0
.idea/node_monitor.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 21 - 0
conf/seelog.xml

@@ -0,0 +1,21 @@
+<seelog type="asynctimer" asyncinterval="5000000" minlevel="info" maxlevel="error">
+    <outputs formatid="main">
+        <console/>
+
+        <filter levels="info">
+            <rollingfile type="size" filename="./logs/info.log" maxsize="10240000" maxrolls="5"/>
+        </filter>
+
+        <filter levels="debug">
+            <rollingfile type="size" filename="./logs/debug.log" maxsize="10240000" maxrolls="5"/>
+        </filter>
+
+        <filter levels="error">
+            <rollingfile type="size" filename="./logs/error.log" maxsize="10240000" maxrolls="5"/>
+        </filter>
+
+    </outputs>
+    <formats>
+        <format id="main" format="%Date/%Time [%Level] %File %Msg%n"/>
+    </formats>
+</seelog>

+ 11 - 0
g/struct.go

@@ -0,0 +1,11 @@
+package g
+
+// Ping Struct
+type PingSt struct {
+	SendPk   int
+	RevcPk   int
+	LossPk   int
+	MinDelay int
+	AvgDelay int
+	MaxDelay int
+}

+ 10 - 0
go.mod

@@ -0,0 +1,10 @@
+module node_monitor
+
+go 1.19
+
+require golang.org/x/net v0.1.0
+
+require (
+	github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+)

+ 6 - 0
go.sum

@@ -0,0 +1,6 @@
+github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs=
+github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

+ 70 - 0
main.go

@@ -0,0 +1,70 @@
+package main
+
+import (
+	"github.com/cihub/seelog"
+	"net"
+	"node_monitor/g"
+	"node_monitor/nettools"
+	"runtime"
+	"time"
+)
+
+func main() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+
+	var ipSlice []string
+	ipSlice = append(ipSlice, "kdvkr-02.xyz")
+	ipSlice = append(ipSlice, "kdvkr-04.xyz")
+	stat := g.PingSt{}
+	stat.MinDelay = -1
+	lossPK := 0
+	for i := 0; i < len(ipSlice); i++ {
+		addr, err := net.ResolveIPAddr("ip", ipSlice[i])
+		seelog.Info("Start Ping " + ipSlice[i] + "..")
+		if err == nil {
+			for i := 0; i < 10; i++ {
+				starttime := time.Now().UnixNano()
+				//tcpping := nettools.NewTcpPing(addr.String(), 22, time.Second*4)
+				icmping := nettools.NewIcmpPing(addr.String(), time.Second*4)
+				rest := icmping.Ping()
+				if rest.Error() == nil {
+					delay := rest.Result()
+					seelog.Info("[func:IcmpPing] Finish Addr:", addr, delay, "ms")
+					stat.AvgDelay = stat.AvgDelay + rest.Result()
+					if stat.MaxDelay < delay {
+						stat.MaxDelay = delay
+					}
+					if stat.MinDelay == -1 || stat.MinDelay > delay {
+						stat.MinDelay = delay
+					}
+					stat.RevcPk = stat.RevcPk + 1
+
+					stat.SendPk = stat.SendPk + 1
+					stat.LossPk = int((float64(lossPK) / float64(stat.SendPk)) * 100)
+					duringtime := time.Now().UnixNano() - starttime
+					time.Sleep(time.Duration(3000*1000000-duringtime) * time.Nanosecond)
+				} else {
+					seelog.Debug("[func:StartPing IcmpPing] ID:", i, " IP:", addr, "| err:", rest.Error())
+					lossPK = lossPK + 1
+				}
+
+			}
+			if stat.RevcPk > 0 {
+				stat.AvgDelay = stat.AvgDelay / stat.RevcPk
+			} else {
+				stat.AvgDelay = 0.0
+			}
+			seelog.Debug("[func:IcmpPing] Finish Addr:", addr, " MaxDelay:", stat.MaxDelay, " MinDelay:", stat.MinDelay, " AvgDelay:", stat.AvgDelay, " Revc:", stat.RevcPk, " LossPK:", stat.LossPk)
+		} else {
+			stat.AvgDelay = 0.00
+			stat.MinDelay = 0.00
+			stat.MaxDelay = 0.00
+			stat.SendPk = 0
+			stat.RevcPk = 0
+			stat.LossPk = 100
+			seelog.Debug("[func:IcmpPing] Finish Addr:", addr, " Unable to resolve destination host")
+		}
+
+	}
+
+}

+ 1 - 0
nettools/icmp_other.go

@@ -0,0 +1 @@
+package nettools

+ 9 - 0
nettools/icmp_unix.go

@@ -0,0 +1,9 @@
+package nettools
+
+import (
+	"context"
+)
+
+func (this *IcmpPing) ping_rootless(ctx context.Context) IPingResult {
+	return this.rawping("udp")
+}

+ 303 - 0
nettools/icmpping.go

@@ -0,0 +1,303 @@
+package nettools
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"golang.org/x/net/context"
+	"golang.org/x/net/icmp"
+	"golang.org/x/net/ipv4"
+	"golang.org/x/net/ipv6"
+	"math/rand"
+	"net"
+	"os"
+	"syscall"
+	"time"
+)
+
+type IcmpPingResult struct {
+	Time int
+	Err  error
+	IP   net.IP
+	TTL  int
+}
+
+func (this *IcmpPingResult) Result() int {
+	return this.Time
+}
+
+func (this *IcmpPingResult) Error() error {
+	return this.Err
+}
+
+func (this *IcmpPingResult) String() string {
+	if this.Err != nil {
+		return fmt.Sprintf("%s", this.Err)
+	} else {
+		return fmt.Sprintf("%s: time=%d ms, TTL=%d", this.IP.String(), this.Time, this.TTL)
+	}
+}
+
+type IcmpPing struct {
+	host    string
+	Timeout time.Duration
+
+	ip         net.IP
+	Privileged bool
+}
+
+func (this *IcmpPing) SetHost(host string) {
+	this.host = host
+	this.ip = net.ParseIP(host)
+}
+
+func (this *IcmpPing) Host() string {
+	return this.host
+}
+
+func NewIcmpPing(host string, timeout time.Duration) *IcmpPing {
+	p := &IcmpPing{
+		Timeout: timeout,
+	}
+	p.SetHost(host)
+	return p
+}
+
+func (this *IcmpPing) Ping() IPingResult {
+	return this.PingContext(context.Background())
+}
+
+func (this *IcmpPing) PingContext(ctx context.Context) IPingResult {
+	pingfunc := this.ping_rootless
+	if this.Privileged {
+		pingfunc = this.ping_root
+	}
+	return pingfunc(ctx)
+}
+
+func (this *IcmpPing) ping_root(ctx context.Context) IPingResult {
+	return this.rawping("ip")
+}
+
+// https://github.com/sparrc/go-ping/blob/master/ping.go
+
+func (this *IcmpPing) rawping(network string) IPingResult {
+	// 解析IP
+	ip, isipv6, err := this.parseip()
+	if err != nil {
+		return this.errorResult(err)
+	}
+
+	// 创建连接
+	conn, err := this.getconn(network, ip, isipv6)
+	if err != nil {
+		return this.errorResult(err)
+	}
+	defer conn.Close()
+	conn.SetDeadline(time.Now().Add(this.Timeout))
+
+	// 发送
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	sendData := make([]byte, 32)
+	r.Read(sendData)
+	id := os.Getpid() & 0xffff
+	sendMsg := this.getmsg(isipv6, id, 0, sendData)
+	sendMsgBytes, err := sendMsg.Marshal(nil)
+	if err != nil {
+		return this.errorResult(err)
+	}
+	var dst net.Addr = &net.IPAddr{IP: ip}
+	if network == "udp" {
+		dst = &net.UDPAddr{IP: ip}
+	}
+	sendAt := time.Now()
+	for {
+		if _, err := conn.WriteTo(sendMsgBytes, dst); err != nil {
+			if neterr, ok := err.(*net.OpError); ok {
+				if neterr.Err == syscall.ENOBUFS {
+					continue
+				}
+			}
+		}
+		break
+	}
+
+	recvBytes := make([]byte, 1500)
+	recvSize := 0
+
+	for {
+		ttl := -1
+		var peer net.Addr
+		if isipv6 {
+			var cm *ipv6.ControlMessage
+			recvSize, cm, peer, err = conn.IPv6PacketConn().ReadFrom(recvBytes)
+			if cm != nil {
+				ttl = cm.HopLimit
+			}
+		} else {
+			var cm *ipv4.ControlMessage
+			recvSize, cm, peer, err = conn.IPv4PacketConn().ReadFrom(recvBytes)
+			if cm != nil {
+				ttl = cm.TTL
+			}
+		}
+		if err != nil {
+			return this.errorResult(err)
+		}
+
+		recvAt := time.Now()
+		recvProto := 1
+		if isipv6 {
+			recvProto = 58
+		}
+		recvMsg, err := icmp.ParseMessage(recvProto, recvBytes[:recvSize])
+		if err != nil {
+			return this.errorResult(err)
+		}
+		recvData, recvID, recvType := this.parserecvmsg(isipv6, recvMsg)
+		// 修正数据长度
+		if len(recvData) > len(sendData) {
+			recvData = recvData[len(recvData)-len(sendData):]
+		}
+		// 收到的数据和发送的数据不一致,继续接收
+		if !bytes.Equal(recvData, sendData) {
+			continue
+		}
+		// 是 echo 回复,但 ID 不一致,继续接收
+		if recvType == 1 && network == "ip" && recvID != id {
+			continue
+		}
+
+		if peer != nil {
+			if _ip := net.ParseIP(peer.String()); _ip != nil {
+				ip = _ip
+			}
+		}
+
+		switch recvType {
+		case 1:
+			// echo
+			return &IcmpPingResult{
+				TTL:  ttl,
+				Time: int(recvAt.Sub(sendAt).Milliseconds()),
+				IP:   ip,
+			}
+		case 2:
+			// destination unreachable
+			return this.errorResult(errors.New(fmt.Sprintf("%s: destination unreachable", ip.String())))
+		case 3:
+			// time exceeded
+			return this.errorResult(errors.New(fmt.Sprintf("%s: time exceeded", ip.String())))
+		}
+	}
+}
+
+func (this *IcmpPing) parseip() (ip net.IP, ipv6 bool, err error) {
+	err = nil
+	ip = cloneIP(this.ip)
+	if ip == nil {
+		ip, err = LookupFunc(this.host)
+		if err != nil {
+			return
+		}
+	}
+	if isIPv4(ip) {
+		ipv6 = false
+	} else if isIPv6(ip) {
+		ipv6 = true
+	} else {
+		err = errors.New("lookup ip failed")
+	}
+	return
+}
+
+func (this *IcmpPing) getconn(network string, ip net.IP, isipv6 bool) (*icmp.PacketConn, error) {
+	ipv4Proto := map[string]string{"ip": "ip4:icmp", "udp": "udp4"}
+	ipv6Proto := map[string]string{"ip": "ip6:ipv6-icmp", "udp": "udp6"}
+	icmpnetwork := ""
+	if isipv6 {
+		icmpnetwork = ipv6Proto[network]
+	} else {
+		icmpnetwork = ipv4Proto[network]
+	}
+	conn, err := icmp.ListenPacket(icmpnetwork, "")
+	if err != nil {
+		return nil, err
+	}
+	if isipv6 {
+		conn.IPv6PacketConn().SetControlMessage(ipv6.FlagHopLimit, true)
+	} else {
+		conn.IPv4PacketConn().SetControlMessage(ipv4.FlagTTL, true)
+	}
+	return conn, nil
+}
+
+func (this *IcmpPing) getmsg(isipv6 bool, id, seq int, data []byte) *icmp.Message {
+	var msgtype icmp.Type = ipv4.ICMPTypeEcho
+	if isipv6 {
+		msgtype = ipv6.ICMPTypeEchoRequest
+	}
+	body := &icmp.Echo{
+		ID:   id,
+		Seq:  seq,
+		Data: data,
+	}
+	msg := &icmp.Message{
+		Type: msgtype,
+		Code: 0,
+		Body: body,
+	}
+	return msg
+}
+
+func (this *IcmpPing) parserecvmsg(isipv6 bool, msg *icmp.Message) (data []byte, id, msgtype int) {
+	id = 0
+	data = nil
+	msgtype = 0
+	if isipv6 {
+		switch msg.Type {
+		case ipv6.ICMPTypeEchoReply:
+			msgtype = 1
+		case ipv6.ICMPTypeDestinationUnreachable:
+			msgtype = 2
+		case ipv6.ICMPTypeTimeExceeded:
+			msgtype = 3
+		}
+	} else {
+		switch msg.Type {
+		case ipv4.ICMPTypeEchoReply:
+			msgtype = 1
+		case ipv4.ICMPTypeDestinationUnreachable:
+			msgtype = 2
+		case ipv4.ICMPTypeTimeExceeded:
+			msgtype = 3
+		}
+	}
+	switch msgtype {
+	case 1:
+		if tempmsg, ok := msg.Body.(*icmp.Echo); ok {
+			data = tempmsg.Data
+			id = tempmsg.ID
+		}
+	case 2:
+		if tempmsg, ok := msg.Body.(*icmp.DstUnreach); ok {
+			data = tempmsg.Data
+		}
+	case 3:
+		if tempmsg, ok := msg.Body.(*icmp.TimeExceeded); ok {
+			data = tempmsg.Data
+		}
+	}
+	return
+}
+
+func (this *IcmpPing) errorResult(err error) IPingResult {
+	r := &IcmpPingResult{}
+	r.Err = err
+	return r
+}
+
+var (
+	_ IPing       = (*IcmpPing)(nil)
+	_ IPingResult = (*IcmpPingResult)(nil)
+)

+ 53 - 0
nettools/pingI.go

@@ -0,0 +1,53 @@
+package nettools
+
+import (
+	"crypto/tls"
+	"fmt"
+	"golang.org/x/net/context"
+	"net"
+)
+
+type IPingResult interface {
+	Result() int
+	Error() error
+
+	fmt.Stringer
+}
+
+type IPing interface {
+	Ping() IPingResult
+	PingContext(context.Context) IPingResult
+}
+
+func tlsVersionToString(ver uint16) string {
+	switch ver {
+	case tls.VersionSSL30:
+		return "SSL 3.0"
+	case tls.VersionTLS10:
+		return "TLS 1.0"
+	case tls.VersionTLS11:
+		return "TLS 1.1"
+	case tls.VersionTLS12:
+		return "TLS 1.2"
+	case tls.VersionTLS13:
+		return "TLS 1.3"
+	default:
+		return "unknown"
+	}
+}
+
+func isIPv4(ip net.IP) bool {
+	return len(ip.To4()) == net.IPv4len
+}
+
+func isIPv6(ip net.IP) bool {
+	return len(ip) == net.IPv6len && !isIPv4(ip)
+}
+func cloneIP(ip net.IP) net.IP {
+	var ip2 net.IP
+	if ip != nil {
+		ip2 = make(net.IP, len(ip))
+		copy(ip2, ip)
+	}
+	return ip2
+}

+ 34 - 0
nettools/resolver.go

@@ -0,0 +1,34 @@
+package nettools
+
+import "net"
+
+func lookupIP(host string, ipv4, ipv6 bool) (net.IP, error) {
+	ip, err := net.LookupIP(host)
+	if err != nil {
+		return nil, err
+	}
+	for _, i := range ip {
+		if (ipv4 && isIPv4(i)) || (ipv6 && isIPv6(i)) {
+			return i, nil
+		}
+	}
+	return nil, &net.DNSError{
+		Name:       host,
+		Err:        "not found",
+		IsNotFound: true,
+	}
+}
+
+func LookupIPv4(host string) (net.IP, error) {
+	return lookupIP(host, true, false)
+}
+
+func LookupIPv6(host string) (net.IP, error) {
+	return lookupIP(host, false, true)
+}
+
+func LookupIP(host string) (net.IP, error) {
+	return lookupIP(host, true, true)
+}
+
+var LookupFunc func(string) (net.IP, error) = LookupIP

+ 88 - 0
nettools/tcpping.go

@@ -0,0 +1,88 @@
+package nettools
+
+import (
+	"fmt"
+	"golang.org/x/net/context"
+	"net"
+	"strconv"
+	"time"
+)
+
+type TcpPingResult struct {
+	Time int
+	Err  error
+	IP   net.IP
+}
+
+func (this *TcpPingResult) Result() int {
+	return this.Time
+}
+
+func (this *TcpPingResult) Error() error {
+	return this.Err
+}
+
+func (this *TcpPingResult) String() string {
+	if this.Err != nil {
+		return fmt.Sprintf("%s", this.Err)
+	} else {
+		return fmt.Sprintf("%s: time=%d ms", this.IP.String(), this.Time)
+	}
+}
+
+type TcpPing struct {
+	host    string
+	Port    int
+	Timeout time.Duration
+
+	ip net.IP
+}
+
+func (this *TcpPing) SetHost(host string) {
+	this.host = host
+	this.ip = net.ParseIP(host)
+}
+
+func (this *TcpPing) Host() string {
+	return this.host
+}
+
+func (this *TcpPing) Ping() IPingResult {
+	return this.PingContext(context.Background())
+}
+
+func (this *TcpPing) PingContext(ctx context.Context) IPingResult {
+	ip := cloneIP(this.ip)
+	if ip == nil {
+		var err error
+		ip, err = LookupFunc(this.host)
+		if err != nil {
+			return &TcpPingResult{0, err, nil}
+		}
+	}
+	dialer := &net.Dialer{
+		Timeout:   this.Timeout,
+		KeepAlive: -1,
+	}
+	t0 := time.Now()
+	conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(this.Port), 10)))
+	if err != nil {
+		return &TcpPingResult{0, err, nil}
+	}
+	defer conn.Close()
+	return &TcpPingResult{int(time.Now().Sub(t0).Milliseconds()), nil, ip}
+}
+
+func NewTcpPing(host string, port int, timeout time.Duration) *TcpPing {
+	return &TcpPing{
+		host:    host,
+		Port:    port,
+		Timeout: timeout,
+		ip:      net.ParseIP(host),
+	}
+}
+
+var (
+	_ IPing       = (*TcpPing)(nil)
+	_ IPingResult = (*TcpPingResult)(nil)
+)