2 * Multicast Traceroute for FRRouting
3 * Copyright (C) 2018 Mladen Sablic
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "pim_igmp_mtrace.h"
28 #include "mtracebis_routeget.h"
30 #include <sys/select.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
34 #include <sys/types.h>
45 #define MTRACEBIS_VERSION "0.1"
46 #define MTRACE_TIMEOUT (5)
48 #define IP_HDR_LEN (sizeof(struct ip))
50 #define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
51 #define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
53 static const char *progname
;
54 static void usage(void)
56 fprintf(stderr
, "Usage : %s <multicast source> [<multicast group>]\n",
59 static void version(void)
61 fprintf(stderr
, "%s %s\n", progname
, MTRACEBIS_VERSION
);
64 static void print_host(struct in_addr addr
)
68 h
= gethostbyaddr(&addr
, sizeof(addr
), AF_INET
);
72 printf("%s", h
->h_name
);
73 printf(" (%s) ", inet_ntoa(addr
));
76 static void print_line_no(int i
)
81 static const char *rtg_proto_str(enum mtrace_rtg_proto proto
)
88 case MTRACE_RTG_PROTO_DVMRP
:
90 case MTRACE_RTG_PROTO_MOSPF
:
92 case MTRACE_RTG_PROTO_PIM
:
94 case MTRACE_RTG_PROTO_CBT
:
96 case MTRACE_RTG_PROTO_PIM_SPECIAL
:
98 case MTRACE_RTG_PROTO_PIM_STATIC
:
100 case MTRACE_RTG_PROTO_DVMRP_STATIC
:
101 return "DVMRP static";
102 case MTRACE_RTG_PROTO_PIM_MBGP
:
104 case MTRACE_RTG_PROTO_CBT_SPECIAL
:
105 return "CBT special";
106 case MTRACE_RTG_PROTO_CBT_STATIC
:
108 case MTRACE_RTG_PROTO_PIM_ASSERT
:
111 snprintf(buf
, sizeof(buf
), "unknown protocol (%d)", proto
);
116 static void print_rtg_proto(uint32_t rtg_proto
)
118 printf("%s", rtg_proto_str(rtg_proto
));
121 static void print_fwd_ttl(uint32_t fwd_ttl
)
123 printf("thresh^ %d", fwd_ttl
);
126 static const char *fwd_code_str(enum mtrace_fwd_code code
)
133 case MTRACE_FWD_CODE_NO_ERROR
:
135 case MTRACE_FWD_CODE_WRONG_IF
:
136 return "wrong interface";
137 case MTRACE_FWD_CODE_PRUNE_SENT
:
139 case MTRACE_FWD_CODE_PRUNE_RCVD
:
140 return "prune received";
141 case MTRACE_FWD_CODE_SCOPED
:
143 case MTRACE_FWD_CODE_NO_ROUTE
:
145 case MTRACE_FWD_CODE_WRONG_LAST_HOP
:
146 return "wrong last hop";
147 case MTRACE_FWD_CODE_NOT_FORWARDING
:
148 return "not forwarding";
149 case MTRACE_FWD_CODE_REACHED_RP
:
151 case MTRACE_FWD_CODE_RPF_IF
:
152 return "RPF interface";
153 case MTRACE_FWD_CODE_NO_MULTICAST
:
154 return "no multicast";
155 case MTRACE_FWD_CODE_INFO_HIDDEN
:
156 return "info hidden";
157 case MTRACE_FWD_CODE_NO_SPACE
:
159 case MTRACE_FWD_CODE_OLD_ROUTER
:
161 case MTRACE_FWD_CODE_ADMIN_PROHIB
:
162 return "admin. prohib.";
164 snprintf(buf
, sizeof(buf
), "unknown fwd. code (%d)", code
);
169 static void print_fwd_code(uint32_t fwd_code
)
171 printf("%s", fwd_code_str(fwd_code
));
174 static void print_rsp(struct igmp_mtrace_rsp
*rsp
)
176 print_host(rsp
->outgoing
);
177 if (rsp
->fwd_code
== 0 || rsp
->fwd_code
== MTRACE_FWD_CODE_REACHED_RP
) {
178 print_rtg_proto(rsp
->rtg_proto
);
180 if (rsp
->fwd_code
== MTRACE_FWD_CODE_REACHED_RP
)
182 if (rsp
->rtg_proto
== MTRACE_RTG_PROTO_PIM
) {
183 switch (rsp
->src_mask
) {
184 case MTRACE_SRC_MASK_GROUP
:
187 case MTRACE_SRC_MASK_SOURCE
:
192 print_fwd_ttl(rsp
->fwd_ttl
);
194 print_fwd_code(rsp
->fwd_code
);
199 static void print_dest(struct igmp_mtrace
*mtrace
)
202 print_host(mtrace
->dst_addr
);
206 static void print_summary(struct igmp_mtrace
*mtrace
, int hops
, long msec
)
211 for (i
= 0; i
< hops
; i
++)
212 t
+= mtrace
->rsp
[i
].fwd_ttl
;
214 printf("Round trip time %ld ms; total ttl of %d required.\n", msec
, t
);
217 static void print_responses(struct igmp_mtrace
*mtrace
, int hops
, long msec
)
223 for (i
= 0; i
< hops
; i
++) {
224 print_line_no(i
+ 1);
225 print_rsp(&mtrace
->rsp
[i
]);
227 print_summary(mtrace
, hops
, msec
);
230 static int send_query(int fd
, struct in_addr to_addr
,
231 struct igmp_mtrace
*mtrace
)
233 struct sockaddr_in to
;
237 memset(&to
, 0, sizeof(to
));
238 to
.sin_family
= AF_INET
;
239 to
.sin_addr
= to_addr
;
242 sent
= sendto(fd
, (char *)mtrace
, sizeof(*mtrace
), MSG_DONTWAIT
,
243 (struct sockaddr
*)&to
, tolen
);
250 static void print_query(struct igmp_mtrace
*mtrace
)
252 char src_str
[INET_ADDRSTRLEN
];
253 char dst_str
[INET_ADDRSTRLEN
];
254 char grp_str
[INET_ADDRSTRLEN
];
256 printf("* Mtrace from %s to %s via group %s\n",
257 inet_ntop(AF_INET
, &mtrace
->src_addr
, src_str
, sizeof(src_str
)),
258 inet_ntop(AF_INET
, &mtrace
->dst_addr
, dst_str
, sizeof(dst_str
)),
259 inet_ntop(AF_INET
, &mtrace
->grp_addr
, grp_str
, sizeof(grp_str
)));
262 static int recv_response(int fd
, int *hops
, struct igmp_mtrace
*mtracer
)
265 char mtrace_buf
[IP_AND_MTRACE_BUF_LEN
];
267 struct igmp_mtrace
*mtrace
;
274 recvd
= recvfrom(fd
, mtrace_buf
, IP_AND_MTRACE_BUF_LEN
, 0, NULL
, 0);
277 fprintf(stderr
, "recvfrom error: %s\n", strerror(errno
));
281 if (recvd
< (int)sizeof(struct ip
)) {
282 fprintf(stderr
, "no ip header\n");
286 ip
= (struct ip
*)mtrace_buf
;
289 fprintf(stderr
, "IP not version 4\n");
296 if (sum
!= in_cksum(ip
, ip
->ip_hl
* 4))
299 /* Header overflow check */
300 mtrace_off
= 4 * ip
->ip_hl
;
301 if (mtrace_off
> MTRACE_BUF_LEN
)
304 /* Underflow/overflow check */
305 ip_len
= ntohs(ip
->ip_len
);
306 if (ip_len
< mtrace_off
|| ip_len
< MTRACE_HDR_SIZE
307 || ip_len
> MTRACE_BUF_LEN
)
310 mtrace_len
= ip_len
- mtrace_off
;
311 mtrace
= (struct igmp_mtrace
*)(mtrace_buf
+ mtrace_off
);
313 sum
= mtrace
->checksum
;
314 mtrace
->checksum
= 0;
315 if (sum
!= in_cksum(mtrace
, mtrace_len
)) {
316 fprintf(stderr
, "mtrace checksum wrong\n");
320 if (mtrace
->type
!= PIM_IGMP_MTRACE_RESPONSE
)
324 responses
= mtrace_len
- sizeof(struct igmp_mtrace
);
325 responses
/= sizeof(struct igmp_mtrace_rsp
);
327 if (responses
> MTRACE_MAX_HOPS
) {
328 fprintf(stderr
, "mtrace too large\n");
336 memcpy(mtracer
, mtrace
, mtrace_len
);
341 static int wait_for_response(int fd
, int *hops
, struct igmp_mtrace
*mtrace
,
345 struct timeval timeout
;
347 long msec
, rmsec
, tmsec
;
350 FD_SET(fd
, &readfds
);
352 memset(&timeout
, 0, sizeof(timeout
));
354 timeout
.tv_sec
= MTRACE_TIMEOUT
;
356 tmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
358 ret
= select(fd
+ 1, &readfds
, NULL
, NULL
, &timeout
);
361 rmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
362 msec
= tmsec
- rmsec
;
363 } while (recv_response(fd
, hops
, mtrace
) != 0);
371 static bool check_end(struct igmp_mtrace
*mtrace
, int hops
)
373 return mtrace
->src_addr
.s_addr
== mtrace
->rsp
[hops
- 1].prev_hop
.s_addr
;
376 int main(int argc
, char *const argv
[])
378 struct in_addr mc_source
;
379 struct in_addr mc_group
;
380 struct in_addr iface_addr
;
381 struct in_addr gw_addr
;
382 struct in_addr mtrace_addr
;
383 struct igmp_mtrace mtrace
;
384 struct igmp_mtrace
*mtracep
;
397 char ifname
[IF_NAMESIZE
];
398 char mbuf
[MTRACE_BUF_LEN
];
401 mtrace_addr
.s_addr
= inet_addr("224.0.1.32");
403 uid_t uid
= getuid();
406 printf("must run as root\n");
411 progname
= "mtracebis";
415 if (argc
!= 2 && argc
!= 3) {
421 static struct option long_options
[] = {
422 {"help", no_argument
, 0, 'h'},
423 {"version", no_argument
, 0, 'v'},
425 int option_index
= 0;
427 c
= getopt_long(argc
, argv
, "vh", long_options
, &option_index
);
444 if (inet_pton(AF_INET
, argv
[1], &mc_source
) != 1) {
446 fprintf(stderr
, "%s: %s is not a valid IPv4 address\n", argv
[0],
451 mc_group
.s_addr
= INADDR_ANY
;
455 if (inet_pton(AF_INET
, argv
[2], &mc_group
) != 1)
457 if (!not_group
&& !IPV4_CLASS_DE(ntohl(mc_group
.s_addr
)))
463 fprintf(stderr
, "%s: %s is not a valid IPv4 group address\n",
468 ifindex
= routeget(mc_source
, &iface_addr
, &gw_addr
);
470 fprintf(stderr
, "%s: failed to get route to source %s\n",
475 if (if_indextoname(ifindex
, ifname
) == NULL
) {
476 fprintf(stderr
, "%s: if_indextoname error: %s\n", argv
[0],
481 /* zero mtrace struct */
482 memset((char *)&mtrace
, 0, sizeof(mtrace
));
485 mtrace
.type
= PIM_IGMP_MTRACE_QUERY_REQUEST
;
488 mtrace
.grp_addr
= mc_group
;
489 mtrace
.src_addr
= mc_source
;
490 mtrace
.dst_addr
= iface_addr
;
491 mtrace
.rsp_addr
= unicast
? iface_addr
: mtrace_addr
;
492 mtrace
.rsp_ttl
= ttl
;
493 mtrace
.qry_id
= 0xffffff & time(NULL
);
495 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
497 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
500 fprintf(stderr
, "%s: socket error: %s\n", argv
[0],
505 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, ifname
,
509 fprintf(stderr
, "%s: setsockopt error: %s\n", argv
[0],
515 print_query(&mtrace
);
516 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
517 fprintf(stderr
, "%s: sendto error: %s\n", argv
[0],
522 printf("Querying full reverse path...\n");
523 mtracep
= (struct igmp_mtrace
*)mbuf
;
524 ret
= wait_for_response(fd
, &rhops
, mtracep
, &msec
);
526 print_responses(mtracep
, rhops
, msec
);
531 fprintf(stderr
, "%s: select error: %s\n", argv
[0],
537 printf("switching to hop-by-hop:\n");
539 for (i
= 1; i
< maxhops
; i
++) {
542 for (j
= 0; j
< perhop
; j
++) {
545 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
546 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
547 fprintf(stderr
, "%s: sendto error: %s\n",
548 argv
[0], strerror(errno
));
552 ret
= wait_for_response(fd
, &rhops
, mtracep
, &msec
);
554 if (check_end(mtracep
, rhops
)) {
555 print_rsp(&mtracep
->rsp
[rhops
- 1]);
556 print_summary(mtracep
, rhops
, msec
);
561 printf(" * ...giving up.\n");
565 print_rsp(&mtracep
->rsp
[rhops
- 1]);
579 #else /* __linux__ */
584 int main(int argc
, char *argv
[])
586 printf("%s implemented only for GNU/Linux\n", argv
[0]);
590 #endif /* __linux__ */