cubefs

Форк
0
226 строк · 6.8 Кб
1
// Copyright 2020 The Prometheus Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package procfs
15

16
import (
17
	"bufio"
18
	"encoding/hex"
19
	"fmt"
20
	"io"
21
	"net"
22
	"os"
23
	"strconv"
24
	"strings"
25
)
26

27
const (
28
	// readLimit is used by io.LimitReader while reading the content of the
29
	// /proc/net/udp{,6} files. The number of lines inside such a file is dynamic
30
	// as each line represents a single used socket.
31
	// In theory, the number of available sockets is 65535 (2^16 - 1) per IP.
32
	// With e.g. 150 Byte per line and the maximum number of 65535,
33
	// the reader needs to handle 150 Byte * 65535 =~ 10 MB for a single IP.
34
	readLimit = 4294967296 // Byte -> 4 GiB
35
)
36

37
// This contains generic data structures for both udp and tcp sockets.
38
type (
39
	// NetIPSocket represents the contents of /proc/net/{t,u}dp{,6} file without the header.
40
	NetIPSocket []*netIPSocketLine
41

42
	// NetIPSocketSummary provides already computed values like the total queue lengths or
43
	// the total number of used sockets. In contrast to NetIPSocket it does not collect
44
	// the parsed lines into a slice.
45
	NetIPSocketSummary struct {
46
		// TxQueueLength shows the total queue length of all parsed tx_queue lengths.
47
		TxQueueLength uint64
48
		// RxQueueLength shows the total queue length of all parsed rx_queue lengths.
49
		RxQueueLength uint64
50
		// UsedSockets shows the total number of parsed lines representing the
51
		// number of used sockets.
52
		UsedSockets uint64
53
	}
54

55
	// netIPSocketLine represents the fields parsed from a single line
56
	// in /proc/net/{t,u}dp{,6}. Fields which are not used by IPSocket are skipped.
57
	// For the proc file format details, see https://linux.die.net/man/5/proc.
58
	netIPSocketLine struct {
59
		Sl        uint64
60
		LocalAddr net.IP
61
		LocalPort uint64
62
		RemAddr   net.IP
63
		RemPort   uint64
64
		St        uint64
65
		TxQueue   uint64
66
		RxQueue   uint64
67
		UID       uint64
68
		Inode     uint64
69
	}
70
)
71

72
func newNetIPSocket(file string) (NetIPSocket, error) {
73
	f, err := os.Open(file)
74
	if err != nil {
75
		return nil, err
76
	}
77
	defer f.Close()
78

79
	var netIPSocket NetIPSocket
80

81
	lr := io.LimitReader(f, readLimit)
82
	s := bufio.NewScanner(lr)
83
	s.Scan() // skip first line with headers
84
	for s.Scan() {
85
		fields := strings.Fields(s.Text())
86
		line, err := parseNetIPSocketLine(fields)
87
		if err != nil {
88
			return nil, err
89
		}
90
		netIPSocket = append(netIPSocket, line)
91
	}
92
	if err := s.Err(); err != nil {
93
		return nil, err
94
	}
95
	return netIPSocket, nil
96
}
97

98
// newNetIPSocketSummary creates a new NetIPSocket{,6} from the contents of the given file.
99
func newNetIPSocketSummary(file string) (*NetIPSocketSummary, error) {
100
	f, err := os.Open(file)
101
	if err != nil {
102
		return nil, err
103
	}
104
	defer f.Close()
105

106
	var netIPSocketSummary NetIPSocketSummary
107

108
	lr := io.LimitReader(f, readLimit)
109
	s := bufio.NewScanner(lr)
110
	s.Scan() // skip first line with headers
111
	for s.Scan() {
112
		fields := strings.Fields(s.Text())
113
		line, err := parseNetIPSocketLine(fields)
114
		if err != nil {
115
			return nil, err
116
		}
117
		netIPSocketSummary.TxQueueLength += line.TxQueue
118
		netIPSocketSummary.RxQueueLength += line.RxQueue
119
		netIPSocketSummary.UsedSockets++
120
	}
121
	if err := s.Err(); err != nil {
122
		return nil, err
123
	}
124
	return &netIPSocketSummary, nil
125
}
126

