1 // SPDX-License-Identifier: GPL-2.0 2 #include <inttypes.h> 3 #include <pthread.h> 4 #include <stdio.h> 5 #include "../../../../../include/linux/compiler.h" 6 #include "../../../../../include/linux/kernel.h" 7 #include "aolib.h" 8 9 struct netstat_counter { 10 uint64_t val; 11 char *name; 12 }; 13 14 struct netstat { 15 char *header_name; 16 struct netstat *next; 17 size_t counters_nr; 18 struct netstat_counter *counters; 19 }; 20 21 static struct netstat *lookup_type(struct netstat *ns, 22 const char *type, size_t len) 23 { 24 while (ns != NULL) { 25 size_t cmp = max(len, strlen(ns->header_name)); 26 27 if (!strncmp(ns->header_name, type, cmp)) 28 return ns; 29 ns = ns->next; 30 } 31 return NULL; 32 } 33 34 static struct netstat *lookup_get(struct netstat *ns, 35 const char *type, const size_t len) 36 { 37 struct netstat *ret; 38 39 ret = lookup_type(ns, type, len); 40 if (ret != NULL) 41 return ret; 42 43 ret = malloc(sizeof(struct netstat)); 44 if (!ret) 45 test_error("malloc()"); 46 47 ret->header_name = strndup(type, len); 48 if (ret->header_name == NULL) 49 test_error("strndup()"); 50 ret->next = ns; 51 ret->counters_nr = 0; 52 ret->counters = NULL; 53 54 return ret; 55 } 56 57 static struct netstat *lookup_get_column(struct netstat *ns, const char *line) 58 { 59 char *column; 60 61 column = strchr(line, ':'); 62 if (!column) 63 test_error("can't parse netstat file"); 64 65 return lookup_get(ns, line, column - line); 66 } 67 68 static void netstat_read_type(FILE *fnetstat, struct netstat **dest, char *line) 69 { 70 struct netstat *type = lookup_get_column(*dest, line); 71 const char *pos = line; 72 size_t i, nr_elems = 0; 73 char tmp; 74 75 while ((pos = strchr(pos, ' '))) { 76 nr_elems++; 77 pos++; 78 } 79 80 *dest = type; 81 type->counters = reallocarray(type->counters, 82 type->counters_nr + nr_elems, 83 sizeof(struct netstat_counter)); 84 if (!type->counters) 85 test_error("reallocarray()"); 86 87 pos = strchr(line, ' ') + 1; 88 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); 93 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; 98 99 if (new_pos == NULL) 100 new_pos = strchr(pos, '\n'); 101 102 nc->name = strndup(pos, new_pos - pos); 103 if (nc->name == NULL) 104 test_error("strndup()"); 105 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); 110 pos = new_pos + 1; 111 } 112 type->counters_nr += nr_elems; 113 114 if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != '\n') 115 test_error("Unexpected netstat format"); 116 } 117 118 static const char *snmp6_name = "Snmp6"; 119 static void snmp6_read(FILE *fnetstat, struct netstat **dest) 120 { 121 struct netstat *type = lookup_get(*dest, snmp6_name, strlen(snmp6_name)); 122 char *counter_name; 123 size_t i; 124 125 for (i = type->counters_nr;; i++) { 126 struct netstat_counter *nc; 127 uint64_t counter; 128 129 if (fscanf(fnetstat, "%ms", &counter_name) == EOF) 130 break; 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)); 135 if (!type->counters) 136 test_error("reallocarray()"); 137 nc = &type->counters[i]; 138 nc->name = counter_name; 139 nc->val = counter; 140 } 141 type->counters_nr = i; 142 *dest = type; 143 } 144 145 struct netstat *netstat_read(void) 146 { 147 struct netstat *ret = 0; 148 size_t line_sz = 0; 149 char *line = NULL; 150 FILE *fnetstat; 151 152 /* 153 * Opening thread-self instead of /proc/net/... as the latter 154 * points to /proc/self/net/ which instantiates thread-leader's 155 * net-ns, see: 156 * commit 155134fef2b6 ("Revert "proc: Point /proc/{mounts,net} at..") 157 */ 158 errno = 0; 159 fnetstat = fopen("/proc/thread-self/net/netstat", "r"); 160 if (fnetstat == NULL) 161 test_error("failed to open /proc/net/netstat"); 162 163 while (getline(&line, &line_sz, fnetstat) != -1) 164 netstat_read_type(fnetstat, &ret, line); 165 fclose(fnetstat); 166 167 errno = 0; 168 fnetstat = fopen("/proc/thread-self/net/snmp", "r"); 169 if (fnetstat == NULL) 170 test_error("failed to open /proc/net/snmp"); 171 172 while (getline(&line, &line_sz, fnetstat) != -1) 173 netstat_read_type(fnetstat, &ret, line); 174 fclose(fnetstat); 175 176 errno = 0; 177 fnetstat = fopen("/proc/thread-self/net/snmp6", "r"); 178 if (fnetstat == NULL) 179 test_error("failed to open /proc/net/snmp6"); 180 181 snmp6_read(fnetstat, &ret); 182 fclose(fnetstat); 183 184 free(line); 185 return ret; 186 } 187 188 void netstat_free(struct netstat *ns) 189 { 190 while (ns != NULL) { 191 struct netstat *prev = ns; 192 size_t i; 193 194 free(ns->header_name); 195 for (i = 0; i < ns->counters_nr; i++) 196 free(ns->counters[i].name); 197 free(ns->counters); 198 ns = ns->next; 199 free(prev); 200 } 201 } 202 203 static inline void 204 __netstat_print_diff(uint64_t a, struct netstat *nsb, size_t i) 205 { 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); 210 return; 211 } 212 213 test_print("%8s %25s: %" PRIu64 " => %" PRIu64, nsb->header_name, 214 nsb->counters[i].name, a, nsb->counters[i].val); 215 } 216 217 void netstat_print_diff(struct netstat *nsa, struct netstat *nsb) 218 { 219 size_t i, j; 220 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); 225 nsb = nsb->next; 226 continue; 227 } 228 229 if (nsb->counters_nr < nsa->counters_nr) 230 test_error("Unexpected: some counters disappeared!"); 231 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); 235 continue; 236 } 237 238 if (nsa->counters[j].val == nsb->counters[i].val) { 239 j++; 240 continue; 241 } 242 243 __netstat_print_diff(nsa->counters[j].val, nsb, i); 244 j++; 245 } 246 if (j != nsa->counters_nr) 247 test_error("Unexpected: some counters disappeared!"); 248 249 nsb = nsb->next; 250 nsa = nsa->next; 251 } 252 } 253 254 uint64_t netstat_get(struct netstat *ns, const char *name, bool *not_found) 255 { 256 if (not_found) 257 *not_found = false; 258 259 while (ns != NULL) { 260 size_t i; 261 262 for (i = 0; i < ns->counters_nr; i++) { 263 if (!strcmp(name, ns->counters[i].name)) 264 return ns->counters[i].val; 265 } 266 267 ns = ns->next; 268 } 269 270 if (not_found) 271 *not_found = true; 272 return 0; 273 } 274