Changes to add multicast support.
authorJordan K. Hubbard <jkh@FreeBSD.org>
Tue, 17 May 1994 21:10:18 +0000 (21:10 +0000)
committerJordan K. Hubbard <jkh@FreeBSD.org>
Tue, 17 May 1994 21:10:18 +0000 (21:10 +0000)
usr.bin/netstat/Makefile
usr.bin/netstat/if.c
usr.bin/netstat/inet.c
usr.bin/netstat/main.c
usr.bin/netstat/mroute.c [new file with mode: 0644]
usr.bin/netstat/netstat.1

index e2db444..8f0933d 100644 (file)
@@ -1,7 +1,7 @@
 #      @(#)Makefile    5.14 (Berkeley) 6/18/90
 
 PROG=  netstat
 #      @(#)Makefile    5.14 (Berkeley) 6/18/90
 
 PROG=  netstat
-SRCS=  inet.c if.c main.c mbuf.c route.c unix.c
+SRCS=  inet.c if.c main.c mbuf.c route.c unix.c mroute.c
 
 #SRCS+=        host.c
 #CFLAGS+= -DIMP
 
 #SRCS+=        host.c
 #CFLAGS+= -DIMP
index 0f8c63b..3022856 100644 (file)
@@ -34,7 +34,7 @@
 #ifndef lint
 /* From: static char sccsid[] = "@(#)if.c      5.15 (Berkeley) 3/1/91"; */
 static const char if_c_rcsid[] = 
 #ifndef lint
 /* From: static char sccsid[] = "@(#)if.c      5.15 (Berkeley) 3/1/91"; */
 static const char if_c_rcsid[] = 
-       "$Id: if.c,v 1.2 1993/11/17 20:19:19 wollman Exp $";
+       "$Id: if.c,v 1.3 1994/02/21 11:35:23 rgrimes Exp $";
 #endif /* not lint */
 
 #include <sys/types.h>
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -61,6 +61,7 @@ static const char if_c_rcsid[] =
 #define        YES     1
 #define        NO      0
 
 #define        YES     1
 #define        NO      0
 
+extern int aflag;
 extern int tflag;
 extern int dflag;
 extern int nflag;
 extern int tflag;
 extern int dflag;
 extern int nflag;
@@ -69,6 +70,21 @@ extern       int unit;
 extern char *routename(), *netname(), *ns_phost();
 char *index();
 
 extern char *routename(), *netname(), *ns_phost();
 char *index();
 
+/*
+ * Return a printable string representation of an Ethernet address.
+ */
+char *etherprint(enaddr)
+       char enaddr[6];
+{
+       static char string[18];
+       unsigned char *en = (unsigned char *)enaddr;
+
+       sprintf(string, "%02x:%02x:%02x:%02x:%02x:%02x",
+               en[0], en[1], en[2], en[3], en[4], en[5] );
+       string[17] = '\0';
+       return(string);
+}
+
 /*
  * Print a description of the network interfaces.
  */
 /*
  * Print a description of the network interfaces.
  */
@@ -87,7 +103,7 @@ intpr(interval, ifnetaddr)
                struct iso_ifaddr iso;
 #endif
        } ifaddr;
                struct iso_ifaddr iso;
 #endif
        } ifaddr;
-       off_t ifaddraddr;
+       off_t ifaddraddr, ifaddrfound, ifnetfound;
        struct sockaddr *sa;
        char name[16];
 
        struct sockaddr *sa;
        char name[16];
 
@@ -110,12 +126,14 @@ intpr(interval, ifnetaddr)
                printf(" %s", "Drop");
        putchar('\n');
        ifaddraddr = 0;
                printf(" %s", "Drop");
        putchar('\n');
        ifaddraddr = 0;
