]> git.proxmox.com Git - mirror_frr.git/blame - pimd/mtracebis.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / pimd / mtracebis.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
4d9ad5dc
MS
2/*
3 * Multicast Traceroute for FRRouting
4 * Copyright (C) 2018 Mladen Sablic
4d9ad5dc
MS
5 */
6
0ce5349f 7#include <zebra.h>
b45ac5f5 8
4d9ad5dc
MS
9#ifdef __linux__
10
11#include "pim_igmp_mtrace.h"
12
13#include "checksum.h"
71e55fb2 14#include "prefix.h"
4d9ad5dc 15#include "mtracebis_routeget.h"
4d9ad5dc
MS
16#include <sys/select.h>
17#include <netinet/in.h>
18#include <arpa/inet.h>
19#include <unistd.h>
20#include <sys/types.h>
21#include <sys/time.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <time.h>
26#include <net/if.h>
5b02bd3a
DS
27#include <unistd.h>
28#include <getopt.h>
04a2eef3 29#include <netdb.h>
4d9ad5dc
MS
30
31#define MTRACEBIS_VERSION "0.1"
32#define MTRACE_TIMEOUT (5)
33
34#define IP_HDR_LEN (sizeof(struct ip))
35#define IP_RA_LEN (4)
36#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
37#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
38
39static const char *progname;
40static void usage(void)
41{
71e55fb2
MS
42 fprintf(stderr, "Usage : %s <multicast source> [<multicast group>]\n",
43 progname);
4d9ad5dc
MS
44}
45static void version(void)
46{
47 fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
48}
49
04a2eef3
MS
50static void print_host(struct in_addr addr)
51{
52 struct hostent *h;
ee2bbf7c 53 char buf[PREFIX_STRLEN];
04a2eef3
MS
54
55 h = gethostbyaddr(&addr, sizeof(addr), AF_INET);
56 if (h == NULL)
57 printf("?");
58 else
59 printf("%s", h->h_name);
ee2bbf7c 60 printf(" (%s) ", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
04a2eef3
MS
61}
62
63static void print_line_no(int i)
64{
65 printf("%3d ", -i);
66}
67
68static const char *rtg_proto_str(enum mtrace_rtg_proto proto)
69{
70 static char buf[80];
71
72 buf[0] = '\0';
73
74 switch (proto) {
75 case MTRACE_RTG_PROTO_DVMRP:
76 return "DVMRP";
77 case MTRACE_RTG_PROTO_MOSPF:
78 return "MOSPF";
79 case MTRACE_RTG_PROTO_PIM:
80 return "PIM";
81 case MTRACE_RTG_PROTO_CBT:
82 return "CBT";
83 case MTRACE_RTG_PROTO_PIM_SPECIAL:
84 return "PIM special";
85 case MTRACE_RTG_PROTO_PIM_STATIC:
86 return "PIM static";
87 case MTRACE_RTG_PROTO_DVMRP_STATIC:
88 return "DVMRP static";
89 case MTRACE_RTG_PROTO_PIM_MBGP:
90 return "PIM MBGP";
91 case MTRACE_RTG_PROTO_CBT_SPECIAL:
92 return "CBT special";
93 case MTRACE_RTG_PROTO_CBT_STATIC:
94 return "CBT static";
95 case MTRACE_RTG_PROTO_PIM_ASSERT:
96 return "PIM assert";
97 default:
772270f3 98 snprintf(buf, sizeof(buf), "unknown protocol (%d)", proto);
04a2eef3
MS
99 return buf;
100 }
101}
102
103static void print_rtg_proto(uint32_t rtg_proto)
104{
105 printf("%s", rtg_proto_str(rtg_proto));
106}
107
108static void print_fwd_ttl(uint32_t fwd_ttl)
109{
110 printf("thresh^ %d", fwd_ttl);
111}
112
113static const char *fwd_code_str(enum mtrace_fwd_code code)
114{
115 static char buf[80];
116
117 buf[0] = '\0';
118
119 switch (code) {
120 case MTRACE_FWD_CODE_NO_ERROR:
121 return "no error";
122 case MTRACE_FWD_CODE_WRONG_IF:
123 return "wrong interface";
124 case MTRACE_FWD_CODE_PRUNE_SENT:
125 return "prune sent";
126 case MTRACE_FWD_CODE_PRUNE_RCVD:
127 return "prune received";
128 case MTRACE_FWD_CODE_SCOPED:
129 return "scoped";
130 case MTRACE_FWD_CODE_NO_ROUTE:
131 return "no route";
132 case MTRACE_FWD_CODE_WRONG_LAST_HOP:
133 return "wrong last hop";
134 case MTRACE_FWD_CODE_NOT_FORWARDING:
135 return "not forwarding";
136 case MTRACE_FWD_CODE_REACHED_RP:
137 return "reached RP";
138 case MTRACE_FWD_CODE_RPF_IF:
139 return "RPF interface";
140 case MTRACE_FWD_CODE_NO_MULTICAST:
141 return "no multicast";
142 case MTRACE_FWD_CODE_INFO_HIDDEN:
143 return "info hidden";
144 case MTRACE_FWD_CODE_NO_SPACE:
145 return "no space";
146 case MTRACE_FWD_CODE_OLD_ROUTER:
147 return "old router";
148 case MTRACE_FWD_CODE_ADMIN_PROHIB:
149 return "admin. prohib.";
150 default:
772270f3 151 snprintf(buf, sizeof(buf), "unknown fwd. code (%d)", code);
04a2eef3
MS
152 return buf;
153 }
154}
155
156static void print_fwd_code(uint32_t fwd_code)
157{
158 printf("%s", fwd_code_str(fwd_code));
159}
160
161static void print_rsp(struct igmp_mtrace_rsp *rsp)
162{
163 print_host(rsp->outgoing);
71e55fb2 164 if (rsp->fwd_code == 0 || rsp->fwd_code == MTRACE_FWD_CODE_REACHED_RP) {
04a2eef3
MS
165 print_rtg_proto(rsp->rtg_proto);
166 printf(" ");
71e55fb2
MS
167 if (rsp->fwd_code == MTRACE_FWD_CODE_REACHED_RP)
168 printf("(RP) ");
169 if (rsp->rtg_proto == MTRACE_RTG_PROTO_PIM) {
170 switch (rsp->src_mask) {
171 case MTRACE_SRC_MASK_GROUP:
172 printf("(*,G) ");
173 break;
174 case MTRACE_SRC_MASK_SOURCE:
175 printf("(S,G) ");
176 break;
177 }
178 }
04a2eef3
MS
179 print_fwd_ttl(rsp->fwd_ttl);
180 } else {
181 print_fwd_code(rsp->fwd_code);
182 }
183 printf("\n");
184}
185
186static void print_dest(struct igmp_mtrace *mtrace)
187{
188 print_line_no(0);
189 print_host(mtrace->dst_addr);
190 printf("\n");
191}
192
193static void print_summary(struct igmp_mtrace *mtrace, int hops, long msec)
194{
195 int i;
196 int t = 0;
197
198 for (i = 0; i < hops; i++)
199 t += mtrace->rsp[i].fwd_ttl;
200
201 printf("Round trip time %ld ms; total ttl of %d required.\n", msec, t);
202}
203
204static void print_responses(struct igmp_mtrace *mtrace, int hops, long msec)
205{
206 int i;
207
208 print_dest(mtrace);
209
210 for (i = 0; i < hops; i++) {
211 print_line_no(i + 1);
212 print_rsp(&mtrace->rsp[i]);
213 }
214 print_summary(mtrace, hops, msec);
215}
216
4d9ad5dc
MS
217static int send_query(int fd, struct in_addr to_addr,
218 struct igmp_mtrace *mtrace)
219{
220 struct sockaddr_in to;
221 socklen_t tolen;
222 int sent;
223
224 memset(&to, 0, sizeof(to));
225 to.sin_family = AF_INET;
226 to.sin_addr = to_addr;
227 tolen = sizeof(to);
228
229 sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
230 (struct sockaddr *)&to, tolen);
231
232 if (sent < 1)
233 return -1;
234 return 0;
235}
236
237static void print_query(struct igmp_mtrace *mtrace)
238{
239 char src_str[INET_ADDRSTRLEN];
240 char dst_str[INET_ADDRSTRLEN];
241 char grp_str[INET_ADDRSTRLEN];
242
243 printf("* Mtrace from %s to %s via group %s\n",
244 inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)),
245 inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)),
246 inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str)));
247}
248
04a2eef3 249static int recv_response(int fd, int *hops, struct igmp_mtrace *mtracer)
4d9ad5dc
MS
250{
251 int recvd;
252 char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
253 struct ip *ip;
254 struct igmp_mtrace *mtrace;
255 int mtrace_len;
256 int responses;
d7c0a89a 257 unsigned short sum;
813099f0 258 size_t mtrace_off;
259 size_t ip_len;
4d9ad5dc
MS
260
261 recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
262
263 if (recvd < 1) {
264 fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
265 return -1;
266 }
267
268 if (recvd < (int)sizeof(struct ip)) {
269 fprintf(stderr, "no ip header\n");
270 return -1;
271 }
272
273 ip = (struct ip *)mtrace_buf;
274
275 if (ip->ip_v != 4) {
276 fprintf(stderr, "IP not version 4\n");
277 return -1;
278 }
279
280 sum = ip->ip_sum;
281 ip->ip_sum = 0;
282
283 if (sum != in_cksum(ip, ip->ip_hl * 4))
284 return -1;
285
813099f0 286 /* Header overflow check */
287 mtrace_off = 4 * ip->ip_hl;
288 if (mtrace_off > MTRACE_BUF_LEN)
18e994a0 289 return -1;
290
813099f0 291 /* Underflow/overflow check */
292 ip_len = ntohs(ip->ip_len);
293 if (ip_len < mtrace_off || ip_len < MTRACE_HDR_SIZE
294 || ip_len > MTRACE_BUF_LEN)
4d9ad5dc
MS
295 return -1;
296
813099f0 297 mtrace_len = ip_len - mtrace_off;
298 mtrace = (struct igmp_mtrace *)(mtrace_buf + mtrace_off);
d94023d8 299
4d9ad5dc
MS
300 sum = mtrace->checksum;
301 mtrace->checksum = 0;
302 if (sum != in_cksum(mtrace, mtrace_len)) {
303 fprintf(stderr, "mtrace checksum wrong\n");
304 return -1;
305 }
306
307 if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
308 return -1;
309
310
311 responses = mtrace_len - sizeof(struct igmp_mtrace);
312 responses /= sizeof(struct igmp_mtrace_rsp);
313
04a2eef3
MS
314 if (responses > MTRACE_MAX_HOPS) {
315 fprintf(stderr, "mtrace too large\n");
316 return -1;
317 }
4d9ad5dc
MS
318
319 if (hops)
320 *hops = responses;
321
04a2eef3
MS
322 if (mtracer)
323 memcpy(mtracer, mtrace, mtrace_len);
4d9ad5dc
MS
324
325 return 0;
326}
327
04a2eef3
MS
328static int wait_for_response(int fd, int *hops, struct igmp_mtrace *mtrace,
329 long *ret_msec)
4d9ad5dc
MS
330{
331 fd_set readfds;
332 struct timeval timeout;
a2b6e694 333 int ret;
4d9ad5dc
MS
334 long msec, rmsec, tmsec;
335
336 FD_ZERO(&readfds);
337 FD_SET(fd, &readfds);
338
339 memset(&timeout, 0, sizeof(timeout));
340
341 timeout.tv_sec = MTRACE_TIMEOUT;
342
343 tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
344 do {
345 ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
346 if (ret <= 0)
347 return ret;
348 rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
349 msec = tmsec - rmsec;
04a2eef3
MS
350 } while (recv_response(fd, hops, mtrace) != 0);
351
352 if (ret_msec)
353 *ret_msec = msec;
4d9ad5dc
MS
354
355 return ret;
356}
357
04a2eef3
MS
358static bool check_end(struct igmp_mtrace *mtrace, int hops)
359{
360 return mtrace->src_addr.s_addr == mtrace->rsp[hops - 1].prev_hop.s_addr;
361}
362
4d9ad5dc
MS
363int main(int argc, char *const argv[])
364{
365 struct in_addr mc_source;
71e55fb2 366 struct in_addr mc_group;
4d9ad5dc
MS
367 struct in_addr iface_addr;
368 struct in_addr gw_addr;
369 struct in_addr mtrace_addr;
370 struct igmp_mtrace mtrace;
04a2eef3 371 struct igmp_mtrace *mtracep;
4d9ad5dc
MS
372 int hops = 255;
373 int rhops;
374 int maxhops = 255;
375 int perhop = 3;
376 int ifindex;
377 int unicast = 1;
378 int ttl = 64;
379 int fd = -1;
380 int ret = -1;
381 int c;
04a2eef3 382 long msec;
4d9ad5dc
MS
383 int i, j;
384 char ifname[IF_NAMESIZE];
04a2eef3 385 char mbuf[MTRACE_BUF_LEN];
71e55fb2 386 bool not_group;
4d9ad5dc
MS
387
388 mtrace_addr.s_addr = inet_addr("224.0.1.32");
389
390 uid_t uid = getuid();
391
392 if (uid != 0) {
393 printf("must run as root\n");
394 exit(EXIT_FAILURE);
395 }
396
397 if (argc <= 0)
398 progname = "mtracebis";
399 else
400 progname = argv[0];
401
71e55fb2 402 if (argc != 2 && argc != 3) {
4d9ad5dc
MS
403 usage();
404 exit(EXIT_FAILURE);
405 }
406
407 while (1) {
408 static struct option long_options[] = {
409 {"help", no_argument, 0, 'h'},
410 {"version", no_argument, 0, 'v'},
996c9314 411 {0, 0, 0, 0}};
4d9ad5dc
MS
412 int option_index = 0;
413
414 c = getopt_long(argc, argv, "vh", long_options, &option_index);
415
416 if (c == -1)
417 break;
418
419 switch (c) {
420 case 'h':
421 usage();
422 exit(0);
423 case 'v':
424 version();
425 exit(0);
426 default:
427 usage();
428 exit(EXIT_FAILURE);
429 }
430 }
431 if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
432 usage();
71e55fb2 433 fprintf(stderr, "%s: %s is not a valid IPv4 address\n", argv[0],
4d9ad5dc
MS
434 argv[1]);
435 exit(EXIT_FAILURE);
436 }
437
975a328e 438 mc_group.s_addr = INADDR_ANY;
71e55fb2
MS
439 not_group = false;
440
441 if (argc == 3) {
442 if (inet_pton(AF_INET, argv[2], &mc_group) != 1)
443 not_group = true;
444 if (!not_group && !IPV4_CLASS_DE(ntohl(mc_group.s_addr)))
445 not_group = true;
446 }
447
448 if (not_group) {
449 usage();
450 fprintf(stderr, "%s: %s is not a valid IPv4 group address\n",
451 argv[0], argv[2]);
452 exit(EXIT_FAILURE);
453 }
454
4d9ad5dc
MS
455 ifindex = routeget(mc_source, &iface_addr, &gw_addr);
456 if (ifindex < 0) {
457 fprintf(stderr, "%s: failed to get route to source %s\n",
458 argv[0], argv[1]);
459 exit(EXIT_FAILURE);
460 }
461
462 if (if_indextoname(ifindex, ifname) == NULL) {
463 fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
464 strerror(errno));
465 exit(EXIT_FAILURE);
466 }
467
468 /* zero mtrace struct */
469 memset((char *)&mtrace, 0, sizeof(mtrace));
470
471 /* set up query */
472 mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
473 mtrace.hops = hops;
474 mtrace.checksum = 0;
71e55fb2 475 mtrace.grp_addr = mc_group;
4d9ad5dc
MS
476 mtrace.src_addr = mc_source;
477 mtrace.dst_addr = iface_addr;
478 mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr;
479 mtrace.rsp_ttl = ttl;
480 mtrace.qry_id = 0xffffff & time(NULL);
481
482 mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
483
484 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
485
486 if (fd < 1) {
487 fprintf(stderr, "%s: socket error: %s\n", argv[0],
488 strerror(errno));
489 exit(EXIT_FAILURE);
490 }
491
492 ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
493 strlen(ifname));
494
495 if (ret < 0) {
496 fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
497 strerror(errno));
498 ret = EXIT_FAILURE;
499 goto close_fd;
500 }
501
502 print_query(&mtrace);
503 if (send_query(fd, gw_addr, &mtrace) < 0) {
504 fprintf(stderr, "%s: sendto error: %s\n", argv[0],
505 strerror(errno));
506 ret = EXIT_FAILURE;
507 goto close_fd;
508 }
509 printf("Querying full reverse path...\n");
04a2eef3
MS
510 mtracep = (struct igmp_mtrace *)mbuf;
511 ret = wait_for_response(fd, &rhops, mtracep, &msec);
4d9ad5dc 512 if (ret > 0) {
04a2eef3 513 print_responses(mtracep, rhops, msec);
4d9ad5dc
MS
514 ret = 0;
515 goto close_fd;
516 }
517 if (ret < 0) {
518 fprintf(stderr, "%s: select error: %s\n", argv[0],
519 strerror(errno));
520 ret = EXIT_FAILURE;
521 goto close_fd;
522 }
523 printf(" * ");
524 printf("switching to hop-by-hop:\n");
04a2eef3 525 print_dest(&mtrace);
4d9ad5dc 526 for (i = 1; i < maxhops; i++) {
04a2eef3 527 print_line_no(i);
4d9ad5dc
MS
528 mtrace.hops = i;
529 for (j = 0; j < perhop; j++) {
530 mtrace.qry_id++;
531 mtrace.checksum = 0;
532 mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
533 if (send_query(fd, gw_addr, &mtrace) < 0) {
534 fprintf(stderr, "%s: sendto error: %s\n",
535 argv[0], strerror(errno));
536 ret = EXIT_FAILURE;
537 goto close_fd;
538 }
04a2eef3 539 ret = wait_for_response(fd, &rhops, mtracep, &msec);
4d9ad5dc 540 if (ret > 0) {
04a2eef3
MS
541 if (check_end(mtracep, rhops)) {
542 print_rsp(&mtracep->rsp[rhops - 1]);
543 print_summary(mtracep, rhops, msec);
544 ret = 0;
545 goto close_fd;
546 }
4d9ad5dc 547 if (i > rhops) {
04a2eef3 548 printf(" * ...giving up.\n");
4d9ad5dc
MS
549 ret = 0;
550 goto close_fd;
551 }
04a2eef3 552 print_rsp(&mtracep->rsp[rhops - 1]);
4d9ad5dc
MS
553 break;
554 }
555 printf(" *");
556 }
557 if (ret <= 0)
558 printf("\n");
559 }
560 ret = 0;
561close_fd:
562 close(fd);
563 exit(ret);
564}
565
566#else /* __linux__ */
567
568#include <stdio.h>
569#include <stdlib.h>
570
571int main(int argc, char *argv[])
572{
573 printf("%s implemented only for GNU/Linux\n", argv[0]);
574 exit(0);
575}
576
577#endif /* __linux__ */