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
22 #include "pim_igmp_mtrace.h"
25 #include "mtracebis_routeget.h"
27 #include <sys/select.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include <sys/types.h>
42 #define MTRACEBIS_VERSION "0.1"
43 #define MTRACE_TIMEOUT (5)
45 #define IP_HDR_LEN (sizeof(struct ip))
47 #define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
48 #define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
50 static const char *progname
;
51 static void usage(void)
53 fprintf(stderr
, "Usage : %s <multicast source>\n", progname
);
55 static void version(void)
57 fprintf(stderr
, "%s %s\n", progname
, MTRACEBIS_VERSION
);
60 static void print_host(struct in_addr addr
)
64 h
= gethostbyaddr(&addr
, sizeof(addr
), AF_INET
);
68 printf("%s", h
->h_name
);
69 printf(" (%s) ", inet_ntoa(addr
));
72 static void print_line_no(int i
)
77 static const char *rtg_proto_str(enum mtrace_rtg_proto proto
)
84 case MTRACE_RTG_PROTO_DVMRP
:
86 case MTRACE_RTG_PROTO_MOSPF
:
88 case MTRACE_RTG_PROTO_PIM
:
90 case MTRACE_RTG_PROTO_CBT
:
92 case MTRACE_RTG_PROTO_PIM_SPECIAL
:
94 case MTRACE_RTG_PROTO_PIM_STATIC
:
96 case MTRACE_RTG_PROTO_DVMRP_STATIC
:
97 return "DVMRP static";
98 case MTRACE_RTG_PROTO_PIM_MBGP
:
100 case MTRACE_RTG_PROTO_CBT_SPECIAL
:
101 return "CBT special";
102 case MTRACE_RTG_PROTO_CBT_STATIC
:
104 case MTRACE_RTG_PROTO_PIM_ASSERT
:
107 sprintf(buf
, "unknown protocol (%d)", proto
);
112 static void print_rtg_proto(uint32_t rtg_proto
)
114 printf("%s", rtg_proto_str(rtg_proto
));
117 static void print_fwd_ttl(uint32_t fwd_ttl
)
119 printf("thresh^ %d", fwd_ttl
);
122 static const char *fwd_code_str(enum mtrace_fwd_code code
)
129 case MTRACE_FWD_CODE_NO_ERROR
:
131 case MTRACE_FWD_CODE_WRONG_IF
:
132 return "wrong interface";
133 case MTRACE_FWD_CODE_PRUNE_SENT
:
135 case MTRACE_FWD_CODE_PRUNE_RCVD
:
136 return "prune received";
137 case MTRACE_FWD_CODE_SCOPED
:
139 case MTRACE_FWD_CODE_NO_ROUTE
:
141 case MTRACE_FWD_CODE_WRONG_LAST_HOP
:
142 return "wrong last hop";
143 case MTRACE_FWD_CODE_NOT_FORWARDING
:
144 return "not forwarding";
145 case MTRACE_FWD_CODE_REACHED_RP
:
147 case MTRACE_FWD_CODE_RPF_IF
:
148 return "RPF interface";
149 case MTRACE_FWD_CODE_NO_MULTICAST
:
150 return "no multicast";
151 case MTRACE_FWD_CODE_INFO_HIDDEN
:
152 return "info hidden";
153 case MTRACE_FWD_CODE_NO_SPACE
:
155 case MTRACE_FWD_CODE_OLD_ROUTER
:
157 case MTRACE_FWD_CODE_ADMIN_PROHIB
:
158 return "admin. prohib.";
160 sprintf(buf
, "unknown fwd. code (%d)", code
);
165 static void print_fwd_code(uint32_t fwd_code
)
167 printf("%s", fwd_code_str(fwd_code
));
170 static void print_rsp(struct igmp_mtrace_rsp
*rsp
)
172 print_host(rsp
->outgoing
);
173 if (rsp
->fwd_code
== 0) {
174 print_rtg_proto(rsp
->rtg_proto
);
176 print_fwd_ttl(rsp
->fwd_ttl
);
178 print_fwd_code(rsp
->fwd_code
);
183 static void print_dest(struct igmp_mtrace
*mtrace
)
186 print_host(mtrace
->dst_addr
);
190 static void print_summary(struct igmp_mtrace
*mtrace
, int hops
, long msec
)
195 for (i
= 0; i
< hops
; i
++)
196 t
+= mtrace
->rsp
[i
].fwd_ttl
;
198 printf("Round trip time %ld ms; total ttl of %d required.\n", msec
, t
);
201 static void print_responses(struct igmp_mtrace
*mtrace
, int hops
, long msec
)
207 for (i
= 0; i
< hops
; i
++) {
208 print_line_no(i
+ 1);
209 print_rsp(&mtrace
->rsp
[i
]);
211 print_summary(mtrace
, hops
, msec
);
214 static int send_query(int fd
, struct in_addr to_addr
,
215 struct igmp_mtrace
*mtrace
)
217 struct sockaddr_in to
;
221 memset(&to
, 0, sizeof(to
));
222 to
.sin_family
= AF_INET
;
223 to
.sin_addr
= to_addr
;
226 sent
= sendto(fd
, (char *)mtrace
, sizeof(*mtrace
), MSG_DONTWAIT
,
227 (struct sockaddr
*)&to
, tolen
);
234 static void print_query(struct igmp_mtrace
*mtrace
)
236 char src_str
[INET_ADDRSTRLEN
];
237 char dst_str
[INET_ADDRSTRLEN
];
238 char grp_str
[INET_ADDRSTRLEN
];
240 printf("* Mtrace from %s to %s via group %s\n",
241 inet_ntop(AF_INET
, &mtrace
->src_addr
, src_str
, sizeof(src_str
)),
242 inet_ntop(AF_INET
, &mtrace
->dst_addr
, dst_str
, sizeof(dst_str
)),
243 inet_ntop(AF_INET
, &mtrace
->grp_addr
, grp_str
, sizeof(grp_str
)));
246 static int recv_response(int fd
, int *hops
, struct igmp_mtrace
*mtracer
)
249 char mtrace_buf
[IP_AND_MTRACE_BUF_LEN
];
251 struct igmp_mtrace
*mtrace
;
256 recvd
= recvfrom(fd
, mtrace_buf
, IP_AND_MTRACE_BUF_LEN
, 0, NULL
, 0);
259 fprintf(stderr
, "recvfrom error: %s\n", strerror(errno
));
263 if (recvd
< (int)sizeof(struct ip
)) {
264 fprintf(stderr
, "no ip header\n");
268 ip
= (struct ip
*)mtrace_buf
;
271 fprintf(stderr
, "IP not version 4\n");
278 if (sum
!= in_cksum(ip
, ip
->ip_hl
* 4))
281 mtrace
= (struct igmp_mtrace
*)(mtrace_buf
+ (4 * ip
->ip_hl
));
283 mtrace_len
= ntohs(ip
->ip_len
) - ip
->ip_hl
* 4;
285 if (mtrace_len
< (int)MTRACE_HDR_SIZE
)
288 sum
= mtrace
->checksum
;
289 mtrace
->checksum
= 0;
290 if (sum
!= in_cksum(mtrace
, mtrace_len
)) {
291 fprintf(stderr
, "mtrace checksum wrong\n");
295 if (mtrace
->type
!= PIM_IGMP_MTRACE_RESPONSE
)
299 responses
= mtrace_len
- sizeof(struct igmp_mtrace
);
300 responses
/= sizeof(struct igmp_mtrace_rsp
);
302 if (responses
> MTRACE_MAX_HOPS
) {
303 fprintf(stderr
, "mtrace too large\n");
311 memcpy(mtracer
, mtrace
, mtrace_len
);
316 static int wait_for_response(int fd
, int *hops
, struct igmp_mtrace
*mtrace
,
320 struct timeval timeout
;
322 long msec
, rmsec
, tmsec
;
325 FD_SET(fd
, &readfds
);
327 memset(&timeout
, 0, sizeof(timeout
));
329 timeout
.tv_sec
= MTRACE_TIMEOUT
;
331 tmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
333 ret
= select(fd
+ 1, &readfds
, NULL
, NULL
, &timeout
);
336 rmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
337 msec
= tmsec
- rmsec
;
338 } while (recv_response(fd
, hops
, mtrace
) != 0);
346 static bool check_end(struct igmp_mtrace
*mtrace
, int hops
)
348 return mtrace
->src_addr
.s_addr
== mtrace
->rsp
[hops
- 1].prev_hop
.s_addr
;
351 int main(int argc
, char *const argv
[])
353 struct in_addr mc_source
;
354 struct in_addr iface_addr
;
355 struct in_addr gw_addr
;
356 struct in_addr mtrace_addr
;
357 struct igmp_mtrace mtrace
;
358 struct igmp_mtrace
*mtracep
;
371 char ifname
[IF_NAMESIZE
];
372 char mbuf
[MTRACE_BUF_LEN
];
374 mtrace_addr
.s_addr
= inet_addr("224.0.1.32");
376 uid_t uid
= getuid();
379 printf("must run as root\n");
384 progname
= "mtracebis";
394 static struct option long_options
[] = {
395 {"help", no_argument
, 0, 'h'},
396 {"version", no_argument
, 0, 'v'},
398 int option_index
= 0;
400 c
= getopt_long(argc
, argv
, "vh", long_options
, &option_index
);
417 if (inet_pton(AF_INET
, argv
[1], &mc_source
) != 1) {
419 fprintf(stderr
, "%s: %s not a valid IPv4 address\n", argv
[0],
424 ifindex
= routeget(mc_source
, &iface_addr
, &gw_addr
);
426 fprintf(stderr
, "%s: failed to get route to source %s\n",
431 if (if_indextoname(ifindex
, ifname
) == NULL
) {
432 fprintf(stderr
, "%s: if_indextoname error: %s\n", argv
[0],
437 /* zero mtrace struct */
438 memset((char *)&mtrace
, 0, sizeof(mtrace
));
441 mtrace
.type
= PIM_IGMP_MTRACE_QUERY_REQUEST
;
444 mtrace
.grp_addr
.s_addr
= 0;
445 mtrace
.src_addr
= mc_source
;
446 mtrace
.dst_addr
= iface_addr
;
447 mtrace
.rsp_addr
= unicast
? iface_addr
: mtrace_addr
;
448 mtrace
.rsp_ttl
= ttl
;
449 mtrace
.qry_id
= 0xffffff & time(NULL
);
451 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
453 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
456 fprintf(stderr
, "%s: socket error: %s\n", argv
[0],
461 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, ifname
,
465 fprintf(stderr
, "%s: setsockopt error: %s\n", argv
[0],
471 print_query(&mtrace
);
472 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
473 fprintf(stderr
, "%s: sendto error: %s\n", argv
[0],
478 printf("Querying full reverse path...\n");
479 mtracep
= (struct igmp_mtrace
*)mbuf
;
480 ret
= wait_for_response(fd
, &rhops
, mtracep
, &msec
);
482 print_responses(mtracep
, rhops
, msec
);
487 fprintf(stderr
, "%s: select error: %s\n", argv
[0],
493 printf("switching to hop-by-hop:\n");
495 for (i
= 1; i
< maxhops
; i
++) {
498 for (j
= 0; j
< perhop
; j
++) {
501 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
502 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
503 fprintf(stderr
, "%s: sendto error: %s\n",
504 argv
[0], strerror(errno
));
508 ret
= wait_for_response(fd
, &rhops
, mtracep
, &msec
);
510 if (check_end(mtracep
, rhops
)) {
511 print_rsp(&mtracep
->rsp
[rhops
- 1]);
512 print_summary(mtracep
, rhops
, msec
);
517 printf(" * ...giving up.\n");
521 print_rsp(&mtracep
->rsp
[rhops
- 1]);
535 #else /* __linux__ */
540 int main(int argc
, char *argv
[])
542 printf("%s implemented only for GNU/Linux\n", argv
[0]);
546 #endif /* __linux__ */