1 // SPDX-License-Identifier: GPL-2.0
5 #include "../../../../../include/linux/compiler.h"
6 #include "../../../../../include/linux/kernel.h"
9 struct netstat_counter
{
18 struct netstat_counter
*counters
;
21 static struct netstat
*lookup_type(struct netstat
*ns
,
22 const char *type
, size_t len
)
25 size_t cmp
= max(len
, strlen(ns
->header_name
));
27 if (!strncmp(ns
->header_name
, type
, cmp
))
34 static struct netstat
*lookup_get(struct netstat
*ns
,
35 const char *type
, const size_t len
)
39 ret
= lookup_type(ns
, type
, len
);
43 ret
= malloc(sizeof(struct netstat
));
45 test_error("malloc()");
47 ret
->header_name
= strndup(type
, len
);
48 if (ret
->header_name
== NULL
)
49 test_error("strndup()");
57 static struct netstat
*lookup_get_column(struct netstat
*ns
, const char *line
)
61 column
= strchr(line
, ':');
63 test_error("can't parse netstat file");
65 return lookup_get(ns
, line
, column
- line
);
68 static void netstat_read_type(FILE *fnetstat
, struct netstat
**dest
, char *line
)
70 struct netstat
*type
= lookup_get_column(*dest
, line
);
71 const char *pos
= line
;
72 size_t i
, nr_elems
= 0;
75 while ((pos
= strchr(pos
, ' '))) {
81 type
->counters
= reallocarray(type
->counters
,
82 type
->counters_nr
+ nr_elems
,
83 sizeof(struct netstat_counter
));
85 test_error("reallocarray()");
87 pos
= strchr(line
, ' ') + 1;
89 if (fscanf(fnetstat
, "%[^ :]", type
->header_name
) == EOF
)
90 test_error("fscanf(%s)", type
->header_name
);
91 if (fread(&tmp
, 1, 1, fnetstat
) != 1 || tmp
!= ':')
92 test_error("Unexpected netstat format (%c)", tmp
);
94 for (i
= type
->counters_nr
; i
< type
->counters_nr
+ nr_elems
; i
++) {
95 struct netstat_counter
*nc
= &type
->counters
[i
];
96 const char *new_pos
= strchr(pos
, ' ');
97 const char *fmt
= " %" PRIu64
;
100 new_pos
= strchr(pos
, '\n');
102 nc
->name
= strndup(pos
, new_pos
- pos
);
103 if (nc
->name
== NULL
)
104 test_error("strndup()");
106 if (unlikely(!strcmp(nc
->name
, "MaxConn")))
107 fmt
= " %" PRId64
; /* MaxConn is signed, RFC 2012 */
108 if (fscanf(fnetstat
, fmt
, &nc
->val
) != 1)
109 test_error("fscanf(%s)", nc
->name
);
112 type
->counters_nr
+= nr_elems
;
114 if (fread(&tmp
, 1, 1, fnetstat
) != 1 || tmp
!= '\n')
115 test_error("Unexpected netstat format");
118 static const char *snmp6_name
= "Snmp6";
119 static void snmp6_read(FILE *fnetstat
, struct netstat
**dest
)
121 struct netstat
*type
= lookup_get(*dest
, snmp6_name
, strlen(snmp6_name
));
125 for (i
= type
->counters_nr
;; i
++) {
126 struct netstat_counter
*nc
;
129 if (fscanf(fnetstat
, "%ms", &counter_name
) == EOF
)
131 if (fscanf(fnetstat
, "%" PRIu64
, &counter
) == EOF
)
132 test_error("Unexpected snmp6 format");
133 type
->counters
= reallocarray(type
->counters
, i
+ 1,
134 sizeof(struct netstat_counter
));
136 test_error("reallocarray()");
137 nc
= &type
->counters
[i
];
138 nc
->name
= counter_name
;
141 type
->counters_nr
= i
;
145 struct netstat
*netstat_read(void)
147 struct netstat
*ret
= 0;
153 * Opening thread-self instead of /proc/net/... as the latter
154 * points to /proc/self/net/ which instantiates thread-leader's
156 * commit 155134fef2b6 ("Revert "proc: Point /proc/{mounts,net} at..")
159 fnetstat
= fopen("/proc/thread-self/net/netstat", "r");
160 if (fnetstat
== NULL
)
161 test_error("failed to open /proc/net/netstat");
163 while (getline(&line
, &line_sz
, fnetstat
) != -1)
164 netstat_read_type(fnetstat
, &ret
, line
);
168 fnetstat
= fopen("/proc/thread-self/net/snmp", "r");
169 if (fnetstat
== NULL
)
170 test_error("failed to open /proc/net/snmp");
172 while (getline(&line
, &line_sz
, fnetstat
) != -1)
173 netstat_read_type(fnetstat
, &ret
, line
);
177 fnetstat
= fopen("/proc/thread-self/net/snmp6", "r");
178 if (fnetstat
== NULL
)
179 test_error("failed to open /proc/net/snmp6");
181 snmp6_read(fnetstat
, &ret
);
188 void netstat_free(struct netstat
*ns
)
191 struct netstat
*prev
= ns
;
194 free(ns
->header_name
);
195 for (i
= 0; i
< ns
->counters_nr
; i
++)
196 free(ns
->counters
[i
].name
);
204 __netstat_print_diff(uint64_t a
, struct netstat
*nsb
, size_t i
)
206 if (unlikely(!strcmp(nsb
->header_name
, "MaxConn"))) {
207 test_print("%8s %25s: %" PRId64
" => %" PRId64
,
208 nsb
->header_name
, nsb
->counters
[i
].name
,
209 a
, nsb
->counters
[i
].val
);
213 test_print("%8s %25s: %" PRIu64
" => %" PRIu64
, nsb
->header_name
,
214 nsb
->counters
[i
].name
, a
, nsb
->counters
[i
].val
);
217 void netstat_print_diff(struct netstat
*nsa
, struct netstat
*nsb
)
221 while (nsb
!= NULL
) {
222 if (unlikely(strcmp(nsb
->header_name
, nsa
->header_name
))) {
223 for (i
= 0; i
< nsb
->counters_nr
; i
++)
224 __netstat_print_diff(0, nsb
, i
);
229 if (nsb
->counters_nr
< nsa
->counters_nr
)
230 test_error("Unexpected: some counters disappeared!");
232 for (j
= 0, i
= 0; i
< nsb
->counters_nr
; i
++) {
233 if (strcmp(nsb
->counters
[i
].name
, nsa
->counters
[j
].name
)) {
234 __netstat_print_diff(0, nsb
, i
);
238 if (nsa
->counters
[j
].val
== nsb
->counters
[i
].val
) {
243 __netstat_print_diff(nsa
->counters
[j
].val
, nsb
, i
);
246 if (j
!= nsa
->counters_nr
)
247 test_error("Unexpected: some counters disappeared!");
254 uint64_t netstat_get(struct netstat
*ns
, const char *name
, bool *not_found
)
262 for (i
= 0; i
< ns
->counters_nr
; i
++) {
263 if (!strcmp(name
, ns
->counters
[i
].name
))
264 return ns
->counters
[i
].val
;