diff --git a/doc/manpages/mtracebis.rst b/doc/manpages/mtracebis.rst index b4d57c1b97..d3ba8036d8 100644 --- a/doc/manpages/mtracebis.rst +++ b/doc/manpages/mtracebis.rst @@ -9,17 +9,22 @@ SYNOPSIS ======== |PROGRAM| |synopsis-options-hv| -|PROGRAM| +|PROGRAM| [] DESCRIPTION =========== -|PROGRAM| is a program to initiate multicast traceroute, or "mtrace", queries. +|PROGRAM| is a program for initiating multicast traceroute, or "mtrace", queries. -The initial version of the program requires multicast source IP address and -initiates a weak traceroute across the network. This tests whether the -interfaces towards the source are multicast enabled. The first query sent is a -full query, capable of crossing the network all the way to the source. If this -fails, hop-by-hop queries are initiated. +It can initiate two types of mtrace queries: weak and group. + +Weak tests whether the interfaces towards the source are multicast enabled and is +initiated by supplying only the multicast source address. + +Group tests whether there is multicast routing protocol state for particular +multicast group and is initiated by supplying mutlicast source and group. + +The first query sent is a full query, capable of crossing the network all the way +to the source. If this fails, hop-by-hop queries are initiated. Hop-by-hop queries start by requesting only a response from the nearest router. Following that, next query is extended to the next two routers, and so on... diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index ce83b420b4..a073fa70be 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -22,6 +22,7 @@ #include "pim_igmp_mtrace.h" #include "checksum.h" +#include "prefix.h" #include "mtracebis_routeget.h" #include @@ -50,7 +51,8 @@ static const char *progname; static void usage(void) { - fprintf(stderr, "Usage : %s \n", progname); + fprintf(stderr, "Usage : %s []\n", + progname); } static void version(void) { @@ -170,9 +172,21 @@ static void print_fwd_code(uint32_t fwd_code) static void print_rsp(struct igmp_mtrace_rsp *rsp) { print_host(rsp->outgoing); - if (rsp->fwd_code == 0) { + if (rsp->fwd_code == 0 || rsp->fwd_code == MTRACE_FWD_CODE_REACHED_RP) { print_rtg_proto(rsp->rtg_proto); printf(" "); + if (rsp->fwd_code == MTRACE_FWD_CODE_REACHED_RP) + printf("(RP) "); + if (rsp->rtg_proto == MTRACE_RTG_PROTO_PIM) { + switch (rsp->src_mask) { + case MTRACE_SRC_MASK_GROUP: + printf("(*,G) "); + break; + case MTRACE_SRC_MASK_SOURCE: + printf("(S,G) "); + break; + } + } print_fwd_ttl(rsp->fwd_ttl); } else { print_fwd_code(rsp->fwd_code); @@ -351,6 +365,7 @@ static bool check_end(struct igmp_mtrace *mtrace, int hops) int main(int argc, char *const argv[]) { struct in_addr mc_source; + struct in_addr mc_group; struct in_addr iface_addr; struct in_addr gw_addr; struct in_addr mtrace_addr; @@ -370,6 +385,7 @@ int main(int argc, char *const argv[]) int i, j; char ifname[IF_NAMESIZE]; char mbuf[MTRACE_BUF_LEN]; + bool not_group; mtrace_addr.s_addr = inet_addr("224.0.1.32"); @@ -385,7 +401,7 @@ int main(int argc, char *const argv[]) else progname = argv[0]; - if (argc != 2) { + if (argc != 2 && argc != 3) { usage(); exit(EXIT_FAILURE); } @@ -416,11 +432,28 @@ int main(int argc, char *const argv[]) } if (inet_pton(AF_INET, argv[1], &mc_source) != 1) { usage(); - fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0], + fprintf(stderr, "%s: %s is not a valid IPv4 address\n", argv[0], argv[1]); exit(EXIT_FAILURE); } + mc_group.s_addr = 0; + not_group = false; + + if (argc == 3) { + if (inet_pton(AF_INET, argv[2], &mc_group) != 1) + not_group = true; + if (!not_group && !IPV4_CLASS_DE(ntohl(mc_group.s_addr))) + not_group = true; + } + + if (not_group) { + usage(); + fprintf(stderr, "%s: %s is not a valid IPv4 group address\n", + argv[0], argv[2]); + exit(EXIT_FAILURE); + } + ifindex = routeget(mc_source, &iface_addr, &gw_addr); if (ifindex < 0) { fprintf(stderr, "%s: failed to get route to source %s\n", @@ -441,7 +474,7 @@ int main(int argc, char *const argv[]) mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST; mtrace.hops = hops; mtrace.checksum = 0; - mtrace.grp_addr.s_addr = 0; + mtrace.grp_addr = mc_group; mtrace.src_addr = mc_source; mtrace.dst_addr = iface_addr; mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr; diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 8274d08a26..d3ae185709 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* based on draft-ietf-idmr-traceroute-ipm-07 */ + #include #include "pimd.h" @@ -56,15 +58,131 @@ static struct in_addr mtrace_primary_address(struct interface *ifp) return any; } +static bool mtrace_fwd_info_weak(struct pim_instance *pim, + struct igmp_mtrace *mtracep, + struct igmp_mtrace_rsp *rspp, + struct interface **ifpp) +{ + struct pim_nexthop nexthop; + struct interface *ifp_in; + struct in_addr nh_addr; + int ret; + char nexthop_str[INET_ADDRSTRLEN]; + + nh_addr.s_addr = 0; + + memset(&nexthop, 0, sizeof(nexthop)); + + ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1); + + if (ret != 0) { + if (PIM_DEBUG_MTRACE) + zlog_debug("mtrace not found neighbor"); + return false; + } + + if (PIM_DEBUG_MTRACE) + zlog_debug("mtrace pim_nexthop_lookup OK"); + + if (PIM_DEBUG_MTRACE) + zlog_warn("mtrace next_hop=%s", + inet_ntop(nexthop.mrib_nexthop_addr.family, + &nexthop.mrib_nexthop_addr.u.prefix, + nexthop_str, sizeof(nexthop_str))); + + if (nexthop.mrib_nexthop_addr.family == AF_INET) + nh_addr = nexthop.mrib_nexthop_addr.u.prefix4; + + ifp_in = nexthop.interface; + + /* return interface for forwarding mtrace packets */ + *ifpp = ifp_in; + + /* 6.2.2. 4. Fill in the Incoming Interface Address... */ + rspp->incoming = mtrace_primary_address(ifp_in); + rspp->prev_hop = nh_addr; + rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); + rspp->total = htonl(MTRACE_UNKNOWN_COUNT); + rspp->rtg_proto = MTRACE_RTG_PROTO_PIM; + return true; +} + +static bool mtrace_fwd_info(struct pim_instance *pim, + struct igmp_mtrace *mtracep, + struct igmp_mtrace_rsp *rspp, + struct interface **ifpp) +{ + struct prefix_sg sg; + struct pim_upstream *up; + struct interface *ifp_in; + struct in_addr nh_addr; + uint32_t total; + char up_str[INET_ADDRSTRLEN]; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.src = mtracep->src_addr; + sg.grp = mtracep->grp_addr; + + up = pim_upstream_find(pim, &sg); + + if (!up) { + sg.src.s_addr = 0; + up = pim_upstream_find(pim, &sg); + } + + if (!up) + return false; + + ifp_in = up->rpf.source_nexthop.interface; + nh_addr = up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4; + total = htonl(MTRACE_UNKNOWN_COUNT); + + if (PIM_DEBUG_MTRACE) + zlog_debug("fwd_info: upstream next hop=%s", + inet_ntop(AF_INET, &(nh_addr), up_str, + sizeof(up_str))); + + if (up->channel_oil) + total = up->channel_oil->cc.pktcnt; + + /* return interface for forwarding mtrace packets */ + *ifpp = ifp_in; + + /* 6.2.2. 4. Fill in the Incoming Interface Address... */ + rspp->incoming = mtrace_primary_address(ifp_in); + rspp->prev_hop = nh_addr; + rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); + rspp->total = total; + rspp->rtg_proto = MTRACE_RTG_PROTO_PIM; + + /* 6.2.2. 4. Fill in ... S, and Src Mask */ + if (sg.src.s_addr) { + rspp->s = 1; + rspp->src_mask = MTRACE_SRC_MASK_SOURCE; + } else { + rspp->s = 0; + rspp->src_mask = MTRACE_SRC_MASK_GROUP; + } + + return true; +} + +static void mtrace_rsp_set_fwd_code(struct igmp_mtrace_rsp *mtrace_rspp, + enum mtrace_fwd_code fwd_code) +{ + if (mtrace_rspp->fwd_code == MTRACE_FWD_CODE_NO_ERROR) + mtrace_rspp->fwd_code = fwd_code; +} + static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp) { mtrace_rspp->arrival = 0; mtrace_rspp->incoming.s_addr = 0; mtrace_rspp->outgoing.s_addr = 0; mtrace_rspp->prev_hop.s_addr = 0; - mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT; - mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT; - mtrace_rspp->total = MTRACE_UNKNOWN_COUNT; + mtrace_rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); + mtrace_rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT); + mtrace_rspp->total = htonl(MTRACE_UNKNOWN_COUNT); mtrace_rspp->rtg_proto = 0; mtrace_rspp->fwd_ttl = 0; mtrace_rspp->mbz = 0; @@ -394,7 +512,6 @@ static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr) return mtrace_un_forward_packet(pim, ip_hdr, NULL); } -/* 6.5 Sending Traceroute Responses */ static int mtrace_send_mc_response(struct pim_instance *pim, struct igmp_mtrace *mtracep, size_t mtrace_len) @@ -439,6 +556,7 @@ static int mtrace_send_mc_response(struct pim_instance *pim, return ret; } +/* 6.5 Sending Traceroute Responses */ static int mtrace_send_response(struct pim_instance *pim, struct igmp_mtrace *mtracep, size_t mtrace_len) { @@ -496,7 +614,6 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, { static uint32_t qry_id, qry_src; char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE]; - struct pim_nexthop nexthop; struct interface *ifp; struct interface *out_ifp; struct pim_interface *pim_ifp; @@ -505,12 +622,13 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, struct igmp_mtrace_rsp *rspp; struct in_addr nh_addr; enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR; - int ret; size_t r_len; int last_rsp_ind = 0; size_t mtrace_len; uint16_t recv_checksum; uint16_t checksum; + bool reached_source; + bool fwd_info; ifp = igmp->interface; pim_ifp = ifp->info; @@ -575,6 +693,8 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, } /* Unicast query on wrong interface */ fwd_code = MTRACE_FWD_CODE_WRONG_IF; + if (PIM_DEBUG_MTRACE) + zlog_debug("Multicast query on wrong interface"); } if (qry_id == mtracep->qry_id && qry_src == from.s_addr) { if (PIM_DEBUG_MTRACE) @@ -619,16 +739,19 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, /* 6.2.2. Normal Processing */ - /* 6.2.2. 1. */ + /* 6.2.2. 1. If there is room in the current buffer? */ if (last_rsp_ind == MTRACE_MAX_HOPS) { + /* ...there was no room... */ mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code = MTRACE_FWD_CODE_NO_SPACE; return mtrace_send_response(pim_ifp->pim, mtracep, igmp_msg_len); } - /* calculate new mtrace mtrace lenght with extra response */ + /* ...insert new response block... */ + + /* calculate new mtrace lenght with extra response */ mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp); /* copy received query/request */ @@ -643,84 +766,86 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, /* initialize extra response field */ mtrace_rsp_init(rspp); + /* carry over any error noted when receiving the query */ + rspp->fwd_code = fwd_code; + + /* ...and fill in Query Arrival Time... */ rspp->arrival = htonl(query_arrival_time()); rspp->outgoing = pim_ifp->primary_address; rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT); + rspp->fwd_ttl = 1; - /* 6.2.2. 2. Attempt to determine forwarding information */ + /* 6.2.2. 2. Attempt to determine the forwarding information... */ - nh_addr.s_addr = 0; - - memset(&nexthop, 0, sizeof(nexthop)); - ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1); - - if (ret == 0) { - char nexthop_str[INET_ADDRSTRLEN]; + if (mtracep->grp_addr.s_addr) + fwd_info = mtrace_fwd_info(pim, mtracep, rspp, &out_ifp); + else + fwd_info = mtrace_fwd_info_weak(pim, mtracep, rspp, &out_ifp); + /* 6.2.2 3. If no forwarding information... */ + if (!fwd_info) { if (PIM_DEBUG_MTRACE) - zlog_debug("mtrace pim_nexthop_lookup OK"); - - if (PIM_DEBUG_MTRACE) - zlog_warn("mtrace next_hop=%s", - inet_ntop(nexthop.mrib_nexthop_addr.family, - &nexthop.mrib_nexthop_addr.u.prefix, - nexthop_str, sizeof(nexthop_str))); - - if (nexthop.mrib_nexthop_addr.family == AF_INET) - nh_addr = nexthop.mrib_nexthop_addr.u.prefix4; - } - /* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */ - else { - if (PIM_DEBUG_MTRACE) - zlog_debug("mtrace not found neighbor"); - if (!fwd_code) - rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE; - else - rspp->fwd_code = fwd_code; - /* 6.5 Sending Traceroute Responses */ + zlog_debug("mtrace not found multicast state"); + mtrace_rsp_set_fwd_code(rspp, MTRACE_FWD_CODE_NO_ROUTE); + /* 6.2.2. 3. forward the packet to requester */ return mtrace_send_response(pim, mtracep, mtrace_len); } - out_ifp = nexthop.interface; + nh_addr = rspp->prev_hop; - rspp->incoming = mtrace_primary_address(out_ifp); - rspp->prev_hop = nh_addr; - rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); - rspp->total = htonl(MTRACE_UNKNOWN_COUNT); - rspp->rtg_proto = MTRACE_RTG_PROTO_PIM; - rspp->fwd_ttl = 1; - rspp->s = 1; - rspp->src_mask = 32; + reached_source = false; if (nh_addr.s_addr == 0) { - /* no pim? */ + /* no pim? i.e. 7.5.3. No Previous Hop */ if (!out_ifp->info) { - rspp->fwd_code = MTRACE_FWD_CODE_NO_MULTICAST; + if (PIM_DEBUG_MTRACE) + zlog_debug("mtrace not found incoming if w/ pim"); + mtrace_rsp_set_fwd_code(rspp, + MTRACE_FWD_CODE_NO_MULTICAST); return mtrace_send_response(pim, mtracep, mtrace_len); } - /* reached source? */ + /* reached source? i.e. 7.5.1 Arriving at source */ if (pim_if_connected_to_source(out_ifp, mtracep->src_addr)) { + reached_source = true; rspp->prev_hop = mtracep->src_addr; - return mtrace_send_response(pim, mtracep, mtrace_len); } /* * 6.4 Forwarding Traceroute Requests: - * Previous-hop router not known + * Previous-hop router not known, + * packet is sent to an appropriate multicast address */ inet_aton(MCAST_ALL_ROUTERS, &nh_addr); } + /* 6.2.2 8. If this router is the Rendez-vous Point */ + if (pim_rp_i_am_rp(pim, mtracep->grp_addr)) { + mtrace_rsp_set_fwd_code(rspp, MTRACE_FWD_CODE_REACHED_RP); + /* 7.7.1. PIM-SM ...RP has not performed source-specific join */ + if (rspp->src_mask == MTRACE_SRC_MASK_GROUP) + return mtrace_send_response(pim, mtracep, mtrace_len); + } + + /* + * 6.4 Forwarding Traceroute Requests: the number of response + * blocks exceeds number of responses, so forward to the requester. + */ if (mtracep->hops <= (last_rsp_ind + 1)) return mtrace_send_response(pim, mtracep, mtrace_len); + /* 7.5.1. Arriving at source: terminate trace */ + if (reached_source) + return mtrace_send_response(pim, mtracep, mtrace_len); + mtracep->checksum = 0; mtracep->checksum = in_cksum(mtrace_buf, mtrace_len); + /* 6.4 Forwarding Traceroute Requests: response blocks less than req. */ return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr, mtracep->grp_addr); } +/* 6.3. Traceroute responses */ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, struct in_addr from, const char *from_str, char *igmp_msg, int igmp_msg_len) diff --git a/pimd/pim_igmp_mtrace.h b/pimd/pim_igmp_mtrace.h index d47da3557a..4ab562ed97 100644 --- a/pimd/pim_igmp_mtrace.h +++ b/pimd/pim_igmp_mtrace.h @@ -26,6 +26,8 @@ #define MTRACE_MAX_HOPS (255) #define MTRACE_UNKNOWN_COUNT (0xffffffff) +#define MTRACE_SRC_MASK_GROUP (0x3f) /* forwarding on group state (*,G) */ +#define MTRACE_SRC_MASK_SOURCE (0x20) /* i.e. 32 forwarding on (S,G) */ enum mtrace_fwd_code { MTRACE_FWD_CODE_NO_ERROR = 0x00, diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index ec212233f6..a289ec08ba 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2792,14 +2792,15 @@ ALIAS(vtysh_traceroute, vtysh_traceroute_ip_cmd, "traceroute ip WORD", DEFUN (vtysh_mtrace, vtysh_mtrace_cmd, - "mtrace WORD", + "mtrace WORD [WORD]", "Multicast trace route to multicast source\n" - "Multicast trace route to multicast source address\n") + "Multicast trace route to multicast source address\n" + "Multicast trace route for multicast group address\n") { - int idx = 1; - - argv_find(argv, argc, "WORD", &idx); - execute_command("mtracebis", 1, argv[idx]->arg, NULL); + if (argc == 2) + execute_command("mtracebis", 1, argv[1]->arg, NULL); + else + execute_command("mtracebis", 2, argv[1]->arg, argv[2]->arg); return CMD_SUCCESS; }