+       ifnetfound = 0;
        while (ifnetaddr || ifaddraddr) {
                struct sockaddr_in *sin;
                register char *cp;
                int n, m;
                struct in_addr inet_makeaddr();
 
        while (ifnetaddr || ifaddraddr) {
                struct sockaddr_in *sin;
                register char *cp;
                int n, m;
                struct in_addr inet_makeaddr();
 
+               ifnetfound = ifnetaddr;
                if (ifaddraddr == 0) {
                        kvm_read(ifnetaddr, (char *)&ifnet, sizeof ifnet);
                        kvm_read((off_t)ifnet.if_name, name, 16);
                if (ifaddraddr == 0) {
                        kvm_read(ifnetaddr, (char *)&ifnet, sizeof ifnet);
                        kvm_read((off_t)ifnet.if_name, name, 16);
@@ -132,6 +150,7 @@ intpr(interval, ifnetaddr)
                        ifaddraddr = (off_t)ifnet.if_addrlist;
                }
                printf("%-5.5s %-5d ", name, ifnet.if_mtu);
                        ifaddraddr = (off_t)ifnet.if_addrlist;
                }
                printf("%-5.5s %-5d ", name, ifnet.if_mtu);
+               ifaddrfound = ifaddraddr;
                if (ifaddraddr == 0) {
                        printf("%-11.11s ", "none");
                        printf("%-15.15s ", "none");
                if (ifaddraddr == 0) {
                        printf("%-11.11s ", "none");
                        printf("%-15.15s ", "none");
@@ -213,6 +232,75 @@ intpr(interval, ifnetaddr)
                if (dflag)
                        printf(" %3d", ifnet.if_snd.ifq_drops);
                putchar('\n');
                if (dflag)
                        printf(" %3d", ifnet.if_snd.ifq_drops);
                putchar('\n');
+
+               /*XXX this needs work for bsdi */
+               if (aflag && ifaddrfound) {
+                       /*
+                        * print any internet multicast addresses
+                        */
+                       switch (sa->sa_family) {
+                       case AF_INET:
+                           {
+                               off_t multiaddr;
+                               struct in_multi inm;
+
+                               multiaddr = (off_t)ifaddr.in.ia_multiaddrs;
+                               while (multiaddr != 0) {
+                                       kvm_read(multiaddr, (char *)&inm,
+                                                sizeof inm);
+                                       multiaddr = (off_t)inm.inm_next;
+                                       printf("%23s %-19.19s\n", "",
+                                              routename(inm.inm_addr.s_addr));
+                               }
+                               break;
+                           }
+                       default:
+                               break;
+                       }
+               }
+#ifdef notyet
+               if (aflag && ifaddraddr == 0) {
+                       /*
+                        * print link-level addresses
+                        * (Is there a better way to determine
+                        *  the type of network??)
+                        */
+                       if (strncmp(name, "qe", 2) == 0 ||    /* Ethernet */
+                           strncmp(name, "de", 2) == 0 ||
+                           strncmp(name, "ex", 2) == 0 ||
+                           strncmp(name, "il", 2) == 0 ||
+                           strncmp(name, "le", 2) == 0 ||
+                           strncmp(name, "se", 2) == 0 ||
+                           strncmp(name, "ie", 2) == 0) {
+                           strncmp(name, "we", 2) == 0) {
+                           strncmp(name, "el", 2) == 0) {
+                           /* "ec", the 3Com interface for Suns, is not    */
+                           /* included, although it does handle multicast, */
+                           /* because it does not filter specific ethernet */
+                           /* multicast addresses, but just accepts all.   */
+                               off_t multiaddr;
+                               struct arpcom ac;
+                               struct ether_multi enm;
+
+                               kvm_read(ifnetfound, (char *)&ac, sizeof ac);
+                               printf("%23s %s\n", "",
+                                       etherprint(&ac.ac_enaddr));
+                               multiaddr = (off_t)ac.ac_multiaddrs;
+                               while (multiaddr != 0) {
+                                       kvm_read(multiaddr, (char *)&enm,
+                                                sizeof enm);
+                                       multiaddr = (off_t)enm.enm_next;
+                                       printf("%23s %s", "",
+                                               etherprint(&enm.enm_addrlo));
+                                       if (bcmp(&enm.enm_addrlo,
+                                                &enm.enm_addrhi, 6) != 0)
+                                               printf(" to %s",
+                                               etherprint(&enm.enm_addrhi));
+                                       printf("\n");
+                               }
+                       }
+               }
+#endif
        }
 }
 
        }
 }
 
index f80fad7..2cb3dc2 100644 (file)
@@ -34,7 +34,7 @@
 #ifndef lint
 /* From: static char sccsid[] = "@(#)inet.c    5.15 (Berkeley) 6/18/90"; */
 static const char inet_c_rcsid[] =
 #ifndef lint
 /* From: static char sccsid[] = "@(#)inet.c    5.15 (Berkeley) 6/18/90"; */
 static const char inet_c_rcsid[] =
