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>
41 #define MTRACEBIS_VERSION "0.1"
42 #define MTRACE_TIMEOUT (5)
44 #define IP_HDR_LEN (sizeof(struct ip))
46 #define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
47 #define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
49 static const char *progname
;
50 static void usage(void)
52 fprintf(stderr
, "Usage : %s <multicast source>\n", progname
);
54 static void version(void)
56 fprintf(stderr
, "%s %s\n", progname
, MTRACEBIS_VERSION
);
59 static int send_query(int fd
, struct in_addr to_addr
,
60 struct igmp_mtrace
*mtrace
)
62 struct sockaddr_in to
;
66 memset(&to
, 0, sizeof(to
));
67 to
.sin_family
= AF_INET
;
68 to
.sin_addr
= to_addr
;
71 sent
= sendto(fd
, (char *)mtrace
, sizeof(*mtrace
), MSG_DONTWAIT
,
72 (struct sockaddr
*)&to
, tolen
);
79 static void print_query(struct igmp_mtrace
*mtrace
)
81 char src_str
[INET_ADDRSTRLEN
];
82 char dst_str
[INET_ADDRSTRLEN
];
83 char grp_str
[INET_ADDRSTRLEN
];
85 printf("* Mtrace from %s to %s via group %s\n",
86 inet_ntop(AF_INET
, &mtrace
->src_addr
, src_str
, sizeof(src_str
)),
87 inet_ntop(AF_INET
, &mtrace
->dst_addr
, dst_str
, sizeof(dst_str
)),
88 inet_ntop(AF_INET
, &mtrace
->grp_addr
, grp_str
, sizeof(grp_str
)));
91 static int recv_response(int fd
, long msec
, int *hops
)
94 char mtrace_buf
[IP_AND_MTRACE_BUF_LEN
];
96 struct igmp_mtrace
*mtrace
;
102 recvd
= recvfrom(fd
, mtrace_buf
, IP_AND_MTRACE_BUF_LEN
, 0, NULL
, 0);
105 fprintf(stderr
, "recvfrom error: %s\n", strerror(errno
));
109 if (recvd
< (int)sizeof(struct ip
)) {
110 fprintf(stderr
, "no ip header\n");
114 ip
= (struct ip
*)mtrace_buf
;
117 fprintf(stderr
, "IP not version 4\n");
124 if (sum
!= in_cksum(ip
, ip
->ip_hl
* 4))
127 mtrace
= (struct igmp_mtrace
*)(mtrace_buf
+ (4 * ip
->ip_hl
));
129 mtrace_len
= ntohs(ip
->ip_len
) - ip
->ip_hl
* 4;
131 if (mtrace_len
< (int)MTRACE_HDR_SIZE
)
134 sum
= mtrace
->checksum
;
135 mtrace
->checksum
= 0;
136 if (sum
!= in_cksum(mtrace
, mtrace_len
)) {
137 fprintf(stderr
, "mtrace checksum wrong\n");
141 if (mtrace
->type
!= PIM_IGMP_MTRACE_RESPONSE
)
145 responses
= mtrace_len
- sizeof(struct igmp_mtrace
);
146 responses
/= sizeof(struct igmp_mtrace_rsp
);
148 printf("%ld ms received responses from %d hops.\n", msec
, responses
);
153 for (i
= 0; i
< responses
; i
++) {
154 struct igmp_mtrace_rsp
*rsp
= &mtrace
->rsp
[i
];
156 if (rsp
->fwd_code
!= 0)
157 printf("-%d fwd. code 0x%2x.\n", i
, rsp
->fwd_code
);
163 static int wait_for_response(int fd
, int *hops
)
166 struct timeval timeout
;
168 long msec
, rmsec
, tmsec
;
171 FD_SET(fd
, &readfds
);
173 memset(&timeout
, 0, sizeof(timeout
));
175 timeout
.tv_sec
= MTRACE_TIMEOUT
;
177 tmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
179 ret
= select(fd
+ 1, &readfds
, NULL
, NULL
, &timeout
);
182 rmsec
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
183 msec
= tmsec
- rmsec
;
184 } while (recv_response(fd
, msec
, hops
) != 0);
189 int main(int argc
, char *const argv
[])
191 struct in_addr mc_source
;
192 struct in_addr iface_addr
;
193 struct in_addr gw_addr
;
194 struct in_addr mtrace_addr
;
195 struct igmp_mtrace mtrace
;
207 char ifname
[IF_NAMESIZE
];
208 char ip_str
[INET_ADDRSTRLEN
];
210 mtrace_addr
.s_addr
= inet_addr("224.0.1.32");
212 uid_t uid
= getuid();
215 printf("must run as root\n");
220 progname
= "mtracebis";
230 static struct option long_options
[] = {
231 {"help", no_argument
, 0, 'h'},
232 {"version", no_argument
, 0, 'v'},
234 int option_index
= 0;
236 c
= getopt_long(argc
, argv
, "vh", long_options
, &option_index
);
253 if (inet_pton(AF_INET
, argv
[1], &mc_source
) != 1) {
255 fprintf(stderr
, "%s: %s not a valid IPv4 address\n", argv
[0],
260 ifindex
= routeget(mc_source
, &iface_addr
, &gw_addr
);
262 fprintf(stderr
, "%s: failed to get route to source %s\n",
267 if (if_indextoname(ifindex
, ifname
) == NULL
) {
268 fprintf(stderr
, "%s: if_indextoname error: %s\n", argv
[0],
273 /* zero mtrace struct */
274 memset((char *)&mtrace
, 0, sizeof(mtrace
));
277 mtrace
.type
= PIM_IGMP_MTRACE_QUERY_REQUEST
;
280 mtrace
.grp_addr
.s_addr
= 0;
281 mtrace
.src_addr
= mc_source
;
282 mtrace
.dst_addr
= iface_addr
;
283 mtrace
.rsp_addr
= unicast
? iface_addr
: mtrace_addr
;
284 mtrace
.rsp_ttl
= ttl
;
285 mtrace
.qry_id
= 0xffffff & time(NULL
);
287 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
289 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
292 fprintf(stderr
, "%s: socket error: %s\n", argv
[0],
297 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, ifname
,
301 fprintf(stderr
, "%s: setsockopt error: %s\n", argv
[0],
307 print_query(&mtrace
);
308 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
309 fprintf(stderr
, "%s: sendto error: %s\n", argv
[0],
314 printf("Querying full reverse path...\n");
315 ret
= wait_for_response(fd
, NULL
);
321 fprintf(stderr
, "%s: select error: %s\n", argv
[0],
327 printf("switching to hop-by-hop:\n");
328 printf("%3d ? (%s)\n", 0,
329 inet_ntop(AF_INET
, &mtrace
.dst_addr
, ip_str
, sizeof(ip_str
)));
330 for (i
= 1; i
< maxhops
; i
++) {
333 for (j
= 0; j
< perhop
; j
++) {
336 mtrace
.checksum
= in_cksum(&mtrace
, sizeof(mtrace
));
337 if (send_query(fd
, gw_addr
, &mtrace
) < 0) {
338 fprintf(stderr
, "%s: sendto error: %s\n",
339 argv
[0], strerror(errno
));
343 ret
= wait_for_response(fd
, &rhops
);
362 #else /* __linux__ */
367 int main(int argc
, char *argv
[])
369 printf("%s implemented only for GNU/Linux\n", argv
[0]);
373 #endif /* __linux__ */