]> git.proxmox.com Git - mirror_frr.git/commitdiff
pimd: adding querying of state to mtrace
authorMladen Sablic <mladen.sablic@gmail.com>
Thu, 12 Apr 2018 11:24:21 +0000 (13:24 +0200)
committerMladen Sablic <mladen.sablic@gmail.com>
Tue, 24 Apr 2018 16:47:50 +0000 (18:47 +0200)
Adding to mtracebis querying with group address. Same change
to vtysh mtrace command. Support for querying (S,G) and (*,G)
state in mtrace router code. Further improvments to mtrace router
code with closer complience to IETF draft. More references in
comments to the draft. Man page has been updated accordingly.

Signed-off-by: Mladen Sablic <mladen.sablic@gmail.com>
doc/manpages/mtracebis.rst
pimd/mtracebis.c
pimd/pim_igmp_mtrace.c
pimd/pim_igmp_mtrace.h
vtysh/vtysh.c

index b4d57c1b972eb947195239348790cd4cefbce907..d3ba8036d8c79f56dda2f90bd2828cf6d8b896a0 100644 (file)
@@ -9,17 +9,22 @@ SYNOPSIS
 ========
 |PROGRAM| |synopsis-options-hv|
 
-|PROGRAM| <multicast source>
+|PROGRAM| <multicast source> [<multicast group>]
 
 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...
index ce83b420b41ed398953166f8cf92df3de5f9fdb0..a073fa70beeead052838a736e6b6a79c5809dd66 100644 (file)
@@ -22,6 +22,7 @@
 #include "pim_igmp_mtrace.h"
 
 #include "checksum.h"
+#include "prefix.h"
 #include "mtracebis_routeget.h"
 
 #include <sys/select.h>
@@ -50,7 +51,8 @@
 static const char *progname;
 static void usage(void)
 {
-       fprintf(stderr, "Usage : %s <multicast source>\n", progname);
+       fprintf(stderr, "Usage : %s <multicast source> [<multicast group>]\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;
index 8274d08a2604b38100dd0a6cb01a72926a9d60c6..d3ae185709079d6a8eb0b54cfd8e030f147fb505 100644 (file)
@@ -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 <zebra.h>
 
 #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 */
-
-       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];
+       /* 6.2.2. 2. Attempt to determine the forwarding information... */
 
-               if (PIM_DEBUG_MTRACE)
-                       zlog_debug("mtrace pim_nexthop_lookup OK");
+       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_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)
index d47da3557a77eef70cce4d8d0ef12669d0b97a48..4ab562ed9799ac04a0a81efa5d792ef4ad6c0640 100644 (file)
@@ -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,
index ec212233f66af48a2058455e301bd8ff5534bd61..a289ec08badd09fabb04136acf0677a693bf830c 100644 (file)
@@ -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;
 }