-       "$Id$";
+       "$Id: inet.c,v 1.2 1993/11/17 20:19:20 wollman Exp $";
 
 #endif /* not lint */
 
 
 #endif /* not lint */
 
@@ -51,6 +51,7 @@ static const char inet_c_rcsid[] =
 #include <netinet/in_pcb.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp_var.h>
 #include <netinet/in_pcb.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp_var.h>
+#include <netinet/igmp_var.h>
 #include <netinet/ip_var.h>
 #include <netinet/tcp.h>
 #include <netinet/tcpip.h>
 #include <netinet/ip_var.h>
 #include <netinet/tcp.h>
 #include <netinet/tcpip.h>
@@ -342,6 +343,47 @@ icmp_stats(off, name)
        printf("\t%u message response%s generated\n",
                icmpstat.icps_reflect, plural(icmpstat.icps_reflect));
 }
        printf("\t%u message response%s generated\n",
                icmpstat.icps_reflect, plural(icmpstat.icps_reflect));
 }
+  
+char*
+pluraly(n)
+{
+       return (n == 1? "y" : "ies");
+}
+
+/*
+ * Dump IGMP statistics.
+ */
+void
+igmp_stats(off, name)
+       off_t off;
+       char *name;
+{
+       struct igmpstat igmpstat;
+       register int i, first;
+
+       if (off == 0)
+               return;
+       kvm_read(off, (char *)&igmpstat, sizeof (igmpstat));
+       printf("%s:\n", name );
+       printf("\t%u message%s received\n",
+         igmpstat.igps_rcv_total, plural(igmpstat.igps_rcv_total));
+       printf("\t%u message%s received with too few bytes\n",
+         igmpstat.igps_rcv_tooshort, plural(igmpstat.igps_rcv_tooshort));
+       printf("\t%u message%s received with bad checksum\n",
+         igmpstat.igps_rcv_badsum, plural(igmpstat.igps_rcv_badsum));
+       printf("\t%u membership quer%s received\n",
+         igmpstat.igps_rcv_queries, pluraly(igmpstat.igps_rcv_queries));
+       printf("\t%u membership quer%s received with invalid field(s)\n",
+         igmpstat.igps_rcv_badqueries, pluraly(igmpstat.igps_rcv_badqueries));
+       printf("\t%u membership report%s received\n",
+         igmpstat.igps_rcv_reports, plural(igmpstat.igps_rcv_reports));
+       printf("\t%u membership report%s received with invalid field(s)\n",
+         igmpstat.igps_rcv_badreports, plural(igmpstat.igps_rcv_badreports));
+       printf("\t%u membership report%s received for groups to which we belong\n",
+         igmpstat.igps_rcv_ourreports, plural(igmpstat.igps_rcv_ourreports));
+       printf("\t%u membership report%s sent\n",
+         igmpstat.igps_snd_reports, plural(igmpstat.igps_snd_reports));
+}
 
 /*
  * Pretty print an Internet address (net address + port).
 
 /*
  * Pretty print an Internet address (net address + port).
index 1798080..c0554c3 100644 (file)
@@ -40,7 +40,7 @@ char copyright[] =
 #ifndef lint
 /* From: static char sccsid[] = "@(#)main.c    5.23 (Berkeley) 7/1/91"; */
 const char main_c_rcsid[] =
 #ifndef lint
 /* From: static char sccsid[] = "@(#)main.c    5.23 (Berkeley) 7/1/91"; */
 const char main_c_rcsid[] =
-       "$Id: main.c,v 1.2 1993/11/17 20:19:22 wollman Exp $";
+       "$Id: main.c,v 1.3 1994/05/04 14:03:20 davidg Exp $";
 #endif /* not lint */
 
 #include <sys/param.h>
 #endif /* not lint */
 
 #include <sys/param.h>