127
// the /proc/net/{t,u}dp{,6} files are network byte order for ipv4 and for ipv6 the address is four words consisting of four bytes each. In each of those four words the four bytes are written in reverse order.
128

129
func parseIP(hexIP string) (net.IP, error) {
130
	var byteIP []byte
131
	byteIP, err := hex.DecodeString(hexIP)
132
	if err != nil {
133
		return nil, fmt.Errorf("cannot parse address field in socket line %q", hexIP)
134
	}
135
	switch len(byteIP) {
136
	case 4:
137
		return net.IP{byteIP[3], byteIP[2], byteIP[1], byteIP[0]}, nil
138
	case 16:
139
		i := net.IP{
140
			byteIP[3], byteIP[2], byteIP[1], byteIP[0],
141
			byteIP[7], byteIP[6], byteIP[5], byteIP[4],
142
			byteIP[11], byteIP[10], byteIP[9], byteIP[8],
143
			byteIP[15], byteIP[14], byteIP[13], byteIP[12],
144
		}
145
		return i, nil
146
	default:
147
		return nil, fmt.Errorf("Unable to parse IP %s", hexIP)
148
	}
149
}
150

151
// parseNetIPSocketLine parses a single line, represented by a list of fields.
152
func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) {
153
	line := &netIPSocketLine{}
154
	if len(fields) < 10 {
155
		return nil, fmt.Errorf(
156
			"cannot parse net socket line as it has less then 10 columns %q",
157
			strings.Join(fields, " "),
158
		)
159
	}
160
	var err error // parse error
161

162
	// sl
163
	s := strings.Split(fields[0], ":")
164
	if len(s) != 2 {
165
		return nil, fmt.Errorf("cannot parse sl field in socket line %q", fields[0])
166
	}
167

168
	if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil {
169
		return nil, fmt.Errorf("cannot parse sl value in socket line: %w", err)
170
	}
171
	// local_address
172
	l := strings.Split(fields[1], ":")
173
	if len(l) != 2 {
174
		return nil, fmt.Errorf("cannot parse local_address field in socket line %q", fields[1])
175
	}
176
	if line.LocalAddr, err = parseIP(l[0]); err != nil {
177
		return nil, err
178
	}
179
	if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil {
180
		return nil, fmt.Errorf("cannot parse local_address port value in socket line: %w", err)
181
	}
182

183
	// remote_address
184
	r := strings.Split(fields[2], ":")
185
	if len(r) != 2 {
186
		return nil, fmt.Errorf("cannot parse rem_address field in socket line %q", fields[1])
187
	}
188
	if line.RemAddr, err = parseIP(r[0]); err != nil {
189
		return nil, err
190
	}
191
	if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil {
192
		return nil, fmt.Errorf("cannot parse rem_address port value in socket line: %w", err)
193
	}
194

195
	// st
196
	if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil {
197
		return nil, fmt.Errorf("cannot parse st value in socket line: %w", err)
198
	}
199

200
	// tx_queue and rx_queue
201
	q := strings.Split(fields[4], ":")
202
	if len(q) != 2 {
203
		return nil, fmt.Errorf(
204
			"cannot parse tx/rx queues in socket line as it has a missing colon %q",
205
			fields[4],
206
		)
207
	}
208
	if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil {
209
		return nil, fmt.Errorf("cannot parse tx_queue value in socket line: %w", err)
210
	}
211
	if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil {
212
		return nil, fmt.Errorf("cannot parse rx_queue value in socket line: %w", err)
213
	}
214

215
	// uid
216
	if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil {
217
		return nil, fmt.Errorf("cannot parse uid value in socket line: %w", err)
218
	}
219

220
	// inode
221
	if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil {
222
		return nil, fmt.Errorf("cannot parse inode value in socket line: %w", err)
223
	}
224

225
	return line, nil
226
}
227

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.