diff --git a/net_ip_socket.go b/net_ip_socket.go index 4da81ea5..ba7d9caa 100644 --- a/net_ip_socket.go +++ b/net_ip_socket.go @@ -50,10 +50,13 @@ type ( // UsedSockets shows the total number of parsed lines representing the // number of used sockets. UsedSockets uint64 + // Drops shows the total number of dropped packets of all UPD sockets. + Drops *uint64 } // netIPSocketLine represents the fields parsed from a single line // in /proc/net/{t,u}dp{,6}. Fields which are not used by IPSocket are skipped. + // Drops is non-nil for udp{,6}, but nil for tcp{,6}. // For the proc file format details, see https://linux.die.net/man/5/proc. netIPSocketLine struct { Sl uint64 @@ -66,6 +69,7 @@ type ( RxQueue uint64 UID uint64 Inode uint64 + Drops *uint64 } ) @@ -77,13 +81,14 @@ func newNetIPSocket(file string) (NetIPSocket, error) { defer f.Close() var netIPSocket NetIPSocket + isUDP := strings.Contains(file, "udp") lr := io.LimitReader(f, readLimit) s := bufio.NewScanner(lr) s.Scan() // skip first line with headers for s.Scan() { fields := strings.Fields(s.Text()) - line, err := parseNetIPSocketLine(fields) + line, err := parseNetIPSocketLine(fields, isUDP) if err != nil { return nil, err } @@ -104,19 +109,25 @@ func newNetIPSocketSummary(file string) (*NetIPSocketSummary, error) { defer f.Close() var netIPSocketSummary NetIPSocketSummary + var udpPacketDrops uint64 + isUDP := strings.Contains(file, "udp") lr := io.LimitReader(f, readLimit) s := bufio.NewScanner(lr) s.Scan() // skip first line with headers for s.Scan() { fields := strings.Fields(s.Text()) - line, err := parseNetIPSocketLine(fields) + line, err := parseNetIPSocketLine(fields, isUDP) if err != nil { return nil, err } netIPSocketSummary.TxQueueLength += line.TxQueue netIPSocketSummary.RxQueueLength += line.RxQueue netIPSocketSummary.UsedSockets++ + if isUDP { + udpPacketDrops += *line.Drops + netIPSocketSummary.Drops = &udpPacketDrops + } } if err := s.Err(); err != nil { return nil, err @@ -149,7 +160,7 @@ func parseIP(hexIP string) (net.IP, error) { } // parseNetIPSocketLine parses a single line, represented by a list of fields. -func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { +func parseNetIPSocketLine(fields []string, isUDP bool) (*netIPSocketLine, error) { line := &netIPSocketLine{} if len(fields) < 10 { return nil, fmt.Errorf( @@ -224,5 +235,14 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { return nil, fmt.Errorf("%s: Cannot parse inode value in %q: %w", ErrFileParse, line.Inode, err) } + // drops + if isUDP { + drops, err := strconv.ParseUint(fields[12], 0, 64) + if err != nil { + return nil, fmt.Errorf("%s: Cannot parse drops value in %q: %w", ErrFileParse, drops, err) + } + line.Drops = &drops + } + return line, nil } diff --git a/net_ip_socket_test.go b/net_ip_socket_test.go index 9bbad87f..3c057fbe 100644 --- a/net_ip_socket_test.go +++ b/net_ip_socket_test.go @@ -25,6 +25,7 @@ func Test_parseNetIPSocketLine(t *testing.T) { name string want *netIPSocketLine wantErr bool + isUDP bool }{ { name: "reading valid lines, no issue should happened", @@ -96,10 +97,17 @@ func Test_parseNetIPSocketLine(t *testing.T) { want: nil, wantErr: true, }, + { + name: "error case - parse Drops - not a valid uint", + fields: []string{"1:", "00000000:0000", "00000000:0000", "07", "00000000:00000001", "0:0", "0", "10", "0", "39309", "2", "000000009bd60d72", "-5"}, + want: nil, + wantErr: true, + isUDP: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := parseNetIPSocketLine(tt.fields) + got, err := parseNetIPSocketLine(tt.fields, tt.isUDP) if (err != nil) != tt.wantErr { t.Errorf("parseNetIPSocketLine() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/net_udp_test.go b/net_udp_test.go index 9abb64d5..65761610 100644 --- a/net_udp_test.go +++ b/net_udp_test.go @@ -41,6 +41,7 @@ func Test_newNetUDP(t *testing.T) { RxQueue: 1, UID: 0, Inode: 2740, + Drops: intToU64(100), }, &netIPSocketLine{ Sl: 1, @@ -53,6 +54,7 @@ func Test_newNetUDP(t *testing.T) { RxQueue: 0, UID: 0, Inode: 2740, + Drops: intToU64(100), }, &netIPSocketLine{ Sl: 2, @@ -65,6 +67,7 @@ func Test_newNetUDP(t *testing.T) { RxQueue: 1, UID: 0, Inode: 2740, + Drops: intToU64(100), }, }, wantErr: false, @@ -84,6 +87,7 @@ func Test_newNetUDP(t *testing.T) { RxQueue: 0, UID: 981, Inode: 21040, + Drops: intToU64(0), }, &netIPSocketLine{ Sl: 6073, @@ -96,6 +100,7 @@ func Test_newNetUDP(t *testing.T) { RxQueue: 0, UID: 1000, Inode: 11337031, + Drops: intToU64(0), }, }, wantErr: false, @@ -137,13 +142,13 @@ func Test_newNetUDPSummary(t *testing.T) { { name: "udp file found, no error should come up", file: "testdata/fixtures/proc/net/udp", - want: &NetUDPSummary{TxQueueLength: 2, RxQueueLength: 2, UsedSockets: 3}, + want: &NetUDPSummary{TxQueueLength: 2, RxQueueLength: 2, UsedSockets: 3, Drops: intToU64(300)}, wantErr: false, }, { name: "udp6 file found, no error should come up", file: "testdata/fixtures/proc/net/udp6", - want: &NetUDPSummary{TxQueueLength: 0, RxQueueLength: 0, UsedSockets: 2}, + want: &NetUDPSummary{TxQueueLength: 0, RxQueueLength: 0, UsedSockets: 2, Drops: intToU64(0)}, wantErr: false, }, { @@ -172,3 +177,9 @@ func Test_newNetUDPSummary(t *testing.T) { }) } } + +// intToU64 convert int to uint64 and return it pointer. +func intToU64(i int) *uint64 { + cast := uint64(i) + return &cast +}