Line data Source code
1 : /*
2 : * dropstats.c - drop statistics
3 : *
4 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
5 : */
6 :
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 : #include <unistd.h>
10 : #include <string.h>
11 : #include <errno.h>
12 : #include <inttypes.h>
13 : #include <stdlib.h>
14 : #include <stdbool.h>
15 : #include <getopt.h>
16 :
17 : #include <sys/types.h>
18 : #include <sys/socket.h>
19 :
20 : #include <net/if.h>
21 :
22 : #include "ini_parser.h"
23 : #include "vr_os.h"
24 : #include "vr_types.h"
25 : #include "vr_nexthop.h"
26 : #include "ini_parser.h"
27 : #include "nl_util.h"
28 : #include "ini_parser.h"
29 : #include "vr_packet.h"
30 :
31 : static struct nl_client *cl;
32 : static int help_set, core_set, offload_set, log_set, clear_set, clear_drop_log_set, debug_set, sock_dir_set;
33 : static unsigned int core = (unsigned)-1;
34 : static unsigned int stats_index = 0;
35 : static int log_type_set, log_type_show, min_log_set;
36 : static uint8_t pkt_drop_log_type = VP_DROP_MAX;
37 : static uint8_t show_pkt_drop_type = VP_DROP_MAX;
38 : static uint8_t min_log = 0;
39 : static int vr_get_pkt_drop_log(struct nl_client *cl,int core,int stats_index);
40 :
41 0 : static void pkt_drop_log_req_process(void *s_req) {
42 :
43 : static int log_all_cores = 0, last_buffer_stats = 0, last_buffer_entry = 0;
44 0 : vr_pkt_drop_log_req *stats = (vr_pkt_drop_log_req *)s_req;
45 :
46 0 : char vr_pkt_droplog_rsn[][50] = {
47 : DROP_RSN_MAP(string)
48 : };
49 :
50 0 : if (log_type_set)
51 : {
52 0 : printf("\nDropstats log type set successfully %s\n\n",
53 0 : vr_pkt_droplog_rsn[pkt_drop_log_type]);
54 0 : return;
55 : }
56 :
57 0 : if ((stats->vdl_pkt_droplog_type != show_pkt_drop_type) &&
58 0 : (show_pkt_drop_type != VP_DROP_MAX) &&
59 0 : ((stats->vdl_pkt_droplog_type > VP_DROP_INVALID) &&
60 0 : (stats->vdl_pkt_droplog_type < VP_DROP_MAX)))
61 : {
62 0 : printf("Pkt drop type already set to %s \n",
63 0 : vr_pkt_droplog_rsn[stats->vdl_pkt_droplog_type]);
64 0 : return;
65 : }
66 :
67 : /* Below check ensures that pkt drop log sysctl enabled during runtime*/
68 0 : if(stats->vdl_pkt_droplog_sysctl_en == 1)
69 : {
70 : /* Below check ensures that drop stats support is enabled at load time*/
71 0 : if(stats->vdl_pkt_droplog_en == 1)
72 : {
73 : /* Print the drop stats log*/
74 0 : vr_print_pkt_drop_log(stats, show_pkt_drop_type);
75 :
76 : /* Since sandesh message doesn't support passing data more than 4KB,
77 : * So the message request sent in serial manner.
78 : * If the configured size more than VR_PKT_DROPLOG_MAX_ALLOW_BUFSZ,
79 : * then index is maintained at utils side
80 : * and request data based on index
81 : * */
82 0 : if(stats->vdl_pkt_droplog_max_bufsz > VR_PKT_DROPLOG_MAX_ALLOW_BUFSZ)
83 : {
84 : /* If stats->index reached MAX size, it will not be processed.
85 : * stats->vdl_log_idx is used for printing serial numbers on
86 : * the console */
87 0 : if(stats->vdl_log_idx < (stats->vdl_pkt_droplog_max_bufsz -
88 : VR_PKT_DROPLOG_MAX_ALLOW_BUFSZ))
89 : {
90 : /* Request packet drop buffer for next iteration by
91 : * incrementing with MAX_ALLOWED_BUFFER */
92 0 : stats_index = stats->vdl_log_idx +
93 : VR_PKT_DROPLOG_MAX_ALLOW_BUFSZ;
94 0 : vr_get_pkt_drop_log(cl, stats->vdl_core, stats_index);
95 : }
96 : /* Below condition will process last iteration buffer,
97 : * If modulus is non-zero */
98 0 : else if( stats->vdl_pkt_droplog_max_bufsz %
99 0 : VR_PKT_DROPLOG_MAX_ALLOW_BUFSZ != 0)
100 : {
101 : /* Below condition to be processed only once per core */
102 0 : if( ! last_buffer_entry)
103 : {
104 0 : stats_index = stats->vdl_log_idx +
105 : VR_PKT_DROPLOG_MAX_ALLOW_BUFSZ;
106 0 : last_buffer_entry = 1;
107 0 : vr_get_pkt_drop_log(cl, stats->vdl_core, stats_index);
108 : }
109 : else
110 : {
111 : /* Resetting index and last buffer entry because all
112 : * processing done for this particular core*/
113 0 : last_buffer_entry = 0;
114 0 : stats_index = 0;
115 : }
116 : }
117 : else
118 0 : stats_index = 0;
119 : }
120 : /* When packet drop log is requested for all cores, below
121 : * condition would be enabled*/
122 0 : if(stats->vdl_core ==0 || log_all_cores == 1)
123 : {
124 : /* Increment the core value*/
125 0 : core++;
126 :
127 0 : if(core < stats->vdl_max_num_cores){
128 0 : log_all_cores = 1;
129 0 : vr_get_pkt_drop_log(cl,core+1,stats_index);
130 : }
131 : else
132 : {
133 0 : log_all_cores = 0;
134 : }
135 :
136 : }
137 : }
138 : else
139 : {
140 0 : printf("\n\nPkt drop stats log support is not enabled or misconfigured \
141 : in vrouter module parameters. Configured value is %d\n",
142 0 : stats->vdl_pkt_droplog_en);
143 0 : printf("You can enable by providing \"options vrouter \
144 : vr_pkt_droplog_buf_en=1\" in /etc/modprobe.d/vrouter.conf\n");
145 : }
146 : }
147 : else
148 : {
149 0 : printf("\n\nPacket Drop Log sysctl is not enabled or misconfigured, \
150 0 : Configured value is %d\n",stats->vdl_pkt_droplog_sysctl_en);
151 0 : printf("You can enable it by passing \"echo 1 > \
152 : /proc/sys/net/vrouter/pkt_drop_log_enable\"\n");
153 : }
154 0 : return;
155 : }
156 :
157 : static void
158 0 : pkt_drop_log_nlutils_callbacks()
159 : {
160 : /* Registering callback for packet drop log in netlink process*/
161 0 : nl_cb.vr_pkt_drop_log_req_process = pkt_drop_log_req_process;
162 0 : }
163 :
164 0 : static int vr_get_pkt_drop_log(struct nl_client *cl,int core,int stats_index) {
165 0 : int ret = 0;
166 :
167 0 : vr_pkt_drop_log_request(cl, 0, core, stats_index);
168 0 : if(ret < 0)
169 0 : return ret;
170 :
171 0 : ret = vr_recvmsg(cl, false);
172 0 : if(ret <= 0)
173 0 : return ret;
174 :
175 0 : return 0;
176 : }
177 :
178 : static void
179 1 : drop_stats_req_process(void *s_req)
180 : {
181 1 : vr_drop_stats_req *stats = (vr_drop_stats_req *)s_req;
182 1 : int platform = get_platform();
183 :
184 1 : if(stats->h_op == SANDESH_OP_RESET)
185 : {
186 0 : printf("\nDropstats counters cleared successfully on all cores \n\n");
187 0 : return;
188 : }
189 :
190 1 : if (core == (unsigned)-2)
191 0 : printf("Statistics for NIC offloads\n\n");
192 1 : else if (core != (unsigned)-1)
193 0 : printf("Statistics for core %u\n\n", core);
194 :
195 1 : if(debug_set) {
196 0 : vr_print_drop_dbg_stats(stats, core);
197 0 : return;
198 : }
199 :
200 1 : vr_print_drop_stats(stats, core);
201 1 : return;
202 : }
203 :
204 : static void
205 1 : dropstats_fill_nl_callbacks()
206 : {
207 1 : nl_cb.vr_drop_stats_req_process = drop_stats_req_process;
208 1 : }
209 :
210 : static int
211 1 : vr_get_drop_stats(struct nl_client *cl)
212 : {
213 : int ret;
214 :
215 : /*
216 : * Implementation of getting per-core drop statistics is based on this
217 : * little trick to avoid making changes in how agent makes requests for
218 : * statistics. From vRouter's and agent's point of view, request for
219 : * stats for 0th core means a request for stats summed up for all the
220 : * cores. So cores are enumerated starting with 1.
221 : * Meanwhile, from user's point of view they are enumerated starting
222 : * with 0 (e.g. dropstats --core 0 means 'drop statistics for the very
223 : * first (0th) core'). This is how Linux enumerates CPUs, so it should
224 : * be more intuitive for the user.
225 : *
226 : * Agent is not aware of possibility of asking for per-core stats. Its
227 : * requests have vds_core implicitly set to 0. So we need to make a
228 : * conversion between those enumerating systems. The dropstats utility
229 : * increments by 1 the core number user asked for. Then it is
230 : * decremented back in vRouter.
231 : *
232 : * vRouter will return only the offloaded dropstats if the "core"
233 : * is passed in as -2. This allows returning of only dropstats offloaded
234 : * on NIC using this same mechanism. If all CPUs are requested, the
235 : * offloaded dropstats are included.
236 : */
237 1 : ret = vr_send_drop_stats_get(cl, 0, core + 1);
238 1 : if (ret < 0)
239 0 : return ret;
240 :
241 1 : ret = vr_recvmsg(cl, false);
242 1 : if (ret <= 0)
243 0 : return ret;
244 :
245 1 : return 0;
246 : }
247 :
248 : static int
249 0 : vr_clear_pkt_drop_log(struct nl_client *cl)
250 : {
251 0 : int ret = vr_pkt_drop_log_reset(cl);
252 0 : if (ret < 0)
253 0 : return ret;
254 :
255 0 : return 0;
256 : }
257 :
258 : static int
259 0 : vr_clear_drop_stats(struct nl_client *cl)
260 : {
261 0 : int ret = vr_drop_stats_reset(cl);
262 0 : if (ret < 0)
263 0 : return ret;
264 :
265 0 : return 0;
266 : }
267 :
268 : static int
269 0 : vr_set_pkt_drop_log_type(struct nl_client *cl, uint8_t pkt_log_type)
270 : {
271 : int ret;
272 0 : ret = vr_drop_type_set(cl, pkt_log_type);
273 0 : if (ret < 0)
274 0 : return ret;
275 :
276 0 : ret = vr_recvmsg(cl, false);
277 0 : if (ret <= 0)
278 0 : return ret;
279 :
280 0 : return 0;
281 : }
282 :
283 : static int
284 0 : min_log_config(struct nl_client *cl, bool min_log_enable)
285 : {
286 : int ret;
287 0 : ret = vr_min_log_enable(cl, min_log_enable);
288 0 : if (ret < 0)
289 0 : return ret;
290 :
291 0 : ret = vr_recvmsg(cl, false);
292 0 : if (ret <= 0)
293 0 : return ret;
294 :
295 0 : return 0;
296 : }
297 :
298 : enum opt_index {
299 : HELP_OPT_INDEX,
300 : CORE_OPT_INDEX,
301 : OFFL_OPT_INDEX,
302 : LOG_OPT_INDEX,
303 : CLEAR_OPT_INDEX,
304 : SOCK_DIR_OPT_INDEX,
305 : DEBUG_OPT_INDEX,
306 : DROP_LOG_TYPE_OPT_INDEX,
307 : SHOW_LOG_TYPE_OPT_INDEX,
308 : CLEAR_DROP_LOG_OPT_INDEX,
309 : MIN_LOG_OPT_INDEX,
310 : MAX_OPT_INDEX,
311 : };
312 :
313 : static struct option long_options[] = {
314 : [HELP_OPT_INDEX] = {"help", no_argument, &help_set, 1},
315 : [CORE_OPT_INDEX] = {"core", required_argument, &core_set, 1},
316 : [OFFL_OPT_INDEX] = {"offload", no_argument, &offload_set, 1},
317 : [LOG_OPT_INDEX] = {"log", required_argument, &log_set, 1},
318 : [CLEAR_OPT_INDEX] = {"clear", no_argument, &clear_set, 1},
319 : [SOCK_DIR_OPT_INDEX] = {"sock-dir", required_argument, &sock_dir_set, 1},
320 : [DEBUG_OPT_INDEX] = {"debug", no_argument, &debug_set, 1},
321 : [DROP_LOG_TYPE_OPT_INDEX] = {"drop-type", required_argument, &log_type_set, 1},
322 : [SHOW_LOG_TYPE_OPT_INDEX] = {"show", required_argument, &log_type_show, 1},
323 : [CLEAR_DROP_LOG_OPT_INDEX] = {"clear-drop-log", no_argument, &clear_drop_log_set, 1},
324 : [MIN_LOG_OPT_INDEX] = {"min-log", required_argument, &min_log_set, 1},
325 : [MAX_OPT_INDEX] = {"NULL", 0, 0, 0},
326 : };
327 :
328 : static void
329 0 : Usage()
330 : {
331 0 : printf("Usage: dropstats [--help]\n");
332 0 : printf("Usage: dropstats [--core|-c] <core number> %s\n\n",
333 0 : get_offload_enabled()?"[--offload|-o]":"");
334 0 : printf("--core <core number>\t Show statistics for a specified CPU core\n");
335 0 : printf("--sock-dir <netlink socket dir>\n");
336 0 : if (get_offload_enabled()) {
337 0 : printf("--offload\t\t Show statistics for pkts offloaded on NIC\n");
338 0 : printf("\t\t\t (offload stats included if no flags given)\n");
339 : }
340 0 : printf("--log <core number> [--show <drop-type>]\t Show Packet drops log for a specified core.. \
341 : Core number starts from 1...n. If core number specified as zero, \
342 : it will log for all cores \n");
343 0 : printf("--clear\t To clear stats counters on all cores\n");
344 0 : printf("--debug\t To Display Debug counters\n");
345 0 : printf("--drop-type <drop log type|help>\t Log specific Packet drops type. \
346 : Use VP_DROP_MAX to clear drop set type\n");
347 0 : printf("--clear-drop-log\t To clear packet drops log on all cores\n");
348 0 : printf("--min-log <1(enable)/ 0<disable)\t To set min log\n");
349 0 : exit(-EINVAL);
350 : }
351 :
352 0 : void display_supported_drop_type()
353 : {
354 : int i;
355 0 : char vr_pkt_droplog_rsn[][50] = {
356 : DROP_RSN_MAP(string)};
357 :
358 0 : for(i=VP_DROP_DISCARD; i<VP_DROP_MAX; i++)
359 0 : printf("%s\n", vr_pkt_droplog_rsn[i]);
360 :
361 0 : exit(-EINVAL);
362 : }
363 :
364 : static int
365 0 : parse_log_type(char *opt_arg)
366 : {
367 : int i;
368 0 : char vr_pkt_droplog_rsn[][50] = {
369 : DROP_RSN_MAP(string)};
370 :
371 0 : for(i=VP_DROP_DISCARD; i<=VP_DROP_MAX; i++)
372 0 : if (!strcmp (opt_arg, vr_pkt_droplog_rsn[i]))
373 0 : return i;
374 :
375 0 : printf("Invalid log type %s\n", opt_arg);
376 0 : display_supported_drop_type();
377 0 : }
378 :
379 : /* Not using atoi and strtol as it returns 0
380 : * for invalid argument
381 : * ignoring -ve cases
382 : */
383 : static int
384 0 : is_valid_num(char *opt_arg)
385 : {
386 0 : int result_num = 0, i = 0;
387 :
388 0 : while (opt_arg[i] == ' ')
389 : {
390 0 : i++;
391 : }
392 :
393 0 : for(; opt_arg[i] != '\0'; i++) {
394 0 : if (opt_arg[i] >= '0' && opt_arg[i] <= '9')
395 : {
396 0 : result_num = 10 * result_num + (opt_arg[i] - '0');
397 : } else {
398 0 : printf("Invalid argument %s\n", opt_arg);
399 0 : Usage();
400 : }
401 : }
402 :
403 0 : return result_num;
404 : }
405 :
406 : static void
407 1 : parse_long_opts(int opt_index, char *opt_arg)
408 : {
409 1 : errno = 0;
410 :
411 1 : switch (opt_index) {
412 0 : case CORE_OPT_INDEX:
413 0 : core = is_valid_num(opt_arg);
414 0 : break;
415 0 : case OFFL_OPT_INDEX:
416 0 : if (!get_offload_enabled()) {
417 0 : printf("Error: hardware offloads not enabled\n");
418 0 : Usage();
419 : }
420 0 : core = -2;
421 0 : break;
422 0 : case LOG_OPT_INDEX:
423 0 : core = is_valid_num(opt_arg);
424 0 : break;
425 0 : case CLEAR_OPT_INDEX:
426 : case CLEAR_DROP_LOG_OPT_INDEX:
427 : case DEBUG_OPT_INDEX:
428 0 : break;
429 0 : case DROP_LOG_TYPE_OPT_INDEX:
430 0 : pkt_drop_log_type = parse_log_type(opt_arg);
431 0 : break;
432 0 : case SHOW_LOG_TYPE_OPT_INDEX:
433 0 : show_pkt_drop_type = parse_log_type(opt_arg);
434 0 : log_type_show =1;
435 0 : break;
436 0 : case MIN_LOG_OPT_INDEX:
437 0 : min_log = is_valid_num(opt_arg);
438 : /* min-log value either 0 or 1 */
439 0 : if (min_log > 1)
440 0 : Usage();
441 0 : break;
442 1 : case SOCK_DIR_OPT_INDEX:
443 1 : vr_socket_dir = opt_arg;
444 1 : break;
445 0 : case HELP_OPT_INDEX:
446 : default:
447 0 : Usage();
448 : }
449 :
450 1 : return;
451 : }
452 :
453 : int
454 1 : main(int argc, char *argv[])
455 : {
456 : char opt;
457 1 : int ret, option_index, log_core = 0, i = 0;
458 :
459 1 : dropstats_fill_nl_callbacks();
460 :
461 1 : parse_ini_file();
462 :
463 2 : while (((opt = getopt_long(argc, argv, "h:c:o:l:s:m:",
464 2 : long_options, &option_index)) >= 0)) {
465 1 : switch (opt) {
466 0 : case 'c':
467 0 : core_set = 1;
468 0 : parse_long_opts(CORE_OPT_INDEX, optarg);
469 0 : break;
470 :
471 0 : case 'o':
472 0 : offload_set = 1;
473 0 : parse_long_opts(OFFL_OPT_INDEX, optarg);
474 0 : break;
475 :
476 0 : case 'l':
477 0 : log_set = 1;
478 0 : parse_long_opts(LOG_OPT_INDEX, optarg);
479 0 : break;
480 :
481 0 : case 'm':
482 0 : min_log_set = 1;
483 0 : parse_long_opts(MIN_LOG_OPT_INDEX, optarg);
484 0 : break;
485 :
486 0 : case 's':
487 0 : sock_dir_set = 1;
488 0 : parse_long_opts(SOCK_DIR_OPT_INDEX, optarg);
489 0 : break;
490 :
491 1 : case 0:
492 1 : parse_long_opts(option_index, optarg);
493 1 : break;
494 :
495 0 : case 'h':
496 : default:
497 0 : Usage();
498 : }
499 : }
500 :
501 1 : if (sock_dir_set) {
502 1 : set_platform_vtest();
503 : }
504 1 : cl = vr_get_nl_client(VR_NETLINK_PROTO_DEFAULT);
505 1 : if (!cl)
506 0 : return -1;
507 :
508 1 : if ((option_index == SHOW_LOG_TYPE_OPT_INDEX) && (!log_set))
509 : {
510 0 : Usage();
511 : }
512 :
513 1 : if (log_type_set)
514 : {
515 0 : pkt_drop_log_nlutils_callbacks();
516 0 : vr_set_pkt_drop_log_type(cl, pkt_drop_log_type);
517 0 : return 0;
518 : }
519 :
520 1 : if (min_log_set)
521 : {
522 0 : min_log_config(cl, min_log == 1);
523 0 : return 0;
524 : }
525 :
526 1 : if (log_set)
527 : {
528 0 : log_core = is_valid_num(argv[2]);
529 :
530 : /* Register nl allback function for pkt drop log buffer*/
531 0 : pkt_drop_log_nlutils_callbacks();
532 :
533 0 : vr_get_pkt_drop_log(cl,log_core,stats_index);
534 0 : return 0;
535 : }
536 :
537 1 : if (option_index == CLEAR_DROP_LOG_OPT_INDEX)
538 : {
539 0 : vr_clear_pkt_drop_log(cl);
540 0 : return 0;
541 : }
542 :
543 1 : if (option_index == CLEAR_OPT_INDEX)
544 : {
545 0 : vr_clear_drop_stats(cl);
546 :
547 0 : ret = vr_recvmsg(cl, false);
548 0 : if (ret <= 0)
549 0 : return ret;
550 : }
551 : else
552 1 : vr_get_drop_stats(cl);
553 :
554 1 : return 0;
555 : }
|