@@ -115,12 +115,23 @@ struct nlist nl[] = {
        { "_cltb"},
 #define N_CLTPSTAT     28
        { "_cltpstat"},
        { "_cltb"},
 #define N_CLTPSTAT     28
        { "_cltpstat"},
+#define        N_IGMPSTAT      29
+       { "_igmpstat" },
+#define        N_MRTPROTO      30
+       { "_ip_mrtproto" },
+#define        N_MRTSTAT       31
+       { "_mrtstat" },
+#define        N_MRTTABLE      32
+       { "_mrttable" },
+#define        N_VIFTABLE      33
+       { "_viftable" },
        "",
 };
 
 /* internet protocols */
 extern int protopr();
 extern int tcp_stats(), udp_stats(), ip_stats(), icmp_stats();
        "",
 };
 
 /* internet protocols */
 extern int protopr();
 extern int tcp_stats(), udp_stats(), ip_stats(), icmp_stats();
+extern int igmp_stats();
 #ifdef NS
 /* ns protocols */
 extern int nsprotopr();
 #ifdef NS
 /* ns protocols */
 extern int nsprotopr();
@@ -152,6 +163,8 @@ struct protox {
          ip_stats,     "ip" },
        { -1,           N_ICMPSTAT,     1,      0,
          icmp_stats,   "icmp" },
          ip_stats,     "ip" },
        { -1,           N_ICMPSTAT,     1,      0,
          icmp_stats,   "icmp" },
+       { -1,           N_IGMPSTAT,     1,      0,
+         igmp_stats,   "igmp"},
        { -1,           -1,             0,      0,
          0,            0 }
 };
        { -1,           -1,             0,      0,
          0,            0 }
 };
@@ -208,6 +221,7 @@ int mflag;
 int    nflag;
 int    pflag;
 int    rflag;
 int    nflag;
 int    pflag;
 int    rflag;
+int    Rflag;  /* Multicast routing stats */
 int    sflag;
 int    tflag;
 int    dflag;
 int    sflag;
 int    tflag;
 int    dflag;
@@ -229,7 +243,7 @@ main(argc, argv)
        int ch;
        void usage(); 
 
        int ch;
        void usage(); 
 
-       while ((ch = getopt(argc, argv, "Aadf:hI:iM:mN:np:rstuw:")) != EOF)
+       while ((ch = getopt(argc, argv, "Aadf:hI:iM:mN:np:Rrstuw:")) != EOF)
                switch((char)ch) {
                case 'A':
                        Aflag = 1;
                switch((char)ch) {
                case 'A':
                        Aflag = 1;
@@ -295,6 +309,9 @@ main(argc, argv)
                case 'r':
                        rflag = 1;
                        break;
                case 'r':
                        rflag = 1;
                        break;
+               case 'R':
+                       Rflag = 1;
+                       break;
                case 's':
                        sflag = 1;
                        break;
                case 's':
                        sflag = 1;
                        break;
@@ -381,6 +398,16 @@ main(argc, argv)
                                (off_t)nl[N_RTREE].n_value);
                exit(0);
        }
                                (off_t)nl[N_RTREE].n_value);
                exit(0);
        }
