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