]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmp_mtrace.c
Merge pull request #11090 from ton31337/fix/plist_alist_duplicate
[mirror_frr.git] / pimd / pim_igmp_mtrace.c
1 /*
2 * Multicast traceroute for FRRouting
3 * Copyright (C) 2017 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 /* based on draft-ietf-idmr-traceroute-ipm-07 */
21
22 #include <zebra.h>
23
24 #include "pimd.h"
25 #include "pim_util.h"
26 #include "pim_sock.h"
27 #include "pim_rp.h"
28 #include "pim_oil.h"
29 #include "pim_ifchannel.h"
30 #include "pim_macro.h"
31 #include "pim_igmp_mtrace.h"
32
33 static struct in_addr mtrace_primary_address(struct interface *ifp)
34 {
35 struct connected *ifc;
36 struct listnode *node;
37 struct in_addr any;
38 struct pim_interface *pim_ifp;
39
40 if (ifp->info) {
41 pim_ifp = ifp->info;
42 return pim_ifp->primary_address;
43 }
44
45 any.s_addr = INADDR_ANY;
46
47 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
48 struct prefix *p = ifc->address;
49
50 if (p->family != AF_INET)
51 continue;
52
53 if (!CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
54 return p->u.prefix4;
55 /* in case no primary found, return a secondary */
56 any = p->u.prefix4;
57 }
58 return any;
59 }
60
61 static bool mtrace_fwd_info_weak(struct pim_instance *pim,
62 struct igmp_mtrace *mtracep,
63 struct igmp_mtrace_rsp *rspp,
64 struct interface **ifpp)
65 {
66 struct pim_nexthop nexthop;
67 struct interface *ifp_in;
68 struct in_addr nh_addr;
69
70 nh_addr.s_addr = INADDR_ANY;
71
72 memset(&nexthop, 0, sizeof(nexthop));
73
74 if (!pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1)) {
75 if (PIM_DEBUG_MTRACE)
76 zlog_debug("mtrace not found neighbor");
77 return false;
78 }
79
80 if (PIM_DEBUG_MTRACE)
81 zlog_debug("mtrace pim_nexthop_lookup OK");
82
83 if (PIM_DEBUG_MTRACE)
84 zlog_debug("mtrace next_hop=%pPAs", &nexthop.mrib_nexthop_addr);
85
86 nh_addr = nexthop.mrib_nexthop_addr;
87
88 ifp_in = nexthop.interface;
89
90 /* return interface for forwarding mtrace packets */
91 *ifpp = ifp_in;
92
93 /* 6.2.2. 4. Fill in the Incoming Interface Address... */
94 rspp->incoming = mtrace_primary_address(ifp_in);
95 rspp->prev_hop = nh_addr;
96 rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
97 rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
98 rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
99 return true;
100 }
101
102 static bool mtrace_fwd_info(struct pim_instance *pim,
103 struct igmp_mtrace *mtracep,
104 struct igmp_mtrace_rsp *rspp,
105 struct interface **ifpp)
106 {
107 pim_sgaddr sg;
108 struct pim_upstream *up;
109 struct interface *ifp_in;
110 struct in_addr nh_addr;
111 uint32_t total;
112
113 memset(&sg, 0, sizeof(sg));
114 sg.src = mtracep->src_addr;
115 sg.grp = mtracep->grp_addr;
116
117 up = pim_upstream_find(pim, &sg);
118
119 if (!up) {
120 sg.src = PIMADDR_ANY;
121 up = pim_upstream_find(pim, &sg);
122 }
123
124 if (!up)
125 return false;
126
127 if (!up->rpf.source_nexthop.interface) {
128 if (PIM_DEBUG_TRACE)
129 zlog_debug("%s: up %s RPF is not present", __func__,
130 up->sg_str);
131 return false;
132 }
133
134 ifp_in = up->rpf.source_nexthop.interface;
135 nh_addr = up->rpf.source_nexthop.mrib_nexthop_addr;
136 total = htonl(MTRACE_UNKNOWN_COUNT);
137
138 if (PIM_DEBUG_MTRACE)
139 zlog_debug("fwd_info: upstream next hop=%pI4", &nh_addr);
140
141 if (up->channel_oil)
142 total = up->channel_oil->cc.pktcnt;
143
144 /* return interface for forwarding mtrace packets */
145 *ifpp = ifp_in;
146
147 /* 6.2.2. 4. Fill in the Incoming Interface Address... */
148 rspp->incoming = mtrace_primary_address(ifp_in);
149 rspp->prev_hop = nh_addr;
150 rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
151 rspp->total = total;
152 rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
153
154 /* 6.2.2. 4. Fill in ... S, and Src Mask */
155 if (!pim_addr_is_any(sg.src)) {
156 rspp->s = 1;
157 rspp->src_mask = MTRACE_SRC_MASK_SOURCE;
158 } else {
159 rspp->s = 0;
160 rspp->src_mask = MTRACE_SRC_MASK_GROUP;
161 }
162
163 return true;
164 }
165
166 static void mtrace_rsp_set_fwd_code(struct igmp_mtrace_rsp *mtrace_rspp,
167 enum mtrace_fwd_code fwd_code)
168 {
169 if (mtrace_rspp->fwd_code == MTRACE_FWD_CODE_NO_ERROR)
170 mtrace_rspp->fwd_code = fwd_code;
171 }
172
173 static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
174 {
175 mtrace_rspp->arrival = 0;
176 mtrace_rspp->incoming.s_addr = INADDR_ANY;
177 mtrace_rspp->outgoing.s_addr = INADDR_ANY;
178 mtrace_rspp->prev_hop.s_addr = INADDR_ANY;
179 mtrace_rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
180 mtrace_rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
181 mtrace_rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
182 mtrace_rspp->rtg_proto = 0;
183 mtrace_rspp->fwd_ttl = 0;
184 mtrace_rspp->mbz = 0;
185 mtrace_rspp->s = 0;
186 mtrace_rspp->src_mask = 0;
187 mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
188 }
189
190 static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
191 struct igmp_mtrace_rsp *mrspp)
192 {
193 struct in_addr incoming = mrspp->incoming;
194 struct in_addr outgoing = mrspp->outgoing;
195 struct in_addr prev_hop = mrspp->prev_hop;
196
197 zlog_debug(
198 "Rx mt(%d) qid=%ud arr=%x in=%pI4 out=%pI4 prev=%pI4 proto=%d fwd=%d",
199 rsp, ntohl(qry_id), mrspp->arrival, &incoming, &outgoing,
200 &prev_hop, mrspp->rtg_proto, mrspp->fwd_code);
201 }
202
203 static void mtrace_debug(struct pim_interface *pim_ifp,
204 struct igmp_mtrace *mtracep, int mtrace_len)
205 {
206 struct in_addr ga, sa, da, ra;
207
208 ga = mtracep->grp_addr;
209 sa = mtracep->src_addr;
210 da = mtracep->dst_addr;
211 ra = mtracep->rsp_addr;
212
213 zlog_debug(
214 "Rx mtrace packet incoming on %pI4: hops=%d type=%d size=%d, grp=%pI4, src=%pI4, dst=%pI4 rsp=%pI4 ttl=%d qid=%ud",
215 &pim_ifp->primary_address, mtracep->hops, mtracep->type,
216 mtrace_len, &ga, &sa, &da, &ra, mtracep->rsp_ttl,
217 ntohl(mtracep->qry_id));
218 if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
219
220 int i;
221
222 int responses = mtrace_len - sizeof(struct igmp_mtrace);
223
224 if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
225 if (PIM_DEBUG_MTRACE)
226 zlog_debug(
227 "Mtrace response block of wrong length");
228
229 responses = responses / sizeof(struct igmp_mtrace_rsp);
230
231 for (i = 0; i < responses; i++)
232 mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
233 }
234 }
235
236 /* 5.1 Query Arrival Time */
237 static uint32_t query_arrival_time(void)
238 {
239 struct timeval tv;
240 uint32_t qat;
241
242 if (gettimeofday(&tv, NULL) < 0) {
243 if (PIM_DEBUG_MTRACE)
244 zlog_debug("Query arrival time lookup failed: errno=%d: %s",
245 errno, safe_strerror(errno));
246 return 0;
247 }
248 /* not sure second offset correct, as I get different value */
249 qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
250
251 return qat;
252 }
253
254 static int mtrace_send_packet(struct interface *ifp,
255 struct igmp_mtrace *mtracep,
256 size_t mtrace_buf_len, struct in_addr dst_addr,
257 struct in_addr group_addr)
258 {
259 struct sockaddr_in to;
260 socklen_t tolen;
261 ssize_t sent;
262 int ret;
263 int fd;
264 uint8_t ttl;
265
266 memset(&to, 0, sizeof(to));
267 to.sin_family = AF_INET;
268 to.sin_addr = dst_addr;
269 tolen = sizeof(to);
270
271 if (PIM_DEBUG_MTRACE) {
272 struct in_addr if_addr;
273 struct in_addr rsp_addr = mtracep->rsp_addr;
274
275 if_addr = mtrace_primary_address(ifp);
276 zlog_debug("Sending mtrace packet to %pI4 on %pI4", &rsp_addr,
277 &if_addr);
278 }
279
280 fd = pim_socket_raw(IPPROTO_IGMP);
281
282 if (fd < 0)
283 return -1;
284
285 ret = pim_socket_bind(fd, ifp);
286
287 if (ret < 0) {
288 ret = -1;
289 goto close_fd;
290 }
291
292 if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
293 if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
294 ttl = 1;
295 } else {
296 if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
297 ttl = mtracep->rsp_ttl;
298 else
299 ttl = 64;
300 }
301 ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
302 sizeof(ttl));
303
304 if (ret < 0) {
305 if (PIM_DEBUG_MTRACE)
306 zlog_debug("Failed to set socket multicast TTL");
307 ret = -1;
308 goto close_fd;
309 }
310 }
311
312 sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
313 (struct sockaddr *)&to, tolen);
314
315 if (sent != (ssize_t)mtrace_buf_len) {
316 char dst_str[INET_ADDRSTRLEN];
317 char group_str[INET_ADDRSTRLEN];
318
319 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
320 pim_inet4_dump("<group?>", group_addr, group_str,
321 sizeof(group_str));
322 if (sent < 0) {
323 if (PIM_DEBUG_MTRACE)
324 zlog_debug(
325 "Send mtrace request failed for %s on%s: group=%s msg_size=%zd: errno=%d: %s",
326 dst_str, ifp->name, group_str,
327 mtrace_buf_len, errno,
328 safe_strerror(errno));
329 } else {
330 if (PIM_DEBUG_MTRACE)
331 zlog_debug(
332 "Send mtrace request failed for %s on %s: group=%s msg_size=%zd: sent=%zd",
333 dst_str, ifp->name, group_str,
334 mtrace_buf_len, sent);
335 }
336 ret = -1;
337 goto close_fd;
338 }
339 ret = 0;
340 close_fd:
341 close(fd);
342 return ret;
343 }
344
345 static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
346 struct interface *interface)
347 {
348 struct pim_nexthop nexthop;
349 struct sockaddr_in to;
350 struct interface *if_out;
351 socklen_t tolen;
352 int ret;
353 int fd;
354 int sent;
355 uint16_t checksum;
356
357 checksum = ip_hdr->ip_sum;
358
359 ip_hdr->ip_sum = 0;
360
361 if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
362 return -1;
363
364 if (ip_hdr->ip_ttl-- <= 1)
365 return -1;
366
367 ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
368
369 fd = pim_socket_raw(IPPROTO_RAW);
370
371 if (fd < 0)
372 return -1;
373
374 pim_socket_ip_hdr(fd);
375
376 if (interface == NULL) {
377 memset(&nexthop, 0, sizeof(nexthop));
378 if (!pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0)) {
379 close(fd);
380 if (PIM_DEBUG_MTRACE)
381 zlog_debug(
382 "Dropping mtrace packet, no route to destination");
383 return -1;
384 }
385
386 if_out = nexthop.interface;
387 } else {
388 if_out = interface;
389 }
390
391 ret = pim_socket_bind(fd, if_out);
392
393 if (ret < 0) {
394 close(fd);
395 return -1;
396 }
397
398 memset(&to, 0, sizeof(to));
399 to.sin_family = AF_INET;
400 to.sin_addr = ip_hdr->ip_dst;
401 tolen = sizeof(to);
402
403 sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
404 (struct sockaddr *)&to, tolen);
405
406 close(fd);
407
408 if (sent < 0) {
409 if (PIM_DEBUG_MTRACE)
410 zlog_debug(
411 "Failed to forward mtrace packet: sendto errno=%d, %s",
412 errno, safe_strerror(errno));
413 return -1;
414 }
415
416 if (PIM_DEBUG_MTRACE) {
417 zlog_debug("Fwd mtrace packet len=%u to %pI4 ttl=%u",
418 ntohs(ip_hdr->ip_len), &ip_hdr->ip_dst,
419 ip_hdr->ip_ttl);
420 }
421
422 return 0;
423 }
424
425 static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
426 {
427 pim_sgaddr sg;
428 struct channel_oil *c_oil;
429 struct listnode *chnode;
430 struct listnode *chnextnode;
431 struct pim_ifchannel *ch = NULL;
432 int ret = -1;
433
434 memset(&sg, 0, sizeof(sg));
435 sg.grp = ip_hdr->ip_dst;
436
437 c_oil = pim_find_channel_oil(pim, &sg);
438
439 if (c_oil == NULL) {
440 if (PIM_DEBUG_MTRACE) {
441 zlog_debug(
442 "Dropping mtrace multicast packet len=%u to %pI4 ttl=%u",
443 ntohs(ip_hdr->ip_len),
444 &ip_hdr->ip_dst, ip_hdr->ip_ttl);
445 }
446 return -1;
447 }
448 if (c_oil->up == NULL)
449 return -1;
450 if (c_oil->up->ifchannels == NULL)
451 return -1;
452 for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
453 if (pim_macro_chisin_oiflist(ch)) {
454 int r;
455
456 r = mtrace_un_forward_packet(pim, ip_hdr,
457 ch->interface);
458 if (r == 0)
459 ret = 0;
460 }
461 }
462 return ret;
463 }
464
465
466 static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
467 {
468 if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
469 return mtrace_mc_forward_packet(pim, ip_hdr);
470 else
471 return mtrace_un_forward_packet(pim, ip_hdr, NULL);
472 }
473
474 static int mtrace_send_mc_response(struct pim_instance *pim,
475 struct igmp_mtrace *mtracep,
476 size_t mtrace_len)
477 {
478 pim_sgaddr sg;
479 struct channel_oil *c_oil;
480 struct listnode *chnode;
481 struct listnode *chnextnode;
482 struct pim_ifchannel *ch = NULL;
483 int ret = -1;
484
485 memset(&sg, 0, sizeof(sg));
486 sg.grp = mtracep->rsp_addr;
487
488 c_oil = pim_find_channel_oil(pim, &sg);
489
490 if (c_oil == NULL) {
491 if (PIM_DEBUG_MTRACE) {
492 struct in_addr rsp_addr = mtracep->rsp_addr;
493
494 zlog_debug(
495 "Dropping mtrace multicast response packet len=%u to %pI4",
496 (unsigned int)mtrace_len, &rsp_addr);
497 }
498 return -1;
499 }
500 if (c_oil->up == NULL)
501 return -1;
502 if (c_oil->up->ifchannels == NULL)
503 return -1;
504 for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
505 if (pim_macro_chisin_oiflist(ch)) {
506 int r;
507
508 r = mtrace_send_packet(ch->interface, mtracep,
509 mtrace_len, mtracep->rsp_addr,
510 mtracep->grp_addr);
511 if (r == 0)
512 ret = 0;
513 }
514 }
515 return ret;
516 }
517
518 /* 6.5 Sending Traceroute Responses */
519 static int mtrace_send_response(struct pim_instance *pim,
520 struct igmp_mtrace *mtracep, size_t mtrace_len)
521 {
522 struct pim_nexthop nexthop;
523
524 mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
525
526 mtracep->checksum = 0;
527 mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
528
529 if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
530 struct pim_rpf *p_rpf;
531
532 if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
533 return mtrace_send_mc_response(pim, mtracep,
534 mtrace_len);
535
536 p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
537
538 if (p_rpf == NULL) {
539 if (PIM_DEBUG_MTRACE) {
540 struct in_addr rsp_addr = mtracep->rsp_addr;
541
542 zlog_debug("mtrace no RP for %pI4", &rsp_addr);
543 }
544 return -1;
545 }
546 nexthop = p_rpf->source_nexthop;
547 if (PIM_DEBUG_MTRACE)
548 zlog_debug("mtrace response to RP");
549 } else {
550 memset(&nexthop, 0, sizeof(nexthop));
551 /* TODO: should use unicast rib lookup */
552 if (!pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1)) {
553 if (PIM_DEBUG_MTRACE)
554 zlog_debug(
555 "Dropped response qid=%ud, no route to response address",
556 mtracep->qry_id);
557 return -1;
558 }
559 }
560
561 return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
562 mtracep->rsp_addr, mtracep->grp_addr);
563 }
564
565 int igmp_mtrace_recv_qry_req(struct gm_sock *igmp, struct ip *ip_hdr,
566 struct in_addr from, const char *from_str,
567 char *igmp_msg, int igmp_msg_len)
568 {
569 static uint32_t qry_id, qry_src;
570 char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
571 struct interface *ifp;
572 struct interface *out_ifp = NULL;
573 struct pim_interface *pim_ifp;
574 struct pim_instance *pim;
575 struct igmp_mtrace *mtracep;
576 struct igmp_mtrace_rsp *rspp;
577 struct in_addr nh_addr;
578 enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
579 size_t r_len;
580 int last_rsp_ind = 0;
581 size_t mtrace_len;
582 uint16_t recv_checksum;
583 uint16_t checksum;
584 bool reached_source;
585 bool fwd_info;
586
587 ifp = igmp->interface;
588 pim_ifp = ifp->info;
589 pim = pim_ifp->pim;
590
591 /*
592 * 6. Router Behaviour
593 * Check if mtrace packet is addressed elsewhere and forward,
594 * if applicable
595 */
596 if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
597 if (!if_address_is_local(&ip_hdr->ip_dst, AF_INET,
598 pim->vrf->vrf_id))
599 return mtrace_forward_packet(pim, ip_hdr);
600
601 if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
602 if (PIM_DEBUG_MTRACE)
603 zlog_debug(
604 "Recv mtrace packet from %s on %s: too short, len=%d, min=%zu",
605 from_str, ifp->name, igmp_msg_len,
606 sizeof(struct igmp_mtrace));
607 return -1;
608 }
609
610 mtracep = (struct igmp_mtrace *)igmp_msg;
611
612 recv_checksum = mtracep->checksum;
613
614 mtracep->checksum = 0;
615
616 checksum = in_cksum(igmp_msg, igmp_msg_len);
617
618 if (recv_checksum != checksum) {
619 if (PIM_DEBUG_MTRACE)
620 zlog_debug(
621 "Recv mtrace packet from %s on %s: checksum mismatch: received=%x computed=%x",
622 from_str, ifp->name, recv_checksum, checksum);
623 return -1;
624 }
625
626 /* Collecting IGMP Rx stats */
627 igmp->igmp_stats.mtrace_req++;
628
629 if (PIM_DEBUG_MTRACE)
630 mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
631
632 /* subtract header from message length */
633 r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
634
635 /* Classify mtrace packet, check if it is a query */
636 if (!r_len) {
637 if (PIM_DEBUG_MTRACE)
638 zlog_debug("Received IGMP multicast traceroute query");
639
640 /* 6.1.1 Packet verification */
641 if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
642 if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
643 if (PIM_DEBUG_MTRACE)
644 zlog_debug(
645 "Dropping multicast query on wrong interface");
646 return -1;
647 }
648 /* Unicast query on wrong interface */
649 fwd_code = MTRACE_FWD_CODE_WRONG_IF;
650 if (PIM_DEBUG_MTRACE)
651 zlog_debug("Multicast query on wrong interface");
652 }
653 if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
654 if (PIM_DEBUG_MTRACE)
655 zlog_debug(
656 "Dropping multicast query with duplicate source and id");
657 return -1;
658 }
659 qry_id = mtracep->qry_id;
660 qry_src = from.s_addr;
661 }
662 /* if response fields length is equal to a whole number of responses */
663 else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
664 r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
665
666 if (r_len != 0)
667 last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
668 if (last_rsp_ind > MTRACE_MAX_HOPS) {
669 if (PIM_DEBUG_MTRACE)
670 zlog_debug("Mtrace request of excessive size");
671 return -1;
672 }
673 } else {
674 if (PIM_DEBUG_MTRACE)
675 zlog_debug(
676 "Recv mtrace packet from %s on %s: invalid length %d",
677 from_str, ifp->name, igmp_msg_len);
678 return -1;
679 }
680
681 /* 6.2.1 Packet Verification - drop not link-local multicast */
682 if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
683 && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
684 if (PIM_DEBUG_MTRACE)
685 zlog_debug(
686 "Recv mtrace packet from %s on %s: not link-local multicast %pI4",
687 from_str, ifp->name, &ip_hdr->ip_dst);
688 return -1;
689 }
690
691 /* 6.2.2. Normal Processing */
692
693 /* 6.2.2. 1. If there is room in the current buffer? */
694
695 if (last_rsp_ind == MTRACE_MAX_HOPS) {
696 /* ...there was no room... */
697 mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
698 MTRACE_FWD_CODE_NO_SPACE;
699 return mtrace_send_response(pim_ifp->pim, mtracep,
700 igmp_msg_len);
701 }
702
703 /* ...insert new response block... */
704
705 /* calculate new mtrace lenght with extra response */
706 mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
707
708 /* copy received query/request */
709 memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
710
711 /* repoint mtracep pointer to copy */
712 mtracep = (struct igmp_mtrace *)mtrace_buf;
713
714 /* pointer for extra response field to be filled in */
715 rspp = &mtracep->rsp[last_rsp_ind];
716
717 /* initialize extra response field */
718 mtrace_rsp_init(rspp);
719
720 /* carry over any error noted when receiving the query */
721 rspp->fwd_code = fwd_code;
722
723 /* ...and fill in Query Arrival Time... */
724 rspp->arrival = htonl(query_arrival_time());
725 rspp->outgoing = pim_ifp->primary_address;
726 rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
727 rspp->fwd_ttl = 1;
728
729 /* 6.2.2. 2. Attempt to determine the forwarding information... */
730
731 if (mtracep->grp_addr.s_addr != INADDR_ANY)
732 fwd_info = mtrace_fwd_info(pim, mtracep, rspp, &out_ifp);
733 else
734 fwd_info = mtrace_fwd_info_weak(pim, mtracep, rspp, &out_ifp);
735
736 /* 6.2.2 3. If no forwarding information... */
737 if (!fwd_info) {
738 if (PIM_DEBUG_MTRACE)
739 zlog_debug("mtrace not found multicast state");
740 mtrace_rsp_set_fwd_code(rspp, MTRACE_FWD_CODE_NO_ROUTE);
741 /* 6.2.2. 3. forward the packet to requester */
742 return mtrace_send_response(pim, mtracep, mtrace_len);
743 }
744
745 nh_addr = rspp->prev_hop;
746
747 reached_source = false;
748
749 if (nh_addr.s_addr == INADDR_ANY) {
750 /* no pim? i.e. 7.5.3. No Previous Hop */
751 if (!out_ifp->info) {
752 if (PIM_DEBUG_MTRACE)
753 zlog_debug("mtrace not found incoming if w/ pim");
754 mtrace_rsp_set_fwd_code(rspp,
755 MTRACE_FWD_CODE_NO_MULTICAST);
756 return mtrace_send_response(pim, mtracep, mtrace_len);
757 }
758 /* reached source? i.e. 7.5.1 Arriving at source */
759 if (pim_if_connected_to_source(out_ifp, mtracep->src_addr)) {
760 reached_source = true;
761 rspp->prev_hop = mtracep->src_addr;
762 }
763 /*
764 * 6.4 Forwarding Traceroute Requests:
765 * Previous-hop router not known,
766 * packet is sent to an appropriate multicast address
767 */
768 (void)inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
769 }
770
771 /* 6.2.2 8. If this router is the Rendez-vous Point */
772 if (pim_rp_i_am_rp(pim, mtracep->grp_addr)) {
773 mtrace_rsp_set_fwd_code(rspp, MTRACE_FWD_CODE_REACHED_RP);
774 /* 7.7.1. PIM-SM ...RP has not performed source-specific join */
775 if (rspp->src_mask == MTRACE_SRC_MASK_GROUP)
776 return mtrace_send_response(pim, mtracep, mtrace_len);
777 }
778
779 /*
780 * 6.4 Forwarding Traceroute Requests: the number of response
781 * blocks exceeds number of responses, so forward to the requester.
782 */
783 if (mtracep->hops <= (last_rsp_ind + 1))
784 return mtrace_send_response(pim, mtracep, mtrace_len);
785
786 /* 7.5.1. Arriving at source: terminate trace */
787 if (reached_source)
788 return mtrace_send_response(pim, mtracep, mtrace_len);
789
790 mtracep->checksum = 0;
791
792 mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
793
794 /* 6.4 Forwarding Traceroute Requests: response blocks less than req. */
795 return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
796 mtracep->grp_addr);
797 }
798
799 /* 6.3. Traceroute responses */
800 int igmp_mtrace_recv_response(struct gm_sock *igmp, struct ip *ip_hdr,
801 struct in_addr from, const char *from_str,
802 char *igmp_msg, int igmp_msg_len)
803 {
804 static uint32_t qry_id, rsp_dst;
805 struct interface *ifp;
806 struct pim_interface *pim_ifp;
807 struct pim_instance *pim;
808 struct igmp_mtrace *mtracep;
809 uint16_t recv_checksum;
810 uint16_t checksum;
811
812 ifp = igmp->interface;
813 pim_ifp = ifp->info;
814 pim = pim_ifp->pim;
815
816 if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
817 if (PIM_DEBUG_MTRACE)
818 zlog_debug(
819 "Recv mtrace packet from %s on %s: too short, len=%d, min=%zu",
820 from_str, ifp->name, igmp_msg_len,
821 sizeof(struct igmp_mtrace));
822 return -1;
823 }
824
825 mtracep = (struct igmp_mtrace *)igmp_msg;
826
827 recv_checksum = mtracep->checksum;
828
829 mtracep->checksum = 0;
830
831 checksum = in_cksum(igmp_msg, igmp_msg_len);
832
833 if (recv_checksum != checksum) {
834 if (PIM_DEBUG_MTRACE)
835 zlog_debug(
836 "Recv mtrace response from %s on %s: checksum mismatch: received=%x computed=%x",
837 from_str, ifp->name, recv_checksum, checksum);
838 return -1;
839 }
840
841 mtracep->checksum = checksum;
842
843 /* Collecting IGMP Rx stats */
844 igmp->igmp_stats.mtrace_rsp++;
845
846 if (PIM_DEBUG_MTRACE)
847 mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
848
849 /* Drop duplicate packets */
850 if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
851 if (PIM_DEBUG_MTRACE)
852 zlog_debug("duplicate mtrace response packet dropped");
853 return -1;
854 }
855
856 qry_id = mtracep->qry_id;
857 rsp_dst = ip_hdr->ip_dst.s_addr;
858
859 return mtrace_forward_packet(pim, ip_hdr);
860 }