]> git.proxmox.com Git - mirror_frr.git/blame - pimd/mtracebis.c
Merge pull request #1792 from pguibert6WIND/issue_1786
[mirror_frr.git] / pimd / mtracebis.c
CommitLineData
4d9ad5dc
MS
1/*
2 * Multicast Traceroute for FRRouting
3 * Copyright (C) 2018 Mladen Sablic
4 *
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.
9 *
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.
14 *
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
18 */
19
20#ifdef __linux__
21
22#include "pim_igmp_mtrace.h"
23
24#include "checksum.h"
25#include "mtracebis_routeget.h"
26
27#include <sys/select.h>
28#include <netinet/in.h>
29#include <arpa/inet.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/time.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <time.h>
37#include <net/if.h>
38
39#define MTRACEBIS_VERSION "0.1"
40#define MTRACE_TIMEOUT (5)
41
42#define IP_HDR_LEN (sizeof(struct ip))
43#define IP_RA_LEN (4)
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)
46
47static const char *progname;
48static void usage(void)
49{
50 fprintf(stderr, "Usage : %s <multicast source>\n", progname);
51}
52static void version(void)
53{
54 fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
55}
56
57static int send_query(int fd, struct in_addr to_addr,
58 struct igmp_mtrace *mtrace)
59{
60 struct sockaddr_in to;
61 socklen_t tolen;
62 int sent;
63
64 memset(&to, 0, sizeof(to));
65 to.sin_family = AF_INET;
66 to.sin_addr = to_addr;
67 tolen = sizeof(to);
68
69 sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
70 (struct sockaddr *)&to, tolen);
71
72 if (sent < 1)
73 return -1;
74 return 0;
75}
76
77static void print_query(struct igmp_mtrace *mtrace)
78{
79 char src_str[INET_ADDRSTRLEN];
80 char dst_str[INET_ADDRSTRLEN];
81 char grp_str[INET_ADDRSTRLEN];
82
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)));
87}
88
89static int recv_response(int fd, long msec, int *hops)
90{
91 int recvd;
92 char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
93 struct ip *ip;
94 struct igmp_mtrace *mtrace;
95 int mtrace_len;
96 int responses;
97 int i;
98 u_short sum;
99
100 recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
101
102 if (recvd < 1) {
103 fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
104 return -1;
105 }
106
107 if (recvd < (int)sizeof(struct ip)) {
108 fprintf(stderr, "no ip header\n");
109 return -1;
110 }
111
112 ip = (struct ip *)mtrace_buf;
113
114 if (ip->ip_v != 4) {
115 fprintf(stderr, "IP not version 4\n");
116 return -1;
117 }
118
119 sum = ip->ip_sum;
120 ip->ip_sum = 0;
121
122 if (sum != in_cksum(ip, ip->ip_hl * 4))
123 return -1;
124
125 mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl));
126
127 mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4;
128
129 if (mtrace_len < (int)MTRACE_HDR_SIZE)
130 return -1;
131
132 sum = mtrace->checksum;
133 mtrace->checksum = 0;
134 if (sum != in_cksum(mtrace, mtrace_len)) {
135 fprintf(stderr, "mtrace checksum wrong\n");
136 return -1;
137 }
138
139 if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
140 return -1;
141
142
143 responses = mtrace_len - sizeof(struct igmp_mtrace);
144 responses /= sizeof(struct igmp_mtrace_rsp);
145
146 printf("%ld ms received responses from %d hops.\n", msec, responses);
147
148 if (hops)
149 *hops = responses;
150
151 for (i = 0; i < responses; i++) {
152 struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i];
153
154 if (rsp->fwd_code != 0)
155 printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code);
156 }
157
158 return 0;
159}
160
161static int wait_for_response(int fd, int *hops)
162{
163 fd_set readfds;
164 struct timeval timeout;
165 int ret = -1;
166 long msec, rmsec, tmsec;
167
168 FD_ZERO(&readfds);
169 FD_SET(fd, &readfds);
170
171 memset(&timeout, 0, sizeof(timeout));
172
173 timeout.tv_sec = MTRACE_TIMEOUT;
174
175 tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
176 do {
177 ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
178 if (ret <= 0)
179 return ret;
180 rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
181 msec = tmsec - rmsec;
182 } while (recv_response(fd, msec, hops) != 0);
183
184 return ret;
185}
186
187int main(int argc, char *const argv[])
188{
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;
194 int hops = 255;
195 int rhops;
196 int maxhops = 255;
197 int perhop = 3;
198 int ifindex;
199 int unicast = 1;
200 int ttl = 64;
201 int fd = -1;
202 int ret = -1;
203 int c;
204 int i, j;
205 char ifname[IF_NAMESIZE];
206 char ip_str[INET_ADDRSTRLEN];
207
208 mtrace_addr.s_addr = inet_addr("224.0.1.32");
209
210 uid_t uid = getuid();
211
212 if (uid != 0) {
213 printf("must run as root\n");
214 exit(EXIT_FAILURE);
215 }
216
217 if (argc <= 0)
218 progname = "mtracebis";
219 else
220 progname = argv[0];
221
222 if (argc != 2) {
223 usage();
224 exit(EXIT_FAILURE);
225 }
226
227 while (1) {
228 static struct option long_options[] = {
229 {"help", no_argument, 0, 'h'},
230 {"version", no_argument, 0, 'v'},
231 {0, 0, 0, 0} };
232 int option_index = 0;
233
234 c = getopt_long(argc, argv, "vh", long_options, &option_index);
235
236 if (c == -1)
237 break;
238
239 switch (c) {
240 case 'h':
241 usage();
242 exit(0);
243 case 'v':
244 version();
245 exit(0);
246 default:
247 usage();
248 exit(EXIT_FAILURE);
249 }
250 }
251 if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
252 usage();
253 fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0],
254 argv[1]);
255 exit(EXIT_FAILURE);
256 }
257
258 ifindex = routeget(mc_source, &iface_addr, &gw_addr);
259 if (ifindex < 0) {
260 fprintf(stderr, "%s: failed to get route to source %s\n",
261 argv[0], argv[1]);
262 exit(EXIT_FAILURE);
263 }
264
265 if (if_indextoname(ifindex, ifname) == NULL) {
266 fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
267 strerror(errno));
268 exit(EXIT_FAILURE);
269 }
270
271 /* zero mtrace struct */
272 memset((char *)&mtrace, 0, sizeof(mtrace));
273
274 /* set up query */
275 mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
276 mtrace.hops = hops;
277 mtrace.checksum = 0;
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);
284
285 mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
286
287 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
288
289 if (fd < 1) {
290 fprintf(stderr, "%s: socket error: %s\n", argv[0],
291 strerror(errno));
292 exit(EXIT_FAILURE);
293 }
294
295 ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
296 strlen(ifname));
297
298 if (ret < 0) {
299 fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
300 strerror(errno));
301 ret = EXIT_FAILURE;
302 goto close_fd;
303 }
304
305 print_query(&mtrace);
306 if (send_query(fd, gw_addr, &mtrace) < 0) {
307 fprintf(stderr, "%s: sendto error: %s\n", argv[0],
308 strerror(errno));
309 ret = EXIT_FAILURE;
310 goto close_fd;
311 }
312 printf("Querying full reverse path...\n");
313 ret = wait_for_response(fd, NULL);
314 if (ret > 0) {
315 ret = 0;
316 goto close_fd;
317 }
318 if (ret < 0) {
319 fprintf(stderr, "%s: select error: %s\n", argv[0],
320 strerror(errno));
321 ret = EXIT_FAILURE;
322 goto close_fd;
323 }
324 printf(" * ");
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++) {
329 printf("%3d ", -i);
330 mtrace.hops = i;
331 for (j = 0; j < perhop; j++) {
332 mtrace.qry_id++;
333 mtrace.checksum = 0;
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));
338 ret = EXIT_FAILURE;
339 goto close_fd;
340 }
341 ret = wait_for_response(fd, &rhops);
342 if (ret > 0) {
343 if (i > rhops) {
344 ret = 0;
345 goto close_fd;
346 }
347 break;
348 }
349 printf(" *");
350 }
351 if (ret <= 0)
352 printf("\n");
353 }
354 ret = 0;
355close_fd:
356 close(fd);
357 exit(ret);
358}
359
360#else /* __linux__ */
361
362#include <stdio.h>
363#include <stdlib.h>
364
365int main(int argc, char *argv[])
366{
367 printf("%s implemented only for GNU/Linux\n", argv[0]);
368 exit(0);
369}
370
371#endif /* __linux__ */