]>
git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmp_mtrace.c
2 * Multicast traceroute for FRRouting
3 * Copyright (C) 2017 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
27 #include "pim_ifchannel.h"
28 #include "pim_macro.h"
29 #include "pim_igmp_mtrace.h"
31 static void mtrace_rsp_init(struct igmp_mtrace_rsp
*mtrace_rspp
)
33 mtrace_rspp
->arrival
= 0;
34 mtrace_rspp
->incoming
.s_addr
= 0;
35 mtrace_rspp
->outgoing
.s_addr
= 0;
36 mtrace_rspp
->prev_hop
.s_addr
= 0;
37 mtrace_rspp
->in_count
= MTRACE_UNKNOWN_COUNT
;
38 mtrace_rspp
->out_count
= MTRACE_UNKNOWN_COUNT
;
39 mtrace_rspp
->total
= MTRACE_UNKNOWN_COUNT
;
40 mtrace_rspp
->rtg_proto
= 0;
41 mtrace_rspp
->fwd_ttl
= 0;
44 mtrace_rspp
->src_mask
= 0;
45 mtrace_rspp
->fwd_code
= MTRACE_FWD_CODE_NO_ERROR
;
48 static void mtrace_rsp_debug(uint32_t qry_id
, int rsp
,
49 struct igmp_mtrace_rsp
*mrspp
)
51 char inc_str
[INET_ADDRSTRLEN
];
52 char out_str
[INET_ADDRSTRLEN
];
53 char prv_str
[INET_ADDRSTRLEN
];
56 "Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
57 rsp
, ntohl(qry_id
), mrspp
->arrival
,
58 inet_ntop(AF_INET
, &(mrspp
->incoming
), inc_str
,
60 inet_ntop(AF_INET
, &(mrspp
->outgoing
), out_str
,
62 inet_ntop(AF_INET
, &(mrspp
->prev_hop
), prv_str
,
64 mrspp
->rtg_proto
, mrspp
->fwd_code
);
67 static void mtrace_debug(struct pim_interface
*pim_ifp
,
68 struct igmp_mtrace
*mtracep
, int mtrace_len
)
70 char inc_str
[INET_ADDRSTRLEN
];
71 char grp_str
[INET_ADDRSTRLEN
];
72 char src_str
[INET_ADDRSTRLEN
];
73 char dst_str
[INET_ADDRSTRLEN
];
74 char rsp_str
[INET_ADDRSTRLEN
];
77 "Rx mtrace packet incoming on %s: "
78 "hops=%d type=%d size=%d, grp=%s, src=%s,"
79 " dst=%s rsp=%s ttl=%d qid=%ud",
80 inet_ntop(AF_INET
, &(pim_ifp
->primary_address
), inc_str
,
82 mtracep
->hops
, mtracep
->type
, mtrace_len
,
83 inet_ntop(AF_INET
, &(mtracep
->grp_addr
), grp_str
,
85 inet_ntop(AF_INET
, &(mtracep
->src_addr
), src_str
,
87 inet_ntop(AF_INET
, &(mtracep
->dst_addr
), dst_str
,
89 inet_ntop(AF_INET
, &(mtracep
->rsp_addr
), rsp_str
,
91 mtracep
->rsp_ttl
, ntohl(mtracep
->qry_id
));
92 if (mtrace_len
> (int)sizeof(struct igmp_mtrace
)) {
96 int responses
= mtrace_len
- sizeof(struct igmp_mtrace
);
98 if ((responses
% sizeof(struct igmp_mtrace_rsp
)) != 0)
101 "Mtrace response block of wrong"
104 responses
= responses
/ sizeof(struct igmp_mtrace_rsp
);
106 for (i
= 0; i
< responses
; i
++)
107 mtrace_rsp_debug(mtracep
->qry_id
, i
, &mtracep
->rsp
[i
]);
111 /* 5.1 Query Arrival Time */
112 static uint32_t query_arrival_time(void)
117 char m_qat
[] = "Query arrival time lookup failed: errno=%d: %s";
119 if (gettimeofday(&tv
, NULL
) < 0) {
120 if (PIM_DEBUG_MTRACE
)
121 zlog_warn(m_qat
, errno
, safe_strerror(errno
));
124 /* not sure second offset correct, as I get different value */
125 qat
= ((tv
.tv_sec
+ 32384) << 16) + ((tv
.tv_usec
<< 10) / 15625);
130 static int mtrace_send_packet(struct interface
*ifp
,
131 struct igmp_mtrace
*mtracep
,
132 size_t mtrace_buf_len
, struct in_addr dst_addr
,
133 struct in_addr group_addr
)
135 struct sockaddr_in to
;
136 struct pim_interface
*pim_ifp
;
141 char pim_str
[INET_ADDRSTRLEN
];
142 char rsp_str
[INET_ADDRSTRLEN
];
147 memset(&to
, 0, sizeof(to
));
148 to
.sin_family
= AF_INET
;
149 to
.sin_addr
= dst_addr
;
152 if (PIM_DEBUG_MTRACE
)
153 zlog_debug("Sending mtrace packet to %s on %s",
154 inet_ntop(AF_INET
, &mtracep
->rsp_addr
, rsp_str
,
156 inet_ntop(AF_INET
, &pim_ifp
->primary_address
,
157 pim_str
, sizeof(pim_str
)));
159 fd
= pim_socket_raw(IPPROTO_IGMP
);
164 ret
= pim_socket_bind(fd
, ifp
);
171 if (IPV4_CLASS_DE(ntohl(dst_addr
.s_addr
))) {
172 if (IPV4_MC_LINKLOCAL(ntohl(dst_addr
.s_addr
))) {
175 if (mtracep
->type
== PIM_IGMP_MTRACE_RESPONSE
)
176 ttl
= mtracep
->rsp_ttl
;
180 ret
= setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
,
184 if (PIM_DEBUG_MTRACE
)
185 zlog_warn("Failed to set socket multicast TTL");
191 sent
= sendto(fd
, (char *)mtracep
, mtrace_buf_len
, MSG_DONTWAIT
,
192 (struct sockaddr
*)&to
, tolen
);
194 if (sent
!= (ssize_t
)mtrace_buf_len
) {
195 char dst_str
[INET_ADDRSTRLEN
];
196 char group_str
[INET_ADDRSTRLEN
];
198 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
199 pim_inet4_dump("<group?>", group_addr
, group_str
,
202 if (PIM_DEBUG_MTRACE
)
204 "Send mtrace request failed for %s on"
205 "%s: group=%s msg_size=%zd: errno=%d: "
207 dst_str
, ifp
->name
, group_str
,
208 mtrace_buf_len
, errno
,
209 safe_strerror(errno
));
211 if (PIM_DEBUG_MTRACE
)
213 "Send mtrace request failed for %s on"
214 " %s: group=%s msg_size=%zd: sent=%zd",
215 dst_str
, ifp
->name
, group_str
,
216 mtrace_buf_len
, sent
);
227 static int mtrace_un_forward_packet(struct pim_instance
*pim
, struct ip
*ip_hdr
,
228 struct interface
*interface
)
230 struct pim_nexthop nexthop
;
231 struct sockaddr_in to
;
232 struct interface
*if_out
;
239 checksum
= ip_hdr
->ip_sum
;
243 if (checksum
!= in_cksum(ip_hdr
, ip_hdr
->ip_hl
* 4))
246 if (ip_hdr
->ip_ttl
-- <= 1)
249 ip_hdr
->ip_sum
= in_cksum(ip_hdr
, ip_hdr
->ip_hl
* 4);
251 fd
= pim_socket_raw(IPPROTO_RAW
);
256 pim_socket_ip_hdr(fd
);
258 if (interface
== NULL
) {
259 memset(&nexthop
, 0, sizeof(nexthop
));
260 ret
= pim_nexthop_lookup(pim
, &nexthop
, ip_hdr
->ip_dst
, 0);
264 if (PIM_DEBUG_MTRACE
)
266 "Dropping mtrace packet, "
267 "no route to destination");
271 if_out
= nexthop
.interface
;
276 ret
= pim_socket_bind(fd
, if_out
);
283 memset(&to
, 0, sizeof(to
));
284 to
.sin_family
= AF_INET
;
285 to
.sin_addr
= ip_hdr
->ip_dst
;
288 sent
= sendto(fd
, ip_hdr
, ntohs(ip_hdr
->ip_len
), 0,
289 (struct sockaddr
*)&to
, tolen
);
294 if (PIM_DEBUG_MTRACE
)
296 "Failed to forward mtrace packet:"
297 " sendto errno=%d, %s",
298 errno
, safe_strerror(errno
));
302 if (PIM_DEBUG_MTRACE
) {
303 zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
304 ntohs(ip_hdr
->ip_len
), inet_ntoa(ip_hdr
->ip_dst
),
311 static int mtrace_mc_forward_packet(struct pim_instance
*pim
, struct ip
*ip_hdr
)
314 struct channel_oil
*c_oil
;
315 struct listnode
*chnode
;
316 struct listnode
*chnextnode
;
317 struct pim_ifchannel
*ch
= NULL
;
320 memset(&sg
, 0, sizeof(struct prefix_sg
));
321 sg
.grp
= ip_hdr
->ip_dst
;
323 c_oil
= pim_find_channel_oil(pim
, &sg
);
326 if (PIM_DEBUG_MTRACE
) {
328 "Dropping mtrace multicast packet "
329 "len=%u to %s ttl=%u",
330 ntohs(ip_hdr
->ip_len
),
331 inet_ntoa(ip_hdr
->ip_dst
), ip_hdr
->ip_ttl
);
335 if (c_oil
->up
== NULL
)
337 if (c_oil
->up
->ifchannels
== NULL
)
339 for (ALL_LIST_ELEMENTS(c_oil
->up
->ifchannels
, chnode
, chnextnode
, ch
)) {
340 if (pim_macro_chisin_oiflist(ch
)) {
343 r
= mtrace_un_forward_packet(pim
, ip_hdr
,
353 static int mtrace_forward_packet(struct pim_instance
*pim
, struct ip
*ip_hdr
)
355 if (IPV4_CLASS_DE(ntohl(ip_hdr
->ip_dst
.s_addr
)))
356 return mtrace_mc_forward_packet(pim
, ip_hdr
);
358 return mtrace_un_forward_packet(pim
, ip_hdr
, NULL
);
361 /* 6.5 Sending Traceroute Responses */
362 static int mtrace_send_mc_response(struct pim_instance
*pim
,
363 struct igmp_mtrace
*mtracep
,
367 struct channel_oil
*c_oil
;
368 struct listnode
*chnode
;
369 struct listnode
*chnextnode
;
370 struct pim_ifchannel
*ch
= NULL
;
373 memset(&sg
, 0, sizeof(struct prefix_sg
));
374 sg
.grp
= mtracep
->rsp_addr
;
376 c_oil
= pim_find_channel_oil(pim
, &sg
);
379 if (PIM_DEBUG_MTRACE
) {
381 "Dropping mtrace multicast response packet "
383 (unsigned int)mtrace_len
,
384 inet_ntoa(mtracep
->rsp_addr
));
388 if (c_oil
->up
== NULL
)
390 if (c_oil
->up
->ifchannels
== NULL
)
392 for (ALL_LIST_ELEMENTS(c_oil
->up
->ifchannels
, chnode
, chnextnode
, ch
)) {
393 if (pim_macro_chisin_oiflist(ch
)) {
396 r
= mtrace_send_packet(ch
->interface
, mtracep
,
397 mtrace_len
, mtracep
->rsp_addr
,
406 static int mtrace_send_response(struct pim_instance
*pim
,
407 struct igmp_mtrace
*mtracep
, size_t mtrace_len
)
409 struct pim_nexthop nexthop
;
412 mtracep
->type
= PIM_IGMP_MTRACE_RESPONSE
;
414 mtracep
->checksum
= 0;
415 mtracep
->checksum
= in_cksum((char *)mtracep
, mtrace_len
);
417 if (IPV4_CLASS_DE(ntohl(mtracep
->rsp_addr
.s_addr
))) {
418 struct pim_rpf
*p_rpf
;
419 char grp_str
[INET_ADDRSTRLEN
];
421 if (pim_rp_i_am_rp(pim
, mtracep
->rsp_addr
))
422 return mtrace_send_mc_response(pim
, mtracep
,
425 p_rpf
= pim_rp_g(pim
, mtracep
->rsp_addr
);
428 if (PIM_DEBUG_MTRACE
)
429 zlog_warn("mtrace no RP for %s",
431 &(mtracep
->rsp_addr
),
432 grp_str
, sizeof(grp_str
)));
435 nexthop
= p_rpf
->source_nexthop
;
436 if (PIM_DEBUG_MTRACE
)
437 zlog_debug("mtrace response to RP");
439 memset(&nexthop
, 0, sizeof(nexthop
));
440 /* TODO: should use unicast rib lookup */
441 ret
= pim_nexthop_lookup(pim
, &nexthop
, mtracep
->rsp_addr
, 1);
444 if (PIM_DEBUG_MTRACE
)
446 "Dropped response qid=%ud, no route to "
453 return mtrace_send_packet(nexthop
.interface
, mtracep
, mtrace_len
,
454 mtracep
->rsp_addr
, mtracep
->grp_addr
);
457 int igmp_mtrace_recv_qry_req(struct igmp_sock
*igmp
, struct ip
*ip_hdr
,
458 struct in_addr from
, const char *from_str
,
459 char *igmp_msg
, int igmp_msg_len
)
461 static uint32_t qry_id
, qry_src
;
462 char mtrace_buf
[MTRACE_HDR_SIZE
+ MTRACE_MAX_HOPS
* MTRACE_RSP_SIZE
];
463 struct pim_nexthop nexthop
;
464 struct interface
*ifp
;
465 struct interface
*out_ifp
;
466 struct pim_interface
*pim_ifp
;
467 struct pim_interface
*pim_out_ifp
;
468 struct pim_instance
*pim
;
469 struct igmp_mtrace
*mtracep
;
470 struct igmp_mtrace_rsp
*rspp
;
471 struct in_addr nh_addr
;
472 enum mtrace_fwd_code fwd_code
= MTRACE_FWD_CODE_NO_ERROR
;
475 int last_rsp_ind
= 0;
477 uint16_t recv_checksum
;
480 ifp
= igmp
->interface
;
485 * 6. Router Behaviour
486 * Check if mtrace packet is addressed elsewhere and forward,
489 if (!IPV4_CLASS_DE(ntohl(ip_hdr
->ip_dst
.s_addr
)))
490 if (!if_lookup_exact_address(&ip_hdr
->ip_dst
, AF_INET
,
492 return mtrace_forward_packet(pim
, ip_hdr
);
494 if (igmp_msg_len
< (int)sizeof(struct igmp_mtrace
)) {
495 if (PIM_DEBUG_MTRACE
)
497 "Recv mtrace packet from %s on %s: too short,"
499 from_str
, ifp
->name
, igmp_msg_len
,
500 sizeof(struct igmp_mtrace
));
504 mtracep
= (struct igmp_mtrace
*)igmp_msg
;
506 recv_checksum
= mtracep
->checksum
;
508 mtracep
->checksum
= 0;
510 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
512 if (recv_checksum
!= checksum
) {
513 if (PIM_DEBUG_MTRACE
)
515 "Recv mtrace packet from %s on %s: checksum"
516 " mismatch: received=%x computed=%x",
517 from_str
, ifp
->name
, recv_checksum
, checksum
);
521 if (PIM_DEBUG_MTRACE
)
522 mtrace_debug(pim_ifp
, mtracep
, igmp_msg_len
);
524 /* subtract header from message length */
525 r_len
= igmp_msg_len
- sizeof(struct igmp_mtrace
);
527 /* Classify mtrace packet, check if it is a query */
529 if (PIM_DEBUG_MTRACE
)
530 zlog_debug("Received IGMP multicast traceroute query");
532 /* 6.1.1 Packet verification */
533 if (!pim_if_connected_to_source(ifp
, mtracep
->dst_addr
)) {
534 if (IPV4_CLASS_DE(ntohl(ip_hdr
->ip_dst
.s_addr
))) {
535 if (PIM_DEBUG_MTRACE
)
537 "Dropping multicast query "
538 "on wrong interface");
541 /* Unicast query on wrong interface */
542 fwd_code
= MTRACE_FWD_CODE_WRONG_IF
;
544 if (qry_id
== mtracep
->qry_id
&& qry_src
== from
.s_addr
) {
545 if (PIM_DEBUG_MTRACE
)
547 "Dropping multicast query with "
548 "duplicate source and id");
551 qry_id
= mtracep
->qry_id
;
552 qry_src
= from
.s_addr
;
554 /* if response fields length is equal to a whole number of responses */
555 else if ((r_len
% sizeof(struct igmp_mtrace_rsp
)) == 0) {
556 r_len
= igmp_msg_len
- sizeof(struct igmp_mtrace
);
559 last_rsp_ind
= r_len
/ sizeof(struct igmp_mtrace_rsp
);
560 if (last_rsp_ind
> MTRACE_MAX_HOPS
) {
561 if (PIM_DEBUG_MTRACE
)
562 zlog_warn("Mtrace request of excessive size");
566 if (PIM_DEBUG_MTRACE
)
568 "Recv mtrace packet from %s on %s: "
570 from_str
, ifp
->name
, igmp_msg_len
);
574 /* 6.2.1 Packet Verification - drop not link-local multicast */
575 if (IPV4_CLASS_DE(ntohl(ip_hdr
->ip_dst
.s_addr
))
576 && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr
->ip_dst
.s_addr
))) {
577 if (PIM_DEBUG_MTRACE
)
579 "Recv mtrace packet from %s on %s:"
580 " not link-local multicast %s",
581 from_str
, ifp
->name
, inet_ntoa(ip_hdr
->ip_dst
));
585 /* 6.2.2. Normal Processing */
589 if (last_rsp_ind
== MTRACE_MAX_HOPS
) {
590 mtracep
->rsp
[MTRACE_MAX_HOPS
- 1].fwd_code
=
591 MTRACE_FWD_CODE_NO_SPACE
;
592 return mtrace_send_response(pim_ifp
->pim
, mtracep
,
596 /* calculate new mtrace mtrace lenght with extra response */
597 mtrace_len
= igmp_msg_len
+ sizeof(struct igmp_mtrace_rsp
);
599 /* copy received query/request */
600 memcpy(mtrace_buf
, igmp_msg
, igmp_msg_len
);
602 /* repoint mtracep pointer to copy */
603 mtracep
= (struct igmp_mtrace
*)mtrace_buf
;
605 /* pointer for extra response field to be filled in */
606 rspp
= &mtracep
->rsp
[last_rsp_ind
];
608 /* initialize extra response field */
609 mtrace_rsp_init(rspp
);
611 rspp
->arrival
= htonl(query_arrival_time());
612 rspp
->outgoing
= pim_ifp
->primary_address
;
613 rspp
->out_count
= htonl(MTRACE_UNKNOWN_COUNT
);
615 /* 6.2.2. 2. Attempt to determine forwarding information */
619 memset(&nexthop
, 0, sizeof(nexthop
));
620 ret
= pim_nexthop_lookup(pim
, &nexthop
, mtracep
->src_addr
, 1);
623 char nexthop_str
[INET_ADDRSTRLEN
];
625 if (PIM_DEBUG_MTRACE
)
626 zlog_debug("mtrace pim_nexthop_lookup OK");
628 if (PIM_DEBUG_MTRACE
)
629 zlog_warn("mtrace next_hop=%s",
630 inet_ntop(nexthop
.mrib_nexthop_addr
.family
,
631 &nexthop
.mrib_nexthop_addr
.u
.prefix
,
632 nexthop_str
, sizeof(nexthop_str
)));
634 if (nexthop
.mrib_nexthop_addr
.family
== AF_INET
)
635 nh_addr
= nexthop
.mrib_nexthop_addr
.u
.prefix4
;
637 /* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */
639 if (PIM_DEBUG_MTRACE
)
640 zlog_debug("mtrace not found neighbor");
642 rspp
->fwd_code
= MTRACE_FWD_CODE_NO_ROUTE
;
644 rspp
->fwd_code
= fwd_code
;
645 /* 6.5 Sending Traceroute Responses */
646 return mtrace_send_response(pim
, mtracep
, mtrace_len
);
649 out_ifp
= nexthop
.interface
;
650 pim_out_ifp
= out_ifp
->info
;
652 rspp
->incoming
= pim_out_ifp
->primary_address
;
653 rspp
->prev_hop
= nh_addr
;
654 rspp
->in_count
= htonl(MTRACE_UNKNOWN_COUNT
);
655 rspp
->total
= htonl(MTRACE_UNKNOWN_COUNT
);
656 rspp
->rtg_proto
= MTRACE_RTG_PROTO_PIM
;
660 if (nh_addr
.s_addr
== 0) {
661 /* reached source? */
662 if (pim_if_connected_to_source(out_ifp
, mtracep
->src_addr
))
663 return mtrace_send_response(pim
, mtracep
, mtrace_len
);
665 * 6.4 Forwarding Traceroute Requests:
666 * Previous-hop router not known
668 inet_aton(MCAST_ALL_ROUTERS
, &nh_addr
);
671 if (mtracep
->hops
<= (last_rsp_ind
+ 1))
672 return mtrace_send_response(pim
, mtracep
, mtrace_len
);
674 mtracep
->checksum
= 0;
676 mtracep
->checksum
= in_cksum(mtrace_buf
, mtrace_len
);
678 return mtrace_send_packet(out_ifp
, mtracep
, mtrace_len
, nh_addr
,
682 int igmp_mtrace_recv_response(struct igmp_sock
*igmp
, struct ip
*ip_hdr
,
683 struct in_addr from
, const char *from_str
,
684 char *igmp_msg
, int igmp_msg_len
)
686 static uint32_t qry_id
, rsp_dst
;
687 struct interface
*ifp
;
688 struct pim_interface
*pim_ifp
;
689 struct pim_instance
*pim
;
690 struct igmp_mtrace
*mtracep
;
691 uint16_t recv_checksum
;
694 ifp
= igmp
->interface
;
698 mtracep
= (struct igmp_mtrace
*)igmp_msg
;
700 recv_checksum
= mtracep
->checksum
;
702 mtracep
->checksum
= 0;
704 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
706 if (recv_checksum
!= checksum
) {
707 if (PIM_DEBUG_MTRACE
)
709 "Recv mtrace response from %s on %s: checksum"
710 " mismatch: received=%x computed=%x",
711 from_str
, ifp
->name
, recv_checksum
, checksum
);
715 mtracep
->checksum
= checksum
;
717 if (PIM_DEBUG_MTRACE
)
718 mtrace_debug(pim_ifp
, mtracep
, igmp_msg_len
);
720 /* Drop duplicate packets */
721 if (qry_id
== mtracep
->qry_id
&& rsp_dst
== ip_hdr
->ip_dst
.s_addr
) {
722 if (PIM_DEBUG_MTRACE
)
723 zlog_debug("duplicate mtrace response packet dropped");
727 qry_id
= mtracep
->qry_id
;
728 rsp_dst
= ip_hdr
->ip_dst
.s_addr
;
730 return mtrace_forward_packet(pim
, ip_hdr
);