+       if (Rflag) {
+               if (sflag)
+                       mrt_stats((off_t)nl[N_MRTPROTO].n_value,
+                                 (off_t)nl[N_MRTSTAT].n_value);
+               else
+                       mroutepr((off_t)nl[N_MRTPROTO].n_value,
+                                (off_t)nl[N_MRTTABLE].n_value,
+                                (off_t)nl[N_VIFTABLE].n_value);
+               exit(0);
+       }
     if (af == AF_INET || af == AF_UNSPEC) {
        setprotoent(1);
        setservent(1);
     if (af == AF_INET || af == AF_UNSPEC) {
        setprotoent(1);
        setservent(1);
@@ -500,7 +527,7 @@ usage()
        (void)fprintf(stderr,
 "usage: netstat [-Aan] [-f address_family] [-M core] [-N system]\n");
        (void)fprintf(stderr,
        (void)fprintf(stderr,
 "usage: netstat [-Aan] [-f address_family] [-M core] [-N system]\n");
        (void)fprintf(stderr,
-"               [-himnrs] [-f address_family] [-M core] [-N system]\n");
+"               [-himnrRs] [-f address_family] [-M core] [-N system]\n");
        (void)fprintf(stderr,
 "               [-n] [-I interface] [-M core] [-N system] [-w wait]\n");
        (void)fprintf(stderr,
        (void)fprintf(stderr,
 "               [-n] [-I interface] [-M core] [-N system] [-w wait]\n");
        (void)fprintf(stderr,
diff --git a/usr.bin/netstat/mroute.c b/usr.bin/netstat/mroute.c
new file mode 100644 (file)
index 0000000..bdaf080
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Print DVMRP multicast routing structures and statistics.
+ *
+ * MROUTING 1.0
+ */
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <netinet/in.h>
+#include <netinet/igmp.h>
+#define KERNEL 1
+#include <netinet/ip_mroute.h>
+#undef KERNEL
+
+extern int kmem;
+extern int nflag;
+extern char *routename();
+extern char *netname();
+extern char *plural();
+
+char *plurales(n)
+       int n;
+{
+       return (n == 1? "" : "es");
+}
+
+mroutepr(mrpaddr, mrtaddr, vifaddr)
+       off_t mrpaddr, mrtaddr, vifaddr;
+{
+       u_int mrtproto;
+#if BSD >= 199006
+       struct mrt *mrttable[MRTHASHSIZ];
+       struct mrt *mp;
+       struct mrt mb;
+       struct mrt *mrt = &mb;
+#else
+       struct mbuf *mrttable[MRTHASHSIZ];
+       struct mbuf *mp;
+       struct mbuf mb;
+       struct mrt *mrt = mtod(&mb, struct mrt *);
+#endif
+       struct vif viftable[MAXVIFS];
+       register struct vif *v;
+       register vifi_t vifi;
+       struct in_addr *grp;
+       int i, n;
+       int banner_printed;
+       int saved_nflag;
+
+       if(mrpaddr == 0) {
+               printf("ip_mrtproto: symbol not in namelist\n");
+               return;
+       }
+
+       kvm_read(mrpaddr, (char *)&mrtproto, sizeof(mrtproto));
+       switch (mrtproto) {
+           case 0:
+               printf("no multicast routing compiled into this system\n");
+               return;
+
+           case IGMP_DVMRP:
+               break;
+
+           default:
+               printf("multicast routing protocol %u, unknown\n", mrtproto);
+               return;
+       }
+
+       if (mrtaddr == 0) {
+               printf("mrttable: symbol not in namelist\n");
+               return;
+       }
+       if (vifaddr == 0) {
+               printf("viftable: symbol not in namelist\n");
+               return;
+       }
+
+       saved_nflag = nflag;
+       nflag = 1;
+
+       kvm_read(vifaddr, (char *)viftable, sizeof(viftable));
+       banner_printed = 0;
+       for (vifi = 0, v = viftable; vifi < MAXVIFS; ++vifi, ++v) {
+               struct in_addr v_lcl_grps[1024];
+
+               if (v->v_lcl_addr.s_addr == 0) continue;
+
+               if (!banner_printed) {
+                       printf("\nVirtual Interface Table\n%s%s",
+                              " Vif   Threshold   Local-Address   ",
+                              "Remote-Address   Groups\n");
+                       banner_printed = 1;
+               }
+
+               printf(" %2u       %3u      %-15.15s",
+                       vifi, v->v_threshold, routename(v->v_lcl_addr));
+               printf(" %-15.15s\n",
+                       (v->v_flags & VIFF_TUNNEL) ?
+                               routename(v->v_rmt_addr) : "");
+
+               n = v->v_lcl_grps_n;
+               if (n == 0)
+                       continue;
+               if (n < 0 || n > 1024)
+                       printf("[v_lcl_grps_n = %d!]\n", n);
+
+               kvm_read(v->v_lcl_grps, (char *)v_lcl_grps, 
+                        n * sizeof(struct in_addr));
+               for (i = 0; i < n; ++i)
+                       printf("%51s %-15.15s\n", "",
+                              routename(v_lcl_grps[i]));
+       }
+       if (!banner_printed) printf("\nVirtual Interface Table is empty\n");
+
+       kvm_read(mrtaddr, (char *)mrttable, sizeof(mrttable));
+       banner_printed = 0;
+       for (i = 0; i < MRTHASHSIZ; ++i) {
+           for (mp = mrttable[i]; mp != NULL;
+#if BSD >= 199006
+                mp = mb.mrt_next
+#else
+                mp = mb.m_next
+#endif
+                ) {
+
+               if (!banner_printed) {
+                       printf("\nMulticast Routing Table\n%s",
+                              " Hash  Origin-Subnet  In-Vif  Out-Vifs\n");
+                       banner_printed = 1;
+               }
+               kvm_read(mp, (char *)&mb, sizeof(mb));
+
+
+               printf(" %3u   %-15.15s  %2u   ",
+                       i,
+                       netname(mrt->mrt_origin.s_addr,
+                               ntohl(mrt->mrt_originmask.s_addr)),
+                       mrt->mrt_parent);
+               for (vifi = 0; vifi < MAXVIFS; ++vifi) {
+                       if (viftable[vifi].v_lcl_addr.s_addr) {
+                               if (VIFM_ISSET(vifi, mrt->mrt_children)) {
+                                       printf(" %u%c",
+                                               vifi,
+                                               VIFM_ISSET(vifi,
+                                                 mrt->mrt_leaves) ?
+                                                   '*' : ' ');
+                               } else
+                                       printf("   ");
+                       }
+               }
+               printf("\n");
+           }
+       }
+       if (!banner_printed) printf("\nMulticast Routing Table is empty\n");
+
+       printf("\n");
+       nflag = saved_nflag;
+}
+
+
+mrt_stats(mrpaddr, mstaddr)
+       off_t mrpaddr, mstaddr;
+{
+       u_int mrtproto;
+       struct mrtstat mrtstat;
+
+       if(mrpaddr == 0) {
+               printf("ip_mrtproto: symbol not in namelist\n");
+               return;
+       }
+
+       kvm_read(mrpaddr, (char *)&mrtproto, sizeof(mrtproto));
+       switch (mrtproto) {
+           case 0:
+               printf("no multicast routing compiled into this system\n");
+               return;
+
+           case IGMP_DVMRP:
+               break;
+
+           default:
+               printf("multicast routing protocol %u, unknown\n", mrtproto);
+               return;
+       }
+
+       if (mstaddr == 0) {
+               printf("mrtstat: symbol not in namelist\n");
+               return;
+       }
+
+       kvm_read(mstaddr, (char *)&mrtstat, sizeof(mrtstat));
+       printf("multicast routing:\n");
+       printf(" %10u multicast route lookup%s\n",
+         mrtstat.mrts_mrt_lookups, plural(mrtstat.mrts_mrt_lookups));
+       printf(" %10u multicast route cache miss%s\n",
+         mrtstat.mrts_mrt_misses, plurales(mrtstat.mrts_mrt_misses));
+       printf(" %10u group address lookup%s\n",
+         mrtstat.mrts_grp_lookups, plural(mrtstat.mrts_grp_lookups));
+       printf(" %10u group address cache miss%s\n",
+         mrtstat.mrts_grp_misses, plurales(mrtstat.mrts_grp_misses));
+       printf(" %10u datagram%s with no route for origin\n",
+         mrtstat.mrts_no_route, plural(mrtstat.mrts_no_route));
+       printf(" %10u datagram%s with malformed tunnel options\n",
+         mrtstat.mrts_bad_tunnel, plural(mrtstat.mrts_bad_tunnel));
+       printf(" %10u datagram%s with no room for tunnel options\n",
+         mrtstat.mrts_cant_tunnel, plural(mrtstat.mrts_cant_tunnel));
+       printf(" %10u datagram%s arrived on wrong interface\n",
+         mrtstat.mrts_wrong_if, plural(mrtstat.mrts_wrong_if));
+}
index 25c3943..375f5bd 100644 (file)
@@ -44,7 +44,7 @@
 .Op Ar system
 .Op Ar core
 .Nm netstat
 .Op Ar system
 .Op Ar core
 .Nm netstat
-.Op Fl himnrs
+.Op Fl himnrRs
 .Op Fl f Ar address_family
 .Op Fl M Ar core
 .Op Fl N Ar system
 .Op Fl f Ar address_family
 .Op Fl M Ar core
 .Op Fl N Ar system
@@ -139,6 +139,10 @@ Show the routing tables.
 When
 .Fl s
 is also present, show routing statistics instead.
 When
 .Fl s
 is also present, show routing statistics instead.
+.It Fl R
+Show multicast routing statistics.  When
+.Fl s
+is also present, show multicast routing statistics instead.
 .It Fl f Ar address_family 
 Limit statistics or address control block reports to those
 of the specified
 .It Fl f Ar address_family 
 Limit statistics or address control block reports to those
 of the specified