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