1package net
2
3import (
4	"fmt"
5	"io/ioutil"
6	"net"
7	"os"
8	"strings"
9	"syscall"
10	"testing"
11
12	"github.com/shirou/gopsutil/v3/internal/common"
13	"github.com/stretchr/testify/assert"
14)
15
16func TestIOCountersByFileParsing(t *testing.T) {
17	// Prpare a temporary file, which will be read during the test
18	tmpfile, err := ioutil.TempFile("", "proc_dev_net")
19	defer os.Remove(tmpfile.Name()) // clean up
20
21	assert.Nil(t, err, "Temporary file creation failed: ", err)
22
23	cases := [4][2]string{
24		[2]string{"eth0:   ", "eth1:   "},
25		[2]string{"eth0:0:   ", "eth1:0:   "},
26		[2]string{"eth0:", "eth1:"},
27		[2]string{"eth0:0:", "eth1:0:"},
28	}
29	for _, testCase := range cases {
30		err = tmpfile.Truncate(0)
31		assert.Nil(t, err, "Temporary file truncating problem: ", err)
32
33		// Parse interface name for assertion
34		interface0 := strings.TrimSpace(testCase[0])
35		interface0 = interface0[:len(interface0)-1]
36
37		interface1 := strings.TrimSpace(testCase[1])
38		interface1 = interface1[:len(interface1)-1]
39
40		// Replace the interfaces from the test case
41		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]))
42
43		// Write /proc/net/dev sample output
44		_, err = tmpfile.Write(proc)
45		assert.Nil(t, err, "Temporary file writing failed: ", err)
46
47		counters, err := IOCountersByFile(true, tmpfile.Name())
48
49		assert.Nil(t, err)
50		assert.NotEmpty(t, counters)
51		assert.Equal(t, 2, len(counters))
52		assert.Equal(t, interface0, counters[0].Name)
53		assert.Equal(t, 1, int(counters[0].BytesRecv))
54		assert.Equal(t, 2, int(counters[0].PacketsRecv))
55		assert.Equal(t, 3, int(counters[0].Errin))
56		assert.Equal(t, 4, int(counters[0].Dropin))
57		assert.Equal(t, 5, int(counters[0].Fifoin))
58		assert.Equal(t, 9, int(counters[0].BytesSent))
59		assert.Equal(t, 10, int(counters[0].PacketsSent))
60		assert.Equal(t, 11, int(counters[0].Errout))
61		assert.Equal(t, 12, int(counters[0].Dropout))
62		assert.Equal(t, 13, int(counters[0].Fifoout))
63		assert.Equal(t, interface1, counters[1].Name)
64		assert.Equal(t, 100, int(counters[1].BytesRecv))
65		assert.Equal(t, 200, int(counters[1].PacketsRecv))
66		assert.Equal(t, 300, int(counters[1].Errin))
67		assert.Equal(t, 400, int(counters[1].Dropin))
68		assert.Equal(t, 500, int(counters[1].Fifoin))
69		assert.Equal(t, 900, int(counters[1].BytesSent))
70		assert.Equal(t, 1000, int(counters[1].PacketsSent))
71		assert.Equal(t, 1100, int(counters[1].Errout))
72		assert.Equal(t, 1200, int(counters[1].Dropout))
73		assert.Equal(t, 1300, int(counters[1].Fifoout))
74	}
75
76	err = tmpfile.Close()
77	assert.Nil(t, err, "Temporary file closing failed: ", err)
78}
79
80func TestGetProcInodesAll(t *testing.T) {
81	waitForServer := make(chan bool)
82	go func() { // TCP listening goroutine to have some opened inodes even in CI
83		addr, err := net.ResolveTCPAddr("tcp", "localhost:0") // dynamically get a random open port from OS
84		if err != nil {
85			t.Skip("unable to resolve localhost:", err)
86		}
87		l, err := net.ListenTCP(addr.Network(), addr)
88		if err != nil {
89			t.Skip(fmt.Sprintf("unable to listen on %v: %v", addr, err))
90		}
91		defer l.Close()
92		waitForServer <- true
93		for {
94			conn, err := l.Accept()
95			if err != nil {
96				t.Skip("unable to accept connection:", err)
97			}
98			defer conn.Close()
99		}
100	}()
101	<-waitForServer
102
103	root := common.HostProc("")
104	v, err := getProcInodesAll(root, 0)
105	assert.Nil(t, err)
106	assert.NotEmpty(t, v)
107}
108
109func TestConnectionsMax(t *testing.T) {
110	if os.Getenv("CI") != "" {
111		t.Skip("Skip CI")
112	}
113
114	max := 10
115	v, err := ConnectionsMax("tcp", max)
116	assert.Nil(t, err)
117	assert.NotEmpty(t, v)
118
119	cxByPid := map[int32]int{}
120	for _, cx := range v {
121		if cx.Pid > 0 {
122			cxByPid[cx.Pid]++
123		}
124	}
125	for _, c := range cxByPid {
126		assert.True(t, c <= max)
127	}
128}
129
130type AddrTest struct {
131	IP    string
132	Port  int
133	Error bool
134}
135
136func TestDecodeAddress(t *testing.T) {
137	assert := assert.New(t)
138
139	addr := map[string]AddrTest{
140		"0500000A:0016": {
141			IP:   "10.0.0.5",
142			Port: 22,
143		},
144		"0100007F:D1C2": {
145			IP:   "127.0.0.1",
146			Port: 53698,
147		},
148		"11111:0035": {
149			Error: true,
150		},
151		"0100007F:BLAH": {
152			Error: true,
153		},
154		"0085002452100113070057A13F025401:0035": {
155			IP:   "2400:8500:1301:1052:a157:7:154:23f",
156			Port: 53,
157		},
158		"00855210011307F025401:0035": {
159			Error: true,
160		},
161	}
162
163	for src, dst := range addr {
164		family := syscall.AF_INET
165		if len(src) > 13 {
166			family = syscall.AF_INET6
167		}
168		addr, err := decodeAddress(uint32(family), src)
169		if dst.Error {
170			assert.NotNil(err, src)
171		} else {
172			assert.Nil(err, src)
173			assert.Equal(dst.IP, addr.IP, src)
174			assert.Equal(dst.Port, int(addr.Port), src)
175		}
176	}
177}
178
179func TestReverse(t *testing.T) {
180	src := []byte{0x01, 0x02, 0x03}
181	assert.Equal(t, []byte{0x03, 0x02, 0x01}, Reverse(src))
182}
183
184func TestConntrackStatFileParsing(t *testing.T) {
185	tmpfile, err := ioutil.TempFile("", "proc_net_stat_conntrack")
186	defer os.Remove(tmpfile.Name())
187	assert.Nil(t, err, "Temporary file creation failed: ", err)
188
189	data := []byte(`
190entries  searched found new invalid ignore delete deleteList insert insertFailed drop earlyDrop icmpError  expectNew expectCreate expectDelete searchRestart
1910000007b  00000000 00000000 00000000 000b115a 00000084 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 0000004a
1920000007b  00000000 00000000 00000000 0007eee5 00000068 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000035
1930000007b  00000000 00000000 00000000 0090346b 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000025
1940000007b  00000000 00000000 00000000 0005920f 00000069 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000064
1950000007b  00000000 00000000 00000000 000331ff 00000059 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 0000003b
1960000007b  00000000 00000000 00000000 000314ea 00000066 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000054
1970000007b  00000000 00000000 00000000 0002b270 00000055 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 0000003d
1980000007b  00000000 00000000 00000000 0002f67d 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000042
199`)
200
201	// Expected results
202	slist := NewConntrackStatList()
203
204	slist.Append(&ConntrackStat{
205		Entries:       123,
206		Searched:      0,
207		Found:         0,
208		New:           0,
209		Invalid:       725338,
210		Ignore:        132,
211		Delete:        0,
212		DeleteList:    0,
213		Insert:        0,
214		InsertFailed:  0,
215		Drop:          0,
216		EarlyDrop:     0,
217		IcmpError:     0,
218		ExpectNew:     0,
219		ExpectCreate:  0,
220		ExpectDelete:  0,
221		SearchRestart: 74,
222	})
223	slist.Append(&ConntrackStat{123, 0, 0, 0, 519909, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53})
224
225	slist.Append(&ConntrackStat{123, 0, 0, 0, 9450603, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37})
226	slist.Append(&ConntrackStat{123, 0, 0, 0, 365071, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100})
227
228	slist.Append(&ConntrackStat{123, 0, 0, 0, 209407, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59})
229	slist.Append(&ConntrackStat{123, 0, 0, 0, 201962, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84})
230
231	slist.Append(&ConntrackStat{123, 0, 0, 0, 176752, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61})
232	slist.Append(&ConntrackStat{123, 0, 0, 0, 194173, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66})
233
234	// Write data to tempfile
235	_, err = tmpfile.Write(data)
236	assert.Nil(t, err, "Temporary file writing failed: ", err)
237
238	// Function under test
239	stats, err := conntrackStatsFromFile(tmpfile.Name(), true)
240	assert.Equal(t, 8, len(stats), "Expected 8 results")
241
242	summary := &ConntrackStat{}
243	for i, exp := range slist.Items() {
244		st := stats[i]
245
246		assert.Equal(t, exp.Entries, st.Entries)
247		summary.Entries += st.Entries
248
249		assert.Equal(t, exp.Searched, st.Searched)
250		summary.Searched += st.Searched
251
252		assert.Equal(t, exp.Found, st.Found)
253		summary.Found += st.Found
254
255		assert.Equal(t, exp.New, st.New)
256		summary.New += st.New
257
258		assert.Equal(t, exp.Invalid, st.Invalid)
259		summary.Invalid += st.Invalid
260
261		assert.Equal(t, exp.Ignore, st.Ignore)
262		summary.Ignore += st.Ignore
263
264		assert.Equal(t, exp.Delete, st.Delete)
265		summary.Delete += st.Delete
266
267		assert.Equal(t, exp.DeleteList, st.DeleteList)
268		summary.DeleteList += st.DeleteList
269
270		assert.Equal(t, exp.Insert, st.Insert)
271		summary.Insert += st.Insert
272
273		assert.Equal(t, exp.InsertFailed, st.InsertFailed)
274		summary.InsertFailed += st.InsertFailed
275
276		assert.Equal(t, exp.Drop, st.Drop)
277		summary.Drop += st.Drop
278
279		assert.Equal(t, exp.EarlyDrop, st.EarlyDrop)
280		summary.EarlyDrop += st.EarlyDrop
281
282		assert.Equal(t, exp.IcmpError, st.IcmpError)
283		summary.IcmpError += st.IcmpError
284
285		assert.Equal(t, exp.ExpectNew, st.ExpectNew)
286		summary.ExpectNew += st.ExpectNew
287
288		assert.Equal(t, exp.ExpectCreate, st.ExpectCreate)
289		summary.ExpectCreate += st.ExpectCreate
290
291		assert.Equal(t, exp.ExpectDelete, st.ExpectDelete)
292		summary.ExpectDelete += st.ExpectDelete
293
294		assert.Equal(t, exp.SearchRestart, st.SearchRestart)
295		summary.SearchRestart += st.SearchRestart
296	}
297
298	// Test summary grouping
299	totals, err := conntrackStatsFromFile(tmpfile.Name(), false)
300	for i, st := range totals {
301		assert.Equal(t, summary.Entries, st.Entries)
302		assert.Equal(t, summary.Searched, st.Searched)
303		assert.Equal(t, summary.Found, st.Found)
304		assert.Equal(t, summary.New, st.New)
305		assert.Equal(t, summary.Invalid, st.Invalid)
306		assert.Equal(t, summary.Ignore, st.Ignore)
307		assert.Equal(t, summary.Delete, st.Delete)
308		assert.Equal(t, summary.DeleteList, st.DeleteList)
309		assert.Equal(t, summary.Insert, st.Insert)
310		assert.Equal(t, summary.InsertFailed, st.InsertFailed)
311		assert.Equal(t, summary.Drop, st.Drop)
312		assert.Equal(t, summary.EarlyDrop, st.EarlyDrop)
313		assert.Equal(t, summary.IcmpError, st.IcmpError)
314		assert.Equal(t, summary.ExpectNew, st.ExpectNew)
315		assert.Equal(t, summary.ExpectCreate, st.ExpectCreate)
316		assert.Equal(t, summary.ExpectDelete, st.ExpectDelete)
317		assert.Equal(t, summary.SearchRestart, st.SearchRestart)
318
319		assert.Equal(t, 0, i) // Should only have one element
320	}
321}
322