package net import ( "context" "fmt" "io/ioutil" "net" "os" "strings" "syscall" "testing" "github.com/stretchr/testify/assert" "github.com/shirou/gopsutil/v3/internal/common" ) func TestIOCountersByFileParsing(t *testing.T) { // Prpare a temporary file, which will be read during the test tmpfile, err := ioutil.TempFile("", "proc_dev_net") defer os.Remove(tmpfile.Name()) // clean up assert.Nil(t, err, "Temporary file creation failed: ", err) cases := [4][2]string{ {"eth0: ", "eth1: "}, {"eth0:0: ", "eth1:0: "}, {"eth0:", "eth1:"}, {"eth0:0:", "eth1:0:"}, } for _, testCase := range cases { err = tmpfile.Truncate(0) assert.Nil(t, err, "Temporary file truncating problem: ", err) // Parse interface name for assertion interface0 := strings.TrimSpace(testCase[0]) interface0 = interface0[:len(interface0)-1] interface1 := strings.TrimSpace(testCase[1]) interface1 = interface1[:len(interface1)-1] // Replace the interfaces from the test case proc := []byte(fmt.Sprintf("Inter-| Receive | Transmit\n face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n %s1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16\n %s100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600\n", testCase[0], testCase[1])) // Write /proc/net/dev sample output _, err = tmpfile.Write(proc) assert.Nil(t, err, "Temporary file writing failed: ", err) counters, err := IOCountersByFile(true, tmpfile.Name()) assert.Nil(t, err) assert.NotEmpty(t, counters) assert.Equal(t, 2, len(counters)) assert.Equal(t, interface0, counters[0].Name) assert.Equal(t, 1, int(counters[0].BytesRecv)) assert.Equal(t, 2, int(counters[0].PacketsRecv)) assert.Equal(t, 3, int(counters[0].Errin)) assert.Equal(t, 4, int(counters[0].Dropin)) assert.Equal(t, 5, int(counters[0].Fifoin)) assert.Equal(t, 9, int(counters[0].BytesSent)) assert.Equal(t, 10, int(counters[0].PacketsSent)) assert.Equal(t, 11, int(counters[0].Errout)) assert.Equal(t, 12, int(counters[0].Dropout)) assert.Equal(t, 13, int(counters[0].Fifoout)) assert.Equal(t, interface1, counters[1].Name) assert.Equal(t, 100, int(counters[1].BytesRecv)) assert.Equal(t, 200, int(counters[1].PacketsRecv)) assert.Equal(t, 300, int(counters[1].Errin)) assert.Equal(t, 400, int(counters[1].Dropin)) assert.Equal(t, 500, int(counters[1].Fifoin)) assert.Equal(t, 900, int(counters[1].BytesSent)) assert.Equal(t, 1000, int(counters[1].PacketsSent)) assert.Equal(t, 1100, int(counters[1].Errout)) assert.Equal(t, 1200, int(counters[1].Dropout)) assert.Equal(t, 1300, int(counters[1].Fifoout)) } err = tmpfile.Close() assert.Nil(t, err, "Temporary file closing failed: ", err) } func TestGetProcInodesAll(t *testing.T) { waitForServer := make(chan bool) go func() { // TCP listening goroutine to have some opened inodes even in CI addr, err := net.ResolveTCPAddr("tcp", "localhost:0") // dynamically get a random open port from OS if err != nil { t.Skipf("unable to resolve localhost: %v", err) } l, err := net.ListenTCP(addr.Network(), addr) if err != nil { t.Skipf("unable to listen on %v: %v", addr, err) } defer l.Close() waitForServer <- true for { conn, err := l.Accept() if err != nil { t.Skipf("unable to accept connection: %v", err) } defer conn.Close() } }() <-waitForServer root := common.HostProcWithContext(context.Background(), "") v, err := getProcInodesAll(root, 0) assert.Nil(t, err) assert.NotEmpty(t, v) } func TestConnectionsMax(t *testing.T) { if os.Getenv("CI") != "" { t.Skip("Skip CI") } max := 10 v, err := ConnectionsMax("tcp", max) assert.Nil(t, err) assert.NotEmpty(t, v) cxByPid := map[int32]int{} for _, cx := range v { if cx.Pid > 0 { cxByPid[cx.Pid]++ } } for _, c := range cxByPid { assert.True(t, c <= max) } } type AddrTest struct { IP string Port int Error bool } func TestDecodeAddress(t *testing.T) { assert := assert.New(t) addr := map[string]AddrTest{ "11111:0035": { Error: true, }, "0100007F:BLAH": { Error: true, }, "0085002452100113070057A13F025401:0035": { IP: "2400:8500:1301:1052:a157:7:154:23f", Port: 53, }, "00855210011307F025401:0035": { Error: true, }, } if common.IsLittleEndian() { addr["0500000A:0016"] = AddrTest{ IP: "10.0.0.5", Port: 22, } addr["0100007F:D1C2"] = AddrTest{ IP: "127.0.0.1", Port: 53698, } } else { addr["0A000005:0016"] = AddrTest{ IP: "10.0.0.5", Port: 22, } addr["7F000001:D1C2"] = AddrTest{ IP: "127.0.0.1", Port: 53698, } } for src, dst := range addr { family := syscall.AF_INET if len(src) > 13 { family = syscall.AF_INET6 } addr, err := decodeAddress(uint32(family), src) if dst.Error { assert.NotNil(err, src) } else { assert.Nil(err, src) assert.Equal(dst.IP, addr.IP, src) assert.Equal(dst.Port, int(addr.Port), src) } } } func TestReverse(t *testing.T) { src := []byte{0x01, 0x02, 0x03} assert.Equal(t, []byte{0x03, 0x02, 0x01}, Reverse(src)) } func TestConntrackStatFileParsing(t *testing.T) { tmpfile, err := ioutil.TempFile("", "proc_net_stat_conntrack") defer os.Remove(tmpfile.Name()) assert.Nil(t, err, "Temporary file creation failed: ", err) data := []byte(` entries searched found new invalid ignore delete deleteList insert insertFailed drop earlyDrop icmpError expectNew expectCreate expectDelete searchRestart 0000007b 00000000 00000000 00000000 000b115a 00000084 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000004a 0000007b 00000000 00000000 00000000 0007eee5 00000068 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000035 0000007b 00000000 00000000 00000000 0090346b 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000025 0000007b 00000000 00000000 00000000 0005920f 00000069 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000064 0000007b 00000000 00000000 00000000 000331ff 00000059 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000003b 0000007b 00000000 00000000 00000000 000314ea 00000066 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000054 0000007b 00000000 00000000 00000000 0002b270 00000055 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000003d 0000007b 00000000 00000000 00000000 0002f67d 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000042 `) // Expected results slist := NewConntrackStatList() slist.Append(&ConntrackStat{ Entries: 123, Searched: 0, Found: 0, New: 0, Invalid: 725338, Ignore: 132, Delete: 0, DeleteList: 0, Insert: 0, InsertFailed: 0, Drop: 0, EarlyDrop: 0, IcmpError: 0, ExpectNew: 0, ExpectCreate: 0, ExpectDelete: 0, SearchRestart: 74, }) slist.Append(&ConntrackStat{123, 0, 0, 0, 519909, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53}) slist.Append(&ConntrackStat{123, 0, 0, 0, 9450603, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37}) slist.Append(&ConntrackStat{123, 0, 0, 0, 365071, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}) slist.Append(&ConntrackStat{123, 0, 0, 0, 209407, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59}) slist.Append(&ConntrackStat{123, 0, 0, 0, 201962, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84}) slist.Append(&ConntrackStat{123, 0, 0, 0, 176752, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61}) slist.Append(&ConntrackStat{123, 0, 0, 0, 194173, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66}) // Write data to tempfile _, err = tmpfile.Write(data) assert.Nil(t, err, "Temporary file writing failed: ", err) // Function under test stats, err := conntrackStatsFromFile(tmpfile.Name(), true) assert.Nil(t, err) assert.Equal(t, 8, len(stats), "Expected 8 results") summary := &ConntrackStat{} for i, exp := range slist.Items() { st := stats[i] assert.Equal(t, exp.Entries, st.Entries) summary.Entries += st.Entries assert.Equal(t, exp.Searched, st.Searched) summary.Searched += st.Searched assert.Equal(t, exp.Found, st.Found) summary.Found += st.Found assert.Equal(t, exp.New, st.New) summary.New += st.New assert.Equal(t, exp.Invalid, st.Invalid) summary.Invalid += st.Invalid assert.Equal(t, exp.Ignore, st.Ignore) summary.Ignore += st.Ignore assert.Equal(t, exp.Delete, st.Delete) summary.Delete += st.Delete assert.Equal(t, exp.DeleteList, st.DeleteList) summary.DeleteList += st.DeleteList assert.Equal(t, exp.Insert, st.Insert) summary.Insert += st.Insert assert.Equal(t, exp.InsertFailed, st.InsertFailed) summary.InsertFailed += st.InsertFailed assert.Equal(t, exp.Drop, st.Drop) summary.Drop += st.Drop assert.Equal(t, exp.EarlyDrop, st.EarlyDrop) summary.EarlyDrop += st.EarlyDrop assert.Equal(t, exp.IcmpError, st.IcmpError) summary.IcmpError += st.IcmpError assert.Equal(t, exp.ExpectNew, st.ExpectNew) summary.ExpectNew += st.ExpectNew assert.Equal(t, exp.ExpectCreate, st.ExpectCreate) summary.ExpectCreate += st.ExpectCreate assert.Equal(t, exp.ExpectDelete, st.ExpectDelete) summary.ExpectDelete += st.ExpectDelete assert.Equal(t, exp.SearchRestart, st.SearchRestart) summary.SearchRestart += st.SearchRestart } // Test summary grouping totals, err := conntrackStatsFromFile(tmpfile.Name(), false) assert.Nil(t, err) for i, st := range totals { assert.Equal(t, summary.Entries, st.Entries) assert.Equal(t, summary.Searched, st.Searched) assert.Equal(t, summary.Found, st.Found) assert.Equal(t, summary.New, st.New) assert.Equal(t, summary.Invalid, st.Invalid) assert.Equal(t, summary.Ignore, st.Ignore) assert.Equal(t, summary.Delete, st.Delete) assert.Equal(t, summary.DeleteList, st.DeleteList) assert.Equal(t, summary.Insert, st.Insert) assert.Equal(t, summary.InsertFailed, st.InsertFailed) assert.Equal(t, summary.Drop, st.Drop) assert.Equal(t, summary.EarlyDrop, st.EarlyDrop) assert.Equal(t, summary.IcmpError, st.IcmpError) assert.Equal(t, summary.ExpectNew, st.ExpectNew) assert.Equal(t, summary.ExpectCreate, st.ExpectCreate) assert.Equal(t, summary.ExpectDelete, st.ExpectDelete) assert.Equal(t, summary.SearchRestart, st.SearchRestart) assert.Equal(t, 0, i) // Should only have one element } }