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>
39 #define MTRACEBIS_VERSION "0.1"
40 #define MTRACE_TIMEOUT (5)
42 #define IP_HDR_LEN (sizeof(struct ip))
44 #define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
45 #define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
47 static const char *progname
;
48 static void usage(void)
50 fprintf(stderr
, "Usage : %s <multicast source>\n", progname
);
52 static void version(void)
54 fprintf(stderr
, "%s %s\n", progname
, MTRACEBIS_VERSION
);
57 static int send_query(int fd
, struct in_addr to_addr
,
58 struct igmp_mtrace
*mtrace
)
60 struct sockaddr_in to
;
64 memset(&to
, 0, sizeof(to
));
65 to
.sin_family
= AF_INET
;
66 to
.sin_addr
= to_addr
;
69 sent
= sendto(fd
, (char *)mtrace
, sizeof(*mtrace
), MSG_DONTWAIT
,
70 (struct sockaddr
*)&to
, tolen
);
77 static void print_query(struct igmp_mtrace
*mtrace
)
79 char src_str
[INET_ADDRSTRLEN
];
80 char dst_str
[INET_ADDRSTRLEN
];
81 char grp_str
[INET_ADDRSTRLEN
];
83 printf("* Mtrace from %s to %s via group %s\n",
84 inet_ntop(AF_INET
, &mtrace
->src_addr
, src_str
, sizeof(src_str
)),
85 inet_ntop(AF_INET
, &mtrace
->dst_addr
, dst_str
, sizeof(dst_str
)),
86 inet_ntop(AF_INET
, &mtrace
->grp_addr
, grp_str
, sizeof(grp_str
)));
89 static int recv_response(int fd
, long msec
, int *hops
)
92 char mtrace_buf
[IP_AND_MTRACE_BUF_LEN
];
94 struct igmp_mtrace
*mtrace
;
100 recvd
= recvfrom(fd
, mtrace_buf
, IP_AND_MTRACE_BUF_LEN
, 0, NULL
, 0);
103 fprintf(stderr
, "recvfrom error: %s\n", strerror(errno
));
107 if (recvd
< (int)sizeof(struct ip
)) {
108 fprintf(stderr
, "no ip header\n");
112 ip
= (struct ip
*)mtrace_buf
;
115 fprintf(stderr
, "IP not version 4\n");
122 if (sum
!= in_cksum(ip
, ip
->ip_hl
* 4))
125 mtrace
= (struct igmp_mtrace
*)(mtrace_buf
+ (4 * ip
->ip_hl
));
127 mtrace_len
= ntohs(ip
->ip_len
) - ip
->ip_hl
* 4;
129 if (mtrace_len
< (int)MTRACE_HDR_SIZE
)
132 sum
= mtrace
->checksum
;
133 mtrace
->checksum
= 0;
134 if (sum
!= in_cksum(mtrace
, mtrace_len
)) {
135 fprintf(stderr
, "mtrace checksum wrong\n");
139 if (mtrace
->type
!= PIM_IGMP_MTRACE_RESPONSE
)
143 responses
= mtrace_len
- sizeof(struct igmp_mtrace
);
144 responses
/= sizeof(struct igmp_mtrace_rsp
);
146 printf("%ld ms received responses from %d hops.\n", msec
, responses
);
151 for (i
= 0; i
< responses
; i
++) {
152 struct igmp_mtrace_rsp
*rsp
= &mtrace
->rsp
[i
];
154 if (rsp
->fwd_code
!= 0)
155 printf("-%d fwd. code 0x%2x.\n", i
, rsp
->fwd_code
);
161 static int wait_for_response(int fd
, int *hops
)
164 struct timeval timeout
;
166 long msec
, rmsec
, tmsec
;
169 FD_SET(fd
, &readfds
);
171 memset(&timeout
, 0, sizeof(timeout
));
173 timeout
.tv_sec
= MTRACE_TIMEOUT
;
175 tmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
177 ret
= select(fd
+ 1, &readfds
, NULL
, NULL
, &timeout
);
180 rmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
181 msec
= tmsec
- rmsec
;
182 } while (recv_response(fd
, msec
, hops
) != 0);
187 int main(int argc
, char *const argv
[])
189 struct in_addr mc_source
;
190 struct in_addr iface_addr
;
191 struct in_addr gw_addr
;
192 struct in_addr mtrace_addr
;
193 struct igmp_mtrace mtrace
;
205 char ifname
[IF_NAMESIZE
];
206 char ip_str
[INET_ADDRSTRLEN
];
208 mtrace_addr
.s_addr
= inet_addr("224.0.1.32");
210 uid_t uid
= getuid();
213 printf("must run as root\n");
218 progname
= "mtracebis";
228 static struct option long_options
[] = {
229 {"help", no_argument
, 0, 'h'},
230 {"version", no_argument
, 0, 'v'},
232 int option_index
= 0;
234 c
= getopt_long(argc
, argv
, "vh", long_options
, &option_index
);
251 if (inet_pton(AF_INET
, argv
[1], &mc_source
) != 1) {
253 fprintf(stderr
, "%s: %s not a valid IPv4 address\n", argv
[0],
258 ifindex
= routeget(mc_source
, &iface_addr
, &gw_addr
);
260 fprintf(stderr
, "%s: failed to get route to source %s\n",
265 if (if_indextoname(ifindex
, ifname
) == NULL
) {
266 fprintf(stderr
, "%s: if_indextoname error: %s\n", argv
[0],
271 /* zero mtrace struct */
272 memset((char *)&mtrace
, 0, sizeof(mtrace
));
275 mtrace
.type
= PIM_IGMP_MTRACE_QUERY_REQUEST
;
278 mtrace
.grp_addr
.s_addr
= 0;
279 mtrace
.src_addr
= mc_source
;
280 mtrace
.dst_addr
= iface_addr
;
281 mtrace
.rsp_addr
= unicast
? iface_addr
: mtrace_addr
;
282 mtrace
.rsp_ttl
= ttl
;
283 mtrace
.qry_id
= 0xffffff & time(NULL
);
285 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
287 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
290 fprintf(stderr
, "%s: socket error: %s\n", argv
[0],
295 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, ifname
,
299 fprintf(stderr
, "%s: setsockopt error: %s\n", argv
[0],
305 print_query(&mtrace
);
306 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
307 fprintf(stderr
, "%s: sendto error: %s\n", argv
[0],
312 printf("Querying full reverse path...\n");
313 ret
= wait_for_response(fd
, NULL
);
319 fprintf(stderr
, "%s: select error: %s\n", argv
[0],
325 printf("switching to hop-by-hop:\n");
326 printf("%3d ? (%s)\n", 0,
327 inet_ntop(AF_INET
, &mtrace
.dst_addr
, ip_str
, sizeof(ip_str
)));
328 for (i
= 1; i
< maxhops
; i
++) {
331 for (j
= 0; j
< perhop
; j
++) {
334 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
335 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
336 fprintf(stderr
, "%s: sendto error: %s\n",
337 argv
[0], strerror(errno
));
341 ret
= wait_for_response(fd
, &rhops
);
360 #else /* __linux__ */
365 int main(int argc
, char *argv
[])
367 printf("%s implemented only for GNU/Linux\n", argv
[0]);
371 #endif /* __linux__ */