]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge remote-tracking branch 'origin/master' into pim_lib_work2
authorDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 18 Jan 2017 02:01:56 +0000 (21:01 -0500)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 18 Jan 2017 02:01:56 +0000 (21:01 -0500)
109 files changed:
bgpd/bgp_attr.c
bgpd/bgp_open.c
bgpd/bgp_open.h
bgpd/bgp_packet.c
bgpd/bgp_route.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/Makefile.am
lib/imsg.c
lib/json.c
lib/json.h
lib/log.c
lib/network.c
lib/prefix.h
lib/thread.c
lib/thread.h
lib/wheel.c [new file with mode: 0644]
lib/wheel.h [new file with mode: 0644]
lib/zebra.h
pimd/DEBUG
pimd/Makefile.am
pimd/pim_assert.c
pimd/pim_br.c
pimd/pim_br.h
pimd/pim_cmd.c
pimd/pim_cmd.h
pimd/pim_hello.c
pimd/pim_hello.h
pimd/pim_iface.c
pimd/pim_iface.h
pimd/pim_ifchannel.c
pimd/pim_ifchannel.h
pimd/pim_igmp.c
pimd/pim_igmp.h
pimd/pim_igmp_join.h
pimd/pim_igmpv2.c [new file with mode: 0644]
pimd/pim_igmpv2.h [new file with mode: 0644]
pimd/pim_igmpv3.c
pimd/pim_igmpv3.h
pimd/pim_join.c
pimd/pim_join.h
pimd/pim_macro.c
pimd/pim_main.c
pimd/pim_memory.c
pimd/pim_memory.h
pimd/pim_mroute.c
pimd/pim_mroute.h
pimd/pim_msdp.c [new file with mode: 0644]
pimd/pim_msdp.h [new file with mode: 0644]
pimd/pim_msdp_packet.c [new file with mode: 0644]
pimd/pim_msdp_packet.h [new file with mode: 0644]
pimd/pim_msdp_socket.c [new file with mode: 0644]
pimd/pim_msdp_socket.h [new file with mode: 0644]
pimd/pim_msg.c
pimd/pim_msg.h
pimd/pim_neighbor.c
pimd/pim_neighbor.h
pimd/pim_oil.c
pimd/pim_oil.h
pimd/pim_pim.c
pimd/pim_pim.h
pimd/pim_register.c
pimd/pim_register.h
pimd/pim_rp.c
pimd/pim_rp.h
pimd/pim_rpf.c
pimd/pim_rpf.h
pimd/pim_signals.c
pimd/pim_signals.h
pimd/pim_sock.c
pimd/pim_sock.h
pimd/pim_ssmpingd.c
pimd/pim_ssmpingd.h
pimd/pim_static.c
pimd/pim_str.c
pimd/pim_str.h
pimd/pim_time.c
pimd/pim_time.h
pimd/pim_tlv.c
pimd/pim_tlv.h
pimd/pim_upstream.c
pimd/pim_upstream.h
pimd/pim_util.c
pimd/pim_util.h
pimd/pim_vty.c
pimd/pim_vty.h
pimd/pim_zebra.c
pimd/pim_zebra.h
pimd/pim_zlookup.c
pimd/pim_zlookup.h
pimd/pimd.c
pimd/pimd.h
pimd/test_igmpv3_join.c
zebra/Makefile.am
zebra/if_netlink.c
zebra/kernel_netlink.c
zebra/kernel_netlink.h
zebra/rt_netlink.c
zebra/rt_netlink.h
zebra/zebra_fpm.c
zebra/zebra_fpm_netlink.c
zebra/zebra_mroute.c [new file with mode: 0644]
zebra/zebra_mroute.h [new file with mode: 0644]
zebra/zebra_rib.c
zebra/zebra_static.c
zebra/zebra_static.h
zebra/zebra_vty.c
zebra/zserv.c
zebra/zserv.h

index 4cf1f0daed7ac3f0d88917e106b905b59c2c3172..5c26ecc6ad922746bfb65a967f6dd654d6a7bf2c 100644 (file)
@@ -1849,7 +1849,8 @@ int
 bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
                     struct bgp_nlri *mp_update)
 {
-  afi_t pkt_afi, afi;
+  iana_afi_t pkt_afi;
+  afi_t  afi;
   safi_t pkt_safi, safi;
   bgp_size_t nlri_len;
   size_t start;
@@ -2000,7 +2001,8 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
                      struct bgp_nlri *mp_withdraw)
 {
   struct stream *s;
-  afi_t pkt_afi, afi;
+  iana_afi_t pkt_afi;
+  afi_t afi;
   safi_t pkt_safi, safi;
   u_int16_t withdraw_len;
   struct peer *const peer = args->peer;  
@@ -2648,7 +2650,7 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi,
                         struct attr *attr)
 {
   size_t sizep;
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   /* Set extended bit always to encode the attribute length as 2 bytes */
@@ -3279,7 +3281,7 @@ size_t
 bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi)
 {
   unsigned long attrlen_pnt;
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   /* Set extended bit always to encode the attribute length as 2 bytes */
index 4a06881041f024ca82e2b2ddca8af944f9d1bfdd..7dbb439be1b7342706f716eb51bd6c41aeeb4040 100644 (file)
@@ -188,7 +188,9 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
 {
   struct capability_mp_data mpc;
   struct stream *s = BGP_INPUT (peer);
-  
+  afi_t afi;
+  safi_t safi;
+
   /* Verify length is 4 */
   if (hdr->length != 4)
     {
@@ -204,14 +206,14 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
                peer->host, mpc.afi, mpc.safi);
   
   /* Convert AFI, SAFI to internal values, check. */
-  if (bgp_map_afi_safi_iana2int (mpc.afi, mpc.safi, &mpc.afi, &mpc.safi))
+  if (bgp_map_afi_safi_iana2int (mpc.afi, mpc.safi, &afi, &safi))
     return -1;
    
   /* Now safi remapped, and afi/safi are valid array indices */
-  peer->afc_recv[mpc.afi][mpc.safi] = 1;
+  peer->afc_recv[afi][safi] = 1;
   
-  if (peer->afc[mpc.afi][mpc.safi])
-    peer->afc_nego[mpc.afi][mpc.safi] = 1;
+  if (peer->afc[afi][safi])
+    peer->afc_nego[afi][safi] = 1;
   else 
     return -1;
 
@@ -219,7 +221,7 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
 }
 
 static void
-bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi,
+bgp_capability_orf_not_support (struct peer *peer, iana_afi_t afi, safi_t safi,
                                u_char type, u_char mode)
 {
   if (bgp_debug_neighbor_events(peer))
@@ -247,7 +249,8 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
 {
   struct stream *s = BGP_INPUT (peer);
   struct capability_orf_entry entry;
-  afi_t pkt_afi, afi;
+  iana_afi_t pkt_afi;
+  afi_t afi;
   safi_t pkt_safi, safi;
   u_char type;
   u_char mode;
@@ -274,7 +277,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
       return 0;
     }
   
-  entry.mpc.afi = afi;
+  entry.mpc.afi = pkt_afi;
   entry.mpc.safi = safi;
 
   /* validate number field */
@@ -418,7 +421,7 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
     {
       afi_t afi;
       safi_t safi;
-      afi_t pkt_afi = stream_getw (s);
+      iana_afi_t pkt_afi = stream_getw (s);
       safi_t pkt_safi = stream_getc (s);
       u_char flag = stream_getc (s);
       
@@ -496,7 +499,7 @@ bgp_capability_addpath (struct peer *peer, struct capability_header *hdr)
     {
       afi_t afi;
       safi_t safi;
-      afi_t pkt_afi = stream_getw (s);
+      iana_afi_t pkt_afi = stream_getw (s);
       safi_t pkt_safi = stream_getc (s);
       u_char send_receive = stream_getc (s);
 
@@ -550,9 +553,11 @@ bgp_capability_enhe (struct peer *peer, struct capability_header *hdr)
 
   while (stream_get_getp (s) + 6 <= end)
     {
-      afi_t afi, pkt_afi = stream_getw (s);
+      iana_afi_t pkt_afi = stream_getw (s);
+      afi_t afi;
       safi_t safi, pkt_safi = stream_getw (s);
-      afi_t nh_afi, pkt_nh_afi = stream_getw (s);
+      iana_afi_t pkt_nh_afi = stream_getw (s);
+      afi_t nh_afi;
 
       if (bgp_debug_neighbor_events(peer))
         zlog_debug ("%s Received with afi/safi/next-hop afi: %u/%u/%u",
@@ -1162,7 +1167,7 @@ bgp_open_capability_orf (struct stream *s, struct peer *peer,
   unsigned long orfp;
   unsigned long numberp;
   int number_of_orfs = 0;
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   /* Convert AFI, SAFI to values for packet. */
@@ -1225,7 +1230,8 @@ bgp_open_capability (struct stream *s, struct peer *peer)
 {
   u_char len;
   unsigned long cp, capp, rcapp;
-  afi_t afi, pkt_afi;
+  iana_afi_t pkt_afi;
+  afi_t afi;
   safi_t safi, pkt_safi;
   as_t local_as;
   u_int32_t restart_time;
index b0a396ec11e342cc8a60da293e23ef71e8b5f1c4..9275b3a101d894607a3a2d05baebc9a6631a2560 100644 (file)
@@ -31,7 +31,7 @@ struct capability_header
 /* Generic MP capability data */
 struct capability_mp_data
 {
-  afi_t afi;
+  iana_afi_t afi;
   u_char reserved;
   safi_t safi;
 };
index 38470a3c7e9ff6d513da368d55afacd633d6e9b5..529a3e46ca91415d17ccf2aef06b36457c358b85 100644 (file)
@@ -147,7 +147,7 @@ static struct stream *
 bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi)
 {
   struct stream *s;
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   if (DISABLE_BGP_ANNOUNCE)
@@ -695,7 +695,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
   struct stream *s;
   struct bgp_filter *filter;
   int orf_refresh = 0;
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   if (DISABLE_BGP_ANNOUNCE)
@@ -782,7 +782,7 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi,
                     int capability_code, int action)
 {
   struct stream *s;
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   /* Convert AFI, SAFI to values for packet. */
@@ -1711,7 +1711,8 @@ bgp_keepalive_receive (struct peer *peer, bgp_size_t size)
 static void
 bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
 {
-  afi_t pkt_afi, afi;
+  iana_afi_t pkt_afi;
+  afi_t afi;
   safi_t pkt_safi, safi;
   struct stream *s;
   struct peer_af *paf;
@@ -1932,7 +1933,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
   struct capability_mp_data mpc;
   struct capability_header *hdr;
   u_char action;
-  afi_t pkt_afi, afi;
+  iana_afi_t pkt_afi;
+  afi_t afi;
   safi_t pkt_safi, safi;
 
   end = pnt + length;
index d2abb40ca9f6eea02200cfab73dc105fb960e2cd..75036b549655e1664658a0cef5c6cb37ede830c1 100644 (file)
@@ -2110,10 +2110,10 @@ bgp_maximum_prefix_restart_timer (struct thread *thread)
 }
 
 int
-bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, 
+bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi,
                              safi_t safi, int always)
 {
-  afi_t pkt_afi;
+  iana_afi_t pkt_afi;
   safi_t pkt_safi;
 
   if (!CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
index 5457822f3be672e61dd2b14b30913fc6869a8acb..c08107f0d931393c2285e8bde0c4f79eee2d1125 100644 (file)
@@ -652,7 +652,7 @@ bgp_listen_limit_unset (struct bgp *bgp)
 }
 
 int
-bgp_map_afi_safi_iana2int (afi_t pkt_afi, safi_t pkt_safi,
+bgp_map_afi_safi_iana2int (iana_afi_t pkt_afi, safi_t pkt_safi,
                            afi_t *afi, safi_t *safi)
 {
   /* Map from IANA values to internal values, return error if
@@ -668,7 +668,7 @@ bgp_map_afi_safi_iana2int (afi_t pkt_afi, safi_t pkt_safi,
 
 int
 bgp_map_afi_safi_int2iana (afi_t afi, safi_t safi,
-                           afi_t *pkt_afi, safi_t *pkt_safi)
+                           iana_afi_t *pkt_afi, safi_t *pkt_safi)
 {
   /* Map from internal values to IANA values, return error if
    * internal values are bad (unexpected).
index 2718805130c66308d6ed951ec0ced372024481a5..3dd5523dce24964d33c5a431a8fa1e9423e023ff 100644 (file)
@@ -1355,11 +1355,11 @@ extern void bgp_route_map_terminate(void);
 extern int peer_cmp (struct peer *p1, struct peer *p2);
 
 extern int
-bgp_map_afi_safi_iana2int (afi_t pkt_afi, safi_t pkt_safi,
+bgp_map_afi_safi_iana2int (iana_afi_t pkt_afi, safi_t pkt_safi,
                            afi_t *afi, safi_t *safi);
 extern int
 bgp_map_afi_safi_int2iana (afi_t afi, safi_t safi,
-                           afi_t *pkt_afi, safi_t *pkt_safi);
+                           iana_afi_t *pkt_afi, safi_t *pkt_safi);
 
 extern struct peer_af * peer_af_create (struct peer *, afi_t, safi_t);
 extern struct peer_af * peer_af_find (struct peer *, afi_t, safi_t);
index 1bb2d395c8702a8786be0db885ff87d0a40e7d30..f2c076c4757953eb5bbcbaee45e6b7ffec541560 100644 (file)
@@ -24,7 +24,7 @@ libzebra_la_SOURCES = \
        sigevent.c pqueue.c jhash.c workqueue.c nexthop.c json.c \
        ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c \
        imsg-buffer.c imsg.c skiplist.c \
-       qobj.c \
+       qobj.c wheel.c \
        event_counter.c \
        strlcpy.c \
        strlcat.c
@@ -45,7 +45,7 @@ pkginclude_HEADERS = \
        workqueue.h route_types.h libospf.h nexthop.h json.h \
        ptm_lib.h csv.h bfd.h vrf.h ns.h systemd.h bitfield.h \
        fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h \
-       skiplist.h qobj.h \
+       skiplist.h qobj.h wheel.h \
        event_counter.h
 
 noinst_HEADERS = \
index 246430cdd5a56c026c2d0494be9a53b78a3c9246..fc62c13734a8bbf036580cdc84f75b108f6d770e 100644 (file)
@@ -182,7 +182,8 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
        else
                imsg->fd = -1;
 
-       memcpy(imsg->data, ibuf->r.rptr, datalen);
+       if (imsg->data)
+         memcpy(imsg->data, ibuf->r.rptr, datalen);
 
        if (imsg->hdr.len < av) {
                left = av - imsg->hdr.len;
index 3edd146a8f4a2dacc8e085c06139a91fdf1edac8..ccbecb726ad420d31923f29dd2813bd7c193cf63 100644 (file)
@@ -87,3 +87,18 @@ json_object_free(struct json_object *obj)
 {
   json_object_put(obj);
 }
+
+#if !defined(HAVE_JSON_C_JSON_H)
+int
+json_object_object_get_ex(struct json_object *obj,
+                         const char *key,
+                         struct json_object **value)
+{
+  *value = json_object_object_get(obj, key);
+
+  if (*value)
+    return 1;
+
+  return 0;
+}
+#endif
index bacc799519a902fa834eb313f6f702c2e6c691a5..7e98614280eefde01ccbe35352088775377a8523 100644 (file)
  * so let's just turn it back to the original usage.
  */
 #define json_object_to_json_string_ext(A, B) json_object_to_json_string (A)
+
+extern int json_object_object_get_ex(struct json_object *obj,
+                                          const char *key,
+                                          struct json_object **value);
 #endif
 
 #include "command.h"
index d48534dc181ff75493ac69b4be3e2e50bef5fa95..13592a024040de93ff0436a58e46d1b0d504fb76 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -989,6 +989,7 @@ static const struct zebra_desc_table command_types[] = {
   DESC_ENTRY   (ZEBRA_IPV4_NEXTHOP_DELETE),
   DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_ADD),
   DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_DELETE),
+  DESC_ENTRY    (ZEBRA_IPMR_ROUTE_STATS),
 };
 #undef DESC_ENTRY
 
index 506e0191364d60bc9e3b3b627071f7d5b8e189aa..2b6f2fbab575246e21841116d48274c25eddc8ea 100644 (file)
@@ -62,8 +62,13 @@ writen(int fd, const u_char *ptr, int nbytes)
   while (nleft > 0) 
     {
       nwritten = write(fd, ptr, nleft);
-      
-      if (nwritten <= 0) 
+
+      if (nwritten < 0)
+       {
+         if (!ERRNO_IO_RETRY(errno))
+           return nwritten;
+       }
+      if (nwritten == 0) 
        return (nwritten);
 
       nleft -= nwritten;
index 5e4b1f9c83f9480f72728c2c3be6219f94015e8d..45e6368463b091a499a7ebba34ef60cc5f046c9d 100644 (file)
@@ -134,6 +134,14 @@ struct prefix_ptr
   uintptr_t prefix __attribute__ ((aligned (8)));
 };
 
+struct prefix_sg
+{
+  u_char family;
+  u_char prefixlen;
+  struct in_addr src __attribute ((aligned (8)));
+  struct in_addr grp;
+};
+
 /* helper to get type safety/avoid casts on calls
  * (w/o this, functions accepting all prefix types need casts on the caller
  * side, which strips type safety since the cast will accept any pointer
@@ -222,13 +230,8 @@ extern const char *prefix_family_str (const struct prefix *);
 extern int prefix_blen (const struct prefix *);
 extern int str2prefix (const char *, struct prefix *);
 
-/*
- * 8 groups of 4 bytes of hexadecimal + 7 seperators is 39
- * /128 = 4 bytes
- * Null = 1 byte
- * 39 + 4 + 1 = 44 bytes
- */
-#define PREFIX2STR_BUFFER  44
+#define PREFIX2STR_BUFFER  PREFIX_STRLEN
+
 extern const char *prefix2str (union prefix46constptr, char *, int);
 extern int prefix_match (const struct prefix *, const struct prefix *);
 extern int prefix_same (const struct prefix *, const struct prefix *);
index eac7f3250b8cc251854e23215e2709c1499d8339..64eaae45b9617c26551ba1cf4c55f285a90c8594 100644 (file)
@@ -221,8 +221,8 @@ static void
 vty_out_cpu_thread_history(struct vty* vty,
                           struct cpu_thread_history *a)
 {
-  vty_out(vty, "%10ld.%03ld %9d %8ld %9ld %8ld %9ld",
-         a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
+  vty_out(vty, "%5d %10ld.%03ld %9d %8ld %9ld %8ld %9ld",
+         a->total_active, a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
          a->cpu.total/a->total_calls, a->cpu.max,
          a->real.total/a->total_calls, a->real.max);
   vty_out(vty, " %c%c%c%c%c%c %s%s",
@@ -247,6 +247,7 @@ cpu_record_hash_print(struct hash_backet *bucket,
   if ( !(a->types & *filter) )
        return;
   vty_out_cpu_thread_history(vty,a);
+  totals->total_active += a->total_active;
   totals->total_calls += a->total_calls;
   totals->real.total += a->real.total;
   if (totals->real.max < a->real.max)
@@ -268,7 +269,7 @@ cpu_record_print(struct vty *vty, thread_type filter)
 
   vty_out(vty, "%21s %18s %18s%s",
          "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
-  vty_out(vty, "   Runtime(ms)   Invoked Avg uSec Max uSecs");
+  vty_out(vty, "Active   Runtime(ms)   Invoked Avg uSec Max uSecs");
   vty_out(vty, " Avg uSec Max uSecs");
   vty_out(vty, "  Type  Thread%s", VTY_NEWLINE);
   hash_iterate(cpu_record,
@@ -570,7 +571,9 @@ thread_add_unuse (struct thread_master *m, struct thread *thread)
   assert (m != NULL && thread != NULL);
   assert (thread->next == NULL);
   assert (thread->prev == NULL);
-  assert (thread->type == THREAD_UNUSED);
+
+  thread->type = THREAD_UNUSED;
+  thread->hist->total_active--;
   thread_list_add (&m->unuse, thread);
 }
 
@@ -693,6 +696,7 @@ thread_get (struct thread_master *m, u_char type,
            int (*func) (struct thread *), void *arg, debugargdef)
 {
   struct thread *thread = thread_trim_head (&m->unuse);
+  struct cpu_thread_history tmp;
 
   if (! thread)
     {
@@ -702,11 +706,30 @@ thread_get (struct thread_master *m, u_char type,
   thread->type = type;
   thread->add_type = type;
   thread->master = m;
-  thread->func = func;
   thread->arg = arg;
   thread->index = -1;
   thread->yield = THREAD_YIELD_TIME_SLOT; /* default */
 
+  /*
+   * So if the passed in funcname is not what we have
+   * stored that means the thread->hist needs to be
+   * updated.  We keep the last one around in unused
+   * under the assumption that we are probably
+   * going to immediately allocate the same
+   * type of thread.
+   * This hopefully saves us some serious
+   * hash_get lookups.
+   */
+  if (thread->funcname != funcname ||
+      thread->func != func)
+    {
+      tmp.func = func;
+      tmp.funcname = funcname;
+      thread->hist = hash_get (cpu_record, &tmp,
+                              (void * (*) (void *))cpu_record_hash_alloc);
+    }
+  thread->hist->total_active++;
+  thread->func = func;
   thread->funcname = funcname;
   thread->schedfrom = schedfrom;
   thread->schedfrom_line = fromln;
@@ -1063,7 +1086,6 @@ thread_cancel (struct thread *thread)
       assert(!"Thread should be either in queue or list or array!");
     }
 
-  thread->type = THREAD_UNUSED;
   thread_add_unuse (thread->master, thread);
 }
 
@@ -1086,7 +1108,6 @@ thread_cancel_event (struct thread_master *m, void *arg)
         {
           ret++;
           thread_list_delete (&m->event, t);
-          t->type = THREAD_UNUSED;
           thread_add_unuse (m, t);
         }
     }
@@ -1104,7 +1125,6 @@ thread_cancel_event (struct thread_master *m, void *arg)
         {
           ret++;
           thread_list_delete (&m->ready, t);
-          t->type = THREAD_UNUSED;
           thread_add_unuse (m, t);
         }
     }
@@ -1128,7 +1148,6 @@ thread_run (struct thread_master *m, struct thread *thread,
            struct thread *fetch)
 {
   *fetch = *thread;
-  thread->type = THREAD_UNUSED;
   thread_add_unuse (m, thread);
   return fetch;
 }
@@ -1427,23 +1446,6 @@ thread_call (struct thread *thread)
   unsigned long realtime, cputime;
   RUSAGE_T before, after;
 
- /* Cache a pointer to the relevant cpu history thread, if the thread
-  * does not have it yet.
-  *
-  * Callers submitting 'dummy threads' hence must take care that
-  * thread->cpu is NULL
-  */
-  if (!thread->hist)
-    {
-      struct cpu_thread_history tmp;
-      
-      tmp.func = thread->func;
-      tmp.funcname = thread->funcname;
-      
-      thread->hist = hash_get (cpu_record, &tmp, 
-                    (void * (*) (void *))cpu_record_hash_alloc);
-    }
-
   GETRUSAGE (&before);
   thread->real = before.real;
 
@@ -1488,18 +1490,22 @@ funcname_thread_execute (struct thread_master *m,
                 int val,
                debugargdef)
 {
-  struct thread dummy; 
+  struct cpu_thread_history tmp;
+  struct thread dummy;
 
   memset (&dummy, 0, sizeof (struct thread));
 
   dummy.type = THREAD_EVENT;
   dummy.add_type = THREAD_EXECUTE;
   dummy.master = NULL;
-  dummy.func = func;
   dummy.arg = arg;
   dummy.u.val = val;
 
-  dummy.funcname = funcname;
+  tmp.func = dummy.func = func;
+  tmp.funcname = dummy.funcname = funcname;
+  dummy.hist = hash_get (cpu_record, &tmp,
+                        (void * (*) (void *))cpu_record_hash_alloc);
+
   dummy.schedfrom = schedfrom;
   dummy.schedfrom_line = fromln;
 
index 5c6b96a32decd76d3732c14e269b47cd6a884171..c22bb4828dbcd78befbbe81825fe07aeed0fa7d6 100644 (file)
@@ -115,6 +115,7 @@ struct cpu_thread_history
 {
   int (*func)(struct thread *);
   unsigned int total_calls;
+  unsigned int total_active;
   struct time_stats
   {
     unsigned long total, max;
diff --git a/lib/wheel.c b/lib/wheel.c
new file mode 100644 (file)
index 0000000..fe53dea
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Timer Wheel
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#include "zebra.h"
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+#include "wheel.h"
+#include "log.h"
+
+DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL,   "Timer Wheel")
+DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL_LIST, "Timer Wheel Slot List")
+
+static int debug_timer_wheel = 0;
+
+static int
+wheel_timer_thread (struct thread *t)
+{
+  struct listnode *node, *nextnode;
+  unsigned long long curr_slot;
+  unsigned int slots_to_skip = 1;
+  struct timer_wheel *wheel;
+  void *data;
+
+  wheel = THREAD_ARG(t);
+  THREAD_OFF(wheel->timer);
+
+  wheel->curr_slot += wheel->slots_to_skip;
+
+  curr_slot = wheel->curr_slot % wheel->slots;
+
+  if (debug_timer_wheel)
+    zlog_debug ("%s: Wheel Slot: %lld(%lld) count: %d",
+               __PRETTY_FUNCTION__,
+               wheel->curr_slot,
+               curr_slot, listcount(wheel->wheel_slot_lists[curr_slot]));
+
+  for (ALL_LIST_ELEMENTS (wheel->wheel_slot_lists[curr_slot], node, nextnode, data))
+    (*wheel->slot_run)(data);
+
+  while (list_isempty(wheel->wheel_slot_lists[(curr_slot + slots_to_skip) % wheel->slots]) &&
+        (curr_slot + slots_to_skip ) % wheel->slots != curr_slot)
+    slots_to_skip++;
+
+  wheel->slots_to_skip = slots_to_skip;
+  THREAD_TIMER_MSEC_ON (wheel->master, wheel->timer,
+                       wheel_timer_thread, wheel,
+                       wheel->nexttime * slots_to_skip);
+
+  return 0;
+}
+
+struct timer_wheel *
+wheel_init (struct thread_master *master, int period, size_t slots,
+           unsigned int (*slot_key) (void *),
+           void (*slot_run) (void *))
+{
+  struct timer_wheel *wheel;
+  size_t i;
+
+  wheel = XCALLOC(MTYPE_TIMER_WHEEL, sizeof (struct timer_wheel));
+
+  wheel->slot_key = slot_key;
+  wheel->slot_run = slot_run;
+
+  wheel->period = period;
+  wheel->slots = slots;
+  wheel->curr_slot = 0;
+  wheel->master = master;
+  wheel->nexttime = period / slots;
+
+  wheel->wheel_slot_lists = XCALLOC(MTYPE_TIMER_WHEEL_LIST,
+                                   slots * sizeof (struct listnode *));
+  for (i = 0; i < slots; i++)
+    wheel->wheel_slot_lists[i] = list_new ();
+
+  THREAD_TIMER_MSEC_ON (wheel->master, wheel->timer,
+                       wheel_timer_thread, wheel,
+                       wheel->nexttime);
+
+  return wheel;
+}
+
+void
+wheel_delete (struct timer_wheel *wheel)
+{
+  int i;
+
+  for (i = 0; i < wheel->slots; i++)
+    {
+      list_delete(wheel->wheel_slot_lists[i]);
+    }
+
+  THREAD_OFF(wheel->timer);
+  XFREE(MTYPE_TIMER_WHEEL_LIST, wheel->wheel_slot_lists);
+  XFREE(MTYPE_TIMER_WHEEL, wheel);
+}
+
+int
+wheel_stop (struct timer_wheel *wheel)
+{
+  THREAD_OFF(wheel->timer);
+  return 0;
+}
+
+int
+wheel_start (struct timer_wheel *wheel)
+{
+  if (!wheel->timer)
+    THREAD_TIMER_MSEC_ON (wheel->master, wheel->timer,
+                         wheel_timer_thread, wheel,
+                         wheel->nexttime);
+
+  return 0;
+}
+
+int
+wheel_add_item (struct timer_wheel *wheel, void *item)
+{
+  long long slot;
+
+  slot = (*wheel->slot_key)(item);
+
+  if (debug_timer_wheel)
+    zlog_debug ("%s: Inserting %p: %lld %lld",
+               __PRETTY_FUNCTION__, item,
+               slot, slot % wheel->slots);
+  listnode_add (wheel->wheel_slot_lists[slot % wheel->slots], item);
+
+  return 0;
+}
+
+int
+wheel_remove_item (struct timer_wheel *wheel, void *item)
+{
+  long long slot;
+
+  slot = (*wheel->slot_key)(item);
+
+  if (debug_timer_wheel)
+    zlog_debug ("%s: Removing %p: %lld %lld",
+               __PRETTY_FUNCTION__, item,
+               slot, slot % wheel->slots);
+  listnode_delete (wheel->wheel_slot_lists[slot % wheel->slots], item);
+
+  return 0;
+}
diff --git a/lib/wheel.h b/lib/wheel.h
new file mode 100644 (file)
index 0000000..ddb7998
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Timer Wheel
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef __WHEEL_H__
+#define __WHEEL_H__
+
+struct timer_wheel
+{
+  struct thread_master *master;
+  int slots;
+  long long curr_slot;
+  unsigned int period;
+  unsigned int nexttime;
+  unsigned int slots_to_skip;
+
+  struct list **wheel_slot_lists;
+  struct thread *timer;
+  /*
+   * Key to determine what slot the item belongs in
+   */
+  unsigned int (*slot_key) (void *);
+
+  void (*slot_run) (void *);
+};
+
+struct timer_wheel *wheel_init (struct thread_master *master, int period, size_t slots,
+                               unsigned int (*slot_key) (void *),
+                               void (*slot_run) (void *));
+void wheel_delete (struct timer_wheel *);
+
+/*
+ * Pause the Wheel from running
+ */
+int wheel_stop (struct timer_wheel *wheel);
+
+/*
+ * Start the wheel from running again
+ */
+int wheel_start (struct timer_wheel *wheel);
+
+/*
+ * Add item to a slot setup by the slot_key,
+ * possibly change next time pop.
+ */
+int wheel_add_item (struct timer_wheel *wheel, void *item);
+
+/*
+ * Remove a item to a slot setup by the slot_key,
+ * possibly change next time pop.
+ */
+int wheel_remove_item (struct timer_wheel *wheel, void *item);
+
+#endif
index 5a58cf8e6e6ec0059428b9deef4d1f6f9fac415a..f89e7f5c3f25c90c1431df19f867ae8a7e682955 100644 (file)
@@ -405,6 +405,7 @@ typedef enum {
   ZEBRA_IPV4_NEXTHOP_DELETE,
   ZEBRA_IPV6_NEXTHOP_ADD,
   ZEBRA_IPV6_NEXTHOP_DELETE,
+  ZEBRA_IPMR_ROUTE_STATS,
 } zebra_message_types_t;
 
 /* Marker value used in new Zserv, in the byte location corresponding
@@ -477,6 +478,12 @@ typedef enum {
 #define SAFI_RESERVED_5           5
 #define SAFI_MAX                  6
 
+#define IANA_SAFI_RESERVED            0
+#define IANA_SAFI_UNICAST             1
+#define IANA_SAFI_MULTICAST           2
+#define IANA_SAFI_ENCAP               7
+#define IANA_SAFI_MPLS_VPN            128
+
 /*
  * The above AFI and SAFI definitions are for internal use. The protocol
  * definitions (IANA values) as for example used in BGP protocol packets
@@ -486,15 +493,14 @@ typedef enum {
  * not optimal for use in data-structure sizing.
  * Note: Only useful (i.e., supported) values are defined below.
  */
-#define IANA_AFI_RESERVED             0
-#define IANA_AFI_IPV4                 1
-#define IANA_AFI_IPV6                 2
-
-#define IANA_SAFI_RESERVED            0
-#define IANA_SAFI_UNICAST             1
-#define IANA_SAFI_MULTICAST           2
-#define IANA_SAFI_ENCAP               7
-#define IANA_SAFI_MPLS_VPN            128
+typedef enum {
+  IANA_AFI_RESERVED = 0,
+  IANA_AFI_IPV4 = 1,
+  IANA_AFI_IPV6 = 2,
+  IANA_AFI_L2VPN = 25,
+  IANA_AFI_IPMR = 128,
+  IANA_AFI_IP6MR = 129
+} iana_afi_t;
 
 /* Default Administrative Distance of each protocol. */
 #define ZEBRA_KERNEL_DISTANCE_DEFAULT      0
@@ -528,7 +534,7 @@ typedef uint32_t route_tag_t;
 #define ROUTE_TAG_MAX UINT32_MAX
 #define ROUTE_TAG_PRI PRIu32
 
-static inline afi_t afi_iana2int (afi_t afi)
+static inline afi_t afi_iana2int (iana_afi_t afi)
 {
   if (afi == IANA_AFI_IPV4)
     return AFI_IP;
@@ -537,7 +543,7 @@ static inline afi_t afi_iana2int (afi_t afi)
   return AFI_MAX;
 }
 
-static inline afi_t afi_int2iana (afi_t afi)
+static inline iana_afi_t afi_int2iana (afi_t afi)
 {
   if (afi == AFI_IP)
     return IANA_AFI_IPV4;
index a6ad260e82243d18e61f588c739f4d625ef08ca0..b87542a4a283ef3e47aee6e4987f9c587b21bcd2 100644 (file)
@@ -1,3 +1,7 @@
+<<<<<<< HEAD
+
+=======
+>>>>>>> origin/master
 DEBUG HINTS
 
   - Check the source is issuing multicast packets with TTL high enough
index bc28aa1e929a24ba47eb19be0f28b088436ddcb4..314a744339294891e1f2bab4d1721615b5998b81 100644 (file)
@@ -19,7 +19,6 @@
 # 330, Boston, MA 02111-1307, USA.
 
 # PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands
-# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging
 # PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex
 # PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch
 # PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries
@@ -27,9 +26,8 @@
 
 PIM_DEFS =
 #PIM_DEFS += -DPIM_DEBUG_BYDEFAULT
-PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY
+#PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY
 #PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH
-PIM_DEFS += -DPIM_ZCLIENT_DEBUG
 PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC
 #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL
 
@@ -47,24 +45,26 @@ noinst_PROGRAMS = test_igmpv3_join
 libpim_a_SOURCES = \
        pim_memory.c \
        pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \
-       pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \
+       pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c pim_igmpv2.c \
        pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \
        pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \
        pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \
        pim_msg.c pim_upstream.c pim_rpf.c pim_macro.c \
        pim_ssmpingd.c pim_int.c pim_rp.c \
-       pim_static.c pim_br.c pim_register.c pim_routemap.c
+       pim_static.c pim_br.c pim_register.c pim_routemap.c \
+       pim_msdp.c pim_msdp_socket.c pim_msdp_packet.c
 
 noinst_HEADERS = \
        pim_memory.h \
        pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \
-       pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \
+       pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h pim_igmpv2.h \
        pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \
        pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \
        pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \
        pim_msg.h pim_upstream.h pim_rpf.h pim_macro.h \
        pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_rp.h \
-       pim_static.h pim_br.h pim_register.h
+       pim_static.h pim_br.h pim_register.h \
+       pim_msdp.h pim_msdp_socket.h pim_msdp_packet.h
 
 pimd_SOURCES = \
        pim_main.c $(libpim_a_SOURCES)
index f09540ea00b9ca46c7ef5fb3671bad176c9e3dab..385b897bf9f74d276f984a09b53af8d40ec8a100 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -54,31 +58,23 @@ void pim_ifassert_winner_set(struct pim_ifchannel     *ch,
 
   if (PIM_DEBUG_PIM_EVENTS) {
     if (ch->ifassert_state != new_state) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-      zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
-               __PRETTY_FUNCTION__,
-               src_str, grp_str,
-               pim_ifchannel_ifassert_name(ch->ifassert_state),
-               pim_ifchannel_ifassert_name(new_state),
-               ch->interface->name);
+      zlog_debug("%s: (S,G)=%s assert state changed from %s to %s on interface %s",
+                __PRETTY_FUNCTION__,
+                ch->sg_str,
+                pim_ifchannel_ifassert_name(ch->ifassert_state),
+                pim_ifchannel_ifassert_name(new_state),
+                ch->interface->name);
     }
 
     if (winner_changed) {
-      char src_str[100];
-      char grp_str[100];
-      char was_str[100];
-      char winner_str[100];
-      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      char was_str[INET_ADDRSTRLEN];
+      char winner_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<was?>", ch->ifassert_winner, was_str, sizeof(was_str));
       pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
-      zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s",
-               __PRETTY_FUNCTION__,
-               src_str, grp_str,
-               was_str, winner_str, ch->interface->name);
+      zlog_debug("%s: (S,G)=%s assert winner changed from %s to %s on interface %s",
+                __PRETTY_FUNCTION__,
+                ch->sg_str,
+                was_str, winner_str, ch->interface->name);
     }
   } /* PIM_DEBUG_PIM_EVENTS */
 
@@ -98,7 +94,7 @@ static void on_trace(const char *label,
                     struct interface *ifp, struct in_addr src)
 {
   if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
     zlog_debug("%s: from %s on %s",
               label, src_str, ifp->name);
@@ -138,13 +134,9 @@ static void if_could_assert_do_a1(const char *caller,
 {
   if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
     if (assert_action_a1(ch)) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-      zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+      zlog_warn("%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
                __PRETTY_FUNCTION__, caller,
-               src_str, grp_str, ch->interface->name);
+               ch->sg_str, ch->interface->name);
       /* log warning only */
     }
   }
@@ -156,16 +148,16 @@ static int dispatch_assert(struct interface *ifp,
                           struct pim_assert_metric recv_metric)
 {
   struct pim_ifchannel *ch;
+  struct prefix_sg sg;
 
-  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = source_addr;
+  sg.grp = group_addr;
+  ch = pim_ifchannel_add(ifp, &sg, 0);
   if (!ch) {
-    char source_str[100];
-    char group_str[100];
-    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
-    zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
+    zlog_warn("%s: (S,G)=%s failure creating channel on interface %s",
              __PRETTY_FUNCTION__,
-             source_str, group_str, ifp->name);
+             pim_str_sg_dump (&sg), ifp->name);
     return -1;
   }
 
@@ -193,7 +185,6 @@ static int dispatch_assert(struct interface *ifp,
     }
     else {
       if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
-       zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
        assert_action_a3(ch);
       }
     }
@@ -222,13 +213,9 @@ static int dispatch_assert(struct interface *ifp,
     break;
   default:
     {
-      char source_str[100];
-      char group_str[100];
-      pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-      pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
-      zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
+      zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
                __PRETTY_FUNCTION__,
-               source_str, group_str, ch->ifassert_state, ifp->name);
+               ch->sg_str, ch->ifassert_state, ifp->name);
     }
     return -2;
   }
@@ -241,7 +228,7 @@ int pim_assert_recv(struct interface *ifp,
                    struct in_addr src_addr,
                    uint8_t *buf, int buf_size)
 {
-  struct prefix            msg_group_addr;
+  struct prefix_sg         sg;
   struct prefix            msg_source_addr;
   struct pim_assert_metric msg_metric;
   int offset;
@@ -256,9 +243,10 @@ int pim_assert_recv(struct interface *ifp,
   /*
     Parse assert group addr
    */
-  offset = pim_parse_addr_group (&msg_group_addr, curr, curr_size);
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  offset = pim_parse_addr_group (&sg, curr, curr_size);
   if (offset < 1) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
              __PRETTY_FUNCTION__,
@@ -273,7 +261,7 @@ int pim_assert_recv(struct interface *ifp,
   */
   offset = pim_parse_addr_ucast (&msg_source_addr, curr, curr_size);
   if (offset < 1) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
              __PRETTY_FUNCTION__,
@@ -284,7 +272,7 @@ int pim_assert_recv(struct interface *ifp,
   curr_size -= offset;
 
   if (curr_size != 8) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
              __PRETTY_FUNCTION__,
@@ -311,12 +299,12 @@ int pim_assert_recv(struct interface *ifp,
   msg_metric.route_metric = pim_read_uint32_host(curr);
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char neigh_str[100];
-    char source_str[100];
-    char group_str[100];
+    char neigh_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
     pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
-    pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
+    pim_inet4_dump("<grp?>", sg.grp, group_str, sizeof(group_str));
     zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
               __PRETTY_FUNCTION__, neigh_str, ifp->name,
               source_str, group_str,
@@ -329,7 +317,7 @@ int pim_assert_recv(struct interface *ifp,
 
   return dispatch_assert(ifp,
                         msg_source_addr.u.prefix4,
-                        msg_group_addr.u.prefix4,
+                        sg.grp,
                         msg_metric);
 }
 
@@ -399,7 +387,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
                                                remain,
                                                group_addr);
   if (!pim_msg_curr) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     zlog_warn("%s: failure encoding group address %s: space left=%d",
              __PRETTY_FUNCTION__, group_str, remain);
@@ -412,7 +400,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
                                                remain,
                                                source_addr);
   if (!pim_msg_curr) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: failure encoding source address %s: space left=%d",
              __PRETTY_FUNCTION__, source_str, remain);
@@ -448,17 +436,23 @@ static int pim_assert_do(struct pim_ifchannel *ch,
   int pim_msg_size;
 
   ifp = ch->interface;
-  zassert(ifp);
-
+  if (!ifp)
+    {
+      if (PIM_DEBUG_PIM_TRACE)
+       zlog_debug("%s: channel%s has no associated interface!",
+                  __PRETTY_FUNCTION__, ch->sg_str);
+      return -1;
+    }
   pim_ifp = ifp->info;
   if (!pim_ifp) {
-    zlog_warn("%s: pim not enabled on interface: %s",
-             __PRETTY_FUNCTION__, ifp->name);
+    if (PIM_DEBUG_PIM_TRACE)
+      zlog_debug("%s: channel %s pim not enabled on interface: %s",
+                __PRETTY_FUNCTION__, ch->sg_str, ifp->name);
     return -1;
   }
 
   pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
-                                     ch->group_addr, ch->source_addr,
+                                     ch->sg.grp, ch->sg.src,
                                      metric.metric_preference,
                                      metric.route_metric,
                                      metric.rpt_bit_flag);
@@ -480,19 +474,16 @@ static int pim_assert_do(struct pim_ifchannel *ch,
   pim_hello_require(ifp);
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char source_str[100];
-    char group_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
-    zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+    zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
               __PRETTY_FUNCTION__, 
-              ifp->name, source_str, group_str,
+              ifp->name, ch->sg_str,
               metric.metric_preference,
               metric.route_metric,
               PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
   }
 
   if (pim_msg_send(pim_ifp->pim_sock_fd,
+                  pim_ifp->primary_address,
                   qpim_all_pim_routers_addr,
                   pim_msg,
                   pim_msg_size,
@@ -523,7 +514,7 @@ static int pim_assert_cancel(struct pim_ifchannel *ch)
   metric.rpt_bit_flag      = 0;
   metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
   metric.route_metric      = PIM_ASSERT_ROUTE_METRIC_MAX;
-  metric.ip_address        = ch->source_addr;
+  metric.ip_address        = ch->sg.src;
 
   return pim_assert_do(ch, metric);
 }
@@ -533,28 +524,20 @@ static int on_assert_timer(struct thread *t)
   struct pim_ifchannel *ch;
   struct interface *ifp;
 
-  zassert(t);
   ch = THREAD_ARG(t);
-  zassert(ch);
 
   ifp = ch->interface;
-  zassert(ifp);
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
+    zlog_debug("%s: (S,G)=%s timer expired on interface %s",
               __PRETTY_FUNCTION__,
-              src_str, grp_str, ifp->name);
+              ch->sg_str, ifp->name);
   }
 
-  ch->t_ifassert_timer = 0;
+  ch->t_ifassert_timer = NULL;
 
   switch (ch->ifassert_state) {
   case PIM_IFASSERT_I_AM_WINNER:
-    zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
     assert_action_a3(ch);
     break;
   case PIM_IFASSERT_I_AM_LOSER:
@@ -562,13 +545,10 @@ static int on_assert_timer(struct thread *t)
     break;
   default:
     {
-      char source_str[100];
-      char group_str[100];
-      pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
-      pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
-      zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
-               __PRETTY_FUNCTION__,
-               source_str, group_str, ch->ifassert_state, ifp->name);
+      if (PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
+                 __PRETTY_FUNCTION__,
+                 ch->sg_str, ch->ifassert_state, ifp->name);
     }
   }
 
@@ -577,46 +557,25 @@ static int on_assert_timer(struct thread *t)
 
 static void assert_timer_off(struct pim_ifchannel *ch)
 {
-  struct interface *ifp;
-
-  zassert(ch);
-  ifp = ch->interface;
-  zassert(ifp);
-
   if (PIM_DEBUG_PIM_TRACE) {
     if (ch->t_ifassert_timer) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-      zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
+      zlog_debug("%s: (S,G)=%s cancelling timer on interface %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, ifp->name);
+                ch->sg_str, ch->interface->name);
     }
   }
   THREAD_OFF(ch->t_ifassert_timer);
-  zassert(!ch->t_ifassert_timer);
 }
 
 static void pim_assert_timer_set(struct pim_ifchannel *ch,
                                 int interval)
 {
-  struct interface *ifp;
-
-  zassert(ch);
-  ifp = ch->interface;
-  zassert(ifp);
-
   assert_timer_off(ch);
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
+    zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
               __PRETTY_FUNCTION__,
-              src_str, grp_str, interval, ifp->name);
+              ch->sg_str, interval, ch->interface->name);
   }
 
   THREAD_TIMER_ON(master, ch->t_ifassert_timer,
@@ -644,17 +603,11 @@ int assert_action_a1(struct pim_ifchannel *ch)
   struct interface *ifp = ch->interface;
   struct pim_interface *pim_ifp;
 
-  zassert(ifp);
-
   pim_ifp = ifp->info;
   if (!pim_ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s",
+    zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, ifp->name);
+             ch->sg_str, ifp->name);
     return -1; /* must return since pim_ifp is used below */
   }
 
@@ -664,19 +617,19 @@ int assert_action_a1(struct pim_ifchannel *ch)
                          pim_macro_spt_assert_metric(&ch->upstream->rpf,
                                                      pim_ifp->primary_address));
 
-  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
   if (assert_action_a3(ch)) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
+    zlog_warn("%s: (S,G)=%s assert_action_a3 failure on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, ifp->name);
+             ch->sg_str, ifp->name);
     /* warning only */
   }
 
-  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+  if (ch->ifassert_state != PIM_IFASSERT_I_AM_WINNER)
+    {
+      if (PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
+                 __PRETTY_FUNCTION__, ch->sg_str);
+    }
 
   return 0;
 }
@@ -699,7 +652,12 @@ static void assert_action_a2(struct pim_ifchannel *ch,
   
   pim_assert_timer_set(ch, PIM_ASSERT_TIME);
 
-  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
+  if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER)
+    {
+      if (PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
+                 __PRETTY_FUNCTION__, ch->sg_str);
+    }
 }
 
 /*
@@ -712,24 +670,23 @@ static void assert_action_a2(struct pim_ifchannel *ch,
 */
 static int assert_action_a3(struct pim_ifchannel *ch)
 {
-  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+  if (ch->ifassert_state != PIM_IFASSERT_I_AM_WINNER)
+    {
+      if (PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
+                 __PRETTY_FUNCTION__, ch->sg_str);
+      return -1;
+    }
 
   pim_assert_timer_reset(ch);
 
   if (pim_assert_send(ch)) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-
-    zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
+    zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, ch->interface->name);
+             ch->sg_str, ch->interface->name);
     return -1;
   }
 
-  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
-
   return 0;
 }
 
@@ -746,19 +703,20 @@ static int assert_action_a3(struct pim_ifchannel *ch)
 void assert_action_a4(struct pim_ifchannel *ch)
 {
   if (pim_assert_cancel(ch)) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
+    zlog_warn("%s: failure sending AssertCancel%s on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, ch->interface->name);
+             ch->sg_str, ch->interface->name);
     /* log warning only */
   }
 
   assert_action_a5(ch);
 
-  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+  if (ch->ifassert_state != PIM_IFASSERT_NOINFO)
+    {
+      if (PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
+                 __PRETTY_FUNCTION__, ch->sg_str);
+    }
 }
 
 /*
@@ -772,7 +730,12 @@ void assert_action_a4(struct pim_ifchannel *ch)
 void assert_action_a5(struct pim_ifchannel *ch)
 {
   reset_ifassert_state(ch);
-  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+  if (ch->ifassert_state != PIM_IFASSERT_NOINFO)
+    {
+      if (PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
+                 __PRETTY_FUNCTION__, ch->sg_str);
+    }
 }
 
 /*
@@ -799,6 +762,11 @@ static void assert_action_a6(struct pim_ifchannel *ch,
    if (ch->upstream->join_state == PIM_UPSTREAM_JOINED)
      ch->upstream->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
 
-  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
+  if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER)
+    {
+      if(PIM_DEBUG_PIM_EVENTS)
+       zlog_warn("%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
+                 __PRETTY_FUNCTION__, ch->sg_str);
+    }
 }
 
index 121a45fd16018f26079402d493b5362e964ade81..3f84de79c8d0d4267b33f43009f2b77e4c3ddf76 100644 (file)
@@ -30,8 +30,7 @@
 #include "linklist.h"
 
 struct pim_br {
-  struct in_addr source;
-  struct in_addr group;
+  struct prefix_sg sg;
   struct in_addr pmbr;
 };
 
@@ -40,14 +39,14 @@ struct in_addr pim_br_unknown = { .s_addr = 0 };
 static struct list *pim_br_list = NULL;
 
 struct in_addr
-pim_br_get_pmbr (struct in_addr source, struct in_addr group)
+pim_br_get_pmbr (struct prefix_sg *sg)
 {
   struct listnode *node;
   struct pim_br   *pim_br;
 
   for (ALL_LIST_ELEMENTS_RO (pim_br_list, node, pim_br)) {
-    if (source.s_addr == pim_br->source.s_addr &&
-       group.s_addr == pim_br->group.s_addr)
+    if (sg->src.s_addr == pim_br->sg.src.s_addr &&
+       sg->grp.s_addr == pim_br->sg.grp.s_addr)
       return pim_br->pmbr;
   }
 
@@ -55,14 +54,14 @@ pim_br_get_pmbr (struct in_addr source, struct in_addr group)
 }
 
 void
-pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr br)
+pim_br_set_pmbr (struct prefix_sg *sg, struct in_addr br)
 {
   struct listnode *node, *next;
   struct pim_br *pim_br;
 
   for (ALL_LIST_ELEMENTS (pim_br_list, node, next, pim_br)) {
-    if (source.s_addr == pim_br->source.s_addr &&
-       group.s_addr == pim_br->group.s_addr)
+    if (sg->src.s_addr == pim_br->sg.src.s_addr &&
+       sg->grp.s_addr == pim_br->sg.grp.s_addr)
       break;
   }
 
@@ -73,8 +72,7 @@ pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr br)
       return;
     }
 
-    pim_br->source = source;
-    pim_br->group = group;
+    pim_br->sg = *sg;
 
     listnode_add(pim_br_list, pim_br);
   }
@@ -86,14 +84,14 @@ pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr br)
  * Remove the (S,G) from the stored values
  */
 void
-pim_br_clear_pmbr (struct in_addr source, struct in_addr group)
+pim_br_clear_pmbr (struct prefix_sg *sg)
 {
   struct listnode *node, *next;
   struct pim_br *pim_br;
 
   for (ALL_LIST_ELEMENTS (pim_br_list, node, next, pim_br)) {
-    if (source.s_addr == pim_br->source.s_addr &&
-       group.s_addr == pim_br->group.s_addr)
+    if (sg->src.s_addr == pim_br->sg.src.s_addr &&
+       sg->grp.s_addr == pim_br->sg.grp.s_addr)
       break;
   }
 
index 06b10ada30f0b7d8eab7ffa89e326bb908a47c7c..8e4f719ed0ddcf8c63547e9aedb1c6fb8c9eacc0 100644 (file)
 #ifndef PIM_BR_H
 #define PIM_BR_H
 
-struct in_addr pim_br_get_pmbr (struct in_addr source, struct in_addr group);
+struct in_addr pim_br_get_pmbr (struct prefix_sg *sg);
 
-void pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr value);
-void pim_br_clear_pmbr (struct in_addr source, struct in_addr group);
+void pim_br_set_pmbr (struct prefix_sg *sg, struct in_addr value);
+void pim_br_clear_pmbr (struct prefix_sg *sg);
 
 void pim_br_init (void);
 
index 088713012eceb04cb28b584ff0eb89ca50c92a90..50578bf1e82a078f50b16f4ccf86c2394e031047 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 
+#include "lib/json.h"
 #include "command.h"
 #include "if.h"
 #include "prefix.h"
 #include "zclient.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_mroute.h"
@@ -50,6 +56,8 @@
 #include "pim_zebra.h"
 #include "pim_static.h"
 #include "pim_rp.h"
+#include "pim_zlookup.h"
+#include "pim_msdp.h"
 
 static struct cmd_node pim_global_node = {
   PIM_NODE,
@@ -63,6 +71,13 @@ static struct cmd_node interface_node = {
   1 /* vtysh ? yes */
 };
 
+static struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",
+  1
+};
+
 static void pim_if_membership_clear(struct interface *ifp)
 {
   struct pim_interface *pim_ifp;
@@ -127,9 +142,12 @@ static void pim_if_membership_refresh(struct interface *ifp)
       for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
 
        if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) {
-         pim_ifchannel_local_membership_add(ifp,
-                                            src->source_addr,
-                                            grp->group_addr);
+         struct prefix_sg sg;
+
+         memset (&sg, 0, sizeof (struct prefix_sg));
+         sg.src = src->source_addr;
+         sg.grp = grp->group_addr;
+         pim_ifchannel_local_membership_add(ifp, &sg);
        }
        
       } /* scan group sources */
@@ -146,9 +164,11 @@ static void pim_if_membership_refresh(struct interface *ifp)
 
 static void pim_show_assert(struct vty *vty)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
-  time_t            now;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+  struct listnode *ch_node;
+  struct in_addr ifaddr;
+  time_t now;
   
   now = pim_time_monotonic_sec();
 
@@ -156,55 +176,50 @@ static void pim_show_assert(struct vty *vty)
          "Interface Address         Source          Group           State  Winner          Uptime   Timer%s",
          VTY_NEWLINE);
 
-  for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *ch_node;
-    struct pim_ifchannel *ch;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+    char ch_src_str[INET_ADDRSTRLEN];
+    char ch_grp_str[INET_ADDRSTRLEN];
+    char winner_str[INET_ADDRSTRLEN];
+    char uptime[10];
+    char timer[10];
 
-    pim_ifp = ifp->info;
+    pim_ifp = ch->interface->info;
     
     if (!pim_ifp)
       continue;
 
     ifaddr = pim_ifp->primary_address;
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
-      char ch_src_str[100];
-      char ch_grp_str[100];
-      char winner_str[100];
-      char uptime[10];
-      char timer[10];
-
-      pim_inet4_dump("<ch_src?>", ch->source_addr,
-                    ch_src_str, sizeof(ch_src_str));
-      pim_inet4_dump("<ch_grp?>", ch->group_addr,
-                    ch_grp_str, sizeof(ch_grp_str));
-      pim_inet4_dump("<assrt_win?>", ch->ifassert_winner,
-                    winner_str, sizeof(winner_str));
+    pim_inet4_dump("<ch_src?>", ch->sg.src,
+                  ch_src_str, sizeof(ch_src_str));
+    pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+                  ch_grp_str, sizeof(ch_grp_str));
+    pim_inet4_dump("<assrt_win?>", ch->ifassert_winner,
+                  winner_str, sizeof(winner_str));
 
-      pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
-      pim_time_timer_to_mmss(timer, sizeof(timer),
-                            ch->t_ifassert_timer);
+    pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
+    pim_time_timer_to_mmss(timer, sizeof(timer),
+                          ch->t_ifassert_timer);
 
-      vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             ch_src_str,
-             ch_grp_str,
-             pim_ifchannel_ifassert_name(ch->ifassert_state),
-             winner_str,
-             uptime,
-             timer,
-             VTY_NEWLINE);
-    } /* scan interface channels */
-  } /* scan interfaces */
+    vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s",
+           ch->interface->name,
+           inet_ntoa(ifaddr),
+           ch_src_str,
+           ch_grp_str,
+           pim_ifchannel_ifassert_name(ch->ifassert_state),
+           winner_str,
+           uptime,
+           timer,
+           VTY_NEWLINE);
+  } /* scan interface channels */
 }
 
 static void pim_show_assert_internal(struct vty *vty)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct listnode *ch_node;
+  struct pim_ifchannel *ch;
+  struct in_addr ifaddr;
 
   vty_out(vty,
          "CA:   CouldAssert%s"
@@ -217,210 +232,301 @@ static void pim_show_assert_internal(struct vty *vty)
          "Interface Address         Source          Group           CA  eCA ATD eATD%s",
          VTY_NEWLINE);
 
-  for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *ch_node;
-    struct pim_ifchannel *ch;
-
-    pim_ifp = ifp->info;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+    pim_ifp = ch->interface->info;
     
     if (!pim_ifp)
       continue;
 
     ifaddr = pim_ifp->primary_address;
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
-      char ch_src_str[100];
-      char ch_grp_str[100];
+    char ch_src_str[INET_ADDRSTRLEN];
+    char ch_grp_str[INET_ADDRSTRLEN];
 
-      pim_inet4_dump("<ch_src?>", ch->source_addr,
-                    ch_src_str, sizeof(ch_src_str));
-      pim_inet4_dump("<ch_grp?>", ch->group_addr,
-                    ch_grp_str, sizeof(ch_grp_str));
-      vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             ch_src_str,
-             ch_grp_str,
-             PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
-             pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
-             PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no",
-             pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no",
-             VTY_NEWLINE);
-    } /* scan interface channels */
-  } /* scan interfaces */
+    pim_inet4_dump("<ch_src?>", ch->sg.src,
+                  ch_src_str, sizeof(ch_src_str));
+    pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+                  ch_grp_str, sizeof(ch_grp_str));
+    vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s",
+           ch->interface->name,
+           inet_ntoa(ifaddr),
+           ch_src_str,
+           ch_grp_str,
+           PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
+           pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
+           PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no",
+           pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no",
+           VTY_NEWLINE);
+  } /* scan interface channels */
 }
 
 static void pim_show_assert_metric(struct vty *vty)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
-  
+  struct pim_interface *pim_ifp;
+  struct listnode *ch_node;
+  struct pim_ifchannel *ch;
+  struct in_addr ifaddr;
+
   vty_out(vty,
          "Interface Address         Source          Group           RPT Pref Metric Address        %s",
          VTY_NEWLINE);
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *ch_node;
-    struct pim_ifchannel *ch;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+    pim_ifp = ch->interface->info;
 
-    pim_ifp = ifp->info;
-    
     if (!pim_ifp)
       continue;
 
     ifaddr = pim_ifp->primary_address;
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
-      char ch_src_str[100];
-      char ch_grp_str[100];
-      char addr_str[100];
-      struct pim_assert_metric am;
+    char ch_src_str[INET_ADDRSTRLEN];
+    char ch_grp_str[INET_ADDRSTRLEN];
+    char addr_str[INET_ADDRSTRLEN];
+    struct pim_assert_metric am;
 
-      am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
+    am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
 
-      pim_inet4_dump("<ch_src?>", ch->source_addr,
-                    ch_src_str, sizeof(ch_src_str));
-      pim_inet4_dump("<ch_grp?>", ch->group_addr,
-                    ch_grp_str, sizeof(ch_grp_str));
-      pim_inet4_dump("<addr?>", am.ip_address,
-                    addr_str, sizeof(addr_str));
+    pim_inet4_dump("<ch_src?>", ch->sg.src,
+                  ch_src_str, sizeof(ch_src_str));
+    pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+                  ch_grp_str, sizeof(ch_grp_str));
+    pim_inet4_dump("<addr?>", am.ip_address,
+                  addr_str, sizeof(addr_str));
 
-      vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             ch_src_str,
-             ch_grp_str,
-             am.rpt_bit_flag ? "yes" : "no",
-             am.metric_preference,
-             am.route_metric,
-             addr_str,
-             VTY_NEWLINE);
+    vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s",
+           ch->interface->name,
+           inet_ntoa(ifaddr),
+           ch_src_str,
+           ch_grp_str,
+           am.rpt_bit_flag ? "yes" : "no",
+           am.metric_preference,
+           am.route_metric,
+           addr_str,
+           VTY_NEWLINE);
     } /* scan interface channels */
-  } /* scan interfaces */
 }
 
 static void pim_show_assert_winner_metric(struct vty *vty)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct listnode *ch_node;
+  struct pim_ifchannel *ch;
+  struct in_addr ifaddr;
   
   vty_out(vty,
          "Interface Address         Source          Group           RPT Pref Metric Address        %s",
          VTY_NEWLINE);
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *ch_node;
-    struct pim_ifchannel *ch;
-
-    pim_ifp = ifp->info;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+    pim_ifp = ch->interface->info;
     
     if (!pim_ifp)
       continue;
 
     ifaddr = pim_ifp->primary_address;
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
-      char ch_src_str[100];
-      char ch_grp_str[100];
-      char addr_str[100];
-      struct pim_assert_metric *am;
-      char pref_str[5];
-      char metr_str[7];
-
-      am = &ch->ifassert_winner_metric;
-
-      pim_inet4_dump("<ch_src?>", ch->source_addr,
-                    ch_src_str, sizeof(ch_src_str));
-      pim_inet4_dump("<ch_grp?>", ch->group_addr,
-                    ch_grp_str, sizeof(ch_grp_str));
-      pim_inet4_dump("<addr?>", am->ip_address,
-                    addr_str, sizeof(addr_str));
-
-      if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
-       snprintf(pref_str, sizeof(pref_str), "INFI");
-      else
-       snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference);
+    char ch_src_str[INET_ADDRSTRLEN];
+    char ch_grp_str[INET_ADDRSTRLEN];
+    char addr_str[INET_ADDRSTRLEN];
+    struct pim_assert_metric *am;
+    char pref_str[5];
+    char metr_str[7];
+
+    am = &ch->ifassert_winner_metric;
+
+    pim_inet4_dump("<ch_src?>", ch->sg.src,
+                  ch_src_str, sizeof(ch_src_str));
+    pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+                  ch_grp_str, sizeof(ch_grp_str));
+    pim_inet4_dump("<addr?>", am->ip_address,
+                  addr_str, sizeof(addr_str));
+
+    if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
+      snprintf(pref_str, sizeof(pref_str), "INFI");
+    else
+      snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference);
+
+    if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
+      snprintf(metr_str, sizeof(metr_str), "INFI");
+    else
+      snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);
+
+    vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s",
+           ch->interface->name,
+           inet_ntoa(ifaddr),
+           ch_src_str,
+           ch_grp_str,
+           am->rpt_bit_flag ? "yes" : "no",
+           pref_str,
+           metr_str,
+           addr_str,
+           VTY_NEWLINE);
+  } /* scan interface channels */
+}
 
-      if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
-       snprintf(metr_str, sizeof(metr_str), "INFI");
-      else
-       snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);
+static void json_object_pim_ifp_add(struct json_object *json, struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
 
-      vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             ch_src_str,
-             ch_grp_str,
-             am->rpt_bit_flag ? "yes" : "no",
-             pref_str,
-             metr_str,
-             addr_str,
-             VTY_NEWLINE);
-    } /* scan interface channels */
-  } /* scan interfaces */
+  pim_ifp = ifp->info;
+  json_object_string_add(json, "name", ifp->name);
+  json_object_string_add(json, "state", if_is_up(ifp) ? "up" : "down");
+  json_object_string_add(json, "address", inet_ntoa(pim_ifp->primary_address));
+  json_object_int_add(json, "index", ifp->ifindex);
+
+  if (if_is_multicast(ifp))
+    json_object_boolean_true_add(json, "flagMulticast");
+
+  if (if_is_broadcast(ifp))
+    json_object_boolean_true_add(json, "flagBroadcast");
+
+  if (ifp->flags & IFF_ALLMULTI)
+    json_object_boolean_true_add(json, "flagAllMulticast");
+
+  if (ifp->flags & IFF_PROMISC)
+    json_object_boolean_true_add(json, "flagPromiscuous");
+
+  if (PIM_IF_IS_DELETED(ifp))
+    json_object_boolean_true_add(json, "flagDeleted");
+
+  if (pim_if_lan_delay_enabled(ifp))
+    json_object_boolean_true_add(json, "lanDelayEnabled");
 }
 
-static void pim_show_membership(struct vty *vty)
+static void pim_show_membership(struct vty *vty, u_char uj)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct listnode *ch_node;
+  struct pim_ifchannel *ch;
+  enum json_type type;
+  json_object *json = NULL;
+  json_object *json_iface = NULL;
+  json_object *json_row = NULL;
+  json_object *json_tmp = NULL;
 
-  vty_out(vty,
-         "Interface Address         Source          Group           Membership%s",
-         VTY_NEWLINE);
+  json = json_object_new_object();
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *ch_node;
-    struct pim_ifchannel *ch;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+
+    pim_ifp = ch->interface->info;
 
-    pim_ifp = ifp->info;
-    
     if (!pim_ifp)
       continue;
 
-    ifaddr = pim_ifp->primary_address;
+    char ch_src_str[INET_ADDRSTRLEN];
+    char ch_grp_str[INET_ADDRSTRLEN];
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
-      char ch_src_str[100];
-      char ch_grp_str[100];
+    pim_inet4_dump("<ch_src?>", ch->sg.src,
+                  ch_src_str, sizeof(ch_src_str));
+    pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+                  ch_grp_str, sizeof(ch_grp_str));
 
-      pim_inet4_dump("<ch_src?>", ch->source_addr,
-                    ch_src_str, sizeof(ch_src_str));
-      pim_inet4_dump("<ch_grp?>", ch->group_addr,
-                    ch_grp_str, sizeof(ch_grp_str));
+    json_object_object_get_ex(json, ch->interface->name, &json_iface);
 
-      vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             ch_src_str,
-             ch_grp_str,
-             ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ?
-             "NOINFO" : "INCLUDE",
-             VTY_NEWLINE);
-    } /* scan interface channels */
-  } /* scan interfaces */
+    if (!json_iface) {
+      json_iface = json_object_new_object();
+      json_object_pim_ifp_add(json_iface, ch->interface);
+      json_object_object_add(json, ch->interface->name, json_iface);
+    }
+
+    json_row = json_object_new_object();
+    json_object_string_add(json_row, "source", ch_src_str);
+    json_object_string_add(json_row, "group", ch_grp_str);
+    json_object_string_add(json_row, "localMembership",
+                          ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ?  "NOINFO" : "INCLUDE");
+    json_object_object_add(json_iface, ch_grp_str, json_row);
+  } /* scan interface channels */
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+  } else {
+    vty_out(vty,
+            "Interface  Address          Source           Group            Membership%s",
+            VTY_NEWLINE);
+
+    /*
+     * Example of the json data we are traversing
+     *
+     * {
+     *   "swp3":{
+     *     "name":"swp3",
+     *     "state":"up",
+     *     "address":"10.1.20.1",
+     *     "index":5,
+     *     "flagMulticast":true,
+     *     "flagBroadcast":true,
+     *     "lanDelayEnabled":true,
+     *     "226.10.10.10":{
+     *       "source":"*",
+     *       "group":"226.10.10.10",
+     *       "localMembership":"INCLUDE"
+     *     }
+     *   }
+     * }
+     */
+
+    /* foreach interface */
+    json_object_object_foreach(json, key, val) {
+
+      /* Find all of the keys where the val is an object. In the example
+       * above the only one is 226.10.10.10
+       */
+      json_object_object_foreach(val, if_field_key, if_field_val) {
+        type = json_object_get_type(if_field_val);
+
+        if (type == json_type_object) {
+          vty_out(vty, "%-9s  ", key);
+
+          json_object_object_get_ex(val, "address", &json_tmp);
+          vty_out(vty, "%-15s  ", json_object_get_string(json_tmp));
+
+          json_object_object_get_ex(if_field_val, "source", &json_tmp);
+          vty_out(vty, "%-15s  ", json_object_get_string(json_tmp));
+
+          /* Group */
+          vty_out(vty, "%-15s  ", if_field_key);
+
+          json_object_object_get_ex(if_field_val, "localMembership", &json_tmp);
+          vty_out(vty, "%-10s%s", json_object_get_string(json_tmp), VTY_NEWLINE);
+        }
+      }
+    }
+  }
 
+  json_object_free(json);
 }
 
-static void igmp_show_interfaces(struct vty *vty)
+static void pim_print_ifp_flags(struct vty *vty, struct interface *ifp, int mloop)
+{
+  vty_out(vty, "Flags%s", VTY_NEWLINE);
+  vty_out(vty, "-----%s", VTY_NEWLINE);
+  vty_out(vty, "All Multicast   : %s%s", (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", VTY_NEWLINE);
+  vty_out(vty, "Broadcast       : %s%s", if_is_broadcast(ifp)? "yes" : "no", VTY_NEWLINE);
+  vty_out(vty, "Deleted         : %s%s", PIM_IF_IS_DELETED(ifp) ? "yes" : "no", VTY_NEWLINE);
+  vty_out(vty, "Interface Index : %d%s", ifp->ifindex, VTY_NEWLINE);
+  vty_out(vty, "Multicast       : %s%s", if_is_multicast(ifp) ? "yes" : "no", VTY_NEWLINE);
+  vty_out(vty, "Multicast Loop  : %d%s", mloop, VTY_NEWLINE);
+  vty_out(vty, "Promiscuous     : %s%s", (ifp->flags & IFF_PROMISC) ? "yes" : "no", VTY_NEWLINE);
+  vty_out(vty, "%s", VTY_NEWLINE);
+  vty_out(vty, "%s", VTY_NEWLINE);
+}
+
+static void igmp_show_interfaces(struct vty *vty, u_char uj)
 {
   struct listnode  *node;
   struct interface *ifp;
   time_t            now;
-  
+  json_object *json = NULL;
+  json_object *json_row = NULL;
+
   now = pim_time_monotonic_sec();
 
-  vty_out(vty,
-         "Interface Address         ifIndex Socket Uptime   Multi Broad MLoop AllMu Prmsc Del%s",
-         VTY_NEWLINE);
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out(vty,
+            "Interface  State          Address  V  Querier  Query Timer    Uptime%s",
+            VTY_NEWLINE);
 
   for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
     struct pim_interface *pim_ifp;
@@ -428,33 +534,176 @@ static void igmp_show_interfaces(struct vty *vty)
     struct igmp_sock *igmp;
 
     pim_ifp = ifp->info;
-    
+
     if (!pim_ifp)
       continue;
 
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
       char uptime[10];
-      int mloop;
+      char query_hhmmss[10];
 
       pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation);
+      pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer);
 
-      mloop = pim_socket_mcastloop_get(igmp->fd);
-      
-      vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
-             ifp->name,
-             inet_ntoa(igmp->ifaddr),
-             ifp->ifindex,
-             igmp->fd,
-             uptime,
-             if_is_multicast(ifp) ? "yes" : "no",
-             if_is_broadcast(ifp) ? "yes" : "no",
-             (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
-             (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
-             (ifp->flags & IFF_PROMISC) ? "yes" : "no",
-             PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
-             VTY_NEWLINE);
+      if (uj) {
+        json_row = json_object_new_object();
+        json_object_pim_ifp_add(json_row, ifp);
+        json_object_string_add(json_row, "upTime", uptime);
+        json_object_int_add(json_row, "version", pim_ifp->igmp_version);
+
+        if (igmp->t_igmp_query_timer) {
+          json_object_boolean_true_add(json_row, "querier");
+          json_object_string_add(json_row, "queryTimer", query_hhmmss);
+        }
+
+        json_object_object_add(json, ifp->name, json_row);
+
+      } else {
+        vty_out(vty, "%-9s  %5s  %15s  %d  %7s  %11s  %8s%s",
+                ifp->name,
+                if_is_up(ifp) ? "up" : "down",
+                inet_ntoa(igmp->ifaddr),
+                pim_ifp->igmp_version,
+               igmp->t_igmp_query_timer ? "local" : "other",
+               query_hhmmss,
+                uptime,
+                VTY_NEWLINE);
+      }
+    }
+  }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
+}
+
+static void igmp_show_interfaces_single(struct vty *vty, const char *ifname, u_char uj)
+{
+  struct igmp_sock *igmp;
+  struct interface *ifp;
+  struct listnode  *node;
+  struct listnode *sock_node;
+  struct pim_interface *pim_ifp;
+  char uptime[10];
+  char query_hhmmss[10];
+  char other_hhmmss[10];
+  int found_ifname = 0;
+  int sqi;
+  int mloop;
+  long gmi_msec;  /* Group Membership Interval */
+  long lmqt_msec;
+  long ohpi_msec;
+  long oqpi_msec; /* Other Querier Present Interval */
+  long qri_msec;
+  time_t now;
+
+  json_object *json = NULL;
+  json_object *json_row = NULL;
+
+  if (uj)
+    json = json_object_new_object();
+
+  now = pim_time_monotonic_sec();
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
+    pim_ifp = ifp->info;
+
+    if (!pim_ifp)
+      continue;
+
+    if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name))
+      continue;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      found_ifname = 1;
+      pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation);
+      pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer);
+      pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer);
+
+      gmi_msec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+                                   igmp->querier_query_interval,
+                                   pim_ifp->igmp_query_max_response_time_dsec);
+
+      sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+
+      oqpi_msec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
+                                     igmp->querier_query_interval,
+                                     pim_ifp->igmp_query_max_response_time_dsec);
+
+      lmqt_msec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec,
+                                     igmp->querier_robustness_variable);
+
+      ohpi_msec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
+                                     igmp->querier_query_interval,
+                                     pim_ifp->igmp_query_max_response_time_dsec) * 100;
+
+      qri_msec = pim_ifp->igmp_query_max_response_time_dsec * 100;
+      mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
+
+      if (uj) {
+        json_row = json_object_new_object();
+        json_object_pim_ifp_add(json_row, ifp);
+        json_object_string_add(json_row, "upTime", uptime);
+        json_object_string_add(json_row, "querier", igmp->t_igmp_query_timer ? "local" : "other");
+        json_object_int_add(json_row, "queryStartCount", igmp->startup_query_count);
+        json_object_string_add(json_row, "queryQueryTimer", query_hhmmss);
+        json_object_string_add(json_row, "queryOtherTimer", other_hhmmss);
+        json_object_int_add(json_row, "version", pim_ifp->igmp_version);
+        json_object_int_add(json_row, "timerGroupMembershipIntervalMsec", gmi_msec);
+        json_object_int_add(json_row, "timerLastMemberQueryMsec", lmqt_msec);
+        json_object_int_add(json_row, "timerOlderHostPresentIntervalMsec", ohpi_msec);
+        json_object_int_add(json_row, "timerOtherQuerierPresentIntervalMsec", oqpi_msec);
+        json_object_int_add(json_row, "timerQueryInterval", igmp->querier_query_interval);
+        json_object_int_add(json_row, "timerQueryResponseIntervalMsec", qri_msec);
+        json_object_int_add(json_row, "timerRobustnessVariable", igmp->querier_robustness_variable);
+        json_object_int_add(json_row, "timerStartupQueryInterval", sqi);
+
+        json_object_object_add(json, ifp->name, json_row);
+
+      } else {
+        vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE);
+        vty_out(vty, "State     : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE);
+        vty_out(vty, "Address   : %s%s", inet_ntoa(pim_ifp->primary_address), VTY_NEWLINE);
+        vty_out(vty, "Uptime    : %s%s", uptime, VTY_NEWLINE);
+        vty_out(vty, "Version   : %d%s", pim_ifp->igmp_version, VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+
+        vty_out(vty, "Querier%s", VTY_NEWLINE);
+        vty_out(vty, "-------%s", VTY_NEWLINE);
+        vty_out(vty, "Querier     : %s%s", igmp->t_igmp_query_timer ? "local" : "other", VTY_NEWLINE);
+        vty_out(vty, "Start Count : %d%s", igmp->startup_query_count, VTY_NEWLINE);
+        vty_out(vty, "Query Timer : %s%s", query_hhmmss, VTY_NEWLINE);
+        vty_out(vty, "Other Timer : %s%s", other_hhmmss, VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+
+        vty_out(vty, "Timers%s", VTY_NEWLINE);
+        vty_out(vty, "------%s", VTY_NEWLINE);
+        vty_out(vty, "Group Membership Interval      : %lis%s", gmi_msec/1000, VTY_NEWLINE);
+        vty_out(vty, "Last Member Query Time         : %lis%s", lmqt_msec/1000, VTY_NEWLINE);
+        vty_out(vty, "Older Host Present Interval    : %lis%s", ohpi_msec/1000, VTY_NEWLINE);
+        vty_out(vty, "Other Querier Present Interval : %lis%s", oqpi_msec/1000, VTY_NEWLINE);
+        vty_out(vty, "Query Interval                 : %ds%s", igmp->querier_query_interval, VTY_NEWLINE);
+        vty_out(vty, "Query Response Interval        : %lis%s", qri_msec/1000, VTY_NEWLINE);
+        vty_out(vty, "Robustness Variable            : %d%s", igmp->querier_robustness_variable, VTY_NEWLINE);
+        vty_out(vty, "Startup Query Interval         : %ds%s", sqi, VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+
+        pim_print_ifp_flags(vty, ifp, mloop);
+      }
     }
   }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  } else {
+    if (!found_ifname)
+      vty_out (vty, "%% No such interface%s", VTY_NEWLINE);
+  }
 }
 
 static void igmp_show_interface_join(struct vty *vty)
@@ -474,7 +723,7 @@ static void igmp_show_interface_join(struct vty *vty)
     struct listnode *join_node;
     struct igmp_join *ij;
     struct in_addr pri_addr;
-    char pri_addr_str[100];
+    char pri_addr_str[INET_ADDRSTRLEN];
 
     pim_ifp = ifp->info;
     
@@ -488,8 +737,8 @@ static void igmp_show_interface_join(struct vty *vty)
     pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
 
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, join_node, ij)) {
-      char group_str[100];
-      char source_str[100];
+      char group_str[INET_ADDRSTRLEN];
+      char source_str[INET_ADDRSTRLEN];
       char uptime[10];
 
       pim_time_uptime(uptime, sizeof(uptime), now - ij->sock_creation);
@@ -510,115 +759,297 @@ static void igmp_show_interface_join(struct vty *vty)
 
 }
 
-static void show_interface_address(struct vty *vty)
+static void pim_show_interfaces_single(struct vty *vty, const char *ifname, u_char uj)
 {
-  struct listnode  *ifpnode;
+  struct in_addr ifaddr;
   struct interface *ifp;
-  
-  vty_out(vty,
-         "Interface Primary         Secondary      %s",
-         VTY_NEWLINE);
-
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifpnode, ifp)) {
-    struct listnode  *ifcnode;
-    struct connected *ifc;
-    struct in_addr    pri_addr;
-    char pri_addr_str[100];
-
-    pri_addr = pim_find_primary_addr(ifp);
-
-    pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
-    
-    for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) {
-      char sec_addr_str[100];
-      struct prefix *p = ifc->address;
-
-      if (p->family != AF_INET)
-       continue;
-
-      if (p->u.prefix4.s_addr == pri_addr.s_addr) {
-       sec_addr_str[0] = '\0';
-      }
-      else {
-       pim_inet4_dump("<sec?>", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str));
-      }
-    
-      vty_out(vty, "%-9s %-15s %-15s%s",
-             ifp->name,
-             pri_addr_str,
-             sec_addr_str,
-             VTY_NEWLINE);
-    }
-  }
-}
+  struct listnode *neighnode;
+  struct listnode*node;
+  struct listnode *upnode;
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
+  struct pim_upstream *up;
+  time_t now;
+  char dr_str[INET_ADDRSTRLEN];
+  char dr_uptime[10];
+  char expire[10];
+  char grp_str[INET_ADDRSTRLEN];
+  char hello_period[10];
+  char hello_timer[10];
+  char neigh_src_str[INET_ADDRSTRLEN];
+  char src_str[INET_ADDRSTRLEN];
+  char stat_uptime[10];
+  char uptime[10];
+  int mloop;
+  int found_ifname = 0;
+  int print_header;
+  json_object *json = NULL;
+  json_object *json_row = NULL;
+  json_object *json_pim_neighbor = NULL;
+  json_object *json_pim_neighbors = NULL;
+  json_object *json_group = NULL;
+  json_object *json_group_source = NULL;
+  json_object *json_fhr_sources = NULL;
+  struct pim_secondary_addr *sec_addr;
+  struct listnode *sec_node;
 
-static void pim_show_dr(struct vty *vty)
-{
-  struct listnode  *node;
-  struct interface *ifp;
-  time_t            now;
-  
   now = pim_time_monotonic_sec();
 
-  vty_out(vty,
-         "NonPri: Number of neighbors missing DR Priority hello option%s"
-         "DrPri: Designated Router Priority sent%s%s",
-         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-  
-  vty_out(vty, "Interface Address         DR              Uptime   Elections Changes NonPri      DrPri%s", VTY_NEWLINE);
+  if (uj)
+    json = json_object_new_object();
 
   for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    char dr_str[100];
-    char dr_uptime[10];
-
     pim_ifp = ifp->info;
-    
+
     if (!pim_ifp)
       continue;
 
     if (pim_ifp->pim_sock_fd < 0)
       continue;
 
+    if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name))
+      continue;
+
+    found_ifname = 1;
     ifaddr = pim_ifp->primary_address;
+    pim_inet4_dump("<dr?>", pim_ifp->pim_dr_addr, dr_str, sizeof(dr_str));
+    pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), now, pim_ifp->pim_dr_election_last);
+    pim_time_timer_to_hhmmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer);
+    pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period);
+    pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start);
+    mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
 
-    pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime),
-                         now, pim_ifp->pim_dr_election_last);
+    if (uj) {
+      json_row = json_object_new_object();
+      json_object_pim_ifp_add(json_row, ifp);
 
-    pim_inet4_dump("<dr?>", pim_ifp->pim_dr_addr,
-                  dr_str, sizeof(dr_str));
+      if (pim_ifp->update_source.s_addr != INADDR_ANY) {
+        json_object_string_add(json_row, "useSource", inet_ntoa(pim_ifp->update_source));
+      }
+      if (pim_ifp->sec_addr_list) {
+        json_object *sec_list = NULL;
+
+        sec_list = json_object_new_array();
+        for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) {
+          json_object_array_add(sec_list, json_object_new_string(inet_ntoa(sec_addr->addr)));
+        }
+        json_object_object_add(json_row, "secondaryAddressList", sec_list);
+      }
 
-    vty_out(vty, "%-9s %-15s %-15s %8s %9d %7d %6d %10d%s",
-           ifp->name,
-           inet_ntoa(ifaddr),
-           dr_str,
-           dr_uptime,
-           pim_ifp->pim_dr_election_count,
-           pim_ifp->pim_dr_election_changes,
-           pim_ifp->pim_dr_num_nondrpri_neighbors,
-           pim_ifp->pim_dr_priority,
-           VTY_NEWLINE);
+      // PIM neighbors
+      if (pim_ifp->pim_neighbor_list->count) {
+        json_pim_neighbors = json_object_new_object();
+
+        for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+          json_pim_neighbor = json_object_new_object();
+          pim_inet4_dump("<src?>", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str));
+          pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+          pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+          json_object_string_add(json_pim_neighbor, "address", neigh_src_str);
+          json_object_string_add(json_pim_neighbor, "upTime", uptime);
+          json_object_string_add(json_pim_neighbor, "holdtime", expire);
+
+          json_object_object_add(json_pim_neighbors, neigh_src_str, json_pim_neighbor);
+        }
+
+        json_object_object_add(json_row, "neighbors", json_pim_neighbors);
+      }
+
+      json_object_string_add(json_row, "drAddress", dr_str);
+      json_object_int_add(json_row, "drPriority", pim_ifp->pim_dr_priority);
+      json_object_string_add(json_row, "drUptime", dr_uptime);
+      json_object_int_add(json_row, "drElections", pim_ifp->pim_dr_election_count);
+      json_object_int_add(json_row, "drChanges", pim_ifp->pim_dr_election_changes);
+
+      // FHR
+      for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+        if (ifp ==  up->rpf.source_nexthop.interface) {
+          if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) {
+            if (!json_fhr_sources) {
+              json_fhr_sources = json_object_new_object();
+            }
+
+            pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+            pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+            pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+
+            /* Does this group live in json_fhr_sources?  If not create it. */
+            json_object_object_get_ex(json_fhr_sources, grp_str, &json_group);
+
+            if (!json_group) {
+                json_group = json_object_new_object();
+                json_object_object_add(json_fhr_sources, grp_str, json_group);
+            }
+
+            json_group_source = json_object_new_object();
+            json_object_string_add(json_group_source, "source", src_str);
+            json_object_string_add(json_group_source, "group", grp_str);
+            json_object_string_add(json_group_source, "upTime", uptime);
+            json_object_object_add(json_group, src_str, json_group_source);
+          }
+        }
+      }
+
+      if (json_fhr_sources) {
+        json_object_object_add(json_row, "firstHopRouter", json_fhr_sources);
+      }
+
+      json_object_int_add(json_row, "helloPeriod", pim_ifp->pim_hello_period);
+      json_object_string_add(json_row, "helloTimer", hello_timer);
+      json_object_string_add(json_row, "helloStatStart", stat_uptime);
+      json_object_int_add(json_row, "helloReceived", pim_ifp->pim_ifstat_hello_recv);
+      json_object_int_add(json_row, "helloReceivedFailed", pim_ifp->pim_ifstat_hello_recvfail);
+      json_object_int_add(json_row, "helloSend", pim_ifp->pim_ifstat_hello_sent);
+      json_object_int_add(json_row, "hellosendFailed", pim_ifp->pim_ifstat_hello_sendfail);
+      json_object_int_add(json_row, "helloGenerationId", pim_ifp->pim_generation_id);
+      json_object_int_add(json_row, "flagMulticastLoop", mloop);
+
+      json_object_int_add(json_row, "effectivePropagationDelay", pim_if_effective_propagation_delay_msec(ifp));
+      json_object_int_add(json_row, "effectiveOverrideInterval", pim_if_effective_override_interval_msec(ifp));
+      json_object_int_add(json_row, "joinPruneOverrideInterval", pim_if_jp_override_interval_msec(ifp));
+
+      json_object_int_add(json_row, "propagationDelay", pim_ifp->pim_propagation_delay_msec);
+      json_object_int_add(json_row, "propagationDelayHighest", pim_ifp->pim_neighbors_highest_propagation_delay_msec);
+      json_object_int_add(json_row, "overrideInterval", pim_ifp->pim_override_interval_msec);
+      json_object_int_add(json_row, "overrideIntervalHighest", pim_ifp->pim_neighbors_highest_override_interval_msec);
+      json_object_object_add(json, ifp->name, json_row);
+
+    } else {
+      vty_out(vty, "Interface  : %s%s", ifp->name, VTY_NEWLINE);
+      vty_out(vty, "State      : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE);
+      if (pim_ifp->update_source.s_addr != INADDR_ANY) {
+        vty_out(vty, "Use Source : %s%s", inet_ntoa(pim_ifp->update_source), VTY_NEWLINE);
+      }
+      if (pim_ifp->sec_addr_list) {
+        vty_out(vty, "Address    : %s (primary)%s",
+                                    inet_ntoa(ifaddr), VTY_NEWLINE);
+        for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) {
+          vty_out(vty, "             %s%s",
+                                    inet_ntoa(sec_addr->addr), VTY_NEWLINE);
+        }
+      } else {
+        vty_out(vty, "Address    : %s%s", inet_ntoa(ifaddr), VTY_NEWLINE);
+      }
+      vty_out(vty, "%s", VTY_NEWLINE);
+
+      // PIM neighbors
+      print_header = 1;
+
+      for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+
+        if (print_header) {
+          vty_out(vty, "PIM Neighbors%s", VTY_NEWLINE);
+          vty_out(vty, "-------------%s", VTY_NEWLINE);
+          print_header = 0;
+        }
+
+        pim_inet4_dump("<src?>", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str));
+        pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+        pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+        vty_out(vty, "%-15s : up for %s, holdtime expires in %s%s", neigh_src_str, uptime, expire, VTY_NEWLINE);
+      }
+
+      if (!print_header) {
+        vty_out(vty, "%s", VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+      }
+
+      vty_out(vty, "Designated Router%s", VTY_NEWLINE);
+      vty_out(vty, "-----------------%s", VTY_NEWLINE);
+      vty_out(vty, "Address   : %s%s", dr_str, VTY_NEWLINE);
+      vty_out(vty, "Priority  : %d%s", pim_ifp->pim_dr_priority, VTY_NEWLINE);
+      vty_out(vty, "Uptime    : %s%s", dr_uptime, VTY_NEWLINE);
+      vty_out(vty, "Elections : %d%s", pim_ifp->pim_dr_election_count, VTY_NEWLINE);
+      vty_out(vty, "Changes   : %d%s", pim_ifp->pim_dr_election_changes, VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+
+      // FHR
+      print_header = 1;
+      for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+        if (strcmp(ifp->name, up->rpf.source_nexthop.interface->name) == 0) {
+          if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) {
+
+            if (print_header) {
+              vty_out(vty, "FHR - First Hop Router%s", VTY_NEWLINE);
+              vty_out(vty, "----------------------%s", VTY_NEWLINE);
+              print_header = 0;
+            }
+
+            pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+            pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+            pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+            vty_out(vty, "%s : %s is a source, uptime is %s%s", grp_str, src_str, uptime, VTY_NEWLINE);
+          }
+        }
+      }
+
+      if (!print_header) {
+        vty_out(vty, "%s", VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+      }
+
+      vty_out(vty, "Hellos%s", VTY_NEWLINE);
+      vty_out(vty, "------%s", VTY_NEWLINE);
+      vty_out(vty, "Period         : %d%s", pim_ifp->pim_hello_period, VTY_NEWLINE);
+      vty_out(vty, "Timer          : %s%s", hello_timer, VTY_NEWLINE);
+      vty_out(vty, "StatStart      : %s%s", stat_uptime, VTY_NEWLINE);
+      vty_out(vty, "Receive        : %d%s", pim_ifp->pim_ifstat_hello_recv, VTY_NEWLINE);
+      vty_out(vty, "Receive Failed : %d%s", pim_ifp->pim_ifstat_hello_recvfail, VTY_NEWLINE);
+      vty_out(vty, "Send           : %d%s", pim_ifp->pim_ifstat_hello_sent, VTY_NEWLINE);
+      vty_out(vty, "Send Failed    : %d%s", pim_ifp->pim_ifstat_hello_sendfail, VTY_NEWLINE);
+      vty_out(vty, "Generation ID  : %08x%s", pim_ifp->pim_generation_id, VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+
+      pim_print_ifp_flags(vty, ifp, mloop);
+
+      vty_out(vty, "Join Prune Interval%s", VTY_NEWLINE);
+      vty_out(vty, "-------------------%s", VTY_NEWLINE);
+      vty_out(vty, "LAN Delay                    : %s%s", pim_if_lan_delay_enabled(ifp) ? "yes" : "no", VTY_NEWLINE);
+      vty_out(vty, "Effective Propagation Delay  : %d msec%s", pim_if_effective_propagation_delay_msec(ifp), VTY_NEWLINE);
+      vty_out(vty, "Effective Override Interval  : %d msec%s", pim_if_effective_override_interval_msec(ifp), VTY_NEWLINE);
+      vty_out(vty, "Join Prune Override Interval : %d msec%s", pim_if_jp_override_interval_msec(ifp), VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+
+      vty_out(vty, "LAN Prune Delay%s", VTY_NEWLINE);
+      vty_out(vty, "---------------%s", VTY_NEWLINE);
+      vty_out(vty, "Propagation Delay           : %d msec%s", pim_ifp->pim_propagation_delay_msec, VTY_NEWLINE);
+      vty_out(vty, "Propagation Delay (Highest) : %d msec%s", pim_ifp->pim_neighbors_highest_propagation_delay_msec, VTY_NEWLINE);
+      vty_out(vty, "Override Interval           : %d msec%s", pim_ifp->pim_override_interval_msec, VTY_NEWLINE);
+      vty_out(vty, "Override Interval (Highest) : %d msec%s", pim_ifp->pim_neighbors_highest_override_interval_msec, VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+    }
+  }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  } else {
+    if (!found_ifname)
+      vty_out (vty, "%% No such interface%s", VTY_NEWLINE);
   }
 }
 
-static void pim_show_hello(struct vty *vty)
+static void pim_show_interfaces(struct vty *vty, u_char uj)
 {
-  struct listnode  *node;
   struct interface *ifp;
-  time_t            now;
-  
-  now = pim_time_monotonic_sec();
-  
-  vty_out(vty, "Interface Address         Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE);
+  struct listnode *node;
+  struct listnode *upnode;
+  struct pim_interface *pim_ifp;
+  struct pim_upstream *up;
+  int fhr = 0;
+  int pim_nbrs = 0;
+  json_object *json = NULL;
+  json_object *json_row = NULL;
+  json_object *json_tmp;
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    char hello_period[10];
-    char hello_timer[10];
-    char stat_uptime[10];
+  json = json_object_new_object();
 
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
     pim_ifp = ifp->info;
     
     if (!pim_ifp)
@@ -627,117 +1058,136 @@ static void pim_show_hello(struct vty *vty)
     if (pim_ifp->pim_sock_fd < 0)
       continue;
 
-    ifaddr = pim_ifp->primary_address;
+    pim_nbrs = pim_ifp->pim_neighbor_list->count;
+    fhr = 0;
 
-    pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer);
-    pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period);
-    pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start);
+    for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up))
+      if (ifp ==  up->rpf.source_nexthop.interface)
+        if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
+          fhr++;
 
-    vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s",
-           ifp->name,
-           inet_ntoa(ifaddr),
-           hello_period,
-           hello_timer,
-           stat_uptime,
-           pim_ifp->pim_ifstat_hello_recv,
-           pim_ifp->pim_ifstat_hello_recvfail,
-           pim_ifp->pim_ifstat_hello_sent,
-           pim_ifp->pim_ifstat_hello_sendfail,
-           VTY_NEWLINE);
-  }
-}
+    json_row = json_object_new_object();
+    json_object_pim_ifp_add(json_row, ifp);
+    json_object_int_add(json_row, "pimNeighbors", pim_nbrs);
+    json_object_int_add(json_row, "firstHopRouter", fhr);
+    json_object_string_add(json_row, "pimDesignatedRouter", inet_ntoa(pim_ifp->pim_dr_addr));
 
-static void pim_show_interfaces(struct vty *vty)
-{
-  struct listnode  *node;
-  struct interface *ifp;
-  time_t            now;
-  
-  now = pim_time_monotonic_sec();
+    if (pim_ifp->pim_dr_addr.s_addr == pim_ifp->primary_address.s_addr)
+      json_object_boolean_true_add(json_row, "pimDesignatedRouterLocal");
 
-  vty_out(vty, "Interface Address         ifIndex Socket Uptime   Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE);
+    json_object_object_add(json, ifp->name, json_row);
+  }
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    char uptime[10];
-    int mloop;
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+  } else {
+    vty_out(vty, "Interface  State          Address  PIM Nbrs           PIM DR  FHR%s", VTY_NEWLINE);
 
-    pim_ifp = ifp->info;
-    
-    if (!pim_ifp)
-      continue;
+    json_object_object_foreach(json, key, val) {
+      vty_out(vty, "%-9s  ", key);
 
-    if (pim_ifp->pim_sock_fd < 0)
-      continue;
+      json_object_object_get_ex(val, "state", &json_tmp);
+      vty_out(vty, "%5s  ", json_object_get_string(json_tmp));
 
-    ifaddr = pim_ifp->primary_address;
+      json_object_object_get_ex(val, "address", &json_tmp);
+      vty_out(vty, "%15s  ", json_object_get_string(json_tmp));
 
-    pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation);
+      json_object_object_get_ex(val, "pimNeighbors", &json_tmp);
+      vty_out(vty, "%8d  ", json_object_get_int(json_tmp));
 
-    mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
-      
-    vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
-           ifp->name,
-           inet_ntoa(ifaddr),
-           ifp->ifindex,
-           pim_ifp->pim_sock_fd,
-           uptime,
-           if_is_multicast(ifp) ? "yes" : "no",
-           if_is_broadcast(ifp) ? "yes" : "no",
-           (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
-           (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
-           (ifp->flags & IFF_PROMISC) ? "yes" : "no",
-           PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
-           VTY_NEWLINE);
+      if (json_object_object_get_ex(val, "pimDesignatedRouterLocal", &json_tmp)) {
+        vty_out(vty, "%15s  ", "local");
+      } else {
+        json_object_object_get_ex(val, "pimDesignatedRouter", &json_tmp);
+        vty_out(vty, "%15s  ", json_object_get_string(json_tmp));
+      }
+
+      json_object_object_get_ex(val, "firstHopRouter", &json_tmp);
+      vty_out(vty, "%3d%s", json_object_get_int(json_tmp), VTY_NEWLINE);
+    }
   }
+
+  json_object_free(json);
 }
 
-static void pim_show_join(struct vty *vty)
+static void pim_show_join(struct vty *vty, u_char uj)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct in_addr ifaddr;
+  struct listnode *ch_node;
+  struct pim_ifchannel *ch;
   time_t            now;
+  json_object *json = NULL;
+  json_object *json_iface = NULL;
+  json_object *json_row = NULL;
+  json_object *json_grp = NULL;
   
   now = pim_time_monotonic_sec();
 
-  vty_out(vty,
-         "Interface Address         Source          Group           State  Uptime   Expire Prune%s",
-         VTY_NEWLINE);
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out(vty,
+            "Interface Address         Source          Group           State  Uptime   Expire Prune%s",
+            VTY_NEWLINE);
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *ch_node;
-    struct pim_ifchannel *ch;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
 
-    pim_ifp = ifp->info;
+    pim_ifp = ch->interface->info;
     
     if (!pim_ifp)
       continue;
 
     ifaddr = pim_ifp->primary_address;
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
-      char ch_src_str[100];
-      char ch_grp_str[100];
-      char uptime[10];
-      char expire[10];
-      char prune[10];
-
-      pim_inet4_dump("<ch_src?>", ch->source_addr,
-                    ch_src_str, sizeof(ch_src_str));
-      pim_inet4_dump("<ch_grp?>", ch->group_addr,
-                    ch_grp_str, sizeof(ch_grp_str));
-
-      pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation);
-      pim_time_timer_to_mmss(expire, sizeof(expire),
-                            ch->t_ifjoin_expiry_timer);
-      pim_time_timer_to_mmss(prune, sizeof(prune),
-                            ch->t_ifjoin_prune_pending_timer);
+    char ch_src_str[INET_ADDRSTRLEN];
+    char ch_grp_str[INET_ADDRSTRLEN];
+    char uptime[10];
+    char expire[10];
+    char prune[10];
+
+    pim_inet4_dump("<ch_src?>", ch->sg.src,
+                  ch_src_str, sizeof(ch_src_str));
+    pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+                  ch_grp_str, sizeof(ch_grp_str));
+
+    pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation);
+    pim_time_timer_to_mmss(expire, sizeof(expire),
+                          ch->t_ifjoin_expiry_timer);
+    pim_time_timer_to_mmss(prune, sizeof(prune),
+                          ch->t_ifjoin_prune_pending_timer);
+
+    if (uj) {
+      json_object_object_get_ex(json, ch->interface->name, &json_iface);
+
+      if (!json_iface) {
+       json_iface = json_object_new_object();
+       json_object_pim_ifp_add(json_iface, ch->interface);
+       json_object_object_add(json, ch->interface->name, json_iface);
+      }
 
+      json_row = json_object_new_object();
+      json_object_string_add(json_row, "source", ch_src_str);
+      json_object_string_add(json_row, "group", ch_grp_str);
+      json_object_string_add(json_row, "upTime", uptime);
+      json_object_string_add(json_row, "expire", expire);
+      json_object_string_add(json_row, "prune", prune);
+      json_object_string_add(json_row, "channelJoinName", pim_ifchannel_ifjoin_name(ch->ifjoin_state));
+      if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+        json_object_int_add(json_row, "SGRpt", 1);
+
+      json_object_object_get_ex(json_iface, ch_grp_str, &json_grp);
+      if (!json_grp)
+        {
+          json_grp = json_object_new_object();
+          json_object_object_add(json_grp, ch_src_str, json_row);
+          json_object_object_add(json_iface, ch_grp_str, json_grp);
+        }
+      else
+        json_object_object_add(json_grp, ch_src_str, json_row);
+    } else {
       vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s",
-             ifp->name,
+             ch->interface->name,
              inet_ntoa(ifaddr),
              ch_src_str,
              ch_grp_str,
@@ -746,172 +1196,366 @@ static void pim_show_join(struct vty *vty)
              expire,
              prune,
              VTY_NEWLINE);
-    } /* scan interface channels */
-  } /* scan interfaces */
+    }
+  } /* scan interface channels */
 
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
 }
 
-static void pim_show_neighbors(struct vty *vty)
+static void pim_show_neighbors_single(struct vty *vty, const char *neighbor, u_char uj)
 {
   struct listnode  *node;
+  struct listnode *neighnode;
   struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
   time_t            now;
-  
-  now = pim_time_monotonic_sec();
+  int found_neighbor = 0;
+  int option_address_list;
+  int option_dr_priority;
+  int option_generation_id;
+  int option_holdtime;
+  int option_lan_prune_delay;
+  int option_t_bit;
+  char uptime[10];
+  char expire[10];
+  char neigh_src_str[INET_ADDRSTRLEN];
+
+  json_object *json = NULL;
+  json_object *json_ifp = NULL;
+  json_object *json_row = NULL;
 
-  vty_out(vty,
-         "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s"
-         "            T=can_disable_join_suppression%s%s",
-         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  now = pim_time_monotonic_sec();
 
-  vty_out(vty, "Interface Address         Neighbor        Uptime   Timer Holdt DrPri GenId    Recv  %s", VTY_NEWLINE);
+  if (uj)
+    json = json_object_new_object();
 
   for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *neighnode;
-    struct pim_neighbor *neigh;
-
     pim_ifp = ifp->info;
-    
+
     if (!pim_ifp)
       continue;
 
     if (pim_ifp->pim_sock_fd < 0)
       continue;
 
-    ifaddr = pim_ifp->primary_address;
-
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
-      char uptime[10];
-      char holdtime[10];
-      char expire[10];
-      char neigh_src_str[100];
-      char recv[7];
-
       pim_inet4_dump("<src?>", neigh->source_addr,
                     neigh_src_str, sizeof(neigh_src_str));
+
+      /*
+       * The user can specify either the interface name or the PIM neighbor IP.
+       * If this pim_ifp matches neither then skip.
+       */
+      if (strcmp(neighbor, "detail") &&
+          strcmp(neighbor, ifp->name) &&
+          strcmp(neighbor, neigh_src_str))
+        continue;
+
+      found_neighbor = 1;
       pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
-      pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime);
-      pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer);
-
-      recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME)                     ? 'H' : ' ';
-      recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY)              ? 'L' : ' ';
-      recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)                  ? 'P' : ' ';
-      recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID)                ? 'G' : ' ';
-      recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST)                 ? 'A' : ' ';
-      recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' ';
-      recv[6] = '\0';
-
-      vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             neigh_src_str,
-             uptime,
-             expire,
-             holdtime,
-             neigh->dr_priority,
-             neigh->generation_id,
-             recv,
-             VTY_NEWLINE);
+      pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+      option_address_list = 0;
+      option_dr_priority = 0;
+      option_generation_id = 0;
+      option_holdtime = 0;
+      option_lan_prune_delay = 0;
+      option_t_bit = 0;
+
+      if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST))
+        option_address_list = 1;
+
+      if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY))
+        option_dr_priority = 1;
+
+      if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID))
+        option_generation_id = 1;
+
+      if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME))
+        option_holdtime = 1;
+
+      if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY))
+        option_lan_prune_delay = 1;
+
+      if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION))
+        option_t_bit = 1;
+
+      if (uj) {
+
+        /* Does this ifp live in json?  If not create it. */
+        json_object_object_get_ex(json, ifp->name, &json_ifp);
+
+        if (!json_ifp) {
+          json_ifp = json_object_new_object();
+          json_object_pim_ifp_add(json_ifp, ifp);
+          json_object_object_add(json, ifp->name, json_ifp);
+        }
+
+        json_row = json_object_new_object();
+        json_object_string_add(json_row, "interface", ifp->name);
+        json_object_string_add(json_row, "address", neigh_src_str);
+        json_object_string_add(json_row, "upTime", uptime);
+        json_object_string_add(json_row, "holdtime", expire);
+        json_object_int_add(json_row, "drPriority", neigh->dr_priority);
+        json_object_int_add(json_row, "generationId", neigh->generation_id);
+
+        if (option_address_list)
+          json_object_boolean_true_add(json_row, "helloOptionAddressList");
+
+        if (option_dr_priority)
+          json_object_boolean_true_add(json_row, "helloOptionDrPriority");
+
+        if (option_generation_id)
+          json_object_boolean_true_add(json_row, "helloOptionGenerationId");
+
+        if (option_holdtime)
+          json_object_boolean_true_add(json_row, "helloOptionHoldtime");
+
+        if (option_lan_prune_delay)
+          json_object_boolean_true_add(json_row, "helloOptionLanPruneDelay");
+
+        if (option_t_bit)
+          json_object_boolean_true_add(json_row, "helloOptionTBit");
+
+        json_object_object_add(json_ifp, neigh_src_str, json_row);
+
+      } else {
+        vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE);
+        vty_out(vty, "Neighbor  : %s%s", neigh_src_str, VTY_NEWLINE);
+        vty_out(vty, "    Uptime                         : %s%s", uptime, VTY_NEWLINE);
+        vty_out(vty, "    Holdtime                       : %s%s", expire, VTY_NEWLINE);
+        vty_out(vty, "    DR Priority                    : %d%s", neigh->dr_priority, VTY_NEWLINE);
+        vty_out(vty, "    Generation ID                  : %08x%s", neigh->generation_id, VTY_NEWLINE);
+        vty_out(vty, "    Override Interval (msec)       : %d%s", neigh->override_interval_msec, VTY_NEWLINE);
+        vty_out(vty, "    Propagation Delay (msec)       : %d%s", neigh->propagation_delay_msec, VTY_NEWLINE);
+        vty_out(vty, "    Hello Option - Address List    : %s%s", option_address_list ? "yes" : "no", VTY_NEWLINE);
+        vty_out(vty, "    Hello Option - DR Priority     : %s%s", option_dr_priority ? "yes" : "no", VTY_NEWLINE);
+        vty_out(vty, "    Hello Option - Generation ID   : %s%s", option_generation_id? "yes" : "no", VTY_NEWLINE);
+        vty_out(vty, "    Hello Option - Holdtime        : %s%s", option_holdtime ? "yes" : "no", VTY_NEWLINE);
+        vty_out(vty, "    Hello Option - LAN Prune Delay : %s%s", option_lan_prune_delay ? "yes" : "no", VTY_NEWLINE);
+        vty_out(vty, "    Hello Option - T-bit           : %s%s", option_t_bit ? "yes" : "no", VTY_NEWLINE);
+        vty_out(vty, "%s", VTY_NEWLINE);
+      }
     }
+  }
 
-
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  } else {
+    {
+      if (!found_neighbor)
+        vty_out (vty, "%% No such interface or neighbor%s", VTY_NEWLINE);
+    }
   }
 }
 
-static void pim_show_lan_prune_delay(struct vty *vty)
+static void
+pim_show_state(struct vty *vty, const char *src_or_group, const char *group, u_char uj)
 {
-  struct listnode  *node;
-  struct interface *ifp;
-
-  vty_out(vty,
-         "PrDly=propagation_delay (msec)           OvInt=override_interval (msec)%s"
-         "HiDly=highest_propagation_delay (msec)   HiInt=highest_override_interval (msec)%s"
-         "NoDly=number_of_non_lan_delay_neighbors%s"
-         "T=t_bit LPD=lan_prune_delay_hello_option%s%s",
-         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  struct channel_oil *c_oil;
+  struct listnode *node;
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_ifp_in = NULL;
+  json_object *json_ifp_out = NULL;
+  json_object *json_source = NULL;
+  time_t now;
+  int first_oif;
+  now = pim_time_monotonic_sec();
 
-  vty_out(vty, "Interface Address         PrDly OvInt NoDly HiDly HiInt T | Neighbor        LPD PrDly OvInt T%s", VTY_NEWLINE);
+  if (uj) {
+    json = json_object_new_object();
+  } else {
+    vty_out(vty, "%sSource           Group            IIF    OIL%s", VTY_NEWLINE, VTY_NEWLINE);
+  }
 
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-    struct listnode *neighnode;
-    struct pim_neighbor *neigh;
+  for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+    char grp_str[INET_ADDRSTRLEN];
+    char src_str[INET_ADDRSTRLEN];
+    char in_ifname[16];
+    char out_ifname[16];
+    int oif_vif_index;
+    struct interface *ifp_in;
+    first_oif = 1;
 
-    pim_ifp = ifp->info;
-    
-    if (!pim_ifp)
+    if (!c_oil->installed)
       continue;
 
-    if (pim_ifp->pim_sock_fd < 0)
-      continue;
+    pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, sizeof(src_str));
+    ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
 
-    ifaddr = pim_ifp->primary_address;
+    if (ifp_in)
+      strcpy(in_ifname, ifp_in->name);
+    else
+      strcpy(in_ifname, "<iif?>");
 
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
-      char neigh_src_str[100];
+    if (src_or_group)
+      {
+        if (strcmp(src_or_group, src_str) && strcmp(src_or_group, grp_str))
+          continue;
 
-      pim_inet4_dump("<src?>", neigh->source_addr,
-                    neigh_src_str, sizeof(neigh_src_str));
+        if (group && strcmp(group, grp_str))
+          continue;
+      }
 
-      vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u | %-15s %-3s %5u %5u %1u%s",
-             ifp->name,
-             inet_ntoa(ifaddr),
-             pim_ifp->pim_propagation_delay_msec,
-             pim_ifp->pim_override_interval_msec,
-             pim_ifp->pim_number_of_nonlandelay_neighbors,
-             pim_ifp->pim_neighbors_highest_propagation_delay_msec,
-             pim_ifp->pim_neighbors_highest_override_interval_msec,
-             PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)),
-             neigh_src_str,
-             PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no",
-             neigh->propagation_delay_msec,
-             neigh->override_interval_msec,
-             PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options,
-                                                 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)),
-             VTY_NEWLINE);
+    if (uj) {
+
+      /* Find the group, create it if it doesn't exist */
+      json_object_object_get_ex(json, grp_str, &json_group);
+
+      if (!json_group) {
+        json_group = json_object_new_object();
+        json_object_object_add(json, grp_str, json_group);
+      }
+
+      /* Find the source nested under the group, create it if it doesn't exist */
+      json_object_object_get_ex(json_group, src_str, &json_source);
+
+      if (!json_source) {
+        json_source = json_object_new_object();
+        json_object_object_add(json_group, src_str, json_source);
+      }
+
+      /* Find the inbound interface nested under the source, create it if it doesn't exist */
+      json_object_object_get_ex(json_source, in_ifname, &json_ifp_in);
+
+      if (!json_ifp_in) {
+        json_ifp_in = json_object_new_object();
+        json_object_object_add(json_source, in_ifname, json_ifp_in);
+      }
+    } else {
+        vty_out(vty, "%-15s  %-15s  %-5s  ",
+                src_str,
+                grp_str,
+                ifp_in->name);
+    }
+
+    for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
+      struct interface *ifp_out;
+      char oif_uptime[10];
+      int ttl;
+
+      ttl = c_oil->oil.mfcc_ttls[oif_vif_index];
+      if (ttl < 1)
+        continue;
+
+      ifp_out = pim_if_find_by_vif_index(oif_vif_index);
+      pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]);
+
+      if (ifp_out)
+        strcpy(out_ifname, ifp_out->name);
+      else
+        strcpy(out_ifname, "<oif?>");
+
+      if (uj) {
+        json_ifp_out = json_object_new_object();
+        json_object_string_add(json_ifp_out, "source", src_str);
+        json_object_string_add(json_ifp_out, "group", grp_str);
+        json_object_string_add(json_ifp_out, "inboundInterface", in_ifname);
+        json_object_string_add(json_ifp_out, "outboundInterface", out_ifname);
+
+        json_object_object_add(json_ifp_in, out_ifname, json_ifp_out);
+      } else {
+        if (first_oif)
+          {
+            first_oif = 0;
+            vty_out(vty, "%s", out_ifname);
+          }
+        else
+          vty_out(vty, ",%s", out_ifname);
+      }
     }
 
+    if (!uj)
+      vty_out(vty, "%s", VTY_NEWLINE);
+  }
+
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  } else {
+    vty_out(vty, "%s", VTY_NEWLINE);
   }
 }
 
-static void pim_show_jp_override_interval(struct vty *vty)
+static void pim_show_neighbors(struct vty *vty, u_char uj)
 {
-  struct listnode  *node;
+  struct listnode *node;
+  struct listnode *neighnode;
   struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
+  time_t now;
+  char uptime[10];
+  char expire[10];
+  char neigh_src_str[INET_ADDRSTRLEN];
+  json_object *json = NULL;
+  json_object *json_ifp_rows = NULL;
+  json_object *json_row = NULL;
 
-  vty_out(vty,
-         "EffPDelay=effective_propagation_delay (msec)%s"
-         "EffOvrInt=override_interval (msec)%s"
-         "JPOvrInt=jp_override_interval (msec)%s%s",
-         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  now = pim_time_monotonic_sec();
 
-  vty_out(vty, "Interface Address         LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE);
+  if (uj) {
+    json = json_object_new_object();
+  } else {
+    vty_out(vty, "Interface         Neighbor    Uptime  Holdtime  DR Pri%s", VTY_NEWLINE);
+  }
 
   for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct in_addr ifaddr;
-
     pim_ifp = ifp->info;
-    
+
     if (!pim_ifp)
       continue;
 
     if (pim_ifp->pim_sock_fd < 0)
       continue;
 
-    ifaddr = pim_ifp->primary_address;
+    if (uj)
+      json_ifp_rows = json_object_new_object();
 
-    vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s",
-           ifp->name,
-           inet_ntoa(ifaddr),
-           pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled",
-           pim_if_effective_propagation_delay_msec(ifp),
-           pim_if_effective_override_interval_msec(ifp),
-           pim_if_jp_override_interval_msec(ifp),
-           VTY_NEWLINE);
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+      pim_inet4_dump("<src?>", neigh->source_addr,
+                    neigh_src_str, sizeof(neigh_src_str));
+      pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+      pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+      if (uj) {
+        json_row = json_object_new_object();
+        json_object_string_add(json_row, "interface", ifp->name);
+        json_object_string_add(json_row, "neighbor", neigh_src_str);
+        json_object_string_add(json_row, "upTime", uptime);
+        json_object_string_add(json_row, "holdTime", expire);
+        json_object_int_add(json_row, "holdTimeMax", neigh->holdtime);
+        json_object_int_add(json_row, "drPriority", neigh->dr_priority);
+        json_object_object_add(json_ifp_rows, neigh_src_str, json_row);
+
+      } else {
+        vty_out(vty, "%-9s  %15s  %8s  %8s  %6d%s",
+                ifp->name,
+                neigh_src_str,
+                uptime,
+                expire,
+                neigh->dr_priority,
+                VTY_NEWLINE);
+      }
+    }
+
+    if (uj) {
+      json_object_object_add(json, ifp->name, json_ifp_rows);
+      json_ifp_rows = NULL;
+    }
+  }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
 }
 
@@ -939,7 +1583,7 @@ static void pim_show_neighbors_secondary(struct vty *vty)
     ifaddr = pim_ifp->primary_address;
 
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
-      char neigh_src_str[100];
+      char neigh_src_str[INET_ADDRSTRLEN];
       struct listnode *prefix_node;
       struct prefix *p;
 
@@ -950,7 +1594,7 @@ static void pim_show_neighbors_secondary(struct vty *vty)
                     neigh_src_str, sizeof(neigh_src_str));
 
       for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, prefix_node, p)) {
-       char neigh_sec_str[100];
+       char neigh_sec_str[INET_ADDRSTRLEN];
 
        if (p->family != AF_INET)
          continue;
@@ -969,68 +1613,169 @@ static void pim_show_neighbors_secondary(struct vty *vty)
   }
 }
 
-static void pim_show_upstream(struct vty *vty)
+static void
+json_object_pim_upstream_add (json_object *json, struct pim_upstream *up)
+{
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+    json_object_boolean_true_add(json, "drJoinDesired");
+
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+    json_object_boolean_true_add(json, "drJoinDesiredUpdated");
+
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
+    json_object_boolean_true_add(json, "firstHopRouter");
+
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+    json_object_boolean_true_add(json, "sourceIgmp");
+
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+    json_object_boolean_true_add(json, "sourcePim");
+
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+    json_object_boolean_true_add(json, "sourceStream");
+
+  /* XXX: need to print ths flag in the plain text display as well */
+  if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
+    json_object_boolean_true_add(json, "sourceMsdp");
+}
+
+static void pim_show_upstream(struct vty *vty, u_char uj)
 {
   struct listnode     *upnode;
   struct pim_upstream *up;
   time_t               now;
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_row = NULL;
 
   now = pim_time_monotonic_sec();
 
-  vty_out(vty, "Iif       Source          Group           State Uptime   JoinTimer RefCnt%s", VTY_NEWLINE);
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out(vty, "Iif       Source          Group           State       Uptime   JoinTimer RSTimer   KATimer   RefCnt%s", VTY_NEWLINE);
 
-  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
-      char src_str[100];
-      char grp_str[100];
-      char uptime[10];
-      char join_timer[10];
+  for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+    char src_str[INET_ADDRSTRLEN];
+    char grp_str[INET_ADDRSTRLEN];
+    char uptime[10];
+    char join_timer[10];
+    char rs_timer[10];
+    char ka_timer[10];
+    char msdp_reg_timer[10];
+
+    pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+    pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+    pim_time_timer_to_hhmmss (join_timer, sizeof(join_timer), up->t_join_timer);
+    pim_time_timer_to_hhmmss (rs_timer, sizeof (rs_timer), up->t_rs_timer);
+    pim_time_timer_to_hhmmss (ka_timer, sizeof (ka_timer), up->t_ka_timer);
+    pim_time_timer_to_hhmmss (msdp_reg_timer, sizeof (msdp_reg_timer), up->t_msdp_reg_timer);
+
+    if (uj) {
+      json_object_object_get_ex(json, grp_str, &json_group);
+
+      if (!json_group) {
+        json_group = json_object_new_object();
+        json_object_object_add(json, grp_str, json_group);
+      }
 
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
-      pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer);
+      json_row = json_object_new_object();
+      json_object_pim_upstream_add(json_row, up);
+      json_object_string_add(json_row, "inboundInterface", up->rpf.source_nexthop.interface->name);
+      json_object_string_add(json_row, "source", src_str);
+      json_object_string_add(json_row, "group", grp_str);
+      json_object_string_add(json_row, "state", pim_upstream_state2str (up->join_state));
+      json_object_string_add(json_row, "upTime", uptime);
+      json_object_string_add(json_row, "joinTimer", join_timer);
+      json_object_string_add(json_row, "resetTimer", rs_timer);
+      json_object_string_add(json_row, "keepaliveTimer", ka_timer);
+      json_object_string_add(json_row, "msdpRegTimer", msdp_reg_timer);
+      json_object_int_add(json_row, "refCount", up->ref_count);
+      json_object_int_add(json_row, "sptBit", up->sptbit);
+      json_object_object_add(json_group, src_str, json_row);
+    } else {
+      vty_out(vty, "%-10s%-15s %-15s %-11s %-8s %-9s %-9s %-9s %6d%s",
+              up->rpf.source_nexthop.interface->name,
+              src_str,
+              grp_str,
+              pim_upstream_state2str (up->join_state),
+              uptime,
+              join_timer,
+              rs_timer,
+              ka_timer,
+              up->ref_count,
+              VTY_NEWLINE);
+    }
+  }
 
-      vty_out(vty, "%-10s%-15s %-15s %-5s %-8s %-9s %6d%s",
-             up->rpf.source_nexthop.interface->name,
-             src_str,
-             grp_str,
-             up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd",
-             uptime,
-             join_timer,
-             up->ref_count,
-             VTY_NEWLINE);
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
 }
 
-static void pim_show_join_desired(struct vty *vty)
+static void pim_show_join_desired(struct vty *vty, u_char uj)
 {
-  struct listnode      *ifnode;
   struct listnode      *chnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
-  char src_str[100];
-  char grp_str[100];
-
-  vty_out(vty,
-         "Interface Source          Group           LostAssert Joins PimInclude JoinDesired EvalJD%s",
-         VTY_NEWLINE);
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_row = NULL;
+
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out(vty,
+            "Interface Source          Group           LostAssert Joins PimInclude JoinDesired EvalJD%s",
+            VTY_NEWLINE);
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    pim_ifp = ifp->info;
+  /* scan per-interface (S,G) state */
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, chnode, ch)) {
+    /* scan all interfaces */
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) {
-      struct pim_upstream *up = ch->upstream;
+    struct pim_upstream *up = ch->upstream;
+
+    pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+
+    if (uj) {
+      json_object_object_get_ex(json, grp_str, &json_group);
+
+      if (!json_group) {
+       json_group = json_object_new_object();
+       json_object_object_add(json, grp_str, json_group);
+      }
+
+      json_row = json_object_new_object();
+      json_object_pim_upstream_add(json_row, up);
+      json_object_string_add(json_row, "interface", ch->interface->name);
+      json_object_string_add(json_row, "source", src_str);
+      json_object_string_add(json_row, "group", grp_str);
+
+      if (pim_macro_ch_lost_assert(ch))
+       json_object_boolean_true_add(json_row, "lostAssert");
+
+      if (pim_macro_chisin_joins(ch))
+       json_object_boolean_true_add(json_row, "joins");
 
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      if (pim_macro_chisin_pim_include(ch))
+       json_object_boolean_true_add(json_row, "pimInclude");
 
+      if (pim_upstream_evaluate_join_desired(up))
+       json_object_boolean_true_add(json_row, "evaluateJoinDesired");
+
+      json_object_object_add(json_group, src_str, json_row);
+
+    } else {
       vty_out(vty, "%-9s %-15s %-15s %-10s %-5s %-10s %-11s %-6s%s",
-             ifp->name,
+             ch->interface->name,
              src_str,
              grp_str,
              pim_macro_ch_lost_assert(ch) ? "yes" : "no",
@@ -1041,61 +1786,109 @@ static void pim_show_join_desired(struct vty *vty)
              VTY_NEWLINE);
     }
   }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
 }
 
-static void pim_show_upstream_rpf(struct vty *vty)
+static void pim_show_upstream_rpf(struct vty *vty, u_char uj)
 {
   struct listnode     *upnode;
   struct pim_upstream *up;
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_row = NULL;
 
-  vty_out(vty,
-         "Source          Group           RpfIface RibNextHop      RpfAddress     %s",
-         VTY_NEWLINE);
-
-  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
-    char src_str[100];
-    char grp_str[100];
-    char rpf_nexthop_str[100];
-    char rpf_addr_str[100];
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out(vty,
+            "Source          Group           RpfIface RibNextHop      RpfAddress     %s",
+            VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+    char src_str[INET_ADDRSTRLEN];
+    char grp_str[INET_ADDRSTRLEN];
+    char rpf_nexthop_str[PREFIX_STRLEN];
+    char rpf_addr_str[PREFIX_STRLEN];
     struct pim_rpf *rpf;
     const char *rpf_ifname;
-    
+
     rpf = &up->rpf;
-    
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str));
-    pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
-    
+
+    pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+    pim_addr_dump("<nexthop?>", &rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str));
+    pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+
     rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
-    
-    vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s",
-           src_str,
-           grp_str,
-           rpf_ifname,
-           rpf_nexthop_str,
-           rpf_addr_str,
-           VTY_NEWLINE);
+
+    if (uj) {
+        json_object_object_get_ex(json, grp_str, &json_group);
+
+        if (!json_group) {
+          json_group = json_object_new_object();
+          json_object_object_add(json, grp_str, json_group);
+        }
+
+        json_row = json_object_new_object();
+        json_object_pim_upstream_add(json_row, up);
+        json_object_string_add(json_row, "source", src_str);
+        json_object_string_add(json_row, "group", grp_str);
+        json_object_string_add(json_row, "rpfInterface", rpf_ifname);
+        json_object_string_add(json_row, "ribNexthop", rpf_nexthop_str);
+        json_object_string_add(json_row, "rpfAddress", rpf_addr_str);
+        json_object_object_add(json_group, src_str, json_row);
+    } else {
+        vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s",
+                src_str,
+                grp_str,
+                rpf_ifname,
+                rpf_nexthop_str,
+                rpf_addr_str,
+                VTY_NEWLINE);
+    }
+  }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
 }
 
-static void show_rpf_refresh_stats(struct vty *vty, time_t now)
+static void show_rpf_refresh_stats(struct vty *vty, time_t now, json_object *json)
 {
   char refresh_uptime[10];
 
   pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now, qpim_rpf_cache_refresh_last);
 
-  vty_out(vty, 
-         "RPF Cache Refresh Delay:    %ld msecs%s"
-         "RPF Cache Refresh Timer:    %ld msecs%s"
-         "RPF Cache Refresh Requests: %lld%s"
-         "RPF Cache Refresh Events:   %lld%s"
-         "RPF Cache Refresh Last:     %s%s",
-         qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE,
-         pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE,
-         (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE,
-         (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE,
-         refresh_uptime, VTY_NEWLINE);
+  if (json) {
+    json_object_int_add(json, "rpfCacheRefreshDelayMsecs", qpim_rpf_cache_refresh_delay_msec);
+    json_object_int_add(json, "rpfCacheRefreshTimer", pim_time_timer_remain_msec(qpim_rpf_cache_refresher));
+    json_object_int_add(json, "rpfCacheRefreshRequests", qpim_rpf_cache_refresh_requests);
+    json_object_int_add(json, "rpfCacheRefreshEvents", qpim_rpf_cache_refresh_events);
+    json_object_string_add(json, "rpfCacheRefreshLast", refresh_uptime);
+    json_object_int_add(json, "nexthopLookups", qpim_nexthop_lookups);
+    json_object_int_add(json, "nexthopLookupsAvoided", nexthop_lookups_avoided);
+  } else {
+    vty_out(vty,
+            "RPF Cache Refresh Delay:    %ld msecs%s"
+            "RPF Cache Refresh Timer:    %ld msecs%s"
+            "RPF Cache Refresh Requests: %lld%s"
+            "RPF Cache Refresh Events:   %lld%s"
+            "RPF Cache Refresh Last:     %s%s"
+            "Nexthop Lookups:            %lld%s"
+           "Nexthop Lookups Avoided:    %lld%s",
+            qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE,
+            pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE,
+            (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE,
+            (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE,
+            refresh_uptime, VTY_NEWLINE,
+            (long long) qpim_nexthop_lookups, VTY_NEWLINE,
+           (long long)nexthop_lookups_avoided, VTY_NEWLINE);
+  }
 }
 
 static void show_scan_oil_stats(struct vty *vty, time_t now)
@@ -1117,90 +1910,93 @@ static void show_scan_oil_stats(struct vty *vty, time_t now)
           uptime_mroute_del, (long long) qpim_mroute_del_events, VTY_NEWLINE);
 }
 
-static void pim_show_rpf(struct vty *vty)
+static void pim_show_rpf(struct vty *vty, u_char uj)
 {
   struct listnode     *up_node;
   struct pim_upstream *up;
   time_t               now = pim_time_monotonic_sec();
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_row = NULL;
+
+  if (uj) {
+    json = json_object_new_object();
+    show_rpf_refresh_stats(vty, now, json);
+  } else {
+    show_rpf_refresh_stats(vty, now, json);
+    vty_out(vty, "%s", VTY_NEWLINE);
+    vty_out(vty,
+            "Source          Group           RpfIface RpfAddress      RibNextHop      Metric Pref%s",
+            VTY_NEWLINE);
+  }
 
-  show_rpf_refresh_stats(vty, now);
-
-  vty_out(vty, "%s", VTY_NEWLINE);
-
-  vty_out(vty,
-         "Source          Group           RpfIface RpfAddress      RibNextHop      Metric Pref%s",
-         VTY_NEWLINE);
-
-  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
-    char src_str[100];
-    char grp_str[100];
-    char rpf_addr_str[100];
-    char rib_nexthop_str[100];
+  for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) {
+    char src_str[INET_ADDRSTRLEN];
+    char grp_str[INET_ADDRSTRLEN];
+    char rpf_addr_str[PREFIX_STRLEN];
+    char rib_nexthop_str[PREFIX_STRLEN];
     const char *rpf_ifname;
     struct pim_rpf  *rpf = &up->rpf;
     
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
-    pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str));
+    pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+    pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+    pim_addr_dump("<nexthop?>", &rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str));
     
     rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
     
-    vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s",
-           src_str,
-           grp_str,
-           rpf_ifname,
-           rpf_addr_str,
-           rib_nexthop_str,
-           rpf->source_nexthop.mrib_route_metric,
-           rpf->source_nexthop.mrib_metric_preference,
-           VTY_NEWLINE);
-  }
-}
-
-static void igmp_show_querier(struct vty *vty)
-{
-  struct listnode  *node;
-  struct interface *ifp;
-
-  vty_out(vty, "Interface Address         Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE);
-
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
-    struct pim_interface *pim_ifp = ifp->info;
-    struct listnode  *sock_node;
-    struct igmp_sock *igmp;
-    
-    if (!pim_ifp)
-      continue;
-
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
-      char query_hhmmss[10];
-      char other_hhmmss[10];
+    if (uj) {
+      json_object_object_get_ex(json, grp_str, &json_group);
 
-      pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer);
-      pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer);
+      if (!json_group) {
+        json_group = json_object_new_object();
+        json_object_object_add(json, grp_str, json_group);
+      }
 
-      vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s",
-             ifp->name,
-             inet_ntoa(igmp->ifaddr),
-             igmp->t_igmp_query_timer ? "THIS" : "OTHER",
-             igmp->startup_query_count,
-             query_hhmmss,
-             other_hhmmss,
-             VTY_NEWLINE);
+      json_row = json_object_new_object();
+      json_object_string_add(json_row, "source", src_str);
+      json_object_string_add(json_row, "group", grp_str);
+      json_object_string_add(json_row, "rpfInterface", rpf_ifname);
+      json_object_string_add(json_row, "rpfAddress", rpf_addr_str);
+      json_object_string_add(json_row, "ribNexthop", rib_nexthop_str);
+      json_object_int_add(json_row, "routeMetric", rpf->source_nexthop.mrib_route_metric);
+      json_object_int_add(json_row, "routePreference", rpf->source_nexthop.mrib_metric_preference);
+      json_object_object_add(json_group, src_str, json_row);
+
+    } else {
+      vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s",
+              src_str,
+              grp_str,
+              rpf_ifname,
+              rpf_addr_str,
+              rib_nexthop_str,
+              rpf->source_nexthop.mrib_route_metric,
+              rpf->source_nexthop.mrib_metric_preference,
+              VTY_NEWLINE);
     }
   }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
 }
 
-static void igmp_show_groups(struct vty *vty)
+static void igmp_show_groups(struct vty *vty, u_char uj)
 {
   struct listnode  *ifnode;
   struct interface *ifp;
   time_t            now;
+  json_object *json = NULL;
+  json_object *json_iface = NULL;
+  json_object *json_row = NULL;
 
   now = pim_time_monotonic_sec();
 
-  vty_out(vty, "Interface Address         Group           Mode Timer    Srcs V Uptime  %s", VTY_NEWLINE);
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out(vty, "Interface Address         Group           Mode Timer    Srcs V Uptime  %s", VTY_NEWLINE);
 
   /* scan interfaces */
   for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
@@ -1213,7 +2009,7 @@ static void igmp_show_groups(struct vty *vty)
     
     /* scan igmp sockets */
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       struct listnode *grpnode;
       struct igmp_group *grp;
 
@@ -1221,7 +2017,7 @@ static void igmp_show_groups(struct vty *vty)
 
       /* scan igmp groups */
       for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
-       char group_str[100];
+       char group_str[INET_ADDRSTRLEN];
        char hhmmss[10];
        char uptime[10];
 
@@ -1229,20 +2025,48 @@ static void igmp_show_groups(struct vty *vty)
        pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer);
        pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation);
 
-       vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s",
-               ifp->name,
-               ifaddr_str,
-               group_str,
-               grp->group_filtermode_isexcl ? "EXCL" : "INCL",
-               hhmmss,
-               grp->group_source_list ? listcount(grp->group_source_list) : 0,
-               igmp_group_compat_mode(igmp, grp),
-               uptime,
-               VTY_NEWLINE);
-
+        if (uj) {
+            json_object_object_get_ex(json, ifp->name, &json_iface);
+
+            if (!json_iface) {
+              json_iface = json_object_new_object();
+              json_object_pim_ifp_add(json_iface, ifp);
+              json_object_object_add(json, ifp->name, json_iface);
+            }
+
+            json_row = json_object_new_object();
+            json_object_string_add(json_row, "source", ifaddr_str);
+            json_object_string_add(json_row, "group", group_str);
+
+            if (grp->igmp_version == 3)
+              json_object_string_add(json_row, "mode", grp->group_filtermode_isexcl ? "EXCLUDE" : "INCLUDE");
+
+            json_object_string_add(json_row, "timer", hhmmss);
+            json_object_int_add(json_row, "sourcesCount", grp->group_source_list ? listcount(grp->group_source_list) : 0);
+            json_object_int_add(json_row, "version", grp->igmp_version);
+            json_object_string_add(json_row, "uptime", uptime);
+            json_object_object_add(json_iface, group_str, json_row);
+
+        } else {
+          vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s",
+                  ifp->name,
+                  ifaddr_str,
+                  group_str,
+                  grp->igmp_version == 3 ? (grp->group_filtermode_isexcl ? "EXCL" : "INCL") : "----",
+                  hhmmss,
+                  grp->group_source_list ? listcount(grp->group_source_list) : 0,
+                  grp->igmp_version,
+                  uptime,
+                  VTY_NEWLINE);
+        }
       } /* scan igmp groups */
     } /* scan igmp sockets */
   } /* scan interfaces */
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
 }
 
 static void igmp_show_group_retransmission(struct vty *vty)
@@ -1263,7 +2087,7 @@ static void igmp_show_group_retransmission(struct vty *vty)
     
     /* scan igmp sockets */
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       struct listnode *grpnode;
       struct igmp_group *grp;
 
@@ -1271,7 +2095,7 @@ static void igmp_show_group_retransmission(struct vty *vty)
 
       /* scan igmp groups */
       for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
-       char group_str[100];
+       char group_str[INET_ADDRSTRLEN];
        char grp_retr_mmss[10];
        struct listnode    *src_node;
        struct igmp_source *src;
@@ -1302,80 +2126,6 @@ static void igmp_show_group_retransmission(struct vty *vty)
   } /* scan interfaces */
 }
 
-static void igmp_show_parameters(struct vty *vty)
-{
-  struct listnode  *ifnode;
-  struct interface *ifp;
-
-  vty_out(vty,
-         "QRV: Robustness Variable             SQI: Startup Query Interval%s"
-         "QQI: Query Interval                  OQPI: Other Querier Present Interval%s"
-         "QRI: Query Response Interval         LMQT: Last Member Query Time%s"
-         "GMI: Group Membership Interval       OHPI: Older Host Present Interval%s%s",
-         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
-  vty_out(vty,
-         "Interface Address         QRV QQI QRI   GMI   SQI OQPI  LMQT  OHPI %s",
-         VTY_NEWLINE);
-
-  /* scan interfaces */
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp = ifp->info;
-    struct listnode  *sock_node;
-    struct igmp_sock *igmp;
-    
-    if (!pim_ifp)
-      continue;
-    
-    /* scan igmp sockets */
-    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
-      char ifaddr_str[100];
-      long gmi_dsec;  /* Group Membership Interval */
-      long oqpi_dsec; /* Other Querier Present Interval */
-      int  sqi;
-      long lmqt_dsec;
-      long ohpi_dsec;
-      long qri_dsec;
-
-      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
-
-      gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
-                                  igmp->querier_query_interval,
-                                  pim_ifp->igmp_query_max_response_time_dsec) / 100;
-
-      sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
-
-      oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
-                                    igmp->querier_query_interval,
-                                    pim_ifp->igmp_query_max_response_time_dsec) / 100;
-
-      lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec,
-                                    igmp->querier_robustness_variable) / 100;
-
-      ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
-                                    igmp->querier_query_interval,
-                                    pim_ifp->igmp_query_max_response_time_dsec);
-
-      qri_dsec = pim_ifp->igmp_query_max_response_time_dsec;
-
-      vty_out(vty,
-             "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s",
-             ifp->name,
-             ifaddr_str,
-             igmp->querier_robustness_variable,
-             igmp->querier_query_interval,
-             qri_dsec / 10, qri_dsec % 10,
-             gmi_dsec / 10, gmi_dsec % 10,
-             sqi,
-             oqpi_dsec / 10, oqpi_dsec % 10,
-             lmqt_dsec / 10, lmqt_dsec % 10,
-             ohpi_dsec / 10, ohpi_dsec % 10,
-             VTY_NEWLINE);
-
-    } /* scan igmp sockets */
-  } /* scan interfaces */
-}
-
 static void igmp_show_sources(struct vty *vty)
 {
   struct listnode  *ifnode;
@@ -1397,7 +2147,7 @@ static void igmp_show_sources(struct vty *vty)
     
     /* scan igmp sockets */
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       struct listnode   *grpnode;
       struct igmp_group *grp;
 
@@ -1405,7 +2155,7 @@ static void igmp_show_sources(struct vty *vty)
 
       /* scan igmp groups */
       for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
-       char group_str[100];
+       char group_str[INET_ADDRSTRLEN];
        struct listnode    *srcnode;
        struct igmp_source *src;
 
@@ -1413,7 +2163,7 @@ static void igmp_show_sources(struct vty *vty)
        
        /* scan group sources */
        for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
-         char source_str[100];
+         char source_str[INET_ADDRSTRLEN];
          char mmss[10];
          char uptime[10];
 
@@ -1457,7 +2207,7 @@ static void igmp_show_source_retransmission(struct vty *vty)
     
     /* scan igmp sockets */
     for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       struct listnode   *grpnode;
       struct igmp_group *grp;
 
@@ -1465,7 +2215,7 @@ static void igmp_show_source_retransmission(struct vty *vty)
 
       /* scan igmp groups */
       for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
-       char group_str[100];
+       char group_str[INET_ADDRSTRLEN];
        struct listnode    *srcnode;
        struct igmp_source *src;
 
@@ -1473,7 +2223,7 @@ static void igmp_show_source_retransmission(struct vty *vty)
        
        /* scan group sources */
        for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
-         char source_str[100];
+         char source_str[INET_ADDRSTRLEN];
 
          pim_inet4_dump("<source?>", src->source_addr, source_str, sizeof(source_str));
 
@@ -1555,11 +2305,11 @@ static void mroute_add_all()
   struct listnode    *node;
   struct channel_oil *c_oil;
 
-  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
-    if (pim_mroute_add(c_oil)) {
+  for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+    if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) {
       /* just log warning */
-      char source_str[100];
-      char group_str[100];
+      char source_str[INET_ADDRSTRLEN];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
       zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
@@ -1574,11 +2324,11 @@ static void mroute_del_all()
   struct listnode    *node;
   struct channel_oil *c_oil;
 
-  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
-    if (pim_mroute_del(c_oil)) {
+  for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+    if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) {
       /* just log warning */
-      char source_str[100];
-      char group_str[100];
+      char source_str[INET_ADDRSTRLEN];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
       zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
@@ -1594,10 +2344,10 @@ static void static_mroute_add_all()
   struct static_route *s_route;
 
   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
-    if (pim_mroute_add(&s_route->c_oil)) {
+    if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
       /* just log warning */
-      char source_str[100];
-      char group_str[100];
+      char source_str[INET_ADDRSTRLEN];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str));
       pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str));
       zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
@@ -1613,10 +2363,10 @@ static void static_mroute_del_all()
    struct static_route *s_route;
 
    for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
-     if (pim_mroute_del(&s_route->c_oil)) {
+     if (pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__)) {
        /* just log warning */
-       char source_str[100];
-       char group_str[100];
+       char source_str[INET_ADDRSTRLEN];
+       char group_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str));
        pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str));
        zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
@@ -1667,13 +2417,20 @@ DEFUN (clear_ip_pim_oil,
 
 DEFUN (show_ip_igmp_interface,
        show_ip_igmp_interface_cmd,
-       "show ip igmp interface",
+       "show ip igmp interface [detail|WORD] [json]",
        SHOW_STR
        IP_STR
        IGMP_STR
-       "IGMP interface information\n")
+       "IGMP interface information\n"
+       "Detailed output\n"
+       "interface name\n"
+       "JavaScript Object Notation\n")
 {
-  igmp_show_interfaces(vty);
+  u_char uj = use_json(argc, argv);
+  if (argv[4]->arg)
+    igmp_show_interfaces_single(vty, argv[4]->arg, uj);
+  else
+    igmp_show_interfaces(vty, uj);
 
   return CMD_SUCCESS;
 }
@@ -1693,13 +2450,15 @@ DEFUN (show_ip_igmp_join,
 
 DEFUN (show_ip_igmp_groups,
        show_ip_igmp_groups_cmd,
-       "show ip igmp groups",
+       "show ip igmp groups [json]",
        SHOW_STR
        IP_STR
        IGMP_STR
-       IGMP_GROUP_STR)
+       IGMP_GROUP_STR
+       "JavaScript Object Notation\n")
 {
-  igmp_show_groups(vty);
+  u_char uj = use_json(argc, argv);
+  igmp_show_groups(vty, uj);
 
   return CMD_SUCCESS;
 }
@@ -1718,19 +2477,6 @@ DEFUN (show_ip_igmp_groups_retransmissions,
   return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_igmp_parameters,
-       show_ip_igmp_parameters_cmd,
-       "show ip igmp parameters",
-       SHOW_STR
-       IP_STR
-       IGMP_STR
-       "IGMP parameters information\n")
-{
-  igmp_show_parameters(vty);
-
-  return CMD_SUCCESS;
-}
-
 DEFUN (show_ip_igmp_sources,
        show_ip_igmp_sources_cmd,
        "show ip igmp sources",
@@ -1758,32 +2504,6 @@ DEFUN (show_ip_igmp_sources_retransmissions,
   return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_igmp_querier,
-       show_ip_igmp_querier_cmd,
-       "show ip igmp querier",
-       SHOW_STR
-       IP_STR
-       IGMP_STR
-       "IGMP querier information\n")
-{
-  igmp_show_querier(vty);
-
-  return CMD_SUCCESS;
-}
-
-DEFUN (show_ip_pim_address,
-       show_ip_pim_address_cmd,
-       "show ip pim address",
-       SHOW_STR
-       IP_STR
-       PIM_STR
-       "PIM interface address\n")
-{
-  show_interface_address(vty);
-
-  return CMD_SUCCESS;
-}
-
 DEFUN (show_ip_pim_assert,
        show_ip_pim_assert_cmd,
        "show ip pim assert",
@@ -1838,169 +2558,207 @@ DEFUN (show_ip_pim_assert_winner_metric,
 
 DEFUN (show_ip_pim_dr,
        show_ip_pim_dr_cmd,
-       "show ip pim designated-router",
+       "show ip pim designated-router [json]",
        SHOW_STR
        IP_STR
        PIM_STR
        "PIM interface designated router\n")
 {
-  pim_show_dr(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_dr(vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_hello,
        show_ip_pim_hello_cmd,
-       "show ip pim hello",
+       "show ip pim hello [json]",
        SHOW_STR
        IP_STR
        PIM_STR
        "PIM interface hello information\n")
 {
-  pim_show_hello(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_hello(vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_interface,
        show_ip_pim_interface_cmd,
-       "show ip pim interface",
+       "show ip pim interface [detail|WORD] [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM interface information\n")
+       "PIM interface information\n"
+       "Detailed output\n"
+       "interface name\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_interfaces(vty);
+  u_char uj = use_json(argc, argv);
+  if (argv[4]->arg)
+    pim_show_interfaces_single(vty, argv[4]->arg, uj);
+  else
+    pim_show_interfaces(vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_join,
        show_ip_pim_join_cmd,
-       "show ip pim join",
+       "show ip pim join [json]",
        SHOW_STR
        IP_STR
        PIM_STR
        "PIM interface join information\n")
 {
-  pim_show_join(vty);
-
-  return CMD_SUCCESS;
-}
-
-DEFUN (show_ip_pim_lan_prune_delay,
-       show_ip_pim_lan_prune_delay_cmd,
-       "show ip pim lan-prune-delay",
-       SHOW_STR
-       IP_STR
-       PIM_STR
-       "PIM neighbors LAN prune delay parameters\n")
-{
-  pim_show_lan_prune_delay(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_join(vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_local_membership,
        show_ip_pim_local_membership_cmd,
-       "show ip pim local-membership",
+       "show ip pim local-membership [json]",
        SHOW_STR
        IP_STR
        PIM_STR
        "PIM interface local-membership\n")
 {
-  pim_show_membership(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_membership(vty, uj);
 
   return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_pim_jp_override_interval,
-       show_ip_pim_jp_override_interval_cmd,
-       "show ip pim jp-override-interval",
+DEFUN (show_ip_pim_neighbor,
+       show_ip_pim_neighbor_cmd,
+       "show ip pim neighbor [detail|WORD] [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM interface J/P override interval\n")
+       "PIM neighbor information\n"
+       "Detailed output\n"
+       "Name of interface or neighbor\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_jp_override_interval(vty);
+  u_char uj = use_json(argc, argv);
+  if (argv[4]->arg)
+    pim_show_neighbors_single(vty, argv[4]->arg, uj);
+  else
+    pim_show_neighbors(vty, uj);
 
   return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_pim_neighbor,
-       show_ip_pim_neighbor_cmd,
-       "show ip pim neighbor",
+DEFUN (show_ip_pim_secondary,
+       show_ip_pim_secondary_cmd,
+       "show ip pim secondary",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM neighbor information\n")
+       "PIM neighbor addresses\n")
 {
-  pim_show_neighbors(vty);
+  pim_show_neighbors_secondary(vty);
 
   return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_pim_secondary,
-       show_ip_pim_secondary_cmd,
-       "show ip pim secondary",
+DEFUN (show_ip_pim_state,
+       show_ip_pim_state_cmd,
+       "show ip pim state [A.B.C.D] [A.B.C.D] [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM neighbor addresses\n")
+       "PIM state information\n"
+       "Unicast or Multicast address\n"
+       "Multicast address\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_neighbors_secondary(vty);
+  const char *src_or_group = NULL;
+  const char *group = NULL;
+  u_char uj = use_json(argc, argv);
+
+  src_or_group = argv[4]->arg;
+  group = argv[5]->arg;
+
+  pim_show_state(vty, src_or_group, group, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_upstream,
        show_ip_pim_upstream_cmd,
-       "show ip pim upstream",
+       "show ip pim upstream [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM upstream information\n")
+       "PIM upstream information\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_upstream(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_upstream(vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_upstream_join_desired,
        show_ip_pim_upstream_join_desired_cmd,
-       "show ip pim upstream-join-desired",
+       "show ip pim upstream-join-desired [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM upstream join-desired\n")
+       "PIM upstream join-desired\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_join_desired(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_join_desired(vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_upstream_rpf,
        show_ip_pim_upstream_rpf_cmd,
-       "show ip pim upstream-rpf",
+       "show ip pim upstream-rpf [json]",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM upstream source rpf\n"
+       "JavaScript Object Notation\n")
+{
+  u_char uj = use_json(argc, argv);
+  pim_show_upstream_rpf(vty, uj);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_rp,
+       show_ip_pim_rp_cmd,
+       "show ip pim rp-info [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM upstream source rpf\n")
+       "PIM RP information\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_upstream_rpf(vty);
+  u_char uj = use_json(argc, argv);
+  pim_rp_show_information (vty, uj);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (show_ip_pim_rpf,
        show_ip_pim_rpf_cmd,
-       "show ip pim rpf",
+       "show ip pim rpf [json]",
        SHOW_STR
        IP_STR
        PIM_STR
-       "PIM cached source rpf information\n")
+       "PIM cached source rpf information\n"
+       "JavaScript Object Notation\n")
 {
-  pim_show_rpf(vty);
+  u_char uj = use_json(argc, argv);
+  pim_show_rpf(vty, uj);
 
   return CMD_SUCCESS;
 }
@@ -2088,14 +2846,8 @@ DEFUN (show_ip_multicast,
   else {
     vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
   }
-  vty_out(vty, "Zclient lookup socket: ");
-  if (qpim_zclient_lookup) {
-    vty_out(vty, "%d failures=%d%s", qpim_zclient_lookup->sock,
-           qpim_zclient_lookup->fail, VTY_NEWLINE);
-  }
-  else {
-    vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
-  }
+
+  pim_zlookup_show_ip_multicast (vty);
 
   vty_out(vty, "%s", VTY_NEWLINE);
   vty_out(vty, "Current highest VifIndex: %d%s",
@@ -2115,7 +2867,7 @@ DEFUN (show_ip_multicast,
 
   vty_out(vty, "%s", VTY_NEWLINE);
 
-  show_rpf_refresh_stats(vty, now);
+  show_rpf_refresh_stats(vty, now, NULL);
 
   vty_out(vty, "%s", VTY_NEWLINE);
 
@@ -2126,127 +2878,298 @@ DEFUN (show_ip_multicast,
   return CMD_SUCCESS;
 }
 
-static void show_mroute(struct vty *vty)
+static void show_mroute(struct vty *vty, u_char uj)
 {
   struct listnode    *node;
   struct channel_oil *c_oil;
   struct static_route *s_route;
   time_t              now;
-
-  vty_out(vty, "Proto: I=IGMP P=PIM S=STATIC O=SOURCE%s%s", VTY_NEWLINE, VTY_NEWLINE);
-  
-  vty_out(vty, "Source          Group           Proto Input iVifI Output oVifI TTL Uptime  %s",
-         VTY_NEWLINE);
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_source = NULL;
+  json_object *json_oil = NULL;
+  json_object *json_ifp_out = NULL;
+  int found_oif = 0;
+  int first = 1;
+
+  if (uj) {
+    json = json_object_new_object();
+  } else {
+    vty_out(vty, "Source          Group           Proto  Input      Output     TTL  Uptime%s",
+            VTY_NEWLINE);
+  }
 
   now = pim_time_monotonic_sec();
 
   /* print list of PIM and IGMP routes */
-  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
-    char group_str[100]; 
-    char source_str[100];
+  for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+    char grp_str[INET_ADDRSTRLEN];
+    char src_str[INET_ADDRSTRLEN];
+    char in_ifname[16];
+    char out_ifname[16];
     int oif_vif_index;
-
-    if (!c_oil->installed)
+    char proto[100];
+    struct interface *ifp_in;
+    found_oif = 0;
+    first = 1;
+    if (!c_oil->installed && !uj)
       continue;
 
-    pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-    pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-    
+    pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, sizeof(src_str));
+    ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
+
+    if (ifp_in)
+      strcpy(in_ifname, ifp_in->name);
+    else
+      strcpy(in_ifname, "<iif?>");
+
+    if (uj) {
+
+      /* Find the group, create it if it doesn't exist */
+      json_object_object_get_ex(json, grp_str, &json_group);
+
+      if (!json_group) {
+        json_group = json_object_new_object();
+        json_object_object_add(json, grp_str, json_group);
+      }
+
+      /* Find the source nested under the group, create it if it doesn't exist */
+      json_object_object_get_ex(json_group, src_str, &json_source);
+
+      if (!json_source) {
+        json_source = json_object_new_object();
+        json_object_object_add(json_group, src_str, json_source);
+      }
+
+      /* Find the inbound interface nested under the source, create it if it doesn't exist */
+      json_object_int_add(json_source, "installed", c_oil->installed);
+      json_object_int_add(json_source, "refCount", c_oil->oil_ref_count);
+      json_object_int_add(json_source, "oilSize", c_oil->oil_size);
+      json_object_int_add(json_source, "OilInheritedRescan", c_oil->oil_inherited_rescan);
+      json_object_string_add(json_source, "iif", in_ifname);
+      json_oil = NULL;
+    }
+
     for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
-      struct interface *ifp_in;
       struct interface *ifp_out;
       char oif_uptime[10];
       int ttl;
-      char proto[5];
 
       ttl = c_oil->oil.mfcc_ttls[oif_vif_index];
       if (ttl < 1)
        continue;
 
-      ifp_in  = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
       ifp_out = pim_if_find_by_vif_index(oif_vif_index);
-
       pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]);
+      found_oif = 1;
 
-      proto[0] = '\0';
-      if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) {
-       strcat(proto, "P");
-      }
-      if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) {
-       strcat(proto, "I");
-      }
-      if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) {
-       strcat(proto, "O");
+      if (ifp_out)
+        strcpy(out_ifname, ifp_out->name);
+      else
+        strcpy(out_ifname, "<oif?>");
+
+      if (uj) {
+        json_ifp_out = json_object_new_object();
+        json_object_string_add(json_ifp_out, "source", src_str);
+        json_object_string_add(json_ifp_out, "group", grp_str);
+
+        if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM)
+          json_object_boolean_true_add(json_ifp_out, "protocolPim");
+
+        if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP)
+          json_object_boolean_true_add(json_ifp_out, "protocolIgmp");
+
+        if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE)
+          json_object_boolean_true_add(json_ifp_out, "protocolSource");
+
+        json_object_string_add(json_ifp_out, "inboundInterface", in_ifname);
+        json_object_int_add(json_ifp_out, "iVifI", c_oil->oil.mfcc_parent);
+        json_object_string_add(json_ifp_out, "outboundInterface", out_ifname);
+        json_object_int_add(json_ifp_out, "oVifI", oif_vif_index);
+        json_object_int_add(json_ifp_out, "ttl", ttl);
+        json_object_string_add(json_ifp_out, "upTime", oif_uptime);
+        if (!json_oil) {
+          json_oil = json_object_new_object();
+          json_object_object_add(json_source, "oil", json_oil);
+        }
+        json_object_object_add(json_oil, out_ifname, json_ifp_out);
+      } else {
+        if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) {
+          strcpy(proto, "PIM");
+        }
+
+        if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) {
+          strcpy(proto, "IGMP");
+        }
+
+        if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) {
+          strcpy(proto, "SRC");
+        }
+
+        vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d  %8s%s",
+                src_str,
+                grp_str,
+                proto,
+                in_ifname,
+                out_ifname,
+                ttl,
+                oif_uptime,
+                VTY_NEWLINE);
+
+        if (first)
+         {
+           src_str[0] = '\0';
+           grp_str[0] = '\0';
+           in_ifname[0] = '\0';
+           first = 0;
+         }
       }
+    }
 
-      vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
-             source_str,
-             group_str,
-             proto,
-             ifp_in ? ifp_in->name : "<iif?>",
-             c_oil->oil.mfcc_parent,
-             ifp_out ? ifp_out->name : "<oif?>",
-             oif_vif_index,
-             ttl,
-             oif_uptime,
-             VTY_NEWLINE);
+    if (!uj && !found_oif) {
+      vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d  %8s%s",
+              src_str,
+              grp_str,
+              "none",
+              in_ifname,
+              "none",
+              0,
+              "--:--:--",
+              VTY_NEWLINE);
     }
   }
 
   /* Print list of static routes */
   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
-    char group_str[100];
-    char source_str[100];
+    char grp_str[INET_ADDRSTRLEN];
+    char src_str[INET_ADDRSTRLEN];
+    char in_ifname[16];
+    char out_ifname[16];
     int oif_vif_index;
+    struct interface *ifp_in;
+    char proto[100];
+    first = 1;
 
     if (!s_route->c_oil.installed)
       continue;
 
-    pim_inet4_dump("<group?>", s_route->group, group_str, sizeof(group_str));
-    pim_inet4_dump("<source?>", s_route->source, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", s_route->group, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<source?>", s_route->source, src_str, sizeof(src_str));
+    ifp_in  = pim_if_find_by_vif_index(s_route->iif);
+    found_oif = 0;
+
+    if (ifp_in)
+      strcpy(in_ifname, ifp_in->name);
+    else
+      strcpy(in_ifname, "<iif?>");
+
+    if (uj) {
+
+      /* Find the group, create it if it doesn't exist */
+      json_object_object_get_ex(json, grp_str, &json_group);
+
+      if (!json_group) {
+        json_group = json_object_new_object();
+        json_object_object_add(json, grp_str, json_group);
+      }
+
+      /* Find the source nested under the group, create it if it doesn't exist */
+      json_object_object_get_ex(json_group, src_str, &json_source);
+
+      if (!json_source) {
+        json_source = json_object_new_object();
+        json_object_object_add(json_group, src_str, json_source);
+      }
+
+      json_object_string_add(json_source, "iif", in_ifname);
+      json_oil = NULL;
+    } else {
+      strcpy(proto, "STATIC");
+    }
 
     for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
-      struct interface *ifp_in;
       struct interface *ifp_out;
       char oif_uptime[10];
       int ttl;
-      char proto[5];
 
       ttl = s_route->oif_ttls[oif_vif_index];
       if (ttl < 1)
          continue;
 
-      ifp_in  = pim_if_find_by_vif_index(s_route->iif);
       ifp_out = pim_if_find_by_vif_index(oif_vif_index);
-
       pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - s_route->c_oil.oif_creation[oif_vif_index]);
+      found_oif = 1;
 
-      proto[0] = '\0';
-      strcat(proto, "S");
-
-      vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
-         source_str,
-         group_str,
-         proto,
-         ifp_in ? ifp_in->name : "<iif?>",
-         s_route->iif,
-         ifp_out ? ifp_out->name : "<oif?>",
-         oif_vif_index,
-         ttl,
-         oif_uptime,
-         VTY_NEWLINE);
+      if (ifp_out)
+        strcpy(out_ifname, ifp_out->name);
+      else
+        strcpy(out_ifname, "<oif?>");
+
+      if (uj) {
+        json_ifp_out = json_object_new_object();
+        json_object_string_add(json_ifp_out, "source", src_str);
+        json_object_string_add(json_ifp_out, "group", grp_str);
+        json_object_boolean_true_add(json_ifp_out, "protocolStatic");
+        json_object_string_add(json_ifp_out, "inboundInterface", in_ifname);
+        json_object_int_add(json_ifp_out, "iVifI", c_oil->oil.mfcc_parent);
+        json_object_string_add(json_ifp_out, "outboundInterface", out_ifname);
+        json_object_int_add(json_ifp_out, "oVifI", oif_vif_index);
+        json_object_int_add(json_ifp_out, "ttl", ttl);
+        json_object_string_add(json_ifp_out, "upTime", oif_uptime);
+        if (!json_oil) {
+          json_oil = json_object_new_object();
+          json_object_object_add(json_source, "oil", json_oil);
+        }
+        json_object_object_add(json_oil, out_ifname, json_ifp_out);
+      } else {
+        vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d  %8s%s",
+                src_str,
+                grp_str,
+                proto,
+                in_ifname,
+                out_ifname,
+                ttl,
+                oif_uptime,
+                VTY_NEWLINE);
+       if (first)
+          {
+           src_str[0] = '\0';
+           grp_str[0] = '\0';
+           in_ifname[0] = '\0';
+           first = 0;
+         }
+      }
+    }
+
+    if (!uj && !found_oif) {
+        vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d  %8s%s",
+                src_str,
+                grp_str,
+                proto,
+                in_ifname,
+                "none",
+                0,
+                "--:--:--",
+                VTY_NEWLINE);
     }
   }
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
 }
 
 DEFUN (show_ip_mroute,
        show_ip_mroute_cmd,
-       "show ip mroute",
+       "show ip mroute [json]",
        SHOW_STR
        IP_STR
        MROUTE_STR)
 {
-  show_mroute(vty);
+  u_char uj = use_json(argc, argv);
+  show_mroute(vty, uj);
   return CMD_SUCCESS;
 }
 
@@ -2258,13 +3181,13 @@ static void show_mroute_count(struct vty *vty)
 
   vty_out(vty, "%s", VTY_NEWLINE);
   
-  vty_out(vty, "Source          Group           Packets      Bytes WrongIf  %s",
+  vty_out(vty, "Source          Group           LastUsed Packets Bytes WrongIf  %s",
          VTY_NEWLINE);
 
   /* Print PIM and IGMP route counts */
-  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
-    char group_str[100]; 
-    char source_str[100];
+  for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+    char group_str[INET_ADDRSTRLEN]; 
+    char source_str[INET_ADDRSTRLEN];
 
     if (!c_oil->installed)
       continue;
@@ -2274,9 +3197,10 @@ static void show_mroute_count(struct vty *vty)
     pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
 
-    vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+    vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld%s",
            source_str,
            group_str,
+           c_oil->cc.lastused/100,
            c_oil->cc.pktcnt,
            c_oil->cc.bytecnt,
            c_oil->cc.wrong_if,
@@ -2285,8 +3209,8 @@ static void show_mroute_count(struct vty *vty)
 
    /* Print static route counts */
   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
 
     if (!s_route->c_oil.installed)
       continue;
@@ -2296,9 +3220,10 @@ static void show_mroute_count(struct vty *vty)
     pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str));
 
-    vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+    vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld%s",
        source_str,
        group_str,
+       s_route->c_oil.cc.lastused,
        s_route->c_oil.cc.pktcnt,
        s_route->c_oil.cc.bytecnt,
        s_route->c_oil.cc.wrong_if,
@@ -2330,9 +3255,10 @@ DEFUN (show_ip_rib,
   struct in_addr addr;
   const char *addr_str;
   struct pim_nexthop nexthop;
-  char nexthop_addr_str[100];
+  char nexthop_addr_str[PREFIX_STRLEN];
   int result;
 
+  memset (&nexthop, 0, sizeof (nexthop));
   addr_str = argv[idx_ipv4]->arg;
   result = inet_pton(AF_INET, addr_str, &addr);
   if (result <= 0) {
@@ -2341,7 +3267,7 @@ DEFUN (show_ip_rib,
     return CMD_WARNING;
   }
 
-  if (pim_nexthop_lookup(&nexthop, addr, NULL)) {
+  if (pim_nexthop_lookup(&nexthop, addr, 0)) {
     vty_out(vty, "Failure querying RIB nexthop for unicast address %s%s",
            addr_str, VTY_NEWLINE);
     return CMD_WARNING;
@@ -2350,8 +3276,8 @@ DEFUN (show_ip_rib,
   vty_out(vty, "Address         NextHop         Interface Metric Preference%s",
          VTY_NEWLINE);
 
-  pim_inet4_dump("<nexthop?>", nexthop.mrib_nexthop_addr,
-                nexthop_addr_str, sizeof(nexthop_addr_str));
+  pim_addr_dump("<nexthop?>", &nexthop.mrib_nexthop_addr,
+               nexthop_addr_str, sizeof(nexthop_addr_str));
 
   vty_out(vty, "%-15s %-15s %-9s %6d %10d%s",
          addr_str,
@@ -2379,11 +3305,11 @@ static void show_ssmpingd(struct vty *vty)
   now = pim_time_monotonic_sec();
 
   for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     char ss_uptime[10];
     struct sockaddr_in bind_addr;
     socklen_t len = sizeof(bind_addr);
-    char bind_addr_str[100];
+    char bind_addr_str[INET_ADDRSTRLEN];
 
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
 
@@ -2417,114 +3343,304 @@ DEFUN (show_ip_ssmpingd,
   return CMD_SUCCESS;
 }
 
-DEFUN (ip_pim_rp,
-       ip_pim_rp_cmd,
-       "ip pim rp A.B.C.D",
-       IP_STR
-       "pim multicast routing\n"
-       "Rendevous Point\n"
-       "ip address of RP\n")
+static int
+pim_rp_cmd_worker (struct vty *vty, const char *rp, const char *group, const char *plist)
 {
-  int idx_ipv4 = 3;
   int result;
 
-  result = inet_pton(AF_INET, argv[idx_ipv4]->arg, &qpim_rp.rpf_addr.s_addr);
-  if (result <= 0) {
-    vty_out(vty, "%% Bad RP address specified: %s", argv[idx_ipv4]->arg);
-    return CMD_WARNING;
-  }
+  result = pim_rp_new (rp, group, plist);
 
-  if (pim_nexthop_lookup(&qpim_rp.source_nexthop, qpim_rp.rpf_addr, NULL) != 0) {
-    vty_out(vty, "%% No Path to RP address specified: %s", argv[idx_ipv4]->arg);
-    return CMD_WARNING;
-  }
+  if (result == PIM_MALLOC_FAIL)
+    {
+      vty_out (vty, "%% Out of memory%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_GROUP_BAD_ADDRESS)
+    {
+      vty_out (vty, "%% Bad group address specified: %s%s", group, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_RP_BAD_ADDRESS)
+    {
+      vty_out (vty, "%% Bad RP address specified: %s%s", rp, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_RP_NO_PATH)
+    {
+      vty_out (vty, "%% No Path to RP address specified: %s%s", rp, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_GROUP_OVERLAP)
+    {
+      vty_out (vty, "%% Group range specified cannot overlap%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_GROUP_PFXLIST_OVERLAP)
+    {
+      vty_out (vty, "%% This group is already covered by a RP prefix-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_RP_PFXLIST_IN_USE)
+    {
+      vty_out (vty, "%% The same prefix-list cannot be applied to multiple RPs%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
 
   return CMD_SUCCESS;
 }
 
-DEFUN (no_ip_pim_rp,
-       no_ip_pim_rp_cmd,
-       "no ip pim rp [A.B.C.D]",
-       NO_STR
+DEFUN (ip_pim_joinprune_time,
+       ip_pim_joinprune_time_cmd,
+       "ip pim join-prune-interval <60-600>",
        IP_STR
        "pim multicast routing\n"
-       "Rendevous Point\n"
-       "ip address of RP\n")
+       "Join Prune Send Interval\n"
+       "Seconds\n")
 {
-  qpim_rp.rpf_addr.s_addr = INADDR_NONE;
+  qpim_t_periodic = atoi(argv[3]->arg);
+  return CMD_SUCCESS;
+}
 
+DEFUN (no_ip_pim_joinprune_time,
+       no_ip_pim_joinprune_time_cmd,
+       "no ip pim join-prune-interval <60-600>",
+       NO_STR
+       IP_STR
+       "pim multicast routing\n"
+       "Join Prune Send Interval\n"
+       "Seconds\n")
+{
+  qpim_t_periodic = PIM_DEFAULT_T_PERIODIC;
   return CMD_SUCCESS;
 }
 
-DEFUN (ip_multicast_routing,
-       ip_multicast_routing_cmd,
-       "ip multicast-routing",
+DEFUN (ip_pim_register_suppress,
+       ip_pim_register_suppress_cmd,
+       "ip pim register-suppress-time <5-60000>",
        IP_STR
-       "Enable IP multicast forwarding\n")
+       "pim multicast routing\n"
+       "Register Suppress Timer\n"
+       "Seconds\n")
 {
-  pim_mroute_socket_enable();
-  pim_if_add_vif_all();
-  mroute_add_all();
-  static_mroute_add_all();
+  qpim_keep_alive_time = atoi (argv[3]->arg);
   return CMD_SUCCESS;
 }
 
-DEFUN (no_ip_multicast_routing,
-       no_ip_multicast_routing_cmd,
-       "no ip multicast-routing",
+DEFUN (no_ip_pim_register_suppress,
+       no_ip_pim_register_suppress_cmd,
+       "no ip pim register-suppress-time <5-60000>",
        NO_STR
        IP_STR
-       "Global IP configuration subcommands\n"
-       "Enable IP multicast forwarding\n")
+       "pim multicast routing\n"
+       "Register Suppress Timer\n"
+       "Seconds\n")
 {
-  mroute_del_all();
-  static_mroute_del_all();
-  pim_if_del_vif_all();
-  pim_mroute_socket_disable();
+  qpim_register_suppress_time = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT;
   return CMD_SUCCESS;
 }
 
-DEFUN (ip_ssmpingd,
-       ip_ssmpingd_cmd,
-       "ip ssmpingd [A.B.C.D]",
+DEFUN (ip_pim_keep_alive,
+       ip_pim_keep_alive_cmd,
+       "ip pim keep-alive-timer <31-60000>",
        IP_STR
-       CONF_SSMPINGD_STR
-       "Source address\n")
+       "pim multicast routing\n"
+       "Keep alive Timer\n"
+       "Seconds\n")
 {
-  int idx_ipv4 = 2;
-  int result;
-  struct in_addr source_addr;
-  const char *source_str = (argc > idx_ipv4) ? argv[idx_ipv4]->arg : "0.0.0.0";
-
-  result = inet_pton(AF_INET, source_str, &source_addr);
-  if (result <= 0) {
-    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
-           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
-  result = pim_ssmpingd_start(source_addr);
-  if (result) {
-    vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s",
-           source_str, result, VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
+  qpim_rp_keep_alive_time = atoi (argv[4]->arg);
   return CMD_SUCCESS;
 }
 
-DEFUN (no_ip_ssmpingd,
-       no_ip_ssmpingd_cmd,
-       "no ip ssmpingd [A.B.C.D]",
+DEFUN (no_ip_pim_keep_alive,
+       no_ip_pim_keep_alive_cmd,
+       "no ip pim keep-alive-timer <31-60000>",
        NO_STR
        IP_STR
-       CONF_SSMPINGD_STR
-       "Source address\n")
+       "pim multicast routing\n"
+       "Keep alive Timer\n"
+       "Seconds\n")
 {
-  int idx_ipv4 = 3;
-  int result;
-  struct in_addr source_addr;
-  const char *source_str = (argc > idx_ipv4) ? argv[idx_ipv4]->arg : "0.0.0.0";
+  qpim_keep_alive_time = PIM_KEEPALIVE_PERIOD;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_pim_packets,
+       ip_pim_packets_cmd,
+       "ip pim packets <1-100>",
+       IP_STR
+       "pim multicast routing\n"
+       "Number of packets to process at one time per fd\n")
+{
+  qpim_packet_process = atoi (argv[3]->arg);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_pim_packets,
+       no_ip_pim_packets_cmd,
+       "no ip pim packets <1-100>",
+       NO_STR
+       IP_STR
+       "pim multicast routing\n"
+       "Number of packets to process at one time per fd\n")
+{
+  qpim_packet_process = PIM_DEFAULT_PACKET_PROCESS;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_pim_rp,
+       ip_pim_rp_cmd,
+       "ip pim rp A.B.C.D [A.B.C.D/M]",
+       IP_STR
+       "pim multicast routing\n"
+       "Rendevous Point\n"
+       "ip address of RP\n")
+{
+  int idx_ipv4 = 3;
+  return pim_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL);
+}
+
+DEFUN (ip_pim_rp_prefix_list,
+       ip_pim_rp_prefix_list_cmd,
+       "ip pim rp A.B.C.D prefix-list WORD",
+       IP_STR
+       "pim multicast routing\n"
+       "Rendevous Point\n"
+       "ip address of RP\n"
+       "group prefix-list filter\n"
+       "Name of a prefix-list\n")
+{
+  return pim_rp_cmd_worker (vty, argv[3]->arg, NULL, argv[5]->arg);
+}
+
+static int
+pim_no_rp_cmd_worker (struct vty *vty, const char *rp, const char *group,
+                      const char *plist)
+{
+  int result = pim_rp_del (rp, group, plist);
+
+  if (result == PIM_GROUP_BAD_ADDRESS)
+    {
+      vty_out (vty, "%% Bad group address specified: %s%s", group, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_RP_BAD_ADDRESS)
+    {
+      vty_out (vty, "%% Bad RP address specified: %s%s", rp, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (result == PIM_RP_NOT_FOUND)
+    {
+      vty_out (vty, "%% Unable to find specified RP%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_pim_rp,
+       no_ip_pim_rp_cmd,
+       "no ip pim rp A.B.C.D [A.B.C.D/M]",
+       NO_STR
+       IP_STR
+       "pim multicast routing\n"
+       "Rendevous Point\n"
+       "ip address of RP\n")
+{
+  int idx_ipv4 = 4;
+  return pim_no_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL);
+}
+
+DEFUN (no_ip_pim_rp_prefix_list,
+       no_ip_pim_rp_prefix_list_cmd,
+       "no ip pim rp A.B.C.D prefix-list WORD",
+       NO_STR
+       IP_STR
+       "pim multicast routing\n"
+       "Rendevous Point\n"
+       "ip address of RP\n"
+       "group prefix-list filter\n"
+       "Name of a prefix-list\n")
+{
+  return pim_no_rp_cmd_worker (vty, argv[4]->arg, NULL, argv[6]->arg);
+}
+
+DEFUN (ip_multicast_routing,
+       ip_multicast_routing_cmd,
+       "ip multicast-routing",
+       IP_STR
+       "Enable IP multicast forwarding\n")
+{
+  pim_mroute_socket_enable();
+  pim_if_add_vif_all();
+  mroute_add_all();
+  static_mroute_add_all();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_multicast_routing,
+       no_ip_multicast_routing_cmd,
+       "no ip multicast-routing",
+       NO_STR
+       IP_STR
+       "Global IP configuration subcommands\n"
+       "Enable IP multicast forwarding\n")
+{
+  mroute_del_all();
+  static_mroute_del_all();
+  pim_if_del_vif_all();
+  pim_mroute_socket_disable();
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_ssmpingd,
+       ip_ssmpingd_cmd,
+       "ip ssmpingd [A.B.C.D]",
+       IP_STR
+       CONF_SSMPINGD_STR
+       "Source address\n")
+{
+  int idx_ipv4 = 2;
+  int result;
+  struct in_addr source_addr;
+  const char *source_str = (argc > idx_ipv4) ? argv[idx_ipv4]->arg : "0.0.0.0";
+
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  result = pim_ssmpingd_start(source_addr);
+  if (result) {
+    vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s",
+           source_str, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ssmpingd,
+       no_ip_ssmpingd_cmd,
+       "no ip ssmpingd [A.B.C.D]",
+       NO_STR
+       IP_STR
+       CONF_SSMPINGD_STR
+       "Source address\n")
+{
+  int idx_ipv4 = 3;
+  int result;
+  struct in_addr source_addr;
+  const char *source_str = (argc > idx_ipv4) ? argv[idx_ipv4]->arg : "0.0.0.0";
 
   result = inet_pton(AF_INET, source_str, &source_addr);
   if (result <= 0) {
@@ -2718,7 +3834,7 @@ static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp)
   pim_ifp = ifp->info;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("%s: Querier %s on %s reconfig query_interval=%d",
               __PRETTY_FUNCTION__,
@@ -2913,16 +4029,66 @@ DEFUN (interface_no_ip_igmp_query_interval,
   return CMD_SUCCESS;
 }
 
-#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1)
-#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25)
+DEFUN (interface_ip_igmp_version,
+       interface_ip_igmp_version_cmd,
+       "ip igmp version <2-3>",
+       IP_STR
+       IFACE_IGMP_STR
+       "IGMP version\n"
+       "IGMP version number\n")
+{
+  VTY_DECLVAR_CONTEXT(interface,ifp);
+  struct pim_interface *pim_ifp;
+  int igmp_version;
+
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty,
+           "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+           ifp->name,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  igmp_version = atoi(argv[3]->arg);
+  pim_ifp->igmp_version = igmp_version;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_version,
+       interface_no_ip_igmp_version_cmd,
+       "no ip igmp version <2-3>",
+       NO_STR
+       IP_STR
+       IFACE_IGMP_STR
+       "IGMP version\n"
+       "IGMP version number\n")
+{
+  VTY_DECLVAR_CONTEXT(interface, ifp);
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp)
+    return CMD_SUCCESS;
+
+  pim_ifp->igmp_version = IGMP_DEFAULT_VERSION;
+
+  return CMD_SUCCESS;
+}
+
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10)
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250)
 
 DEFUN (interface_ip_igmp_query_max_response_time,
        interface_ip_igmp_query_max_response_time_cmd,
-       "ip igmp query-max-response-time (1-25)",
+       "ip igmp query-max-response-time (10-250)",
        IP_STR
        IFACE_IGMP_STR
        IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR
-       "Query response value in seconds\n")
+       "Query response value in deci-seconds\n")
 {
   VTY_DECLVAR_CONTEXT(interface, ifp);
   struct pim_interface *pim_ifp;
@@ -2940,26 +4106,7 @@ DEFUN (interface_ip_igmp_query_max_response_time,
 
   query_max_response_time = atoi(argv[4]->arg);
 
-  /*
-    It seems we don't need to check bounds since command.c does it
-    already, but we verify them anyway for extra safety.
-  */
-  if (query_max_response_time < IGMP_QUERY_MAX_RESPONSE_TIME_MIN) {
-    vty_out(vty, "Query max response time %d sec lower than minimum %d sec%s",
-           query_max_response_time,
-           IGMP_QUERY_MAX_RESPONSE_TIME_MIN,
-           VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-  if (query_max_response_time > IGMP_QUERY_MAX_RESPONSE_TIME_MAX) {
-    vty_out(vty, "Query max response time %d sec higher than maximum %d sec%s",
-           query_max_response_time,
-           IGMP_QUERY_MAX_RESPONSE_TIME_MAX,
-           VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
-  if (query_max_response_time >= pim_ifp->igmp_default_query_interval) {
+  if (query_max_response_time >= pim_ifp->igmp_default_query_interval * 10) {
     vty_out(vty,
            "Can't set query max response time %d sec >= general query interval %d sec%s",
            query_max_response_time, pim_ifp->igmp_default_query_interval,
@@ -2967,14 +4114,14 @@ DEFUN (interface_ip_igmp_query_max_response_time,
     return CMD_WARNING;
   }
 
-  change_query_max_response_time(pim_ifp, 10 * query_max_response_time);
+  change_query_max_response_time(pim_ifp, query_max_response_time);
 
   return CMD_SUCCESS;
 }
 
 DEFUN (interface_no_ip_igmp_query_max_response_time,
        interface_no_ip_igmp_query_max_response_time_cmd,
-       "no ip igmp query-max-response-time",
+       "no ip igmp query-max-response-time <10-250>",
        NO_STR
        IP_STR
        IFACE_IGMP_STR
@@ -2982,23 +4129,12 @@ DEFUN (interface_no_ip_igmp_query_max_response_time,
 {
   VTY_DECLVAR_CONTEXT(interface, ifp);
   struct pim_interface *pim_ifp;
-  int default_query_interval_dsec;
 
   pim_ifp = ifp->info;
 
   if (!pim_ifp)
     return CMD_SUCCESS;
 
-  default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
-
-  if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) {
-    vty_out(vty,
-           "Can't set default query max response time %d dsec >= general query interval %d dsec.%s",
-           IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec,
-           VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
   change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC);
 
   return CMD_SUCCESS;
@@ -3007,13 +4143,13 @@ DEFUN (interface_no_ip_igmp_query_max_response_time,
 #define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10)
 #define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250)
 
-DEFUN (interface_ip_igmp_query_max_response_time_dsec,
-       interface_ip_igmp_query_max_response_time_dsec_cmd,
-       "ip igmp query-max-response-time-dsec (10-250)",
-       IP_STR
-       IFACE_IGMP_STR
-       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR
-       "Query response value in deciseconds\n")
+DEFUN_HIDDEN (interface_ip_igmp_query_max_response_time_dsec,
+             interface_ip_igmp_query_max_response_time_dsec_cmd,
+             "ip igmp query-max-response-time-dsec (10-250)",
+             IP_STR
+             IFACE_IGMP_STR
+             IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR
+             "Query response value in deciseconds\n")
 {
   VTY_DECLVAR_CONTEXT(interface, ifp);
   struct pim_interface *pim_ifp;
@@ -3032,25 +4168,6 @@ DEFUN (interface_ip_igmp_query_max_response_time_dsec,
 
   query_max_response_time_dsec = atoi(argv[4]->arg);
 
-  /*
-    It seems we don't need to check bounds since command.c does it
-    already, but we verify them anyway for extra safety.
-  */
-  if (query_max_response_time_dsec < IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC) {
-    vty_out(vty, "Query max response time %d dsec lower than minimum %d dsec%s",
-           query_max_response_time_dsec,
-           IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC,
-           VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-  if (query_max_response_time_dsec > IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC) {
-    vty_out(vty, "Query max response time %d dsec higher than maximum %d dsec%s",
-           query_max_response_time_dsec,
-           IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC,
-           VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
   default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
 
   if (query_max_response_time_dsec >= default_query_interval_dsec) {
@@ -3066,33 +4183,22 @@ DEFUN (interface_ip_igmp_query_max_response_time_dsec,
   return CMD_SUCCESS;
 }
 
-DEFUN (interface_no_ip_igmp_query_max_response_time_dsec,
-       interface_no_ip_igmp_query_max_response_time_dsec_cmd,
-       "no ip igmp query-max-response-time-dsec",
-       NO_STR
-       IP_STR
-       IFACE_IGMP_STR
-       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR)
+DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec,
+             interface_no_ip_igmp_query_max_response_time_dsec_cmd,
+             "no ip igmp query-max-response-time-dsec",
+             NO_STR
+             IP_STR
+             IFACE_IGMP_STR
+             IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR)
 {
   VTY_DECLVAR_CONTEXT(interface, ifp);
   struct pim_interface *pim_ifp;
-  int default_query_interval_dsec;
 
   pim_ifp = ifp->info;
 
   if (!pim_ifp)
     return CMD_SUCCESS;
 
-  default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
-
-  if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) {
-    vty_out(vty,
-           "Can't set default query max response time %d dsec >= general query interval %d dsec.%s",
-           IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec,
-           VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
   change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC);
 
   return CMD_SUCCESS;
@@ -3162,7 +4268,6 @@ static int
 pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype)
 {
   struct pim_interface *pim_ifp = ifp->info;
-  struct in_addr null = { .s_addr = 0 };
 
   if (!pim_ifp) {
     pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */);
@@ -3177,8 +4282,6 @@ pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype)
   pim_ifp->itype = itype;
   pim_if_addr_add_all(ifp);
   pim_if_membership_refresh(ifp);
-
-  pim_rp_check_rp (null, pim_ifp->primary_address);
   return 1;
 }
 
@@ -3230,12 +4333,6 @@ pim_cmd_interface_delete (struct interface *ifp)
 
   pim_if_membership_clear(ifp);
 
-  /*
-    pim_if_addr_del_all() removes all sockets from
-    pim_ifp->igmp_socket_list.
-   */
-  pim_if_addr_del_all(ifp);
-
   /*
     pim_sock_delete() removes all neighbors from
     pim_ifp->pim_neighbor_list.
@@ -3243,6 +4340,7 @@ pim_cmd_interface_delete (struct interface *ifp)
   pim_sock_delete(ifp, "pim unconfigured on interface");
 
   if (!PIM_IF_TEST_IGMP(pim_ifp->options)) {
+    pim_if_addr_del_all(ifp);
     pim_if_delete(ifp);
   }
 
@@ -3644,6 +4742,17 @@ DEFUN (debug_mroute,
   return CMD_SUCCESS;
 }
 
+DEFUN (debug_mroute_detail,
+       debug_mroute_detail_cmd,
+       "debug mroute detail",
+       DEBUG_STR
+       DEBUG_MROUTE_STR
+       "detailed\n")
+{
+  PIM_DO_DEBUG_MROUTE_DETAIL;
+  return CMD_SUCCESS;
+}
+
 DEFUN (no_debug_mroute,
        no_debug_mroute_cmd,
        "no debug mroute",
@@ -3655,6 +4764,17 @@ DEFUN (no_debug_mroute,
   return CMD_SUCCESS;
 }
 
+DEFUN (no_debug_mroute_detail,
+       no_debug_mroute_detail_cmd,
+       "no debug mroute detail",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MROUTE_STR
+       "detailed\n")
+{
+  PIM_DONT_DEBUG_MROUTE_DETAIL;
+  return CMD_SUCCESS;
+}
 
 DEFUN (debug_static,
        debug_static_cmd,
@@ -3687,6 +4807,8 @@ DEFUN (debug_pim,
   PIM_DO_DEBUG_PIM_EVENTS;
   PIM_DO_DEBUG_PIM_PACKETS;
   PIM_DO_DEBUG_PIM_TRACE;
+  PIM_DO_DEBUG_MSDP_EVENTS;
+  PIM_DO_DEBUG_MSDP_PACKETS;
   return CMD_SUCCESS;
 }
 
@@ -3700,6 +4822,8 @@ DEFUN (no_debug_pim,
   PIM_DONT_DEBUG_PIM_EVENTS;
   PIM_DONT_DEBUG_PIM_PACKETS;
   PIM_DONT_DEBUG_PIM_TRACE;
+  PIM_DONT_DEBUG_MSDP_EVENTS;
+  PIM_DONT_DEBUG_MSDP_PACKETS;
 
   PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
   PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
@@ -3746,23 +4870,29 @@ DEFUN (debug_pim_packets,
 
 DEFUN (debug_pim_packets_filter,
        debug_pim_packets_filter_cmd,
-       "debug pim packets <hello|joins>",
+       "debug pim packets <hello|joins|register>",
        DEBUG_STR
        DEBUG_PIM_STR
        DEBUG_PIM_PACKETS_STR
        DEBUG_PIM_HELLO_PACKETS_STR
-       DEBUG_PIM_J_P_PACKETS_STR)
+       DEBUG_PIM_J_P_PACKETS_STR
+       DEBUG_PIM_PIM_REG_PACKETS_STR)
 {
   int idx_hello_join = 3;
-    if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0) 
+  if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0) 
     {
       PIM_DO_DEBUG_PIM_HELLO;
-      vty_out (vty, "PIM Hello debugging is on %s", VTY_NEWLINE);
+      vty_out (vty, "PIM Hello debugging is on%s", VTY_NEWLINE);
     }
-    else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0)
+  else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0)
     {
       PIM_DO_DEBUG_PIM_J_P;
-      vty_out (vty, "PIM Join/Prune debugging is on %s", VTY_NEWLINE);
+      vty_out (vty, "PIM Join/Prune debugging is on%s", VTY_NEWLINE);
+    }
+  else if (strncmp(argv[idx_hello_join]->arg,"r",1) == 0)
+    {
+      PIM_DO_DEBUG_PIM_REG;
+      vty_out (vty, "PIM Register debugging is on%s", VTY_NEWLINE);
     }
   return CMD_SUCCESS;
 }
@@ -3784,7 +4914,7 @@ DEFUN (no_debug_pim_packets,
 
 DEFUN (no_debug_pim_packets_filter,
        no_debug_pim_packets_filter_cmd,
-       "no debug pim packets <hello|joins>",
+       "no debug pim packets <hello|joins|register>",
        NO_STR
        DEBUG_STR
        DEBUG_PIM_STR
@@ -3793,17 +4923,22 @@ DEFUN (no_debug_pim_packets_filter,
        DEBUG_PIM_J_P_PACKETS_STR)
 {
   int idx_hello_join = 4;
-    if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0) 
+  if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0) 
     {
       PIM_DONT_DEBUG_PIM_HELLO;
       vty_out (vty, "PIM Hello debugging is off %s", VTY_NEWLINE);
     }
-    else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0)
+  else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0)
     {
       PIM_DONT_DEBUG_PIM_J_P;
       vty_out (vty, "PIM Join/Prune debugging is off %s", VTY_NEWLINE);
     }
-    return CMD_SUCCESS;
+  else if (strncmp (argv[idx_hello_join]->arg, "r", 1) == 0)
+    {
+      PIM_DONT_DEBUG_PIM_REG;
+      vty_out (vty, "PIM Register debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
 }
 
 
@@ -3931,824 +5066,935 @@ DEFUN (no_debug_pim_zebra,
 }
 
 
-DEFUN (show_debugging_pim,
-       show_debugging_pim_cmd,
-       "show debugging pim",
-       SHOW_STR
+DEFUN (debug_msdp,
+       debug_msdp_cmd,
+       "debug msdp",
        DEBUG_STR
-       PIM_STR)
+       DEBUG_MSDP_STR)
 {
-  pim_debug_config_write(vty);
+  PIM_DO_DEBUG_MSDP_EVENTS;
+  PIM_DO_DEBUG_MSDP_PACKETS;
   return CMD_SUCCESS;
 }
 
-static struct igmp_sock *find_igmp_sock_by_fd(int fd)
+DEFUN (no_debug_msdp,
+       no_debug_msdp_cmd,
+       "no debug msdp",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MSDP_STR)
 {
-  struct listnode  *ifnode;
-  struct interface *ifp;
-
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
-    struct pim_interface *pim_ifp;
-    struct igmp_sock     *igmp;
-    
-    if (!ifp->info)
-      continue;
+  PIM_DONT_DEBUG_MSDP_EVENTS;
+  PIM_DONT_DEBUG_MSDP_PACKETS;
+  return CMD_SUCCESS;
+}
 
-    pim_ifp = ifp->info;
+ALIAS (no_debug_msdp,
+       undebug_msdp_cmd,
+       "undebug msdp",
+       UNDEBUG_STR
+       DEBUG_MSDP_STR)
 
-    /* lookup igmp socket under current interface */
-    igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd);
-    if (igmp)
-      return igmp;
-  }
-
-  return 0;
-}
-
-DEFUN (test_igmp_receive_report,
-       test_igmp_receive_report_cmd,
-       "test igmp receive report (0-65535) A.B.C.D (1-6) LINE...",
-       "Test\n"
-       "Test IGMP protocol\n"
-       "Test IGMP message\n"
-       "Test IGMP report\n"
-       "Socket\n"
-       "IGMP group address\n"
-       "Record type\n"
-       "Sources\n")
-{
-  int idx_number = 4;
-  int idx_ipv4 = 5;
-  int idx_number_2 = 6;
-  int idx_line = 7;
-  char              buf[1000];
-  char             *igmp_msg;
-  struct ip        *ip_hdr;
-  size_t            ip_hlen; /* ip header length in bytes */
-  int               ip_msg_len;
-  int               igmp_msg_len;
-  const char       *socket;
-  int               socket_fd;
-  const char       *grp_str;
-  struct in_addr    grp_addr;
-  const char       *record_type_str;
-  int               record_type;
-  const char       *src_str;
-  int               result;
-  struct igmp_sock *igmp;
-  char             *group_record;
-  int               num_sources;
-  struct in_addr   *sources;
-  struct in_addr   *src_addr;
-  int               argi;
-
-  socket = argv[idx_number]->arg;
-  socket_fd = atoi(socket);
-  igmp = find_igmp_sock_by_fd(socket_fd);
-  if (!igmp) {
-    vty_out(vty, "Could not find IGMP socket %s: fd=%d%s",
-           socket, socket_fd, VTY_NEWLINE);
-    return CMD_WARNING;
-  }
-
-  grp_str = argv[idx_ipv4]->arg;
-  result = inet_pton(AF_INET, grp_str, &grp_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
-           grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
-  }
+DEFUN (debug_msdp_events,
+       debug_msdp_events_cmd,
+       "debug msdp events",
+       DEBUG_STR
+       DEBUG_MSDP_STR
+       DEBUG_MSDP_EVENTS_STR)
+{
+  PIM_DO_DEBUG_MSDP_EVENTS;
+  return CMD_SUCCESS;
+}
 
-  record_type_str = argv[idx_number_2]->arg;
-  record_type = atoi(record_type_str);
+DEFUN (no_debug_msdp_events,
+       no_debug_msdp_events_cmd,
+       "no debug msdp events",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MSDP_STR
+       DEBUG_MSDP_EVENTS_STR)
+{
+  PIM_DONT_DEBUG_MSDP_EVENTS;
+  return CMD_SUCCESS;
+}
 
-  /*
-    Tweak IP header
-   */
-  ip_hdr = (struct ip *) buf;
-  ip_hdr->ip_p = PIM_IP_PROTO_IGMP;
-  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
-  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
-  ip_hdr->ip_src = igmp->ifaddr;
-  ip_hdr->ip_dst = igmp->ifaddr;
+ALIAS (no_debug_msdp_events,
+       undebug_msdp_events_cmd,
+       "undebug msdp events",
+       UNDEBUG_STR
+       DEBUG_MSDP_STR
+       DEBUG_MSDP_EVENTS_STR)
 
-  /*
-    Build IGMP v3 report message
-   */
-  igmp_msg = buf + ip_hlen;
-  group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
-  *igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */
-  *(uint16_t *)      (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
-  *(uint16_t *)      (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */
-  *(uint8_t  *)      (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type;
-  memcpy(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, &grp_addr, sizeof(struct in_addr));
-
-  /* Scan LINE sources */
-  sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET);
-  src_addr = sources;
-  for (argi = idx_line; argi < argc; ++argi,++src_addr) {
-    src_str = argv[argi]->arg;
-    result = inet_pton(AF_INET, src_str, src_addr);
-    if (result <= 0) {
-      vty_out(vty, "Bad source address %s: errno=%d: %s%s",
-             src_str, errno, safe_strerror(errno), VTY_NEWLINE);
-      return CMD_WARNING;
-    }
-  }
-  num_sources = src_addr - sources;
+DEFUN (debug_msdp_packets,
+       debug_msdp_packets_cmd,
+       "debug msdp packets",
+       DEBUG_STR
+       DEBUG_MSDP_STR
+       DEBUG_MSDP_PACKETS_STR)
+{
+  PIM_DO_DEBUG_MSDP_PACKETS;
+  return CMD_SUCCESS;
+}
 
-  *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources);
+DEFUN (no_debug_msdp_packets,
+       no_debug_msdp_packets_cmd,
+       "no debug msdp packets",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MSDP_STR
+       DEBUG_MSDP_PACKETS_STR)
+{
+  PIM_DONT_DEBUG_MSDP_PACKETS;
+  return CMD_SUCCESS;
+}
 
-  igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4);   /* v3 report for one single group record */
+ALIAS (no_debug_msdp_packets,
+       undebug_msdp_packets_cmd,
+       "undebug msdp packets",
+       UNDEBUG_STR
+       DEBUG_MSDP_STR
+       DEBUG_MSDP_PACKETS_STR)
 
-  /* compute checksum */
-  *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len);
+DEFUN (show_debugging_pim,
+       show_debugging_pim_cmd,
+       "show debugging pim",
+       SHOW_STR
+       DEBUG_STR
+       PIM_STR)
+{
+  pim_debug_config_write(vty);
+  return CMD_SUCCESS;
+}
 
-  /* "receive" message */
+static int
+interface_pim_use_src_cmd_worker(struct vty *vty, const char *source)
+{
+  int result;
+  struct in_addr source_addr;
+  VTY_DECLVAR_CONTEXT(interface, ifp);
 
-  ip_msg_len = ip_hlen + igmp_msg_len;
-  result = pim_igmp_packet(igmp, buf, ip_msg_len);
-  if (result) {
-    vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s",
-           ip_msg_len, result, VTY_NEWLINE);
+  result = inet_pton(AF_INET, source, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+        source, errno, safe_strerror(errno), VTY_NEWLINE);
     return CMD_WARNING;
   }
 
-  return CMD_SUCCESS;
+  result = pim_update_source_set(ifp, source_addr);
+  switch (result) {
+    case PIM_SUCCESS:
+      break;
+   case PIM_IFACE_NOT_FOUND:
+      vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE);
+      break;
+    case PIM_UPDATE_SOURCE_DUP:
+      vty_out(vty, "%% Source already set to %s%s", source, VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% Source set failed%s", VTY_NEWLINE);
+  }
+
+  return result?CMD_WARNING:CMD_SUCCESS;
 }
 
-static int hexval(uint8_t ch)
-{
-  return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a');
-}
-
-DEFUN (test_pim_receive_dump,
-       test_pim_receive_dump_cmd,
-       "test pim receive dump INTERFACE A.B.C.D LINE...",
-       "Test\n"
-       "Test PIM protocol\n"
-       "Test PIM message reception\n"
-       "Test PIM packet dump reception from neighbor\n"
-       "Interface\n"
-       "Neighbor address\n"
-       "Packet dump\n")
-{
-  int idx_interface = 4;
-  int idx_ipv4 = 5;
-  int idx_line = 6;
-  uint8_t           buf[1000];
-  uint8_t          *pim_msg;
-  struct ip        *ip_hdr;
-  size_t            ip_hlen; /* ip header length in bytes */
-  int               ip_msg_len;
-  int               pim_msg_size;
-  const char       *neigh_str;
-  struct in_addr    neigh_addr;
-  const char       *ifname;
-  struct interface *ifp;
-  int               argi;
-  int               result;
-
-  /* Find interface */
-  ifname = argv[idx_interface]->arg;
-  ifp = if_lookup_by_name(ifname);
-  if (!ifp) {
-    vty_out(vty, "No such interface name %s%s",
-           ifname, VTY_NEWLINE);
+DEFUN (interface_pim_use_source,
+       interface_pim_use_source_cmd,
+       "ip pim use-source A.B.C.D",
+       IP_STR
+       "pim multicast routing\n"
+       "Configure primary IP address\n"
+       "source ip address\n")
+{
+  return interface_pim_use_src_cmd_worker (vty, argv[3]->arg);
+}
+
+DEFUN (interface_no_pim_use_source,
+       interface_no_pim_use_source_cmd,
+       "no ip pim use-source",
+       NO_STR
+       IP_STR
+       "pim multicast routing\n"
+       "Delete source IP address\n")
+{
+  return interface_pim_use_src_cmd_worker (vty, "0.0.0.0");
+}
+
+static int
+ip_msdp_peer_cmd_worker (struct vty *vty, const char *peer, const char *local)
+{
+  enum pim_msdp_err result;
+  struct in_addr peer_addr;
+  struct in_addr local_addr;
+
+  result = inet_pton(AF_INET, peer, &peer_addr);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad peer address %s: errno=%d: %s%s",
+        peer, errno, safe_strerror(errno), VTY_NEWLINE);
     return CMD_WARNING;
   }
 
-  /* Neighbor address */
-  neigh_str = argv[idx_ipv4]->arg;
-  result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+  result = inet_pton(AF_INET, local, &local_addr);
   if (result <= 0) {
-    vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
-           neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+        local, errno, safe_strerror(errno), VTY_NEWLINE);
     return CMD_WARNING;
   }
 
-  /*
-    Tweak IP header
-   */
-  ip_hdr = (struct ip *) buf;
-  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
-  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
-  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
-  ip_hdr->ip_src = neigh_addr;
-  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+  result = pim_msdp_peer_add(peer_addr, local_addr, "default", NULL/* mp_p */);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_OOM:
+      vty_out(vty, "%% Out of memory%s", VTY_NEWLINE);
+      break;
+    case PIM_MSDP_ERR_PEER_EXISTS:
+      vty_out(vty, "%% Peer exists%s", VTY_NEWLINE);
+      break;
+    case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+      vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% peer add failed%s", VTY_NEWLINE);
+  }
 
-  /*
-    Build PIM hello message
-  */
-  pim_msg = buf + ip_hlen;
-  pim_msg_size = 0;
-
-  /* Scan LINE dump into buffer */
-  for (argi = idx_line; argi < argc; ++argi) {
-    const char *str = argv[argi]->arg;
-    int str_len = strlen(str);
-    int str_last = str_len - 1;
-    int i;
-
-    if (str_len % 2) {
-      vty_out(vty, "%% Uneven hex array arg %d=%s%s",
-             argi, str, VTY_NEWLINE);
-      return CMD_WARNING;
-    }
+  return result?CMD_WARNING:CMD_SUCCESS;
+}
 
-    for (i = 0; i < str_last; i += 2) {
-      uint8_t octet;
-      int left;
-      uint8_t h1 = str[i];
-      uint8_t h2 = str[i + 1];
+DEFUN_HIDDEN (ip_msdp_peer,
+       ip_msdp_peer_cmd,
+       "ip msdp peer A.B.C.D source A.B.C.D",
+       IP_STR
+       CFG_MSDP_STR
+       "Configure MSDP peer\n"
+       "peer ip address\n"
+       "Source address for TCP connection\n"
+       "local ip address\n")
+{
+  return ip_msdp_peer_cmd_worker (vty, argv[3]->arg, argv[5]->arg);
+}
 
-      if (!isxdigit(h1) || !isxdigit(h2)) {
-       vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s",
-               h1, h2, argi, str, VTY_NEWLINE);
-       return CMD_WARNING;
-      }
-      octet = (hexval(h1) << 4) + hexval(h2);
+static int
+ip_no_msdp_peer_cmd_worker (struct vty *vty, const char *peer)
+{
+  enum pim_msdp_err result;
+  struct in_addr peer_addr;
 
-      left = sizeof(buf) - ip_hlen - pim_msg_size;
-      if (left < 1) {
-       vty_out(vty, "%% Overflow buf_size=%zu buf_left=%d at hex array arg %d=%s octet %02x%s",
-               sizeof(buf), left, argi, str, octet, VTY_NEWLINE);
-       return CMD_WARNING;
-      }
-      
-      pim_msg[pim_msg_size++] = octet;
-    }
+  result = inet_pton(AF_INET, peer, &peer_addr);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad peer address %s: errno=%d: %s%s",
+        peer, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
   }
 
-  ip_msg_len = ip_hlen + pim_msg_size;
+  result = pim_msdp_peer_del(peer_addr);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_NO_PEER:
+      vty_out(vty, "%% Peer does not exist%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% peer del failed%s", VTY_NEWLINE);
+  }
 
-  vty_out(vty, "Receiving: buf_size=%zu ip_msg_size=%d pim_msg_size=%d%s",
-         sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE);
+  return result?CMD_WARNING:CMD_SUCCESS;
+}
 
-  /* "receive" message */
+DEFUN_HIDDEN (no_ip_msdp_peer,
+       no_ip_msdp_peer_cmd,
+       "no ip msdp peer A.B.C.D",
+       NO_STR
+       IP_STR
+       CFG_MSDP_STR
+       "Delete MSDP peer\n"
+       "peer ip address\n")
+{
+  return ip_no_msdp_peer_cmd_worker (vty, argv[4]->arg);
+}
 
-  result = pim_pim_packet(ifp, buf, ip_msg_len);
-  if (result) {
-    vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s",
-           ip_msg_len, result, VTY_NEWLINE);
+static int
+ip_msdp_mesh_group_member_cmd_worker(struct vty *vty, const char *mg, const char *mbr)
+{
+  enum pim_msdp_err result;
+  struct in_addr mbr_ip;
+
+  result = inet_pton(AF_INET, mbr, &mbr_ip);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad member address %s: errno=%d: %s%s",
+        mbr, errno, safe_strerror(errno), VTY_NEWLINE);
     return CMD_WARNING;
   }
 
-  return CMD_SUCCESS;
+  result = pim_msdp_mg_mbr_add(mg, mbr_ip);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_OOM:
+      vty_out(vty, "%% Out of memory%s", VTY_NEWLINE);
+      break;
+    case PIM_MSDP_ERR_MG_MBR_EXISTS:
+      vty_out(vty, "%% mesh-group member exists%s", VTY_NEWLINE);
+      break;
+    case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+      vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% member add failed%s", VTY_NEWLINE);
+  }
+
+  return result?CMD_WARNING:CMD_SUCCESS;
 }
 
-DEFUN (test_pim_receive_hello,
-       test_pim_receive_hello_cmd,
-       "test pim receive hello INTERFACE A.B.C.D (0-65535) (0-65535) (0-65535) (0-32767) (0-65535) (0-1) [LINE]",
-       "Test\n"
-       "Test PIM protocol\n"
-       "Test PIM message reception\n"
-       "Test PIM hello reception from neighbor\n"
-       "Interface\n"
-       "Neighbor address\n"
-       "Neighbor holdtime\n"
-       "Neighbor DR priority\n"
-       "Neighbor generation ID\n"
-       "Neighbor propagation delay (msec)\n"
-       "Neighbor override interval (msec)\n"
-       "Neighbor LAN prune delay T-bit\n"
-       "Neighbor secondary addresses\n")
-{
-  int idx_interface = 4;
-  int idx_ipv4 = 5;
-  int idx_number = 6;
-  int idx_number_2 = 7;
-  int idx_number_3 = 8;
-  int idx_number_4 = 9;
-  int idx_number_5 = 10;
-  int idx_number_6 = 11;
-  int idx_line = 12;
-  uint8_t           buf[1000];
-  uint8_t          *pim_msg;
-  struct ip        *ip_hdr;
-  size_t            ip_hlen; /* ip header length in bytes */
-  int               ip_msg_len;
-  int               pim_tlv_size;
-  int               pim_msg_size;
-  const char       *neigh_str;
-  struct in_addr    neigh_addr;
-  const char       *ifname;
-  struct interface *ifp;
-  uint16_t          neigh_holdtime;
-  uint16_t          neigh_propagation_delay;
-  uint16_t          neigh_override_interval;
-  int               neigh_can_disable_join_suppression;
-  uint32_t          neigh_dr_priority;
-  uint32_t          neigh_generation_id;
-  int               argi;
-  int               result;
-
-  /* Find interface */
-  ifname = argv[idx_interface]->arg;
-  ifp = if_lookup_by_name(ifname);
-  if (!ifp) {
-    vty_out(vty, "No such interface name %s%s",
-           ifname, VTY_NEWLINE);
-    return CMD_WARNING;
-  }
+DEFUN (ip_msdp_mesh_group_member,
+       ip_msdp_mesh_group_member_cmd,
+       "ip msdp mesh-group WORD member A.B.C.D",
+       IP_STR
+       CFG_MSDP_STR
+       "Configure MSDP mesh-group\n"
+       "mesh group name\n"
+       "mesh group member\n"
+       "peer ip address\n")
+{
+  return ip_msdp_mesh_group_member_cmd_worker(vty, argv[3]->arg, argv[5]->arg);
+}
 
-  /* Neighbor address */
-  neigh_str = argv[idx_ipv4]->arg;
-  result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+static int
+ip_no_msdp_mesh_group_member_cmd_worker(struct vty *vty, const char *mg, const char *mbr)
+{
+  enum pim_msdp_err result;
+  struct in_addr mbr_ip;
+
+  result = inet_pton(AF_INET, mbr, &mbr_ip);
   if (result <= 0) {
-    vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
-           neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    vty_out(vty, "%% Bad member address %s: errno=%d: %s%s",
+        mbr, errno, safe_strerror(errno), VTY_NEWLINE);
     return CMD_WARNING;
   }
 
-  neigh_holdtime                     = atoi(argv[idx_number]->arg);
-  neigh_dr_priority                  = atoi(argv[idx_number_2]->arg);
-  neigh_generation_id                = atoi(argv[idx_number_3]->arg);
-  neigh_propagation_delay            = atoi(argv[idx_number_4]->arg);
-  neigh_override_interval            = atoi(argv[idx_number_5]->arg);
-  neigh_can_disable_join_suppression = atoi(argv[idx_number_6]->arg);
+  result = pim_msdp_mg_mbr_del(mg, mbr_ip);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_NO_MG:
+      vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE);
+      break;
+    case PIM_MSDP_ERR_NO_MG_MBR:
+      vty_out(vty, "%% mesh-group member does not exist%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% mesh-group member del failed%s", VTY_NEWLINE);
+  }
 
-  /*
-    Tweak IP header
-   */
-  ip_hdr = (struct ip *) buf;
-  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
-  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
-  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
-  ip_hdr->ip_src = neigh_addr;
-  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+  return result?CMD_WARNING:CMD_SUCCESS;
+}
+DEFUN (no_ip_msdp_mesh_group_member,
+       no_ip_msdp_mesh_group_member_cmd,
+       "no ip msdp mesh-group WORD member A.B.C.D",
+       NO_STR
+       IP_STR
+       CFG_MSDP_STR
+       "Delete MSDP mesh-group member\n"
+       "mesh group name\n"
+       "mesh group member\n"
+       "peer ip address\n")
+{
+  return ip_no_msdp_mesh_group_member_cmd_worker(vty, argv[4]->arg, argv[6]->arg);
+}
 
-  /*
-    Build PIM hello message
-  */
-  pim_msg = buf + ip_hlen;
-
-  /* Scan LINE addresses */
-  for (argi = idx_line; argi < argc; ++argi) {
-    const char *sec_str = argv[argi]->arg;
-    struct in_addr sec_addr;
-    result = inet_pton(AF_INET, sec_str, &sec_addr);
-    if (result <= 0) {
-      vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s",
-             sec_str, errno, safe_strerror(errno), VTY_NEWLINE);
-      return CMD_WARNING;
-    }
+static int
+ip_msdp_mesh_group_source_cmd_worker(struct vty *vty, const char *mg, const char *src)
+{
+  enum pim_msdp_err result;
+  struct in_addr src_ip;
 
-    vty_out(vty,
-           "FIXME WRITEME consider neighbor secondary address %s%s",
-           sec_str, VTY_NEWLINE);
-  }
-
-  pim_tlv_size = pim_hello_build_tlv(ifp->name,
-                                    pim_msg + PIM_PIM_MIN_LEN,
-                                    sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN,
-                                    neigh_holdtime,
-                                    neigh_dr_priority,
-                                    neigh_generation_id,
-                                    neigh_propagation_delay,
-                                    neigh_override_interval,
-                                    neigh_can_disable_join_suppression,
-                                    0 /* FIXME secondary address list */);
-  if (pim_tlv_size < 0) {
-    vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s",
-           pim_tlv_size, VTY_NEWLINE);
+  result = inet_pton(AF_INET, src, &src_ip);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+        src, errno, safe_strerror(errno), VTY_NEWLINE);
     return CMD_WARNING;
   }
 
-  pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
+  result = pim_msdp_mg_src_add(mg, src_ip);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_OOM:
+      vty_out(vty, "%% Out of memory%s", VTY_NEWLINE);
+      break;
+    case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+      vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% source add failed%s", VTY_NEWLINE);
+  }
 
-  pim_msg_build_header(pim_msg, pim_msg_size,
-                      PIM_MSG_TYPE_HELLO);
+  return result?CMD_WARNING:CMD_SUCCESS;
+}
 
-  /* "receive" message */
 
-  ip_msg_len = ip_hlen + pim_msg_size;
-  result = pim_pim_packet(ifp, buf, ip_msg_len);
-  if (result) {
-    vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
-           ip_msg_len, result, VTY_NEWLINE);
-    return CMD_WARNING;
+DEFUN (ip_msdp_mesh_group_source,
+       ip_msdp_mesh_group_source_cmd,
+       "ip msdp mesh-group WORD source A.B.C.D",
+       IP_STR
+       CFG_MSDP_STR
+       "Configure MSDP mesh-group\n"
+       "mesh group name\n"
+       "mesh group local address\n"
+       "source ip address for the TCP connection\n")
+{
+  return ip_msdp_mesh_group_source_cmd_worker(vty, argv[3]->arg, argv[5]->arg);
+}
+
+static int
+ip_no_msdp_mesh_group_source_cmd_worker(struct vty *vty, const char *mg)
+{
+  enum pim_msdp_err result;
+
+  result = pim_msdp_mg_src_del(mg);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_NO_MG:
+      vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% mesh-group source del failed%s", VTY_NEWLINE);
   }
 
-  return CMD_SUCCESS;
+  return result?CMD_WARNING:CMD_SUCCESS;
 }
 
-DEFUN (test_pim_receive_assert,
-       test_pim_receive_assert_cmd,
-       "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D (0-65535) (0-65535) (0-1)",
-       "Test\n"
-       "Test PIM protocol\n"
-       "Test PIM message reception\n"
-       "Test reception of PIM assert\n"
-       "Interface\n"
-       "Neighbor address\n"
-       "Assert multicast group address\n"
-       "Assert unicast source address\n"
-       "Assert metric preference\n"
-       "Assert route metric\n"
-       "Assert RPT bit flag\n")
-{
-  int idx_interface = 4;
-  int idx_ipv4 = 5;
-  int idx_ipv4_2 = 6;
-  int idx_ipv4_3 = 7;
-  int idx_number = 8;
-  int idx_number_2 = 9;
-  int idx_number_3 = 10;
-  uint8_t           buf[1000];
-  uint8_t          *buf_pastend = buf + sizeof(buf);
-  uint8_t          *pim_msg;
-  struct ip        *ip_hdr;
-  size_t            ip_hlen; /* ip header length in bytes */
-  int               ip_msg_len;
-  int               pim_msg_size;
-  const char       *neigh_str;
-  struct in_addr    neigh_addr;
-  const char       *group_str;
-  struct in_addr    group_addr;
-  const char       *source_str;
-  struct in_addr    source_addr;
-  const char       *ifname;
-  struct interface *ifp;
-  uint32_t          assert_metric_preference;
-  uint32_t          assert_route_metric;
-  uint32_t          assert_rpt_bit_flag;
-  int               remain;
-  int               result;
-
-  /* Find interface */
-  ifname = argv[idx_interface]->arg;
-  ifp = if_lookup_by_name(ifname);
-  if (!ifp) {
-    vty_out(vty, "No such interface name %s%s",
-           ifname, VTY_NEWLINE);
-    return CMD_WARNING;
+static int
+ip_no_msdp_mesh_group_cmd_worker(struct vty *vty, const char *mg)
+{
+  enum pim_msdp_err result;
+
+  result = pim_msdp_mg_del(mg);
+  switch (result) {
+    case PIM_MSDP_ERR_NONE:
+      break;
+    case PIM_MSDP_ERR_NO_MG:
+      vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE);
+      break;
+    default:
+      vty_out(vty, "%% mesh-group source del failed%s", VTY_NEWLINE);
   }
 
-  /* Neighbor address */
-  neigh_str = argv[idx_ipv4]->arg;
-  result = inet_pton(AF_INET, neigh_str, &neigh_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
-           neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
-  }
+  return result ? CMD_WARNING : CMD_SUCCESS;
+}
 
-  /* Group address */
-  group_str = argv[idx_ipv4_2]->arg;
-  result = inet_pton(AF_INET, group_str, &group_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
-           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+DEFUN (no_ip_msdp_mesh_group_source,
+       no_ip_msdp_mesh_group_source_cmd,
+       "no ip msdp mesh-group WORD source [A.B.C.D]",
+       NO_STR
+       IP_STR
+       CFG_MSDP_STR
+       "Delete MSDP mesh-group source\n"
+       "mesh group name\n"
+       "mesh group local address\n")
+{
+  if (argv[6]->arg)
+    return ip_no_msdp_mesh_group_cmd_worker(vty, argv[6]->arg);
+  else
+    return ip_no_msdp_mesh_group_source_cmd_worker(vty, argv[4]->arg);
+}
+
+static void
+print_empty_json_obj(struct vty *vty)
+{
+  json_object *json;
+  json = json_object_new_object();
+  vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+  json_object_free(json);
+}
+
+static void
+ip_msdp_show_mesh_group(struct vty *vty, u_char uj)
+{
+  struct listnode *mbrnode;
+  struct pim_msdp_mg_mbr *mbr;
+  struct pim_msdp_mg *mg = msdp->mg;
+  char mbr_str[INET_ADDRSTRLEN];
+  char src_str[INET_ADDRSTRLEN];
+  char state_str[PIM_MSDP_STATE_STRLEN];
+  enum pim_msdp_peer_state state;
+  json_object *json = NULL;
+  json_object *json_mg_row = NULL;
+  json_object *json_members = NULL;
+  json_object *json_row = NULL;
+
+  if (!mg) {
+    if (uj)
+      print_empty_json_obj(vty);
+    return;
   }
 
-  /* Source address */
-  source_str = argv[idx_ipv4_3]->arg;
-  result = inet_pton(AF_INET, source_str, &source_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
-           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+  pim_inet4_dump("<source?>", mg->src_ip, src_str, sizeof(src_str));
+  if (uj) {
+    json = json_object_new_object();
+    /* currently there is only one mesh group but we should still make
+     * it a dict with mg-name as key */
+    json_mg_row = json_object_new_object();
+    json_object_string_add(json_mg_row, "name", mg->mesh_group_name);
+    json_object_string_add(json_mg_row, "source", src_str);
+  } else {
+    vty_out(vty, "Mesh group : %s%s", mg->mesh_group_name, VTY_NEWLINE);
+    vty_out(vty, "  Source : %s%s", src_str, VTY_NEWLINE);
+    vty_out(vty, "  Member                 State%s", VTY_NEWLINE);
   }
 
-  assert_metric_preference = atoi(argv[idx_number]->arg);
-  assert_route_metric      = atoi(argv[idx_number_2]->arg);
-  assert_rpt_bit_flag      = atoi(argv[idx_number_3]->arg);
-
-  remain = buf_pastend - buf;
-  if (remain < (int) sizeof(struct ip)) {
-    vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%zu%s",
-           remain, sizeof(struct ip), VTY_NEWLINE);
-    return CMD_WARNING;
+  for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
+    pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
+    if (mbr->mp) {
+      state = mbr->mp->state;
+    } else {
+      state = PIM_MSDP_DISABLED;
+    }
+    pim_msdp_state_dump(state, state_str, sizeof(state_str));
+    if (uj) {
+      json_row = json_object_new_object();
+      json_object_string_add(json_row, "member", mbr_str);
+      json_object_string_add(json_row, "state", state_str);
+      if (!json_members) {
+        json_members = json_object_new_object();
+        json_object_object_add(json_mg_row, "members", json_members);
+      }
+      json_object_object_add(json_members, mbr_str, json_row);
+    } else {
+      vty_out(vty, "  %-15s  %11s%s",
+          mbr_str, state_str, VTY_NEWLINE);
+    }
   }
 
-  /*
-    Tweak IP header
-   */
-  ip_hdr = (struct ip *) buf;
-  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
-  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
-  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
-  ip_hdr->ip_src = neigh_addr;
-  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
-
-  /*
-    Build PIM assert message
-  */
-  pim_msg = buf + ip_hlen; /* skip ip header */
-
-  pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp,
-                                     group_addr, source_addr,
-                                     assert_metric_preference,
-                                     assert_route_metric,
-                                     assert_rpt_bit_flag);
-  if (pim_msg_size < 0) {
-    vty_out(vty, "Failure building PIM assert message: size=%d%s",
-           pim_msg_size, VTY_NEWLINE);
-    return CMD_WARNING;
+  if (uj) {
+    json_object_object_add(json, mg->mesh_group_name, json_mg_row);
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
+}
 
-  /* "receive" message */
-
-  ip_msg_len = ip_hlen + pim_msg_size;
-  result = pim_pim_packet(ifp, buf, ip_msg_len);
-  if (result) {
-    vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
-           ip_msg_len, result, VTY_NEWLINE);
-    return CMD_WARNING;
-  }
+DEFUN (show_ip_msdp_mesh_group,
+       show_ip_msdp_mesh_group_cmd,
+       "show ip msdp mesh-group [json]",
+       SHOW_STR
+       IP_STR
+       MSDP_STR
+       "MSDP mesh-group information\n"
+       "JavaScript Object Notation\n")
+{
+  u_char uj = use_json(argc, argv);
+  ip_msdp_show_mesh_group(vty, uj);
 
   return CMD_SUCCESS;
 }
 
-static int recv_joinprune(struct vty *vty,
-                         struct cmd_token **argv,
-                         int src_is_join)
-{
-  uint8_t           buf[1000];
-  const uint8_t    *buf_pastend = buf + sizeof(buf);
-  uint8_t          *pim_msg;
-  uint8_t          *pim_msg_curr;
-  int               pim_msg_size;
-  struct ip        *ip_hdr;
-  size_t            ip_hlen; /* ip header length in bytes */
-  int               ip_msg_len;
-  uint16_t          neigh_holdtime;
-  const char       *neigh_dst_str;
-  struct in_addr    neigh_dst_addr;
-  const char       *neigh_src_str;
-  struct in_addr    neigh_src_addr;
-  const char       *group_str;
-  struct in_addr    group_addr;
-  const char       *source_str;
-  struct in_addr    source_addr;
-  const char       *ifname;
-  struct interface *ifp;
-  int               result;
-  int               remain;
-  uint16_t          num_joined;
-  uint16_t          num_pruned;
-
-  /* Find interface */
-  ifname = argv[0]->arg;
-  ifp = if_lookup_by_name(ifname);
-  if (!ifp) {
-    vty_out(vty, "No such interface name %s%s",
-           ifname, VTY_NEWLINE);
-    return CMD_WARNING;
-  }
+static void
+ip_msdp_show_peers(struct vty *vty, u_char uj)
+{
+  struct listnode *mpnode;
+  struct pim_msdp_peer *mp;
+  char peer_str[INET_ADDRSTRLEN];
+  char local_str[INET_ADDRSTRLEN];
+  char state_str[PIM_MSDP_STATE_STRLEN];
+  char timebuf[PIM_MSDP_UPTIME_STRLEN];
+  int64_t now;
+  json_object *json = NULL;
+  json_object *json_row = NULL;
 
-  neigh_holdtime = atoi(argv[1]->arg);
 
-  /* Neighbor destination address */
-  neigh_dst_str = argv[2]->arg;
-  result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s",
-           neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+  if (uj) {
+    json = json_object_new_object();
+  } else {
+    vty_out(vty, "Peer                       Local        State    Uptime   SaCnt%s", VTY_NEWLINE);
   }
 
-  /* Neighbor source address */
-  neigh_src_str = argv[3]->arg;
-  result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s",
-           neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+  for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+    if (mp->state == PIM_MSDP_ESTABLISHED) {
+      now = pim_time_monotonic_sec();
+      pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime);
+    } else {
+      strcpy(timebuf, "-");
+    }
+    pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
+    pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
+    pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
+    if (uj) {
+      json_row = json_object_new_object();
+      json_object_string_add(json_row, "peer", peer_str);
+      json_object_string_add(json_row, "local", local_str);
+      json_object_string_add(json_row, "state", state_str);
+      json_object_string_add(json_row, "upTime", timebuf);
+      json_object_int_add(json_row, "saCount", mp->sa_cnt);
+      json_object_object_add(json, peer_str, json_row);
+    } else {
+      vty_out(vty, "%-15s  %15s  %11s  %8s  %6d%s",
+          peer_str, local_str, state_str,
+          timebuf, mp->sa_cnt, VTY_NEWLINE);
+    }
   }
 
-  /* Multicast group address */
-  group_str = argv[4]->arg;
-  result = inet_pton(AF_INET, group_str, &group_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
-           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
+}
 
-  /* Multicast source address */
-  source_str = argv[5]->arg;
-  result = inet_pton(AF_INET, source_str, &source_addr);
-  if (result <= 0) {
-    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
-           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+static void
+ip_msdp_show_peers_detail(struct vty *vty, const char *peer, u_char uj)
+{
+  struct listnode *mpnode;
+  struct pim_msdp_peer *mp;
+  char peer_str[INET_ADDRSTRLEN];
+  char local_str[INET_ADDRSTRLEN];
+  char state_str[PIM_MSDP_STATE_STRLEN];
+  char timebuf[PIM_MSDP_UPTIME_STRLEN];
+  char katimer[PIM_MSDP_TIMER_STRLEN];
+  char crtimer[PIM_MSDP_TIMER_STRLEN];
+  char holdtimer[PIM_MSDP_TIMER_STRLEN];
+  int64_t now;
+  json_object *json = NULL;
+  json_object *json_row = NULL;
+
+  if (uj) {
+    json = json_object_new_object();
   }
 
-  /*
-    Tweak IP header
-   */
-  ip_hdr = (struct ip *) buf;
-  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
-  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
-  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
-  ip_hdr->ip_src = neigh_src_addr;
-  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+  for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+    pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
+    if (strcmp(peer, "detail") &&
+        strcmp(peer, peer_str))
+      continue;
 
-  /*
-    Build PIM message
-  */
-  pim_msg = buf + ip_hlen;
-
-  /* skip room for pim header */
-  pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN;
-
-  remain = buf_pastend - pim_msg_curr;
-  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
-                                               remain,
-                                               neigh_dst_addr);
-  if (!pim_msg_curr) {
-    vty_out(vty, "Failure encoding destination address %s: space left=%d%s",
-           neigh_dst_str, remain, VTY_NEWLINE);
-    return CMD_WARNING;
+    if (mp->state == PIM_MSDP_ESTABLISHED) {
+      now = pim_time_monotonic_sec();
+      pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime);
+    } else {
+      strcpy(timebuf, "-");
+    }
+    pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
+    pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
+    pim_time_timer_to_hhmmss(katimer, sizeof(katimer), mp->ka_timer);
+    pim_time_timer_to_hhmmss(crtimer, sizeof(crtimer), mp->cr_timer);
+    pim_time_timer_to_hhmmss(holdtimer, sizeof(holdtimer), mp->hold_timer);
+
+    if (uj) {
+      json_row = json_object_new_object();
+      json_object_string_add(json_row, "peer", peer_str);
+      json_object_string_add(json_row, "local", local_str);
+      json_object_string_add(json_row, "meshGroupName", mp->mesh_group_name);
+      json_object_string_add(json_row, "state", state_str);
+      json_object_string_add(json_row, "upTime", timebuf);
+      json_object_string_add(json_row, "keepAliveTimer", katimer);
+      json_object_string_add(json_row, "connRetryTimer", crtimer);
+      json_object_string_add(json_row, "holdTimer", holdtimer);
+      json_object_string_add(json_row, "lastReset", mp->last_reset);
+      json_object_int_add(json_row, "connAttempts", mp->conn_attempts);
+      json_object_int_add(json_row, "establishedChanges", mp->est_flaps);
+      json_object_int_add(json_row, "saCount", mp->sa_cnt);
+      json_object_int_add(json_row, "kaSent", mp->ka_tx_cnt);
+      json_object_int_add(json_row, "kaRcvd", mp->ka_rx_cnt);
+      json_object_int_add(json_row, "saSent", mp->sa_tx_cnt);
+      json_object_int_add(json_row, "saRcvd", mp->sa_rx_cnt);
+      json_object_object_add(json, peer_str, json_row);
+    } else {
+      vty_out(vty, "Peer : %s%s", peer_str, VTY_NEWLINE);
+      vty_out(vty, "  Local               : %s%s", local_str, VTY_NEWLINE);
+      vty_out(vty, "  Mesh Group          : %s%s", mp->mesh_group_name, VTY_NEWLINE);
+      vty_out(vty, "  State               : %s%s", state_str, VTY_NEWLINE);
+      vty_out(vty, "  Uptime              : %s%s", timebuf, VTY_NEWLINE);
+
+      vty_out(vty, "  Keepalive Timer     : %s%s", katimer, VTY_NEWLINE);
+      vty_out(vty, "  Conn Retry Timer    : %s%s", crtimer, VTY_NEWLINE);
+      vty_out(vty, "  Hold Timer          : %s%s", holdtimer, VTY_NEWLINE);
+      vty_out(vty, "  Last Reset          : %s%s", mp->last_reset, VTY_NEWLINE);
+      vty_out(vty, "  Conn Attempts       : %d%s", mp->conn_attempts, VTY_NEWLINE);
+      vty_out(vty, "  Established Changes : %d%s", mp->est_flaps, VTY_NEWLINE);
+      vty_out(vty, "  SA Count            : %d%s", mp->sa_cnt, VTY_NEWLINE);
+      vty_out(vty, "  Statistics          :%s", VTY_NEWLINE);
+      vty_out(vty, "                       Sent       Rcvd%s", VTY_NEWLINE);
+      vty_out(vty, "    Keepalives : %10d %10d%s",
+          mp->ka_tx_cnt, mp->ka_rx_cnt, VTY_NEWLINE);
+      vty_out(vty, "    SAs        : %10d %10d%s",
+          mp->sa_tx_cnt, mp->sa_rx_cnt, VTY_NEWLINE);
+      vty_out(vty, "%s", VTY_NEWLINE);
+    }
   }
 
-  remain = buf_pastend - pim_msg_curr;
-  if (remain < 4) {
-    vty_out(vty, "Group will not fit: space left=%d%s",
-           remain, VTY_NEWLINE);
-    return CMD_WARNING;
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
+}
 
-  *pim_msg_curr = 0; /* reserved */
-  ++pim_msg_curr;
-  *pim_msg_curr = 1; /* number of groups */
-  ++pim_msg_curr;
-  *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime);
-  ++pim_msg_curr;
-  ++pim_msg_curr;
-
-  remain = buf_pastend - pim_msg_curr;
-  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
-                                               remain,
-                                               group_addr);
-  if (!pim_msg_curr) {
-    vty_out(vty, "Failure encoding group address %s: space left=%d%s",
-           group_str, remain, VTY_NEWLINE);
-    return CMD_WARNING;
+DEFUN (show_ip_msdp_peer_detail,
+       show_ip_msdp_peer_detail_cmd,
+       "show ip msdp peer [detail|A.B.C.D] [json]",
+       SHOW_STR
+       IP_STR
+       MSDP_STR
+       "MSDP peer information\n"
+       "Detailed output\n"
+       "peer ip address\n"
+       "JavaScript Object Notation\n")
+{
+  u_char uj = use_json(argc, argv);
+  if (argv[4]->arg)
+    ip_msdp_show_peers_detail(vty, argv[4]->arg, uj);
+  else
+    ip_msdp_show_peers(vty, uj);
+
+  return CMD_SUCCESS;
+}
+
+static void
+ip_msdp_show_sa(struct vty *vty, u_char uj)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+  char rp_str[INET_ADDRSTRLEN];
+  char timebuf[PIM_MSDP_UPTIME_STRLEN];
+  char spt_str[8];
+  char local_str[8];
+  int64_t now;
+  json_object *json = NULL;
+  json_object *json_group = NULL;
+  json_object *json_row = NULL;
+
+  if (uj) {
+    json = json_object_new_object();
+  } else {
+    vty_out(vty, "Source                     Group               RP  Local  SPT    Uptime%s", VTY_NEWLINE);
   }
 
-  remain = buf_pastend - pim_msg_curr;
-  if (remain < 4) {
-    vty_out(vty, "Sources will not fit: space left=%d%s",
-           remain, VTY_NEWLINE);
-    return CMD_WARNING;
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    now = pim_time_monotonic_sec();
+    pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime);
+    pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+    if (sa->flags & PIM_MSDP_SAF_PEER) {
+      pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
+      if (sa->up) {
+        strcpy(spt_str, "yes");
+      } else {
+        strcpy(spt_str, "no");
+      }
+    } else {
+      strcpy(rp_str, "-");
+      strcpy(spt_str, "-");
+    }
+    if (sa->flags & PIM_MSDP_SAF_LOCAL) {
+      strcpy(local_str, "yes");
+    } else {
+      strcpy(local_str, "no");
+    }
+    if (uj) {
+      json_object_object_get_ex(json, grp_str, &json_group);
+
+      if (!json_group) {
+        json_group = json_object_new_object();
+        json_object_object_add(json, grp_str, json_group);
+      }
+
+      json_row = json_object_new_object();
+      json_object_string_add(json_row, "source", src_str);
+      json_object_string_add(json_row, "group", grp_str);
+      json_object_string_add(json_row, "rp", rp_str);
+      json_object_string_add(json_row, "local", local_str);
+      json_object_string_add(json_row, "sptSetup", spt_str);
+      json_object_string_add(json_row, "upTime", timebuf);
+      json_object_object_add(json_group, src_str, json_row);
+    } else {
+      vty_out(vty, "%-15s  %15s  %15s  %5c  %3c  %8s%s",
+          src_str, grp_str, rp_str, local_str[0], spt_str[0], timebuf, VTY_NEWLINE);
+    }
+  }
+
+
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
+}
+
+static void
+ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa, const char *src_str,
+                             const char *grp_str, struct vty *vty,
+                             u_char uj, json_object *json)
+{
+  char rp_str[INET_ADDRSTRLEN];
+  char peer_str[INET_ADDRSTRLEN];
+  char timebuf[PIM_MSDP_UPTIME_STRLEN];
+  char spt_str[8];
+  char local_str[8];
+  char statetimer[PIM_MSDP_TIMER_STRLEN];
+  int64_t now;
+  json_object *json_group = NULL;
+  json_object *json_row = NULL;
 
-  if (src_is_join) {
-    num_joined = 1;
-    num_pruned = 0;
+  now = pim_time_monotonic_sec();
+  pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime);
+  if (sa->flags & PIM_MSDP_SAF_PEER) {
+    pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
+    pim_inet4_dump("<peer?>", sa->peer, peer_str, sizeof(peer_str));
+    if (sa->up) {
+      strcpy(spt_str, "yes");
+    } else {
+      strcpy(spt_str, "no");
+    }
+  } else {
+    strcpy(rp_str, "-");
+    strcpy(peer_str, "-");
+    strcpy(spt_str, "-");
   }
-  else {
-    num_joined = 0;
-    num_pruned = 1;
-  }
-
-  /* number of joined sources */
-  *((uint16_t *) pim_msg_curr) = htons(num_joined);
-  ++pim_msg_curr;
-  ++pim_msg_curr;
-
-  /* number of pruned sources */
-  *((uint16_t *) pim_msg_curr) = htons(num_pruned);
-  ++pim_msg_curr;
-  ++pim_msg_curr;
-
-  remain = buf_pastend - pim_msg_curr;
-  pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
-                                                remain,
-                                                source_addr);
-  if (!pim_msg_curr) {
-    vty_out(vty, "Failure encoding source address %s: space left=%d%s",
-           source_str, remain, VTY_NEWLINE);
-    return CMD_WARNING;
+  if (sa->flags & PIM_MSDP_SAF_LOCAL) {
+      strcpy(local_str, "yes");
+  } else {
+      strcpy(local_str, "no");
   }
+  pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer), sa->sa_state_timer);
+  if (uj) {
+    json_object_object_get_ex(json, grp_str, &json_group);
 
-  /* Add PIM header */
+    if (!json_group) {
+      json_group = json_object_new_object();
+      json_object_object_add(json, grp_str, json_group);
+    }
 
-  pim_msg_size = pim_msg_curr - pim_msg;
+    json_row = json_object_new_object();
+    json_object_string_add(json_row, "source", src_str);
+    json_object_string_add(json_row, "group", grp_str);
+    json_object_string_add(json_row, "rp", rp_str);
+    json_object_string_add(json_row, "local", local_str);
+    json_object_string_add(json_row, "sptSetup", spt_str);
+    json_object_string_add(json_row, "upTime", timebuf);
+    json_object_string_add(json_row, "stateTimer", statetimer);
+    json_object_object_add(json_group, src_str, json_row);
+  } else {
+    vty_out(vty, "SA : %s%s", sa->sg_str, VTY_NEWLINE);
+    vty_out(vty, "  RP          : %s%s", rp_str, VTY_NEWLINE);
+    vty_out(vty, "  Peer        : %s%s", peer_str, VTY_NEWLINE);
+    vty_out(vty, "  Local       : %s%s", local_str, VTY_NEWLINE);
+    vty_out(vty, "  SPT Setup   : %s%s", spt_str, VTY_NEWLINE);
+    vty_out(vty, "  Uptime      : %s%s", timebuf, VTY_NEWLINE);
+    vty_out(vty, "  State Timer : %s%s", statetimer, VTY_NEWLINE);
+    vty_out(vty, "%s", VTY_NEWLINE);
+  }
+}
 
-  pim_msg_build_header(pim_msg, pim_msg_size,
-                      PIM_MSG_TYPE_JOIN_PRUNE);
+static void
+ip_msdp_show_sa_detail(struct vty *vty, u_char uj)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+  json_object *json = NULL;
 
-  /*
-    "Receive" message
-  */
+  if (uj) {
+    json = json_object_new_object();
+  }
 
-  ip_msg_len = ip_hlen + pim_msg_size;
-  result = pim_pim_packet(ifp, buf, ip_msg_len);
-  if (result) {
-    vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
-           ip_msg_len, result, VTY_NEWLINE);
-    return CMD_WARNING;
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+    ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json);
   }
 
-  return CMD_SUCCESS;
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
 }
 
-DEFUN (test_pim_receive_join,
-       test_pim_receive_join_cmd,
-       "test pim receive join INTERFACE (0-65535) A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
-       "Test\n"
-       "Test PIM protocol\n"
-       "Test PIM message reception\n"
-       "Test PIM join reception from neighbor\n"
-       "Interface\n"
-       "Neighbor holdtime\n"
-       "Upstream neighbor unicast destination address\n"
-       "Downstream neighbor unicast source address\n"
-       "Multicast group address\n"
-       "Unicast source address\n")
-{
-  return recv_joinprune(vty, argv, 1 /* src_is_join=true */);
-}
-
-DEFUN (test_pim_receive_prune,
-       test_pim_receive_prune_cmd,
-       "test pim receive prune INTERFACE (0-65535) A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
-       "Test\n"
-       "Test PIM protocol\n"
-       "Test PIM message reception\n"
-       "Test PIM prune reception from neighbor\n"
-       "Interface\n"
-       "Neighbor holdtime\n"
-       "Upstream neighbor unicast destination address\n"
-       "Downstream neighbor unicast source address\n"
-       "Multicast group address\n"
-       "Unicast source address\n")
-{
-  return recv_joinprune(vty, argv, 0 /* src_is_join=false */);
-}
-
-DEFUN (test_pim_receive_upcall,
-       test_pim_receive_upcall_cmd,
-       "test pim receive upcall <nocache|wrongvif|wholepkt> (0-65535) A.B.C.D A.B.C.D",
-       "Test\n"
-       "Test PIM protocol\n"
-       "Test PIM message reception\n"
-       "Test reception of kernel upcall\n"
-       "NOCACHE kernel upcall\n"
-       "WRONGVIF kernel upcall\n"
-       "WHOLEPKT kernel upcall\n"
-       "Input interface vif index\n"
-       "Multicast group address\n"
-       "Multicast source address\n")
-{
-  int idx_type = 4;
-  int idx_number = 5;
-  int idx_ipv4 = 6;
-  int idx_ipv4_2 = 7;
-  struct igmpmsg msg;
-  const char *upcall_type;
-  const char *group_str;
-  const char *source_str;
-  int result;
+DEFUN (show_ip_msdp_sa_detail,
+       show_ip_msdp_sa_detail_cmd,
+       "show ip msdp sa detail [json]",
+       SHOW_STR
+       IP_STR
+       MSDP_STR
+       "MSDP active-source information\n"
+       "Detailed output\n"
+       "JavaScript Object Notation\n")
+{
+  u_char uj = use_json(argc, argv);
+  ip_msdp_show_sa_detail(vty, uj);
 
-  upcall_type = argv[idx_type]->arg;
+  return CMD_SUCCESS;
+}
 
-  if (upcall_type[0] == 'n')
-    msg.im_msgtype = IGMPMSG_NOCACHE;
-  else if (upcall_type[1] == 'r')
-    msg.im_msgtype = IGMPMSG_WRONGVIF;
-  else if (upcall_type[1] == 'h')
-    msg.im_msgtype = IGMPMSG_WHOLEPKT;
-  else {
-    vty_out(vty, "Unknown kernel upcall type: %s%s",
-           upcall_type, VTY_NEWLINE);
-    return CMD_WARNING;
+static void
+ip_msdp_show_sa_addr(struct vty *vty, const char *addr, u_char uj)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+  json_object *json = NULL;
+
+  if (uj) {
+    json = json_object_new_object();
   }
 
-  msg.im_vif = atoi(argv[idx_number]->arg);
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+    if (!strcmp(addr, src_str) || !strcmp(addr, grp_str)) {
+      ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json);
+    }
+  }
 
-  /* Group address */
-  group_str = argv[idx_ipv4]->arg;
-  result = inet_pton(AF_INET, group_str, &msg.im_dst);
-  if (result <= 0) {
-    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
-           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
+}
 
-  /* Source address */
-  source_str = argv[idx_ipv4_2]->arg;
-  result = inet_pton(AF_INET, source_str, &msg.im_src);
-  if (result <= 0) {
-    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
-           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
-    return CMD_WARNING;
+static void
+ip_msdp_show_sa_sg(struct vty *vty, const char *src, const char *grp, u_char uj)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+  json_object *json = NULL;
+
+  if (uj) {
+    json = json_object_new_object();
   }
 
-  msg.im_mbz = 0; /* Must be zero */
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+    if (!strcmp(src, src_str) && !strcmp(grp, grp_str)) {
+      ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json);
+    }
+  }
 
-  result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg));
-  if (result) {
-    vty_out(vty, "pim_mroute_msg(len=%zu) returned failure: %d%s",
-           sizeof(msg), result, VTY_NEWLINE);
-    return CMD_WARNING;
+  if (uj) {
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
   }
+}
+
+DEFUN (show_ip_msdp_sa_sg,
+       show_ip_msdp_sa_sg_cmd,
+       "show ip msdp sa [A.B.C.D] [A.B.C.D] [json]",
+       SHOW_STR
+       IP_STR
+       MSDP_STR
+       "MSDP active-source information\n"
+       "source or group ip\n"
+       "JavaScript Object Notation\n")
+{
+  u_char uj = use_json(argc, argv);
+  if (argv[5]->arg)
+    ip_msdp_show_sa_sg(vty, argv[4]->arg, argv[5]->arg, uj);
+  else if (argv[4]->arg)
+    ip_msdp_show_sa_addr(vty, argv[4]->arg, uj);
+  else
+    ip_msdp_show_sa(vty, uj);
 
   return CMD_SUCCESS;
 }
@@ -4759,17 +6005,33 @@ void pim_cmd_init()
   install_node (&interface_node, pim_interface_config_write); /* INTERFACE_NODE */
   if_cmd_init ();
 
+  install_node (&debug_node, pim_debug_config_write);
+
   install_element (CONFIG_NODE, &ip_multicast_routing_cmd);
   install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd);
   install_element (CONFIG_NODE, &ip_pim_rp_cmd);
   install_element (CONFIG_NODE, &no_ip_pim_rp_cmd);
+  install_element (CONFIG_NODE, &ip_pim_rp_prefix_list_cmd);
+  install_element (CONFIG_NODE, &no_ip_pim_rp_prefix_list_cmd);
+  install_element (CONFIG_NODE, &ip_pim_register_suppress_cmd);
+  install_element (CONFIG_NODE, &no_ip_pim_register_suppress_cmd);
+  install_element (CONFIG_NODE, &ip_pim_joinprune_time_cmd);
+  install_element (CONFIG_NODE, &no_ip_pim_joinprune_time_cmd);
+  install_element (CONFIG_NODE, &ip_pim_keep_alive_cmd);
+  install_element (CONFIG_NODE, &no_ip_pim_keep_alive_cmd);
+  install_element (CONFIG_NODE, &ip_pim_packets_cmd);
+  install_element (CONFIG_NODE, &no_ip_pim_packets_cmd);
   install_element (CONFIG_NODE, &ip_ssmpingd_cmd);
   install_element (CONFIG_NODE, &no_ip_ssmpingd_cmd); 
+  install_element (CONFIG_NODE, &ip_msdp_peer_cmd);
+  install_element (CONFIG_NODE, &no_ip_msdp_peer_cmd);
 
   install_element (INTERFACE_NODE, &interface_ip_igmp_cmd);
   install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd); 
   install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd);
   install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); 
+  install_element (INTERFACE_NODE, &interface_ip_igmp_version_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_igmp_version_cmd);
   install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd);
   install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); 
   install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd);
@@ -4793,29 +6055,25 @@ void pim_cmd_init()
 
   install_element (VIEW_NODE, &show_ip_igmp_interface_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_join_cmd);
-  install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_groups_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_sources_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd);
-  install_element (VIEW_NODE, &show_ip_igmp_querier_cmd);
   install_element (VIEW_NODE, &show_ip_pim_assert_cmd);
   install_element (VIEW_NODE, &show_ip_pim_assert_internal_cmd);
   install_element (VIEW_NODE, &show_ip_pim_assert_metric_cmd);
   install_element (VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd);
-  install_element (VIEW_NODE, &show_ip_pim_dr_cmd);
-  install_element (VIEW_NODE, &show_ip_pim_hello_cmd);
   install_element (VIEW_NODE, &show_ip_pim_interface_cmd);
   install_element (VIEW_NODE, &show_ip_pim_join_cmd);
-  install_element (VIEW_NODE, &show_ip_pim_jp_override_interval_cmd);
-  install_element (VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd);
   install_element (VIEW_NODE, &show_ip_pim_local_membership_cmd);
   install_element (VIEW_NODE, &show_ip_pim_neighbor_cmd);
   install_element (VIEW_NODE, &show_ip_pim_rpf_cmd);
   install_element (VIEW_NODE, &show_ip_pim_secondary_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_state_cmd);
   install_element (VIEW_NODE, &show_ip_pim_upstream_cmd);
   install_element (VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd);
   install_element (VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_rp_cmd);
   install_element (VIEW_NODE, &show_ip_multicast_cmd);
   install_element (VIEW_NODE, &show_ip_mroute_cmd);
   install_element (VIEW_NODE, &show_ip_mroute_count_cmd);
@@ -4823,22 +6081,12 @@ void pim_cmd_init()
   install_element (VIEW_NODE, &show_ip_ssmpingd_cmd);
   install_element (VIEW_NODE, &show_debugging_pim_cmd);
 
-  install_element (ENABLE_NODE, &show_ip_pim_address_cmd);
-
   install_element (ENABLE_NODE, &clear_ip_interfaces_cmd);
   install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd);
   install_element (ENABLE_NODE, &clear_ip_mroute_cmd);
   install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd);
   install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd);
 
-  install_element (ENABLE_NODE, &test_igmp_receive_report_cmd);
-  install_element (ENABLE_NODE, &test_pim_receive_assert_cmd);
-  install_element (ENABLE_NODE, &test_pim_receive_dump_cmd);
-  install_element (ENABLE_NODE, &test_pim_receive_hello_cmd);
-  install_element (ENABLE_NODE, &test_pim_receive_join_cmd);
-  install_element (ENABLE_NODE, &test_pim_receive_prune_cmd);
-  install_element (ENABLE_NODE, &test_pim_receive_upcall_cmd);
-
   install_element (ENABLE_NODE, &debug_igmp_cmd);
   install_element (ENABLE_NODE, &no_debug_igmp_cmd);
   install_element (ENABLE_NODE, &debug_igmp_events_cmd);
@@ -4848,7 +6096,9 @@ void pim_cmd_init()
   install_element (ENABLE_NODE, &debug_igmp_trace_cmd);
   install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd);
   install_element (ENABLE_NODE, &debug_mroute_cmd);
+  install_element (ENABLE_NODE, &debug_mroute_detail_cmd);
   install_element (ENABLE_NODE, &no_debug_mroute_cmd);
+  install_element (ENABLE_NODE, &no_debug_mroute_detail_cmd);
   install_element (ENABLE_NODE, &debug_static_cmd);
   install_element (ENABLE_NODE, &no_debug_static_cmd);
   install_element (ENABLE_NODE, &debug_pim_cmd);
@@ -4869,6 +6119,15 @@ void pim_cmd_init()
   install_element (ENABLE_NODE, &no_debug_ssmpingd_cmd);
   install_element (ENABLE_NODE, &debug_pim_zebra_cmd);
   install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd);
+  install_element (ENABLE_NODE, &debug_msdp_cmd);
+  install_element (ENABLE_NODE, &no_debug_msdp_cmd);
+  install_element (ENABLE_NODE, &undebug_msdp_cmd);
+  install_element (ENABLE_NODE, &debug_msdp_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_msdp_events_cmd);
+  install_element (ENABLE_NODE, &undebug_msdp_events_cmd);
+  install_element (ENABLE_NODE, &debug_msdp_packets_cmd);
+  install_element (ENABLE_NODE, &no_debug_msdp_packets_cmd);
+  install_element (ENABLE_NODE, &undebug_msdp_packets_cmd);
 
   install_element (CONFIG_NODE, &debug_igmp_cmd);
   install_element (CONFIG_NODE, &no_debug_igmp_cmd);
@@ -4879,7 +6138,9 @@ void pim_cmd_init()
   install_element (CONFIG_NODE, &debug_igmp_trace_cmd);
   install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd);
   install_element (CONFIG_NODE, &debug_mroute_cmd);
+  install_element (CONFIG_NODE, &debug_mroute_detail_cmd);
   install_element (CONFIG_NODE, &no_debug_mroute_cmd);
+  install_element (CONFIG_NODE, &no_debug_mroute_detail_cmd);
   install_element (CONFIG_NODE, &debug_static_cmd);
   install_element (CONFIG_NODE, &no_debug_static_cmd);
   install_element (CONFIG_NODE, &debug_pim_cmd);
@@ -4896,4 +6157,25 @@ void pim_cmd_init()
   install_element (CONFIG_NODE, &no_debug_ssmpingd_cmd);
   install_element (CONFIG_NODE, &debug_pim_zebra_cmd);
   install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd);
+  install_element (CONFIG_NODE, &debug_msdp_cmd);
+  install_element (CONFIG_NODE, &no_debug_msdp_cmd);
+  install_element (CONFIG_NODE, &undebug_msdp_cmd);
+  install_element (CONFIG_NODE, &debug_msdp_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_msdp_events_cmd);
+  install_element (CONFIG_NODE, &undebug_msdp_events_cmd);
+  install_element (CONFIG_NODE, &debug_msdp_packets_cmd);
+  install_element (CONFIG_NODE, &no_debug_msdp_packets_cmd);
+  install_element (CONFIG_NODE, &undebug_msdp_packets_cmd);
+  install_element (CONFIG_NODE, &ip_msdp_peer_cmd);
+  install_element (CONFIG_NODE, &no_ip_msdp_peer_cmd);
+  install_element (CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
+  install_element (CONFIG_NODE, &no_ip_msdp_mesh_group_member_cmd);
+  install_element (CONFIG_NODE, &ip_msdp_mesh_group_source_cmd);
+  install_element (CONFIG_NODE, &no_ip_msdp_mesh_group_source_cmd);
+  install_element (VIEW_NODE, &show_ip_msdp_peer_detail_cmd);
+  install_element (VIEW_NODE, &show_ip_msdp_sa_detail_cmd);
+  install_element (VIEW_NODE, &show_ip_msdp_sa_sg_cmd);
+  install_element (VIEW_NODE, &show_ip_msdp_mesh_group_cmd);
+  install_element (INTERFACE_NODE, &interface_pim_use_source_cmd);
+  install_element (INTERFACE_NODE, &interface_no_pim_use_source_cmd);
 }
index 34f350e36bf51ce587f1f1230ab1db68facebd4f..e08cefb29b7e171484893f4609b56526e201f317 100644 (file)
@@ -47,6 +47,7 @@
 #define DEBUG_PIM_PACKETS_STR                       "PIM protocol packets\n"
 #define DEBUG_PIM_HELLO_PACKETS_STR                 "PIM Hello protocol packets\n"
 #define DEBUG_PIM_J_P_PACKETS_STR                   "PIM Join/Prune protocol packets\n"
+#define DEBUG_PIM_PIM_REG_PACKETS_STR               "PIM Register/Reg-Stop protocol packets\n"
 #define DEBUG_PIM_PACKETDUMP_STR                    "PIM packet dump\n"
 #define DEBUG_PIM_PACKETDUMP_SEND_STR               "Dump sent packets\n"
 #define DEBUG_PIM_PACKETDUMP_RECV_STR               "Dump received packets\n"
 #define CLEAR_IP_PIM_STR                            "PIM clear commands\n"
 #define MROUTE_STR                                  "IP multicast routing table\n"
 #define RIB_STR                                     "IP unicast routing table\n"
+#define CFG_MSDP_STR                                "Configure multicast source discovery protocol\n"
+#define MSDP_STR                                    "MSDP information\n"
+#define DEBUG_MSDP_STR                              "MSDP protocol activity\n"
+#define DEBUG_MSDP_EVENTS_STR                       "MSDP protocol events\n"
+#define DEBUG_MSDP_INTERNAL_STR                     "MSDP protocol internal\n"
+#define DEBUG_MSDP_PACKETS_STR                      "MSDP protocol packets\n"
 
 void pim_cmd_init(void);
 
index 1cd44f253921f3e664f4b8e6f21a59d753e70a4b..3d7ae4ad22bdc977bdd2b45848aab3157341b1a3 100644 (file)
@@ -37,7 +37,7 @@ static void on_trace(const char *label,
                     struct interface *ifp, struct in_addr src)
 {
   if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
     zlog_debug("%s: from %s on %s",
               label, src_str, ifp->name);
@@ -49,7 +49,7 @@ static void tlv_trace_bool(const char *label, const char *tlv_name,
                           int isset, int value)
 {
   if (isset) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
               label, 
@@ -63,7 +63,7 @@ static void tlv_trace_uint16(const char *label, const char *tlv_name,
                             int isset, uint16_t value)
 {
   if (isset) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
               label, 
@@ -77,7 +77,7 @@ static void tlv_trace_uint32(const char *label, const char *tlv_name,
                             int isset, uint32_t value)
 {
   if (isset) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
               label, 
@@ -91,7 +91,7 @@ static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
                                 int isset, uint32_t value)
 {
   if (isset) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
               label, 
@@ -106,7 +106,7 @@ static void tlv_trace(const char *label, const char *tlv_name,
                      int isset)
 {
   if (isset) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_debug("%s: PIM hello option from %s on interface %s: %s",
               label, 
@@ -121,7 +121,7 @@ static void tlv_trace_list(const char *label, const char *tlv_name,
                           int isset, struct list *addr_list)
 {
   if (isset) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
               label, 
@@ -181,7 +181,7 @@ int pim_hello_recv(struct interface *ifp,
 
     if (remain < PIM_TLV_MIN_SIZE) {
       if (PIM_DEBUG_PIM_HELLO) {
-       char src_str[100];
+       char src_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
                   __PRETTY_FUNCTION__,
@@ -198,7 +198,7 @@ int pim_hello_recv(struct interface *ifp,
 
     if ((tlv_curr + option_len) > tlv_pastend) {
       if (PIM_DEBUG_PIM_HELLO) {
-       char src_str[100];
+       char src_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
                   __PRETTY_FUNCTION__,
@@ -209,7 +209,7 @@ int pim_hello_recv(struct interface *ifp,
     }
 
     if (PIM_DEBUG_PIM_HELLO) {
-      char src_str[100];
+      char src_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
       zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
                 __PRETTY_FUNCTION__,
@@ -267,7 +267,7 @@ int pim_hello_recv(struct interface *ifp,
       break;
     case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
       if (PIM_DEBUG_PIM_HELLO) {
-       char src_str[100];
+       char src_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
                   __PRETTY_FUNCTION__,
@@ -277,7 +277,7 @@ int pim_hello_recv(struct interface *ifp,
       break;
     default:
       if (PIM_DEBUG_PIM_HELLO) {
-       char src_str[100];
+       char src_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
                   __PRETTY_FUNCTION__,
@@ -326,7 +326,7 @@ int pim_hello_recv(struct interface *ifp,
 
   if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
     if (PIM_DEBUG_PIM_HELLO) {
-      char src_str[100];
+      char src_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
       zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
                __PRETTY_FUNCTION__,
@@ -349,10 +349,11 @@ int pim_hello_recv(struct interface *ifp,
                             hello_option_override_interval,
                             hello_option_dr_priority,
                             hello_option_generation_id,
-                            hello_option_addr_list);
+                            hello_option_addr_list,
+                            PIM_NEIGHBOR_SEND_DELAY);
     if (!neigh) {
       if (PIM_DEBUG_PIM_HELLO) {
-       char src_str[100];
+       char src_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
                  __PRETTY_FUNCTION__,
@@ -373,15 +374,10 @@ int pim_hello_recv(struct interface *ifp,
     /* GenID mismatch ? */
     if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
        (hello_option_generation_id != neigh->generation_id)) {
-
-      /* GenID changed */
-
-      pim_upstream_rpf_genid_changed(neigh->source_addr);
-
       /* GenID mismatch, then replace neighbor */
       
       if (PIM_DEBUG_PIM_HELLO) {
-       char src_str[100];
+       char src_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
                   __PRETTY_FUNCTION__,
@@ -400,10 +396,11 @@ int pim_hello_recv(struct interface *ifp,
                               hello_option_override_interval,
                               hello_option_dr_priority,
                               hello_option_generation_id,
-                              hello_option_addr_list);
+                              hello_option_addr_list,
+                              PIM_NEIGHBOR_SEND_NOW);
       if (!neigh) {
        if (PIM_DEBUG_PIM_HELLO) {
-         char src_str[100];
+         char src_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
          zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
                     __PRETTY_FUNCTION__,
index 3a6d3361ba3b877de661a33744aa199feed9e88c..f8e722cd04b7b304a62cbd568efb81276eb7aa20 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_HELLO_H
index bac9692caa4ca406e41d58f59dce050117ad563d..7b2c7e2e93e6aab794d391c50f4c1b32f3f0c3da 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -25,6 +29,8 @@
 #include "memory.h"
 #include "prefix.h"
 #include "vrf.h"
+#include "linklist.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_iface.h"
 #include "pim_sock.h"
 #include "pim_time.h"
 #include "pim_ssmpingd.h"
+#include "pim_rp.h"
 
 struct interface *pim_regiface = NULL;
+struct list *pim_ifchannel_list = NULL;
 
 static void pim_if_igmp_join_del_all(struct interface *ifp);
 
-void pim_if_init()
+void
+pim_if_init (void)
 {
   vrf_iflist_create(VRF_DEFAULT);
+  pim_ifchannel_list = list_new();
+  pim_ifchannel_list->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
+}
+
+void
+pim_if_terminate (void)
+{
+  if (pim_ifchannel_list)
+    list_free (pim_ifchannel_list);
 }
 
 static void *if_list_clean(struct pim_interface *pim_ifp)
@@ -78,15 +96,16 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
   zassert(ifp);
   zassert(!ifp->info);
 
-  pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp));
+  pim_ifp = XCALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp));
   if (!pim_ifp) {
-    zlog_err("PIM XMALLOC(%zu) failure", sizeof(*pim_ifp));
+    zlog_err("PIM XCALLOC(%zu) failure", sizeof(*pim_ifp));
     return 0;
   }
 
   pim_ifp->options                           = 0;
   pim_ifp->mroute_vif_index                  = -1;
 
+  pim_ifp->igmp_version                               = IGMP_DEFAULT_VERSION;
   pim_ifp->igmp_default_robustness_variable           = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
   pim_ifp->igmp_default_query_interval                = IGMP_GENERAL_QUERY_INTERVAL;
   pim_ifp->igmp_query_max_response_time_dsec          = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC;
@@ -104,15 +123,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
   if (igmp)
     PIM_IF_DO_IGMP(pim_ifp->options);
 
-#if 0
-  /* FIXME: Should join? */
   PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options);
-#endif
 
-  pim_ifp->igmp_join_list = 0;
-  pim_ifp->igmp_socket_list = 0;
-  pim_ifp->pim_neighbor_list = 0;
-  pim_ifp->pim_ifchannel_list = 0;
+  pim_ifp->igmp_join_list = NULL;
+  pim_ifp->igmp_socket_list = NULL;
+  pim_ifp->pim_neighbor_list = NULL;
+  pim_ifp->pim_ifchannel_list = NULL;
   pim_ifp->pim_generation_id = 0;
 
   /* list of struct igmp_sock */
@@ -141,6 +157,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
     return if_list_clean(pim_ifp);
   }
   pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free;
+  pim_ifp->pim_ifchannel_list->cmp = (int (*)(void *, void *)) pim_ifchannel_compare;
 
   ifp->info = pim_ifp;
 
@@ -164,16 +181,11 @@ void pim_if_delete(struct interface *ifp)
   if (pim_ifp->igmp_join_list) {
     pim_if_igmp_join_del_all(ifp);
   }
-  zassert(!pim_ifp->igmp_join_list);
-
-  zassert(pim_ifp->igmp_socket_list);
-  zassert(!listcount(pim_ifp->igmp_socket_list));
 
-  zassert(pim_ifp->pim_neighbor_list);
-  zassert(!listcount(pim_ifp->pim_neighbor_list));
+  pim_ifchannel_delete_all (ifp);
+  igmp_sock_delete_all (ifp);
 
-  zassert(pim_ifp->pim_ifchannel_list);
-  zassert(!listcount(pim_ifp->pim_ifchannel_list));
+  pim_neighbor_delete_all (ifp, "Interface removed from configuration");
 
   if (PIM_MROUTE_IS_ENABLED) {
     pim_if_del_vif(ifp);
@@ -185,7 +197,7 @@ void pim_if_delete(struct interface *ifp)
 
   XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
 
-  ifp->info = 0;
+  ifp->info = NULL;
 }
 
 void pim_if_update_could_assert(struct interface *ifp)
@@ -258,14 +270,10 @@ static int detect_primary_address_change(struct interface *ifp,
                                         int force_prim_as_any,
                                         const char *caller)
 {
-  struct pim_interface *pim_ifp;
+  struct pim_interface *pim_ifp = ifp->info;
   struct in_addr new_prim_addr;
   int changed;
 
-  pim_ifp = ifp->info;
-  if (!pim_ifp)
-    return 0;
-
   if (force_prim_as_any)
     new_prim_addr = qpim_inaddr_any;
   else
@@ -274,8 +282,8 @@ static int detect_primary_address_change(struct interface *ifp,
   changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr;
 
   if (PIM_DEBUG_ZEBRA) {
-    char new_prim_str[100];
-    char old_prim_str[100];
+    char new_prim_str[INET_ADDRSTRLEN];
+    char old_prim_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str));
     pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str));
     zlog_debug("%s: old=%s new=%s on interface %s: %s",
@@ -286,57 +294,230 @@ static int detect_primary_address_change(struct interface *ifp,
 
   if (changed) {
     pim_ifp->primary_address = new_prim_addr;
+  }
 
-    if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
-      return changed;
+  return changed;
+}
+
+static int pim_sec_addr_comp(const void *p1, const void *p2)
+{
+  const struct pim_secondary_addr *sec1 = p1;
+  const struct pim_secondary_addr *sec2 = p2;
+
+  if (ntohl(sec1->addr.s_addr) < ntohl(sec2->addr.s_addr))
+    return -1;
+
+  if (ntohl(sec1->addr.s_addr) > ntohl(sec2->addr.s_addr))
+    return 1;
+
+  return 0;
+}
+
+static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr)
+{
+  XFREE(MTYPE_PIM_SEC_ADDR, sec_addr);
+}
+
+static struct pim_secondary_addr *
+pim_sec_addr_find(struct pim_interface *pim_ifp, struct in_addr addr)
+{
+  struct pim_secondary_addr *sec_addr;
+  struct listnode *node;
+
+  if (!pim_ifp->sec_addr_list) {
+    return NULL;
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+    if (sec_addr->addr.s_addr == addr.s_addr) {
+      return sec_addr;
     }
+  }
 
-    pim_addr_change(ifp);
+  return NULL;
+}
+
+static void pim_sec_addr_del(struct pim_interface *pim_ifp,
+                             struct pim_secondary_addr *sec_addr)
+{
+  listnode_delete(pim_ifp->sec_addr_list, sec_addr);
+  pim_sec_addr_free(sec_addr);
+}
+
+static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct in_addr addr)
+{
+  int changed = 0;
+  struct pim_secondary_addr *sec_addr;
+
+  sec_addr = pim_sec_addr_find(pim_ifp, addr);
+  if (sec_addr) {
+    sec_addr->flags &= ~PIM_SEC_ADDRF_STALE;
+    return changed;
+  }
+
+  if (!pim_ifp->sec_addr_list) {
+    pim_ifp->sec_addr_list = list_new();
+    pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free;
+    pim_ifp->sec_addr_list->cmp = (int (*)(void *, void *))pim_sec_addr_comp;
+  }
+
+  sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr));
+  if (!sec_addr) {
+    if (list_isempty(pim_ifp->sec_addr_list)) {
+      list_free(pim_ifp->sec_addr_list);
+      pim_ifp->sec_addr_list = NULL;
+    }
+    return changed;
+  }
+
+  changed = 1;
+  sec_addr->addr = addr;
+  listnode_add_sort(pim_ifp->sec_addr_list, sec_addr);
+
+  return changed;
+}
+
+static int pim_sec_addr_del_all(struct pim_interface *pim_ifp)
+{
+  int changed = 0;
+
+  if (!pim_ifp->sec_addr_list) {
+    return changed;
+  }
+  if (!list_isempty(pim_ifp->sec_addr_list)) {
+    changed = 1;
+    /* remove all nodes and free up the list itself */
+    list_delete_all_node(pim_ifp->sec_addr_list);
+    list_free(pim_ifp->sec_addr_list);
+    pim_ifp->sec_addr_list = NULL;
+  }
+
+  return changed;
+}
+
+static int pim_sec_addr_update(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp = ifp->info;
+  struct connected *ifc;
+  struct listnode *node;
+  struct listnode *nextnode;
+  struct pim_secondary_addr *sec_addr;
+  int changed = 0;
+
+  if (pim_ifp->sec_addr_list) {
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+      sec_addr->flags |= PIM_SEC_ADDRF_STALE;
+    }
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+    struct prefix *p = ifc->address;
+
+    if (p->family != AF_INET) {
+      continue;
+    }
+
+    if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
+      continue;
+    }
+
+    if (pim_ifp->primary_address.s_addr == p->u.prefix4.s_addr) {
+      /* don't add the primary address into the secondary address list */
+      continue;
+    }
+
+    if (pim_sec_addr_add(pim_ifp, p->u.prefix4)) {
+      changed = 1;
+    }
+  }
+
+  if (pim_ifp->sec_addr_list) {
+    /* Drop stale entries */
+    for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, sec_addr)) {
+      if (sec_addr->flags & PIM_SEC_ADDRF_STALE) {
+        pim_sec_addr_del(pim_ifp, sec_addr);
+        changed = 1;
+      }
+    }
+
+    /* If the list went empty free it up */
+    if (list_isempty(pim_ifp->sec_addr_list)) {
+      list_free(pim_ifp->sec_addr_list);
+      pim_ifp->sec_addr_list = NULL;
+    }
   }
 
   return changed;
 }
 
-static void detect_secondary_address_change(struct interface *ifp,
+static int detect_secondary_address_change(struct interface *ifp,
+              int force_prim_as_any,
                                            const char *caller)
 {
+  struct pim_interface *pim_ifp = ifp->info;
+  int changed = 0;
+
+  if (force_prim_as_any) {
+    /* if primary address is being forced to zero just flush the
+     * secondary address list */
+    changed = pim_sec_addr_del_all(pim_ifp);
+  } else {
+    /* re-evaluate the secondary address list */
+    changed = pim_sec_addr_update(ifp);
+  }
+
+  return changed;
+}
+
+static void detect_address_change(struct interface *ifp,
+                                int force_prim_as_any,
+                                const char *caller)
+{
+  int changed = 0;
   struct pim_interface *pim_ifp;
-  int changed;
 
   pim_ifp = ifp->info;
   if (!pim_ifp)
     return;
 
-  changed = 1; /* true */
-  if (PIM_DEBUG_ZEBRA)
-    zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change",
-             __PRETTY_FUNCTION__, ifp->name);
+  if (detect_primary_address_change(ifp, force_prim_as_any, caller)) {
+    changed = 1;
+  }
 
-  if (!changed) {
-    return;
+  if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) {
+    changed = 1;
   }
 
-  if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
-    return;
+
+  if (changed) {
+    if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
+      return;
+    }
+
+    pim_addr_change(ifp);
   }
 
-  pim_addr_change(ifp);
+  /* XXX: if we have unnumbered interfaces we need to run detect address
+   * address change on all of them when the lo address changes */
 }
 
-static void detect_address_change(struct interface *ifp,
-                                int force_prim_as_any,
-                                const char *caller)
+int pim_update_source_set(struct interface *ifp, struct in_addr source)
 {
-  int prim_changed;
+  struct pim_interface *pim_ifp = ifp->info;
 
-  prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller);
-  if (prim_changed) {
-    /* no need to detect secondary change because
-       the reaction would be the same */
-    return;
+  if (!pim_ifp) {
+    return PIM_IFACE_NOT_FOUND;
   }
 
-  detect_secondary_address_change(ifp, caller);
+  if (pim_ifp->update_source.s_addr == source.s_addr) {
+    return PIM_UPDATE_SOURCE_DUP;
+  }
+
+  pim_ifp->update_source = source;
+  detect_address_change(ifp, 0 /* force_prim_as_any */,
+                        __PRETTY_FUNCTION__);
+
+  return PIM_SUCCESS;
 }
 
 void pim_if_addr_add(struct connected *ifc)
@@ -406,6 +587,7 @@ void pim_if_addr_add(struct connected *ifc)
     if (pim_ifp->mroute_vif_index < 0) {
       pim_if_add_vif(ifp);
     }
+    pim_ifchannel_scan_forward_start (ifp);
   }
 }
 
@@ -496,19 +678,59 @@ void pim_if_addr_add_all(struct interface *ifp)
   struct connected *ifc;
   struct listnode *node;
   struct listnode *nextnode;
+  int v4_addrs = 0;
+  int v6_addrs = 0;
+  struct pim_interface *pim_ifp = ifp->info;
+
 
   /* PIM/IGMP enabled ? */
-  if (!ifp->info)
+  if (!pim_ifp)
     return;
 
   for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
     struct prefix *p = ifc->address;
     
     if (p->family != AF_INET)
-      continue;
+      {
+        v6_addrs++;
+        continue;
+      }
 
+    v4_addrs++;
     pim_if_addr_add(ifc);
   }
+
+  if (!v4_addrs && v6_addrs && !if_is_loopback (ifp))
+    {
+      if (PIM_IF_TEST_PIM(pim_ifp->options)) {
+
+       /* Interface has a valid primary address ? */
+       if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
+
+         /* Interface has a valid socket ? */
+         if (pim_ifp->pim_sock_fd < 0) {
+           if (pim_sock_add(ifp)) {
+             zlog_warn("Failure creating PIM socket for interface %s",
+                       ifp->name);
+           }
+         }
+
+       }
+      } /* pim */
+    }
+  if (PIM_MROUTE_IS_ENABLED) {
+    /*
+     * PIM or IGMP is enabled on interface, and there is at least one
+     * address assigned, then try to create a vif_index.
+     */
+    if (pim_ifp->mroute_vif_index < 0) {
+      pim_if_add_vif(ifp);
+    }
+    pim_ifchannel_scan_forward_start (ifp);
+  }
+
+  pim_rp_setup();
+  pim_rp_check_on_if_add(pim_ifp);
 }
 
 void pim_if_addr_del_all(struct interface *ifp)
@@ -529,6 +751,9 @@ void pim_if_addr_del_all(struct interface *ifp)
 
     pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */);
   }
+
+  pim_rp_setup();
+  pim_i_am_rp_re_evaluate();
 }
 
 void pim_if_addr_del_all_igmp(struct interface *ifp)
@@ -571,17 +796,28 @@ void pim_if_addr_del_all_pim(struct interface *ifp)
   }
 }
 
-static struct in_addr find_first_nonsec_addr(struct interface *ifp)
+struct in_addr
+pim_find_primary_addr (struct interface *ifp)
 {
   struct connected *ifc;
   struct listnode *node;
   struct in_addr addr;
+  int v4_addrs = 0;
+  int v6_addrs = 0;
+  struct pim_interface *pim_ifp = ifp->info;
+
+  if (pim_ifp && PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) {
+    return pim_ifp->update_source;
+  }
 
   for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
     struct prefix *p = ifc->address;
-    
+
     if (p->family != AF_INET)
-      continue;
+      {
+       v6_addrs++;
+       continue;
+      }
 
     if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
       zlog_warn("%s: null IPv4 address connected to interface %s",
@@ -589,22 +825,33 @@ static struct in_addr find_first_nonsec_addr(struct interface *ifp)
       continue;
     }
 
+    v4_addrs++;
+
     if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
       continue;
 
     return p->u.prefix4;
   }
 
+  /*
+   * If we have no v4_addrs and v6 is configured
+   * We probably are using unnumbered
+   * So let's grab the loopbacks v4 address
+   * and use that as the primary address
+   */
+  if (!v4_addrs && v6_addrs && !if_is_loopback (ifp))
+    {
+      struct interface *lo_ifp;
+      lo_ifp = if_lookup_by_name_vrf ("lo", VRF_DEFAULT);
+      if (lo_ifp)
+       return pim_find_primary_addr (lo_ifp);
+    }
+
   addr.s_addr = PIM_NET_INADDR_ANY;
 
   return addr;
 }
 
-struct in_addr pim_find_primary_addr(struct interface *ifp)
-{
-  return find_first_nonsec_addr(ifp);
-}
-
 static int pim_iface_vif_index = 0;
 
 static int
@@ -800,9 +1047,9 @@ int pim_if_find_vifindex_by_ifindex(ifindex_t ifindex)
   struct interface *ifp;
 
   ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT);
-  pim_ifp = ifp->info;
-  if (!pim_ifp)
+  if (!ifp || !ifp->info)
     return -1;
+  pim_ifp = ifp->info;
 
   return pim_ifp->mroute_vif_index;
 }
@@ -899,14 +1146,14 @@ struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
   }
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char addr_str[100];
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
     zlog_debug("%s: neighbor not found for address %s on interface %s",
               __PRETTY_FUNCTION__, 
               addr_str, ifp->name);
   }
 
-  return 0;
+  return NULL;
 }
 
 long pim_if_t_suppressed_msec(struct interface *ifp)
@@ -985,8 +1232,8 @@ static struct igmp_join *igmp_join_new(struct interface *ifp,
 
   join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr);
   if (join_fd < 0) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
@@ -995,13 +1242,13 @@ static struct igmp_join *igmp_join_new(struct interface *ifp,
     return 0;
   }
 
-  ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
+  ij = XCALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
   if (!ij) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-    zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s",
+    zlog_err("%s: XCALLOC(%zu) failure for IGMP group %s source %s on interface %s",
             __PRETTY_FUNCTION__,
             sizeof(*ij), group_str, source_str, ifp->name);
     close(join_fd);
@@ -1045,8 +1292,8 @@ int pim_if_igmp_join_add(struct interface *ifp,
 
   ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
   if (ij) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s",
@@ -1057,8 +1304,8 @@ int pim_if_igmp_join_add(struct interface *ifp,
 
   ij = igmp_join_new(ifp, group_addr, source_addr);
   if (!ij) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s",
@@ -1068,8 +1315,8 @@ int pim_if_igmp_join_add(struct interface *ifp,
   }
 
   if (PIM_DEBUG_IGMP_EVENTS) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s",
@@ -1106,8 +1353,8 @@ int pim_if_igmp_join_del(struct interface *ifp,
 
   ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
   if (!ij) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: could not find IGMP group %s source %s on interface %s",
@@ -1117,14 +1364,13 @@ int pim_if_igmp_join_del(struct interface *ifp,
   }
 
   if (close(ij->sock_fd)) {
-    int e = errno;
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s",
              __PRETTY_FUNCTION__,
-             ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e));
+             ij->sock_fd, group_str, source_str, ifp->name, errno, safe_strerror(errno));
     /* warning only */
   }
   listnode_delete(pim_ifp->igmp_join_list, ij);
@@ -1249,3 +1495,40 @@ void pim_if_create_pimreg (void)
     pim_if_new(pim_regiface, 0, 0);
   }
 }
+
+int
+pim_if_connected_to_source (struct interface *ifp, struct in_addr src)
+{
+  struct listnode *cnode;
+  struct connected *c;
+  struct prefix p;
+
+  p.family = AF_INET;
+  p.u.prefix4 = src;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+    {
+      if ((c->address->family == AF_INET) &&
+         prefix_match (CONNECTED_PREFIX (c), &p))
+       {
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+struct interface *
+pim_if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id)
+{
+  struct listnode *ifnode;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist(vrf_id), ifnode, ifp))
+    {
+      if (pim_if_connected_to_source (ifp, src) && ifp->info)
+       return ifp;
+    }
+  return NULL;
+}
index e56559ca462a513b8e9aa0fa672a30943c6c037b..bd4ebd257d468224ae5861688971c19ce4bb421c 100644 (file)
@@ -16,7 +16,7 @@
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
-*/
+s*/
 
 #ifndef PIM_IFACE_H
 #define PIM_IFACE_H
@@ -58,12 +58,26 @@ enum pim_interface_type {
   PIM_INTERFACE_SM
 };
 
+enum pim_secondary_addr_flags {
+  PIM_SEC_ADDRF_NONE = 0,
+  PIM_SEC_ADDRF_STALE = (1 << 0)
+};
+
+struct pim_secondary_addr {
+  struct in_addr addr;
+  enum pim_secondary_addr_flags flags;
+};
+
 struct pim_interface {
   enum pim_interface_type itype;
   uint32_t       options;                            /* bit vector */
   ifindex_t      mroute_vif_index;
   struct in_addr primary_address; /* remember addr to detect change */
+  struct list    *sec_addr_list; /* list of struct pim_secondary_addr */
+  struct in_addr update_source;  /* user can statically set the primary
+                                  * address of the interface */
 
+  int          igmp_version;                                /* IGMP version */
   int          igmp_default_robustness_variable;            /* IGMPv3 QRV */
   int          igmp_default_query_interval;                 /* IGMPv3 secs between general queries */
   int          igmp_query_max_response_time_dsec;           /* IGMPv3 Max Response Time in dsecs for general queries */
@@ -106,6 +120,7 @@ struct pim_interface {
 };
 
 extern struct interface *pim_regiface;
+extern struct list *pim_ifchannel_list;
 /*
   if default_holdtime is set (>= 0), use it;
   otherwise default_holdtime is 3.5 * hello_period
@@ -116,6 +131,7 @@ extern struct interface *pim_regiface;
   ((pim_ifp)->pim_default_holdtime))
 
 void pim_if_init(void);
+void pim_if_terminate (void);
 
 struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim);
 void                  pim_if_delete(struct interface *ifp);
@@ -126,6 +142,8 @@ void pim_if_addr_del_all(struct interface *ifp);
 void pim_if_addr_del_all_igmp(struct interface *ifp);
 void pim_if_addr_del_all_pim(struct interface *ifp);
 
+struct interface *pim_if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id);
+
 int pim_if_add_vif(struct interface *ifp);
 int pim_if_del_vif(struct interface *ifp);
 void pim_if_add_vif_all(void);
@@ -166,4 +184,8 @@ void pim_if_update_join_desired(struct pim_interface *pim_ifp);
 void pim_if_update_assert_tracking_desired(struct interface *ifp);
 
 void pim_if_create_pimreg(void);
+
+int pim_if_connected_to_source (struct interface *ifp, struct in_addr src);
+int pim_update_source_set(struct interface *ifp, struct in_addr source);
+
 #endif /* PIM_IFACE_H */
index 7afb7a5bdfe33af393e0b0d57292bb4b6c55b9ce..07318791e789bd3e6dc937e21da817d41150e8c6 100644 (file)
@@ -24,6 +24,7 @@
 #include "thread.h"
 #include "memory.h"
 #include "if.h"
+#include "vrf.h"
 
 #include "pimd.h"
 #include "pim_str.h"
 #include "pim_join.h"
 #include "pim_rpf.h"
 #include "pim_macro.h"
+#include "pim_oil.h"
+#include "pim_upstream.h"
 
-void pim_ifchannel_free(struct pim_ifchannel *ch)
+int
+pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2)
+{
+  struct pim_interface *pim_ifp1;
+  struct pim_interface *pim_ifp2;
+
+  if (ntohl(ch1->sg.grp.s_addr) < ntohl(ch2->sg.grp.s_addr))
+    return -1;
+
+  if (ntohl(ch1->sg.grp.s_addr) > ntohl(ch2->sg.grp.s_addr))
+    return 1;
+
+  if (ntohl(ch1->sg.src.s_addr) < ntohl(ch2->sg.src.s_addr))
+    return -1;
+
+  if (ntohl(ch1->sg.src.s_addr) > ntohl(ch2->sg.src.s_addr))
+    return 1;
+
+  pim_ifp1 = ch1->interface->info;
+  pim_ifp2 = ch2->interface->info;
+  if (ntohl(pim_ifp1->primary_address.s_addr) < ntohl(pim_ifp2->primary_address.s_addr))
+    return -1;
+
+  if (ntohl(pim_ifp1->primary_address.s_addr) > ntohl(pim_ifp2->primary_address.s_addr))
+    return 1;
+
+  if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
+    return -1;
+
+  if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
+    return 1;
+
+  return 0;
+}
+
+/*
+ * A (*,G) or a (*,*) is going away
+ * remove the parent pointer from
+ * those pointing at us
+ */
+static void
+pim_ifchannel_remove_children (struct pim_ifchannel *ch)
 {
-  zassert(!ch->t_ifjoin_expiry_timer);
-  zassert(!ch->t_ifjoin_prune_pending_timer);
-  zassert(!ch->t_ifassert_timer);
+  struct pim_ifchannel *child;
+
+  if (!ch->sources)
+    return;
+
+  while (!list_isempty (ch->sources))
+    {
+      child = listnode_head (ch->sources);
+      child->parent = NULL;
+      listnode_delete (ch->sources, child);
+    }
+}
+
+/*
+ * A (*,G) or a (*,*) is being created
+ * find all the children that would point
+ * at us.
+ */
+static void
+pim_ifchannel_find_new_children (struct pim_ifchannel *ch)
+{
+  struct pim_interface *pim_ifp = ch->interface->info;
+  struct pim_ifchannel *child;
+  struct listnode *ch_node;
+
+  // Basic Sanity that we are not being silly
+  if ((ch->sg.src.s_addr != INADDR_ANY) &&
+      (ch->sg.grp.s_addr != INADDR_ANY))
+    return;
+
+  if ((ch->sg.src.s_addr == INADDR_ANY) &&
+      (ch->sg.grp.s_addr == INADDR_ANY))
+    return;
 
+  for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child))
+    {
+      if ((ch->sg.grp.s_addr != INADDR_ANY) &&
+         (child->sg.grp.s_addr == ch->sg.grp.s_addr) &&
+         (child != ch))
+       {
+         child->parent = ch;
+         listnode_add_sort (ch->sources, child);
+       }
+    }
+}
+
+void pim_ifchannel_free(struct pim_ifchannel *ch)
+{
   XFREE(MTYPE_PIM_IFCHANNEL, ch);
 }
 
@@ -51,48 +139,87 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
   struct pim_interface *pim_ifp;
 
   pim_ifp = ch->interface->info;
-  zassert(pim_ifp);
+
+  if (ch->upstream->channel_oil)
+    {
+      pim_channel_del_oif (ch->upstream->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+      /*
+       * Do we have any S,G's that are inheriting?
+       * Nuke from on high too.
+       */
+      if (ch->upstream->sources)
+       {
+         struct pim_upstream *child;
+         struct listnode *up_node;
+
+         for (ALL_LIST_ELEMENTS_RO (ch->upstream->sources, up_node, child))
+           pim_channel_del_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+       }
+    }
+
+  /*
+   * When this channel is removed
+   * we need to find all our children
+   * and make sure our pointers are fixed
+   */
+  pim_ifchannel_remove_children (ch);
+
+  if (ch->sources)
+    list_delete (ch->sources);
 
   if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
     pim_upstream_update_join_desired(ch->upstream);
   }
 
-  pim_upstream_del(ch->upstream);
+  pim_upstream_del(ch->upstream, __PRETTY_FUNCTION__);
+  ch->upstream = NULL;
 
   THREAD_OFF(ch->t_ifjoin_expiry_timer);
   THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
   THREAD_OFF(ch->t_ifassert_timer);
 
+  if (ch->parent)
+    {
+      listnode_delete (ch->parent->sources, ch);
+      ch->parent = NULL;
+    }
   /*
     notice that listnode_delete() can't be moved
     into pim_ifchannel_free() because the later is
     called by list_delete_all_node()
   */
   listnode_delete(pim_ifp->pim_ifchannel_list, ch);
+  listnode_delete(pim_ifchannel_list, ch);
 
   pim_ifchannel_free(ch);
 }
 
-#define IFCHANNEL_NOINFO(ch)                                   \
-  (                                                            \
-   ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)       \
-   &&                                                          \
-   ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO)                   \
-   &&                                                          \
-   ((ch)->ifassert_state == PIM_IFASSERT_NOINFO)               \
-   )
+void
+pim_ifchannel_delete_all (struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *ifchannel_node;
+  struct listnode *ifchannel_nextnode;
+  struct pim_ifchannel *ifchannel;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return;
+
+  for (ALL_LIST_ELEMENTS (pim_ifp->pim_ifchannel_list, ifchannel_node,
+                         ifchannel_nextnode, ifchannel))
+    {
+      pim_ifchannel_delete (ifchannel);
+    }
+}
    
 static void delete_on_noinfo(struct pim_ifchannel *ch)
 {
-  if (IFCHANNEL_NOINFO(ch)) {
-
-    /* In NOINFO state, timers should have been cleared */
-    zassert(!ch->t_ifjoin_expiry_timer);
-    zassert(!ch->t_ifjoin_prune_pending_timer);
-    zassert(!ch->t_ifassert_timer);
-    
+  if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO &&
+      ch->ifjoin_state == PIM_IFJOIN_NOINFO &&
+      ch->t_ifjoin_expiry_timer == NULL)
     pim_ifchannel_delete(ch);
-  }
+
 }
 
 void pim_ifchannel_ifjoin_switch(const char *caller,
@@ -101,6 +228,14 @@ void pim_ifchannel_ifjoin_switch(const char *caller,
 {
   enum pim_ifjoin_state old_state = ch->ifjoin_state;
 
+  if (PIM_DEBUG_PIM_EVENTS)
+    zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
+               ch->interface->name,
+               ch->sg_str,
+               pim_ifchannel_ifjoin_name (ch->ifjoin_state),
+               pim_ifchannel_ifjoin_name (new_state));
+
+
   if (old_state == new_state) {
     if (PIM_DEBUG_PIM_EVENTS) {
       zlog_debug("%s calledby %s: non-transition on state %d (%s)",
@@ -110,25 +245,68 @@ void pim_ifchannel_ifjoin_switch(const char *caller,
     return;
   }
 
-  zassert(old_state != new_state);
-
   ch->ifjoin_state = new_state;
 
+  if (ch->sg.src.s_addr == INADDR_ANY)
+    {
+      struct pim_upstream *up = ch->upstream;
+      struct pim_upstream *child;
+      struct listnode *up_node;
+
+      if (up)
+       {
+         if (ch->ifjoin_state == PIM_IFJOIN_NOINFO)
+           {
+             for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+               {
+                 struct channel_oil *c_oil = child->channel_oil;
+                 struct pim_interface *pim_ifp = ch->interface->info;
+
+                 if (PIM_DEBUG_PIM_TRACE)
+                   zlog_debug("%s %s: Prune(S,G)=%s from %s",
+                              __FILE__, __PRETTY_FUNCTION__,
+                              child->sg_str, up->sg_str);
+                 if (!c_oil)
+                   continue;
+
+                 if (!pim_upstream_evaluate_join_desired (child))
+                   pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+
+                 /*
+                  * If the S,G has no if channel and the c_oil still
+                  * has output here then the *,G was supplying the implied
+                  * if channel.  So remove it.
+                  */
+                 if (!ch && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
+                   pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+               }
+           }
+         if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
+           {
+             for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+               {
+                 if (PIM_DEBUG_PIM_TRACE)
+                   zlog_debug("%s %s: Join(S,G)=%s from %s",
+                              __FILE__, __PRETTY_FUNCTION__,
+                              child->sg_str, up->sg_str);
+
+                 if (pim_upstream_evaluate_join_desired (child))
+                   {
+                     pim_channel_add_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+                     pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
+                   }
+               }
+           }
+       }
+    }
   /* Transition to/from NOINFO ? */
-  if (
-      (old_state == PIM_IFJOIN_NOINFO)
-      ||
-      (new_state == PIM_IFJOIN_NOINFO)
-      ) {
+  if ((old_state == PIM_IFJOIN_NOINFO) ||
+      (new_state == PIM_IFJOIN_NOINFO)) {
 
     if (PIM_DEBUG_PIM_EVENTS) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-      zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
+      zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
                 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
-                src_str, grp_str, ch->interface->name);
+                ch->sg_str, ch->interface->name);
     }
 
     /*
@@ -145,9 +323,12 @@ void pim_ifchannel_ifjoin_switch(const char *caller,
 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
 {
   switch (ifjoin_state) {
-  case PIM_IFJOIN_NOINFO:        return "NOINFO";
-  case PIM_IFJOIN_JOIN:          return "JOIN";
-  case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
+  case PIM_IFJOIN_NOINFO:            return "NOINFO";
+  case PIM_IFJOIN_JOIN:              return "JOIN";
+  case PIM_IFJOIN_PRUNE:             return "PRUNE";
+  case PIM_IFJOIN_PRUNE_PENDING:     return "PRUNEP";
+  case PIM_IFJOIN_PRUNE_TMP:         return "PRUNET";
+  case PIM_IFJOIN_PRUNE_PENDING_TMP: return "PRUNEPT";
   }
 
   return "ifjoin_bad_state";
@@ -180,77 +361,8 @@ void reset_ifassert_state(struct pim_ifchannel *ch)
                          qpim_infinite_assert_metric);
 }
 
-static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
-                                              struct in_addr source_addr,
-                                              struct in_addr group_addr)
-{
-  struct pim_ifchannel *ch;
-  struct pim_interface *pim_ifp;
-  struct pim_upstream  *up;
-
-  pim_ifp = ifp->info;
-  zassert(pim_ifp);
-
-  up = pim_upstream_add(source_addr, group_addr, NULL);
-  if (!up) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
-    zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
-            __PRETTY_FUNCTION__,
-            src_str, grp_str, ifp->name);
-    return 0;
-  }
-
-  ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
-  if (!ch) {
-    zlog_err("%s: PIM XMALLOC(%zu) failure",
-            __PRETTY_FUNCTION__, sizeof(*ch));
-    return 0;
-  }
-
-  ch->flags                        = 0;
-  ch->upstream                     = up;
-  ch->interface                    = ifp;
-  ch->source_addr                  = source_addr;
-  ch->group_addr                   = group_addr;
-  ch->local_ifmembership           = PIM_IFMEMBERSHIP_NOINFO;
-
-  ch->ifjoin_state                 = PIM_IFJOIN_NOINFO;
-  ch->t_ifjoin_expiry_timer        = 0;
-  ch->t_ifjoin_prune_pending_timer = 0;
-  ch->ifjoin_creation              = 0;
-
-  ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
-  ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
-
-  ch->ifassert_winner.s_addr = 0;
-
-  /* Assert state */
-  ch->t_ifassert_timer   = 0;
-  reset_ifassert_state(ch);
-  if (pim_macro_ch_could_assert_eval(ch))
-    PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
-  else
-    PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
-
-  if (pim_macro_assert_tracking_desired_eval(ch))
-    PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
-  else
-    PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
-
-  /* Attach to list */
-  listnode_add(pim_ifp->pim_ifchannel_list, ch);
-
-  zassert(IFCHANNEL_NOINFO(ch));
-
-  return ch;
-}
-
 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
-                                        struct in_addr source_addr,
-                                        struct in_addr group_addr)
+                                        struct prefix_sg *sg)
 {
   struct pim_interface *pim_ifp;
   struct listnode      *ch_node;
@@ -261,21 +373,17 @@ struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
   pim_ifp = ifp->info;
 
   if (!pim_ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+    zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str,
+             pim_str_sg_dump (sg),
              ifp->name);
     return 0;
   }
 
   for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
     if (
-       (source_addr.s_addr == ch->source_addr.s_addr) &&
-       (group_addr.s_addr == ch->group_addr.s_addr)
+       (sg->src.s_addr == ch->sg.src.s_addr) &&
+       (sg->grp.s_addr == ch->sg.grp.s_addr)
        ) {
       return ch;
     }
@@ -291,13 +399,9 @@ static void ifmembership_set(struct pim_ifchannel *ch,
     return;
 
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
+    zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
               __PRETTY_FUNCTION__,
-              src_str, grp_str,
+              ch->sg_str,
               membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
               ch->interface->name);
   }
@@ -339,29 +443,110 @@ void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
   }
 }
 
-struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
-                                       struct in_addr source_addr,
-                                       struct in_addr group_addr)
+/*
+ * For a given Interface, if we are given a S,G
+ * Find the *,G (If we have it).
+ * If we are passed a *,G, find the *,* ifchannel
+ * if we have it.
+ */
+static struct pim_ifchannel *
+pim_ifchannel_find_parent (struct pim_ifchannel *ch)
+{
+  struct prefix_sg parent_sg = ch->sg;
+  struct pim_ifchannel *parent = NULL;
+
+  // (S,G)
+  if ((parent_sg.src.s_addr != INADDR_ANY) &&
+      (parent_sg.grp.s_addr != INADDR_ANY))
+    {
+      parent_sg.src.s_addr = INADDR_ANY;
+      parent = pim_ifchannel_find (ch->interface, &parent_sg);
+
+      if (parent)
+       listnode_add (parent->sources, ch);
+      return parent;
+    }
+
+  return NULL;
+}
+
+struct pim_ifchannel *
+pim_ifchannel_add(struct interface *ifp,
+                 struct prefix_sg *sg, int flags)
 {
+  struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
-  char src_str[100];
-  char grp_str[100];
+  struct pim_upstream *up;
 
-  ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+  ch = pim_ifchannel_find(ifp, sg);
   if (ch)
     return ch;
 
-  ch = pim_ifchannel_new(ifp, source_addr, group_addr);
-  if (ch)
-    return ch;
-    
-  pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-  pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
-  zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
-           __PRETTY_FUNCTION__,
-           src_str, grp_str, ifp->name);
+  pim_ifp = ifp->info;
 
-  return 0;
+  up = pim_upstream_add(sg, NULL, flags, __PRETTY_FUNCTION__);
+  if (!up) {
+    zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
+            __PRETTY_FUNCTION__,
+            pim_str_sg_dump (sg), ifp->name);
+    return NULL;
+  }
+
+  ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
+  if (!ch) {
+    zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
+             __PRETTY_FUNCTION__,
+             up->sg_str, ifp->name);
+
+    pim_upstream_del (up, __PRETTY_FUNCTION__);
+    return NULL;
+  }
+
+  ch->flags                        = 0;
+  ch->upstream                     = up;
+  ch->interface                    = ifp;
+  ch->sg                           = *sg;
+  pim_str_sg_set (sg, ch->sg_str);
+  ch->parent                       = pim_ifchannel_find_parent (ch);
+  if (ch->sg.src.s_addr == INADDR_ANY)
+    {
+      ch->sources = list_new ();
+      ch->sources->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
+    }
+  else
+    ch->sources = NULL;
+
+  pim_ifchannel_find_new_children (ch);
+  ch->local_ifmembership           = PIM_IFMEMBERSHIP_NOINFO;
+
+  ch->ifjoin_state                 = PIM_IFJOIN_NOINFO;
+  ch->t_ifjoin_expiry_timer        = NULL;
+  ch->t_ifjoin_prune_pending_timer = NULL;
+  ch->ifjoin_creation              = 0;
+
+  ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
+  ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
+
+  ch->ifassert_winner.s_addr = 0;
+
+  /* Assert state */
+  ch->t_ifassert_timer   = NULL;
+  reset_ifassert_state(ch);
+  if (pim_macro_ch_could_assert_eval(ch))
+    PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
+  else
+    PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
+
+  if (pim_macro_assert_tracking_desired_eval(ch))
+    PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
+  else
+    PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
+
+  /* Attach to list */
+  listnode_add_sort(pim_ifp->pim_ifchannel_list, ch);
+  listnode_add_sort(pim_ifchannel_list, ch);
+
+  return ch;
 }
 
 static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
@@ -375,13 +560,9 @@ static int on_ifjoin_expiry_timer(struct thread *t)
 {
   struct pim_ifchannel *ch;
 
-  zassert(t);
   ch = THREAD_ARG(t);
-  zassert(ch);
 
-  ch->t_ifjoin_expiry_timer = 0;
-
-  zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
+  ch->t_ifjoin_expiry_timer = NULL;
 
   ifjoin_to_noinfo(ch);
   /* ch may have been deleted */
@@ -389,64 +570,37 @@ static int on_ifjoin_expiry_timer(struct thread *t)
   return 0;
 }
 
-static void prune_echo(struct interface *ifp,
-                      struct in_addr source_addr,
-                      struct in_addr group_addr)
-{
-  struct pim_interface *pim_ifp;
-  struct in_addr neigh_dst_addr;
-
-  pim_ifp = ifp->info;
-  zassert(pim_ifp);
-
-  neigh_dst_addr = pim_ifp->primary_address;
-
-  if (PIM_DEBUG_PIM_EVENTS) {
-    char source_str[100];
-    char group_str[100];
-    char neigh_dst_str[100];
-    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
-    pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
-    zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
-              __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
-  }
-
-  pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
-                    0 /* boolean: send_join=false (prune) */);
-}
-
 static int on_ifjoin_prune_pending_timer(struct thread *t)
 {
   struct pim_ifchannel *ch;
   int send_prune_echo; /* boolean */
   struct interface *ifp;
   struct pim_interface *pim_ifp;
-  struct in_addr ch_source;
-  struct in_addr ch_group;
 
-  zassert(t);
   ch = THREAD_ARG(t);
-  zassert(ch);
-
-  ch->t_ifjoin_prune_pending_timer = 0;
-
-  zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
 
-  /* Send PruneEcho(S,G) ? */
-  ifp = ch->interface;
-  pim_ifp = ifp->info;
-  send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
+  ch->t_ifjoin_prune_pending_timer = NULL;
 
-  /* Save (S,G) */
-  ch_source = ch->source_addr;
-  ch_group = ch->group_addr;
+  if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING)
+    {
+      /* Send PruneEcho(S,G) ? */
+      ifp = ch->interface;
+      pim_ifp = ifp->info;
+      send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
 
-  ifjoin_to_noinfo(ch);
-  /* from here ch may have been deleted */
+      ifjoin_to_noinfo(ch);
+      /* from here ch may have been deleted */
 
-  if (send_prune_echo)
-    prune_echo(ifp, ch_source, ch_group);
+      if (send_prune_echo)
+       pim_joinprune_send (ifp, pim_ifp->primary_address,
+                           ch->upstream, 0);
+    }
+  else
+    {
+      zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
+               __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg),
+               pim_ifchannel_ifjoin_name (ch->ifjoin_state));
+    }
 
   return 0;
 }
@@ -454,15 +608,14 @@ static int on_ifjoin_prune_pending_timer(struct thread *t)
 static void check_recv_upstream(int is_join,
                                struct interface *recv_ifp,
                                struct in_addr upstream,
-                               struct in_addr source_addr,
-                               struct in_addr group_addr,
+                               struct prefix_sg *sg,
                                uint8_t source_flags,
                                int holdtime)
 {
   struct pim_upstream *up;
 
   /* Upstream (S,G) in Joined state ? */
-  up = pim_upstream_find(source_addr, group_addr);
+  up = pim_upstream_find(sg);
   if (!up)
     return;
   if (up->join_state != PIM_UPSTREAM_JOINED)
@@ -470,31 +623,23 @@ static void check_recv_upstream(int is_join,
 
   /* Upstream (S,G) in Joined state */
 
-  if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
+  if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
     /* RPF'(S,G) not found */
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s %s: RPF'(%s,%s) not found",
+    zlog_warn("%s %s: RPF'%s not found",
              __FILE__, __PRETTY_FUNCTION__, 
-             src_str, grp_str);
+             up->sg_str);
     return;
   }
 
   /* upstream directed to RPF'(S,G) ? */
-  if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
-    char src_str[100];
-    char grp_str[100];
-    char up_str[100];
-    char rpf_str[100];
-    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+  if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
+    char up_str[INET_ADDRSTRLEN];
+    char rpf_str[PREFIX_STRLEN];
     pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
-    pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
-    zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
+    pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+    zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
              __FILE__, __PRETTY_FUNCTION__, 
-             src_str, grp_str,
+             up->sg_str,
              up_str, rpf_str, recv_ifp->name);
     return;
   }
@@ -502,7 +647,7 @@ static void check_recv_upstream(int is_join,
 
   if (is_join) {
     /* Join(S,G) to RPF'(S,G) */
-    pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
+    pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime);
     return;
   }
 
@@ -512,26 +657,25 @@ static void check_recv_upstream(int is_join,
     if (source_flags & PIM_WILDCARD_BIT_MASK) {
       /* Prune(*,G) to RPF'(S,G) */
       pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
-                                                    up, up->rpf.rpf_addr);
+                                                    up, up->rpf.rpf_addr.u.prefix4);
       return;
     }
 
     /* Prune(S,G,rpt) to RPF'(S,G) */
     pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
-                                                  up, up->rpf.rpf_addr);
+                                                  up, up->rpf.rpf_addr.u.prefix4);
     return;
   }
 
   /* Prune(S,G) to RPF'(S,G) */
   pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
-                                                up->rpf.rpf_addr);
+                                                up->rpf.rpf_addr.u.prefix4);
 }
 
 static int nonlocal_upstream(int is_join,
                             struct interface *recv_ifp,
                             struct in_addr upstream,
-                            struct in_addr source_addr,
-                            struct in_addr group_addr,
+                            struct prefix_sg *sg,
                             uint8_t source_flags,
                             uint16_t holdtime)
 {
@@ -543,17 +687,13 @@ static int nonlocal_upstream(int is_join,
 
   is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
   
-  if (PIM_DEBUG_PIM_TRACE) {
-    char up_str[100];
-    char src_str[100];
-    char grp_str[100];
+  if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+    char up_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
-    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
+    zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s",
              __PRETTY_FUNCTION__,
              is_join ? "join" : "prune",
-             src_str, grp_str,
+             pim_str_sg_dump (sg),
              is_local ? "local" : "non-local",
              up_str, recv_ifp->name);
   }
@@ -565,7 +705,7 @@ static int nonlocal_upstream(int is_join,
     Since recv upstream addr was not directed to our primary
     address, check if we should react to it in any way.
   */
-  check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
+  check_recv_upstream(is_join, recv_ifp, upstream, sg,
                      source_flags, holdtime);
 
   return 1; /* non-local */
@@ -574,8 +714,7 @@ static int nonlocal_upstream(int is_join,
 void pim_ifchannel_join_add(struct interface *ifp,
                            struct in_addr neigh_addr,
                            struct in_addr upstream,
-                           struct in_addr source_addr,
-                           struct in_addr group_addr,
+                           struct prefix_sg *sg,
                            uint8_t source_flags,
                            uint16_t holdtime)
 {
@@ -583,11 +722,11 @@ void pim_ifchannel_join_add(struct interface *ifp,
   struct pim_ifchannel *ch;
 
   if (nonlocal_upstream(1 /* join */, ifp, upstream,
-                       source_addr, group_addr, source_flags, holdtime)) {
+                       sg, source_flags, holdtime)) {
     return;
   }
 
-  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
   if (!ch)
     return;
 
@@ -608,15 +747,11 @@ void pim_ifchannel_join_add(struct interface *ifp,
     address of the join message is our primary address.
    */
   if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
-    char src_str[100];
-    char grp_str[100];
-    char neigh_str[100];
-    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    char neigh_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
-    zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
+    zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, neigh_str, ifp->name);
+             ch->sg_str, neigh_str, ifp->name);
 
     assert_action_a5(ch);
   }
@@ -628,6 +763,7 @@ void pim_ifchannel_join_add(struct interface *ifp,
   case PIM_IFJOIN_NOINFO:
     pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
     if (pim_macro_chisin_oiflist(ch)) {
+      pim_upstream_inherited_olist (ch->upstream);
       pim_forward_start(ch);
     }
     break;
@@ -660,16 +796,26 @@ void pim_ifchannel_join_add(struct interface *ifp,
     }
     THREAD_OFF(ch->t_ifjoin_expiry_timer);
     break;
+  case PIM_IFJOIN_PRUNE:
+    if (source_flags & PIM_ENCODE_RPT_BIT)
+      pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
+    break;
   case PIM_IFJOIN_PRUNE_PENDING:
-    zassert(!ch->t_ifjoin_expiry_timer);
-    zassert(ch->t_ifjoin_prune_pending_timer);
     THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
-    pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
+    if (source_flags & PIM_ENCODE_RPT_BIT)
+      {
+       THREAD_OFF(ch->t_ifjoin_expiry_timer);
+       pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
+      }
+    else
+      pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
+    break;
+  case PIM_IFJOIN_PRUNE_TMP:
+    break;
+  case PIM_IFJOIN_PRUNE_PENDING_TMP:
     break;
   }
 
-  zassert(!IFCHANNEL_NOINFO(ch));
-
   if (holdtime != 0xFFFF) {
     THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
                    on_ifjoin_expiry_timer,
@@ -679,66 +825,112 @@ void pim_ifchannel_join_add(struct interface *ifp,
 
 void pim_ifchannel_prune(struct interface *ifp,
                         struct in_addr upstream,
-                        struct in_addr source_addr,
-                        struct in_addr group_addr,
+                        struct prefix_sg *sg,
                         uint8_t source_flags,
                         uint16_t holdtime)
 {
   struct pim_ifchannel *ch;
+  struct pim_interface *pim_ifp;
   int jp_override_interval_msec;
 
   if (nonlocal_upstream(0 /* prune */, ifp, upstream,
-                       source_addr, group_addr, source_flags, holdtime)) {
+                       sg, source_flags, holdtime)) {
     return;
   }
 
-  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  ch = pim_ifchannel_find (ifp, sg);
+  if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT))
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
+                   __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags);
+      return;
+    }
+
+  ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
   if (!ch)
     return;
 
+  pim_ifp = ifp->info;
+
   switch (ch->ifjoin_state) {
   case PIM_IFJOIN_NOINFO:
+    if (source_flags & PIM_ENCODE_RPT_BIT)
+      {
+       PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
+       ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
+        if (listcount(pim_ifp->pim_neighbor_list) > 1)
+          jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+        else
+          jp_override_interval_msec = 0; /* schedule to expire immediately */
+          /* If we called ifjoin_prune() directly instead, care should
+             be taken not to use "ch" afterwards since it would be
+             deleted. */
+
+       THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+       THREAD_OFF(ch->t_ifjoin_expiry_timer);
+       THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
+                            on_ifjoin_prune_pending_timer,
+                            ch, jp_override_interval_msec);
+       THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+                       on_ifjoin_expiry_timer,
+                       ch, holdtime);
+      }
+    break;
   case PIM_IFJOIN_PRUNE_PENDING:
     /* nothing to do */
     break;
   case PIM_IFJOIN_JOIN:
-    {
-      struct pim_interface *pim_ifp;
-
-      pim_ifp = ifp->info;
+    THREAD_OFF(ch->t_ifjoin_expiry_timer);
 
-      zassert(ch->t_ifjoin_expiry_timer);
-      zassert(!ch->t_ifjoin_prune_pending_timer);
+    pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
 
-      THREAD_OFF(ch->t_ifjoin_expiry_timer);
-      
-      pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
-      
-      if (listcount(pim_ifp->pim_neighbor_list) > 1) {
-       jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+    if (listcount(pim_ifp->pim_neighbor_list) > 1)
+      jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+    else
+      jp_override_interval_msec = 0; /* schedule to expire immediately */
+      /* If we called ifjoin_prune() directly instead, care should
+        be taken not to use "ch" afterwards since it would be
+        deleted. */
+    THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+    THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
+                        on_ifjoin_prune_pending_timer,
+                        ch, jp_override_interval_msec);
+    break;
+  case PIM_IFJOIN_PRUNE:
+    if (source_flags & PIM_ENCODE_RPT_BIT)
+      {
+       THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+       THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+                       on_ifjoin_expiry_timer,
+                       ch, holdtime);
+      }
+    break;
+  case PIM_IFJOIN_PRUNE_TMP:
+    if (source_flags & PIM_ENCODE_RPT_BIT)
+      {
+       ch->ifjoin_state = PIM_IFJOIN_PRUNE;
+       THREAD_OFF(ch->t_ifjoin_expiry_timer);
+       THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+                       on_ifjoin_expiry_timer,
+                       ch, holdtime);
       }
-      else {
-       jp_override_interval_msec = 0; /* schedule to expire immediately */
-       /* If we called ifjoin_prune() directly instead, care should
-          be taken not to use "ch" afterwards since it would be
-          deleted. */
+    break;
+  case PIM_IFJOIN_PRUNE_PENDING_TMP:
+    if (source_flags & PIM_ENCODE_RPT_BIT)
+      {
+       ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
+       THREAD_OFF(ch->t_ifjoin_expiry_timer);
+       THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+                       on_ifjoin_expiry_timer,
+                       ch, holdtime);
       }
-      
-      THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
-                          on_ifjoin_prune_pending_timer,
-                          ch, jp_override_interval_msec);
-      
-      zassert(!ch->t_ifjoin_expiry_timer);
-      zassert(ch->t_ifjoin_prune_pending_timer);
-    }
     break;
   }
-
 }
 
 void pim_ifchannel_local_membership_add(struct interface *ifp,
-                                       struct in_addr source_addr,
-                                       struct in_addr group_addr)
+                                       struct prefix_sg *sg)
 {
   struct pim_ifchannel *ch;
   struct pim_interface *pim_ifp;
@@ -750,19 +942,37 @@ void pim_ifchannel_local_membership_add(struct interface *ifp,
   if (!PIM_IF_TEST_PIM(pim_ifp->options))
     return;
 
-  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
   if (!ch) {
     return;
   }
 
   ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
 
-  zassert(!IFCHANNEL_NOINFO(ch));
+  if (sg->src.s_addr == INADDR_ANY)
+    {
+      struct pim_upstream *up = pim_upstream_find (sg);
+      struct pim_upstream *child;
+      struct listnode *up_node;
+
+      for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+        {
+         if (PIM_DEBUG_EVENTS)
+           zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
+                      __FILE__, __PRETTY_FUNCTION__,
+                      child->sg_str, ifp->name, up->sg_str);
+
+         if (pim_upstream_evaluate_join_desired (child))
+           {
+             pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
+             pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
+           }
+        }
+    }
 }
 
 void pim_ifchannel_local_membership_del(struct interface *ifp,
-                                       struct in_addr source_addr,
-                                       struct in_addr group_addr)
+                                       struct prefix_sg *sg)
 {
   struct pim_ifchannel *ch;
   struct pim_interface *pim_ifp;
@@ -774,12 +984,41 @@ void pim_ifchannel_local_membership_del(struct interface *ifp,
   if (!PIM_IF_TEST_PIM(pim_ifp->options))
     return;
 
-  ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+  ch = pim_ifchannel_find(ifp, sg);
   if (!ch)
     return;
 
   ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
 
+  if (sg->src.s_addr == INADDR_ANY)
+    {
+      struct pim_upstream *up = pim_upstream_find (sg);
+      struct pim_upstream *child;
+      struct listnode *up_node;
+
+      for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+        {
+         struct channel_oil *c_oil = child->channel_oil;
+         struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg);
+         struct pim_interface *pim_ifp = ifp->info;
+
+         if (PIM_DEBUG_EVENTS)
+           zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
+                      __FILE__, __PRETTY_FUNCTION__,
+                      up->sg_str, ifp->name, child->sg_str);
+
+         if (c_oil && !pim_upstream_evaluate_join_desired (child))
+           pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
+
+         /*
+          * If the S,G has no if channel and the c_oil still
+          * has output here then the *,G was supplying the implied
+          * if channel.  So remove it.
+          */
+         if (!chchannel && c_oil && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
+           pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
+        }
+    }
   delete_on_noinfo(ch);
 }
 
@@ -792,10 +1031,10 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
     return;
 
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    char src_str[INET_ADDRSTRLEN];
+    char grp_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
     zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
               __PRETTY_FUNCTION__,
               src_str, grp_str, ch->interface->name,
@@ -834,12 +1073,12 @@ void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
       return;
 
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    char old_addr_str[100];
-    char new_addr_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    char src_str[INET_ADDRSTRLEN];
+    char grp_str[INET_ADDRSTRLEN];
+    char old_addr_str[INET_ADDRSTRLEN];
+    char new_addr_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
     pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
     pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
     zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
@@ -872,10 +1111,10 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
     return;
 
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    char src_str[INET_ADDRSTRLEN];
+    char grp_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
     zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
               __PRETTY_FUNCTION__,
               src_str, grp_str, ch->interface->name,
@@ -895,3 +1134,88 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
     }
   }
 }
+
+/*
+ * If we have a new pim interface, check to
+ * see if any of the pre-existing channels have
+ * their upstream out that way and turn on forwarding
+ * for that ifchannel then.
+ */
+void
+pim_ifchannel_scan_forward_start (struct interface *new_ifp)
+{
+  struct listnode *ifnode;
+  struct interface *ifp;
+  struct pim_interface *new_pim_ifp = new_ifp->info;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
+    {
+      struct pim_interface *loop_pim_ifp = ifp->info;
+      struct listnode *ch_node;
+      struct pim_ifchannel *ch;
+
+      if (!loop_pim_ifp)
+        continue;
+
+      if (new_pim_ifp == loop_pim_ifp)
+        continue;
+
+      for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch))
+        {
+          if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
+            {
+              struct pim_upstream *up = ch->upstream;
+              if ((!up->channel_oil) &&
+                 (up->rpf.source_nexthop.interface == new_ifp))
+                pim_forward_start (ch);
+            }
+        }
+    }
+}
+
+/*
+ * Downstream per-interface (S,G,rpt) state machine
+ * states that we need to move (S,G,rpt) items
+ * into different states at the start of the
+ * reception of a *,G join as well, when
+ * we get End of Message
+ */
+void
+pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom)
+{
+  struct pim_ifchannel *child;
+  struct listnode *ch_node;
+
+  if (PIM_DEBUG_PIM_TRACE)
+    zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__,
+                pim_ifchannel_ifjoin_name(ch->ifjoin_state),
+                ch->sg_str, eom);
+  if (!ch->sources)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (ch->sources, ch_node, child))
+    {
+      if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
+        continue;
+
+      switch (child->ifjoin_state)
+       {
+       case PIM_IFJOIN_NOINFO:
+       case PIM_IFJOIN_JOIN:
+         break;
+       case PIM_IFJOIN_PRUNE:
+         if (!eom)
+           child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
+         break;
+       case PIM_IFJOIN_PRUNE_PENDING:
+         if (!eom)
+           child->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING_TMP;
+         break;
+       case PIM_IFJOIN_PRUNE_TMP:
+       case PIM_IFJOIN_PRUNE_PENDING_TMP:
+         if (eom)
+           child->ifjoin_state = PIM_IFJOIN_NOINFO;
+         break;
+       }
+    }
+}
index ce753222ee0bb2d3553c7e4e98c9e893b781c57f..c1ce52449af435e386baa5d7edd9a9a3fb1c9096 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_IFCHANNEL_H
@@ -24,6 +28,7 @@
 #include <zebra.h>
 
 #include "if.h"
+#include "prefix.h"
 
 #include "pim_upstream.h"
 
@@ -35,7 +40,10 @@ enum pim_ifmembership {
 enum pim_ifjoin_state {
   PIM_IFJOIN_NOINFO,
   PIM_IFJOIN_JOIN,
-  PIM_IFJOIN_PRUNE_PENDING
+  PIM_IFJOIN_PRUNE,
+  PIM_IFJOIN_PRUNE_PENDING,
+  PIM_IFJOIN_PRUNE_TMP,
+  PIM_IFJOIN_PRUNE_PENDING_TMP,
 };
 
 enum pim_ifassert_state {
@@ -66,12 +74,22 @@ struct pim_assert_metric {
 #define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
 #define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
 
+/*
+ * Flat to tell us if the ifchannel is (S,G,rpt)
+ */
+#define PIM_IF_FLAG_MASK_S_G_RPT         (1 << 2)
+#define PIM_IF_FLAG_TEST_S_G_RPT(flags)  ((flags) & PIM_IF_FLAG_MASK_S_G_RPT)
+#define PIM_IF_FLAG_SET_S_G_RPT(flags)   ((flags) |= PIM_IF_FLAG_MASK_S_G_RPT)
+#define PIM_IF_FLAG_UNSET_S_G_RPT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_S_G_RPT)
+
 /*
   Per-interface (S,G) state
 */
 struct pim_ifchannel {
-  struct in_addr            source_addr; /* (S,G) source key */
-  struct in_addr            group_addr;  /* (S,G) group key */
+  struct pim_ifchannel     *parent;
+  struct list              *sources;
+  struct prefix_sg          sg;
+  char                      sg_str[PIM_SG_LEN];
   struct interface         *interface;   /* backpointer to interface */
   uint32_t                  flags;
 
@@ -98,33 +116,28 @@ struct pim_ifchannel {
 
 void pim_ifchannel_free(struct pim_ifchannel *ch);
 void pim_ifchannel_delete(struct pim_ifchannel *ch);
+void pim_ifchannel_delete_all (struct interface *ifp);
 void pim_ifchannel_membership_clear(struct interface *ifp);
 void pim_ifchannel_delete_on_noinfo(struct interface *ifp);
 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
-                                        struct in_addr source_addr,
-                                        struct in_addr group_addr);
+                                        struct prefix_sg *sg);
 struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
-                                       struct in_addr source_addr,
-                                       struct in_addr group_addr);
+                                       struct prefix_sg *sg, int flags);
 void pim_ifchannel_join_add(struct interface *ifp,
                            struct in_addr neigh_addr,
                            struct in_addr upstream,
-                           struct in_addr source_addr,
-                           struct in_addr group_addr,
+                           struct prefix_sg *sg,
                            uint8_t source_flags,
                            uint16_t holdtime);
 void pim_ifchannel_prune(struct interface *ifp,
                         struct in_addr upstream,
-                        struct in_addr source_addr,
-                        struct in_addr group_addr,
+                        struct prefix_sg *sg,
                         uint8_t source_flags,
                         uint16_t holdtime);
 void pim_ifchannel_local_membership_add(struct interface *ifp,
-                                       struct in_addr source_addr,
-                                       struct in_addr group_addr);
+                                       struct prefix_sg *sg);
 void pim_ifchannel_local_membership_del(struct interface *ifp,
-                                       struct in_addr source_addr,
-                                       struct in_addr group_addr);
+                                       struct prefix_sg *sg);
 
 void pim_ifchannel_ifjoin_switch(const char *caller,
                                 struct pim_ifchannel *ch,
@@ -140,4 +153,8 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch);
 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch);
 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch);
 
+void pim_ifchannel_scan_forward_start (struct interface *new_ifp);
+void pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom);
+
+int pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2);
 #endif /* PIM_IFCHANNEL_H */
index ef1b3cbac0434bc8185eb796bf2216f2a805cc1c..a309de690bdd2686861ead68a778565c8837b77a 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -26,6 +30,7 @@
 
 #include "pimd.h"
 #include "pim_igmp.h"
+#include "pim_igmpv2.h"
 #include "pim_igmpv3.h"
 #include "pim_iface.h"
 #include "pim_sock.h"
 #include "pim_time.h"
 #include "pim_zebra.h"
 
-#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE        (1)
-#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE        (2)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
-#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES      (5)
-#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES      (6)
-
 static void group_timer_off(struct igmp_group *group);
 
+/* This socket is used for TXing IGMP packets only, IGMP RX happens
+ * in pim_mroute_msg()
+ */
 static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim_options)
 {
   int fd;
   int join = 0;
   struct in_addr group;
 
-  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */);
+  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1);
+
   if (fd < 0)
     return -1;
 
   if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
     if (inet_aton(PIM_ALL_ROUTERS, &group)) {
       if (!pim_socket_join(fd, group, ifaddr, ifindex))
-       ++join;
+       ++join;
     }
     else {
       zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
-               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
-               PIM_ALL_ROUTERS, errno, safe_strerror(errno));
+               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+               PIM_ALL_ROUTERS, errno, safe_strerror(errno));
     }
   }
 
@@ -76,8 +78,8 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim
   }
   else {
     zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
-             __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
-             PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
+             __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+             PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
   }
 
   if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
@@ -87,13 +89,13 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim
   }
   else {
       zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
-               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
-               PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
+               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+               PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
   }    
 
   if (!join) {
     zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
-            fd, inet_ntoa(ifaddr));
+            fd, inet_ntoa(ifaddr));
     close(fd);
     fd = -1;
   }
@@ -154,22 +156,20 @@ static int pim_igmp_other_querier_expire(struct thread *t)
 {
   struct igmp_sock *igmp;
 
-  zassert(t);
   igmp = THREAD_ARG(t);
-  zassert(igmp);
 
   zassert(igmp->t_other_querier_timer);
   zassert(!igmp->t_igmp_query_timer);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("%s: Querier %s resuming",
               __PRETTY_FUNCTION__,
               ifaddr_str);
   }
 
-  igmp->t_other_querier_timer = 0;
+  igmp->t_other_querier_timer = NULL;
 
   /*
     We are the current querier, then
@@ -198,7 +198,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
     */
 
     if (PIM_DEBUG_IGMP_TRACE) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
                 ifaddr_str);
@@ -210,7 +210,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
   else {
     /*
       We are the current querier, then stop sending general queries:
-      igmp->t_igmp_query_timer = 0;
+      igmp->t_igmp_query_timer = NULL;
     */
     pim_igmp_general_query_off(igmp);
   }
@@ -241,7 +241,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
                       pim_ifp->igmp_query_max_response_time_dsec);
   
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
               ifaddr_str,
@@ -260,7 +260,7 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
 
   if (PIM_DEBUG_IGMP_TRACE) {
     if (igmp->t_other_querier_timer) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
                 ifaddr_str, igmp->fd, igmp->interface->name);
@@ -270,31 +270,27 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
   zassert(!igmp->t_other_querier_timer);
 }
 
-static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
-                          int max_resp_code,
-                          struct in_addr from, const char *from_str,
-                          char *igmp_msg, int igmp_msg_len)
+static int
+igmp_recv_query(struct igmp_sock *igmp, int query_version,
+                int max_resp_code,
+                struct in_addr from, const char *from_str,
+                char *igmp_msg, int igmp_msg_len)
 {
   struct interface     *ifp;
   struct pim_interface *pim_ifp;
-  uint8_t               resv_s_qrv = 0;
-  uint8_t               s_flag = 0;
-  uint8_t               qrv = 0;
   struct in_addr        group_addr;
   uint16_t              recv_checksum;
   uint16_t              checksum;
-  int                   i;
 
-  //group_addr = *(struct in_addr *)(igmp_msg + 4);
   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
 
   ifp = igmp->interface;
   pim_ifp = ifp->info;
 
-  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
 
   /* for computing checksum */
-  *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+  *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
 
   checksum = in_cksum(igmp_msg, igmp_msg_len);
   if (checksum != recv_checksum) {
@@ -303,12 +299,33 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
     return -1;
   }
 
+  /* RFC 3376 defines some guidelines on operating in backwards compatibility
+   * with older versions of IGMP but there are some gaps in the logic:
+   *
+   * - once we drop from say version 3 to version 2 we will never go back to
+   *   version 3 even if the node that TXed an IGMP v2 query upgrades to v3
+   *
+   * - The node with the lowest IP is the querier so we will only know to drop
+   *   from v3 to v2 if the node that is the querier is also the one that is
+   *   running igmp v2.  If a non-querier only supports igmp v2 we will have
+   *   no way of knowing.
+   *
+   * For now we will simplify things and inform the user that they need to
+   * configure all PIM routers to use the same version of IGMP.
+   */
+  if (query_version != pim_ifp->igmp_version) {
+    zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
+              "configure all PIM routers on this subnet to use the same "
+              "IGMP version",
+              query_version, from_str, ifp->name, pim_ifp->igmp_version);
+    return 0;
+  }
+
   if (PIM_DEBUG_IGMP_PACKETS) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-    zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
-              query_version, from_str, ifp->name,
-              igmp_msg_len, checksum, group_str);
+    zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
+              query_version, from_str, ifp->name, group_str);
   }
 
   /*
@@ -320,9 +337,9 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
     elected querier.
    */
   if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
-    
+
     if (PIM_DEBUG_IGMP_TRACE) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
                 ifp->name,
@@ -333,272 +350,11 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
     pim_igmp_other_querier_timer_on(igmp);
   }
 
+  /* IGMP version 3 is the only one where we process the RXed query */
   if (query_version == 3) {
-    /*
-      RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
-
-      Routers adopt the QRV value from the most recently received Query
-      as their own [Robustness Variable] value, unless that most
-      recently received QRV was zero, in which case the receivers use
-      the default [Robustness Variable] value specified in section 8.1
-      or a statically configured value.
-    */
-    resv_s_qrv = igmp_msg[8];
-    qrv = 7 & resv_s_qrv;
-    igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
-  }
-
-  /*
-    RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
-
-    Multicast routers that are not the current querier adopt the QQI
-    value from the most recently received Query as their own [Query
-    Interval] value, unless that most recently received QQI was zero,
-    in which case the receiving routers use the default.
-  */
-  if (igmp->t_other_querier_timer && query_version == 3) {
-    /* other querier present */
-    uint8_t  qqic;
-    uint16_t qqi;
-    qqic = igmp_msg[9];
-    qqi = igmp_msg_decode8to16(qqic);
-    igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
-
-    if (PIM_DEBUG_IGMP_TRACE) {
-      char ifaddr_str[100];
-      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
-      zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
-                ifaddr_str,
-                qqi ? "recv-non-default" : "default",
-                igmp->querier_query_interval,
-                qqic,
-                from_str);
-    }
-  }
-
-  /*
-    RFC 3376: 6.6.1. Timer Updates
-
-    When a router sends or receives a query with a clear Suppress
-    Router-Side Processing flag, it must update its timers to reflect
-    the correct timeout values for the group or sources being queried.
-
-    General queries don't trigger timer update.
-  */
-  if (query_version == 3) {
-    s_flag = (1 << 3) & resv_s_qrv;
-  }
-  else {
-    /* Neither V1 nor V2 have this field. Pimd should really go into
-     * a compatibility mode here and run as V2 (or V1) but it doesn't
-     * so for now, lets just set the flag to suppress these timer updates.
-     */
-    s_flag = 1;
-  }
-  
-  if (!s_flag) {
-    /* s_flag is clear */
-
-    if (PIM_INADDR_IS_ANY(group_addr)) {
-      /* this is a general query */
-
-      /* log that general query should have the s_flag set */
-      zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
-               query_version, from_str, ifp->name);
-    }
-    else {
-      struct igmp_group *group;
-
-      /* this is a non-general query: perform timer updates */
-
-      group = find_group_by_addr(igmp, group_addr);
-      if (group) {
-       int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
-
-       /*
-         RFC 3376: 6.6.1. Timer Updates
-         Query Q(G,A): Source Timer for sources in A are lowered to LMQT
-         Query Q(G): Group Timer is lowered to LMQT
-       */
-       if (recv_num_sources < 1) {
-         /* Query Q(G): Group Timer is lowered to LMQT */
-
-         igmp_group_timer_lower_to_lmqt(group);
-       }
-       else {
-         /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
-
-         /* Scan sources in query and lower their timers to LMQT */
-         struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
-         for (i = 0; i < recv_num_sources; ++i) {
-           //struct in_addr src_addr = sources[i];
-           //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
-           struct in_addr src_addr;
-           struct igmp_source *src;
-            memcpy(&src_addr, sources + i, sizeof(struct in_addr));
-           src = igmp_find_source_by_addr(group, src_addr);
-           if (src) {
-             igmp_source_timer_lower_to_lmqt(src);
-           }
-         }
-       }
-
-      }
-      else {
-       char group_str[100];
-       pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-       zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
-                 query_version, from_str, ifp->name, group_str);
-      }
-    }
-  } /* s_flag is clear: timer updates */
-  
-  return 0;
-}
-
-static int igmp_v3_report(struct igmp_sock *igmp,
-                         struct in_addr from, const char *from_str,
-                         char *igmp_msg, int igmp_msg_len)
-{
-  uint16_t          recv_checksum;
-  uint16_t          checksum;
-  int               num_groups;
-  uint8_t          *group_record;
-  uint8_t          *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
-  struct interface *ifp = igmp->interface;
-  int               i;
-  int               local_ncb = 0;
-
-  if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
-    zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
-    return -1;
-  }
-
-  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
-
-  /* for computing checksum */
-  *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
-
-  checksum = in_cksum(igmp_msg, igmp_msg_len);
-  if (checksum != recv_checksum) {
-    zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
-             from_str, ifp->name, recv_checksum, checksum);
-    return -1;
-  }
-
-  num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
-  if (num_groups < 1) {
-    zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
-             from_str, ifp->name);
-    return -1;
+    igmp_v3_recv_query(igmp, from_str, igmp_msg);
   }
 
-  if (PIM_DEBUG_IGMP_PACKETS) {
-    zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
-              from_str, ifp->name, igmp_msg_len, checksum, num_groups);
-  }
-
-  group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
-
-  /* Scan groups */
-  for (i = 0; i < num_groups; ++i) {
-    struct in_addr  rec_group;
-    uint8_t        *sources;
-    uint8_t        *src;
-    int             rec_type;
-    int             rec_auxdatalen;
-    int             rec_num_sources;
-    int             j;
-    struct prefix   lncb;
-    struct prefix   g;
-
-    if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
-      zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
-               from_str, ifp->name);
-      return -1;
-    }
-
-    rec_type        = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
-    rec_auxdatalen  = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
-    rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
-
-    //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
-    memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
-
-    if (PIM_DEBUG_IGMP_PACKETS) {
-      zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
-                from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
-    }
-
-    /* Scan sources */
-    
-    sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
-
-    for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
-
-      if ((src + 4) > report_pastend) {
-       zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
-                 from_str, ifp->name);
-       return -1;
-      }
-
-      if (PIM_DEBUG_IGMP_PACKETS) {
-       char src_str[200];
-
-       if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
-         sprintf(src_str, "<source?>");
-       
-       zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
-                  from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
-      }
-    } /* for (sources) */
-
-
-    lncb.family = AF_INET;
-    lncb.u.prefix4.s_addr = 0x000000E0;
-    lncb.prefixlen = 24;
-
-    g.family = AF_INET;
-    g.u.prefix4 = rec_group;
-    g.prefixlen = 32;
-    /*
-     * If we receive a igmp report with the group in 224.0.0.0/24
-     * then we should ignore it
-     */
-    if (prefix_match(&lncb, &g))
-      local_ncb = 1;
-
-    if (!local_ncb)
-      switch (rec_type) {
-      case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
-       igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
-       igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
-       igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
-       igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
-       igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
-       igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      default:
-       zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
-                 from_str, ifp->name, rec_type);
-      }
-
-    group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
-    local_ncb = 0;
-
-  } /* for (group records) */
-
   return 0;
 }
 
@@ -606,73 +362,17 @@ static void on_trace(const char *label,
                     struct interface *ifp, struct in_addr from)
 {
   if (PIM_DEBUG_IGMP_TRACE) {
-    char from_str[100];
+    char from_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
     zlog_debug("%s: from %s on %s",
               label, from_str, ifp->name);
   }
 }
 
-static int igmp_v2_report(struct igmp_sock *igmp,
-                         struct in_addr from, const char *from_str,
-                         char *igmp_msg, int igmp_msg_len)
-{
-  struct interface *ifp = igmp->interface;
-  struct igmp_group *group;
-  struct in_addr group_addr;
-
-  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
-  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
-    zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
-    return -1;
-  }
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    zlog_warn("%s %s: FIXME WRITEME",
-             __FILE__, __PRETTY_FUNCTION__);
-  }
-
-  //group_addr = *(struct in_addr *)(igmp_msg + 4);
-  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
-
-  /* non-existant group is created as INCLUDE {empty} */
-  group = igmp_add_group_by_addr(igmp, group_addr);
-  if (!group) {
-    return -1;
-  }
-
-  group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
-
-  return 0;
-}
-
-static int igmp_v2_leave(struct igmp_sock *igmp,
-                        struct in_addr from, const char *from_str,
-                        char *igmp_msg, int igmp_msg_len)
-{
-  struct interface *ifp = igmp->interface;
-
-  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
-  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
-    zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
-    return -1;
-  }
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    zlog_warn("%s %s: FIXME WRITEME",
-             __FILE__, __PRETTY_FUNCTION__);
-  }
-
-  return 0;
-}
-
-static int igmp_v1_report(struct igmp_sock *igmp,
-                         struct in_addr from, const char *from_str,
-                         char *igmp_msg, int igmp_msg_len)
+static int
+igmp_v1_recv_report (struct igmp_sock *igmp,
+                     struct in_addr from, const char *from_str,
+                     char *igmp_msg, int igmp_msg_len)
 {
   struct interface *ifp = igmp->interface;
   struct igmp_group *group;
@@ -691,7 +391,6 @@ static int igmp_v1_report(struct igmp_sock *igmp,
              __FILE__, __PRETTY_FUNCTION__);
   }
 
-  //group_addr = *(struct in_addr *)(igmp_msg + 4);
   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
 
   /* non-existant group is created as INCLUDE {empty} */
@@ -712,8 +411,8 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
   char *igmp_msg;
   int igmp_msg_len;
   int msg_type;
-  char from_str[100];
-  char to_str[100];
+  char from_str[INET_ADDRSTRLEN];
+  char to_str[INET_ADDRSTRLEN];
     
   if (len < sizeof(*ip_hdr)) {
     zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
@@ -790,26 +489,26 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
        return -1;
       }
 
-      return recv_igmp_query(igmp, query_version, max_resp_code,
+      return igmp_recv_query(igmp, query_version, max_resp_code,
                             ip_hdr->ip_src, from_str,
                             igmp_msg, igmp_msg_len);
     }
 
   case PIM_IGMP_V3_MEMBERSHIP_REPORT:
-    return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
-                         igmp_msg, igmp_msg_len);
+    return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
+                               igmp_msg, igmp_msg_len);
 
   case PIM_IGMP_V2_MEMBERSHIP_REPORT:
-    return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
-                         igmp_msg, igmp_msg_len);
+    return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
+                               igmp_msg, igmp_msg_len);
 
   case PIM_IGMP_V1_MEMBERSHIP_REPORT:
-    return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
-                         igmp_msg, igmp_msg_len);
+    return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
+                               igmp_msg, igmp_msg_len);
 
   case PIM_IGMP_V2_LEAVE_GROUP:
-    return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
-                        igmp_msg, igmp_msg_len);
+    return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
+                              igmp_msg, igmp_msg_len);
   }
 
   zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
@@ -825,9 +524,6 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
   int startup_mode;
   int query_interval;
 
-  zassert(igmp);
-  zassert(igmp->interface);
-
   /*
     Since this socket is starting as querier,
     there should not exist a timer for other-querier-present.
@@ -841,20 +537,31 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
 
     The Startup Query Interval is the interval between General Queries
     sent by a Querier on startup.  Default: 1/4 the Query Interval.
+    The first one should be sent out immediately instead of 125/4
+    seconds from now.
   */
   startup_mode = igmp->startup_query_count > 0;
   if (startup_mode) {
-    --igmp->startup_query_count;
+    /*
+     * If this is the first time we are sending a query on a
+     * newly configured igmp interface send it out in 1 second
+     * just to give the entire world a tiny bit of time to settle
+     * else the query interval is:
+     * query_interval = pim_ifp->igmp_default_query_interval >> 2;
+     */
+    if (igmp->startup_query_count == igmp->querier_robustness_variable)
+      query_interval = 1;
+    else
+      query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
 
-    /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
-    query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+    --igmp->startup_query_count;
   }
   else {
     query_interval = igmp->querier_query_interval;
   }
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
               ifaddr_str,
@@ -862,8 +569,7 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
               startup_mode ? "startup" : "non-startup",
               igmp->fd);
   }
-  igmp->t_igmp_query_timer = 0;
-  zassert(!igmp->t_igmp_query_timer);
+  igmp->t_igmp_query_timer = NULL;
   THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
                  pim_igmp_general_query,
                  igmp, query_interval);
@@ -875,35 +581,39 @@ void pim_igmp_general_query_off(struct igmp_sock *igmp)
 
   if (PIM_DEBUG_IGMP_TRACE) {
     if (igmp->t_igmp_query_timer) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
                 ifaddr_str, igmp->fd, igmp->interface->name);
     }
   }
   THREAD_OFF(igmp->t_igmp_query_timer);
-  zassert(!igmp->t_igmp_query_timer);
 }
 
 /* Issue IGMP general query */
 static int pim_igmp_general_query(struct thread *t)
 {
-  char   query_buf[PIM_IGMP_BUFSIZE_WRITE];
   struct igmp_sock *igmp;
   struct in_addr dst_addr;
   struct in_addr group_addr;
   struct pim_interface *pim_ifp;
-
-  zassert(t);
+  int    query_buf_size;
 
   igmp = THREAD_ARG(t);
 
-  zassert(igmp);
   zassert(igmp->interface);
   zassert(igmp->interface->info);
 
   pim_ifp = igmp->interface->info;
 
+  if (pim_ifp->igmp_version == 3) {
+    query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+  } else {
+    query_buf_size = IGMP_V12_MSG_SIZE;
+  }
+
+  char query_buf[query_buf_size];
+
   /*
     RFC3376: 4.1.12. IP Destination Addresses for Queries
 
@@ -917,8 +627,8 @@ static int pim_igmp_general_query(struct thread *t)
   group_addr.s_addr = PIM_NET_INADDR_ANY;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char querier_str[100];
-    char dst_str[100];
+    char querier_str[INET_ADDRSTRLEN];
+    char dst_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
                   sizeof(querier_str));
     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
@@ -926,124 +636,25 @@ static int pim_igmp_general_query(struct thread *t)
               querier_str, dst_str, igmp->interface->name);
   }
 
-  pim_igmp_send_membership_query(0 /* igmp_group */,
-                                igmp->fd,
-                                igmp->interface->name,
-                                query_buf,
-                                sizeof(query_buf),
-                                0 /* num_sources */,
-                                dst_addr,
-                                group_addr,
-                                pim_ifp->igmp_query_max_response_time_dsec,
-                                1 /* s_flag: always set for general queries */,
-                                igmp->querier_robustness_variable,
-                                igmp->querier_query_interval);
+  igmp_send_query (pim_ifp->igmp_version,
+                   0 /* igmp_group */,
+                   igmp->fd,
+                   igmp->interface->name,
+                   query_buf,
+                   sizeof(query_buf),
+                   0 /* num_sources */,
+                   dst_addr,
+                   group_addr,
+                   pim_ifp->igmp_query_max_response_time_dsec,
+                   1 /* s_flag: always set for general queries */,
+                   igmp->querier_robustness_variable,
+                   igmp->querier_query_interval);
 
   pim_igmp_general_query_on(igmp);
 
   return 0;
 }
 
-static int pim_igmp_read(struct thread *t);
-
-static void igmp_read_on(struct igmp_sock *igmp)
-{
-  zassert(igmp);
-
-  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
-    zlog_debug("Scheduling READ event on IGMP socket fd=%d",
-              igmp->fd);
-  }
-  igmp->t_igmp_read = 0;
-  zassert(!igmp->t_igmp_read);
-  THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
-}
-
-static int pim_igmp_read(struct thread *t)
-{
-  struct igmp_sock *igmp;
-  int fd;
-  struct sockaddr_in from;
-  struct sockaddr_in to;
-  socklen_t fromlen = sizeof(from);
-  socklen_t tolen = sizeof(to);
-  uint8_t buf[PIM_IGMP_BUFSIZE_READ];
-  int len;
-  ifindex_t ifindex = -1;
-  int result = -1; /* defaults to bad */
-
-  zassert(t);
-
-  igmp = THREAD_ARG(t);
-
-  zassert(igmp);
-
-  fd = THREAD_FD(t);
-
-  zassert(fd == igmp->fd);
-
-  len = pim_socket_recvfromto(fd, buf, sizeof(buf),
-                             &from, &fromlen,
-                             &to, &tolen,
-                             &ifindex);
-  if (len < 0) {
-    zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
-             fd, errno, safe_strerror(errno));
-    goto done;
-  }
-
-  if (PIM_DEBUG_IGMP_PACKETS) {
-    char from_str[100];
-    char to_str[100];
-    
-    if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
-      sprintf(from_str, "<from?>");
-    if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
-      sprintf(to_str, "<to?>");
-    
-    zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
-              len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
-  }
-
-#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
-  /* ifindex sanity check */
-  if (ifindex != igmp->interface->ifindex) {
-    char from_str[100];
-    char to_str[100];
-    struct interface *ifp;
-
-    if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
-      sprintf(from_str, "<from?>");
-    if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
-      sprintf(to_str, "<to?>");
-
-    ifp = if_lookup_by_index(ifindex);
-    if (ifp) {
-      zassert(ifindex == ifp->ifindex);
-    }
-
-#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
-    zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
-             from_str, to_str, fd,
-             ifindex, ifp ? ifp->name : "<if-notfound>",
-             igmp->interface->ifindex, igmp->interface->name);
-#endif
-    goto done;
-  }
-#endif
-
-  if (pim_igmp_packet(igmp, (char *)buf, len)) {
-    goto done;
-  }
-
-  result = 0; /* good */
-
- done:
-  igmp_read_on(igmp);
-
-  return result;
-}
-
 static void sock_close(struct igmp_sock *igmp)
 {
   pim_igmp_other_querier_timer_off(igmp);
@@ -1057,7 +668,6 @@ static void sock_close(struct igmp_sock *igmp)
     }
   }
   THREAD_OFF(igmp->t_igmp_read);
-  zassert(!igmp->t_igmp_read);
   
   if (close(igmp->fd)) {
     zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
@@ -1106,7 +716,7 @@ static void igmp_group_delete(struct igmp_group *group)
   struct igmp_source *src;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Deleting IGMP group %s from socket %d interface %s",
               group_str,
@@ -1168,6 +778,22 @@ void igmp_sock_delete(struct igmp_sock *igmp)
   igmp_sock_free(igmp);
 }
 
+void
+igmp_sock_delete_all (struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *igmp_node, *igmp_nextnode;
+  struct igmp_sock *igmp;
+
+  pim_ifp = ifp->info;
+
+  for (ALL_LIST_ELEMENTS (pim_ifp->igmp_socket_list, igmp_node,
+                         igmp_nextnode, igmp))
+    {
+      igmp_sock_delete(igmp);
+    }
+}
+
 static struct igmp_sock *igmp_sock_new(int fd,
                                       struct in_addr ifaddr,
                                       struct interface *ifp)
@@ -1182,9 +808,9 @@ static struct igmp_sock *igmp_sock_new(int fd,
               fd, inet_ntoa(ifaddr), ifp->name);
   }
 
-  igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
+  igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
   if (!igmp) {
-    zlog_warn("%s %s: XMALLOC() failure",
+    zlog_warn("%s %s: XCALLOC() failure",
               __FILE__, __PRETTY_FUNCTION__);
     return 0;
   }
@@ -1200,9 +826,9 @@ static struct igmp_sock *igmp_sock_new(int fd,
   igmp->fd                          = fd;
   igmp->interface                   = ifp;
   igmp->ifaddr                      = ifaddr;
-  igmp->t_igmp_read                 = 0;
-  igmp->t_igmp_query_timer          = 0;
-  igmp->t_other_querier_timer       = 0; /* no other querier present */
+  igmp->t_igmp_read                 = NULL;
+  igmp->t_igmp_query_timer          = NULL;
+  igmp->t_other_querier_timer       = NULL; /* no other querier present */
   igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
   igmp->sock_creation               = pim_time_monotonic_sec();
 
@@ -1212,13 +838,63 @@ static struct igmp_sock *igmp_sock_new(int fd,
     igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
   */
   igmp_startup_mode_on(igmp);
-
-  igmp_read_on(igmp);
   pim_igmp_general_query_on(igmp);
 
   return igmp;
 }
 
+static void igmp_read_on (struct igmp_sock *igmp);
+
+static int
+pim_igmp_read (struct thread *t)
+{
+  uint8_t buf[10000];
+  struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t);
+  struct sockaddr_in from;
+  struct sockaddr_in to;
+  socklen_t fromlen = sizeof(from);
+  socklen_t tolen = sizeof(to);
+  ifindex_t ifindex = -1;
+  int cont = 1;
+  int len;
+
+  while (cont)
+    {
+      len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf),
+                                 &from, &fromlen,
+                                 &to, &tolen,
+                                 &ifindex);
+      if (len < 0)
+       {
+         if (errno == EINTR)
+           continue;
+         if (errno == EWOULDBLOCK || errno == EAGAIN)
+         {
+           cont = 0;
+           break;
+         }
+         goto done;
+       }
+    }
+
+ done:
+  igmp_read_on(igmp);
+  return 0;
+}
+
+static void
+igmp_read_on (struct igmp_sock *igmp)
+{
+
+  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
+    zlog_debug("Scheduling READ event on IGMP socket fd=%d",
+              igmp->fd);
+  }
+  igmp->t_igmp_read = NULL;
+  THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
+
+}
+
 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
                                    struct in_addr ifaddr,
                                    struct interface *ifp)
@@ -1244,6 +920,8 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
     return 0;
   }
 
+  igmp_read_on (igmp);
+
   listnode_add(igmp_sock_list, igmp);
 
 #ifdef IGMP_SOCK_DUMP
@@ -1271,12 +949,10 @@ static int igmp_group_timer(struct thread *t)
 {
   struct igmp_group *group;
 
-  zassert(t);
   group = THREAD_ARG(t);
-  zassert(group);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("%s: Timer for group %s on interface %s",
               __PRETTY_FUNCTION__,
@@ -1315,7 +991,7 @@ static void group_timer_off(struct igmp_group *group)
     return;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Cancelling TIMER event for group %s on %s",
               group_str, group->group_igmp_sock->interface->name);
@@ -1331,7 +1007,7 @@ void igmp_group_timer_on(struct igmp_group *group,
   group_timer_off(group);
 
   if (PIM_DEBUG_IGMP_EVENTS) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
               interval_msec / 1000,
@@ -1377,6 +1053,19 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
     return group;
   }
 
+  if (!pim_is_group_224_4 (group_addr))
+    {
+      zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
+               __PRETTY_FUNCTION__);
+      return NULL;
+    }
+
+  if (pim_is_group_224_0_0_0_24 (group_addr))
+    {
+      zlog_warn("%s: Group specified is part of 224.0.0.0/24",
+               __PRETTY_FUNCTION__);
+      return NULL;
+    }
   /*
     Non-existant group is created as INCLUDE {empty}:
 
@@ -1390,11 +1079,11 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
     of INCLUDE and an empty source list.
   */
 
-  group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
+  group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
   if (!group) {
-    zlog_warn("%s %s: XMALLOC() failure",
+    zlog_warn("%s %s: XCALLOC() failure",
              __FILE__, __PRETTY_FUNCTION__);
-    return 0; /* error, not found, could not create */
+    return NULL; /* error, not found, could not create */
   }
 
   group->group_source_list = list_new();
@@ -1402,7 +1091,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
     zlog_warn("%s %s: list_new() failure",
              __FILE__, __PRETTY_FUNCTION__);
     XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
-    return 0; /* error, not found, could not initialize */
+    return NULL; /* error, not found, could not initialize */
   }
   group->group_source_list->del = (void (*)(void *)) igmp_source_free;
 
@@ -1414,6 +1103,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
   group->last_igmp_v1_report_dsec              = -1;
   group->last_igmp_v2_report_dsec              = -1;
   group->group_creation                        = pim_time_monotonic_sec();
+  group->igmp_version                          = IGMP_DEFAULT_VERSION;
 
   /* initialize new group as INCLUDE {empty} */
   group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
@@ -1421,7 +1111,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
   listnode_add(igmp->igmp_group_list, group);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Creating new IGMP group %s on socket %d interface %s",
               group_str, igmp->fd, igmp->interface->name);
@@ -1442,3 +1132,32 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
 
   return group;
 }
+
+void
+igmp_send_query (int igmp_version,
+                 struct igmp_group *group,
+                 int fd,
+                 const char *ifname,
+                 char *query_buf,
+                 int query_buf_size,
+                 int num_sources,
+                 struct in_addr dst_addr,
+                 struct in_addr group_addr,
+                 int query_max_response_time_dsec,
+                 uint8_t s_flag,
+                 uint8_t querier_robustness_variable,
+                 uint16_t querier_query_interval)
+{
+  if (igmp_version == 3) {
+    igmp_v3_send_query (group, fd, ifname, query_buf,
+                        query_buf_size, num_sources,
+                        dst_addr, group_addr,
+                        query_max_response_time_dsec, s_flag,
+                        querier_robustness_variable,
+                        querier_query_interval);
+  } else if (igmp_version == 2) {
+    igmp_v2_send_query (group, fd, ifname, query_buf,
+                        dst_addr, group_addr,
+                        query_max_response_time_dsec);
+  }
+}
index eb0377ce8c8258fdadf62e02e2c3be0943c1556f..802f1ba471866c4281137b2e220e18b469c216be 100644 (file)
@@ -51,6 +51,7 @@
 #define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2)
 #define IGMP_V3_GROUP_RECORD_GROUP_OFFSET      (4)
 #define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET     (8)
+#define IGMP_CHECKSUM_OFFSET                   (2)
 
 /* RFC 3376: 8.1. Robustness Variable - Default: 2 */
 #define IGMP_DEFAULT_ROBUSTNESS_VARIABLE           (2)
@@ -64,6 +65,8 @@
 /* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */
 #define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10)
 
+#define IGMP_DEFAULT_VERSION (3)
+
 struct igmp_join {
   struct in_addr group_addr;
   struct in_addr source_addr;
@@ -97,7 +100,7 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
                                    struct interface *ifp);
 void igmp_sock_delete(struct igmp_sock *igmp);
 void igmp_sock_free(struct igmp_sock *igmp);
-
+void igmp_sock_delete_all (struct interface *ifp);
 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len);
 
 void pim_igmp_general_query_on(struct igmp_sock *igmp);
@@ -151,6 +154,9 @@ struct igmp_group {
      since sources have their counters) */
   int               group_specific_query_retransmit_count;
 
+  /* compatibility mode - igmp v1, v2 or v3 */
+  int               igmp_version;
+
   struct in_addr    group_addr;
   int               group_filtermode_isexcl;  /* 0=INCLUDE, 1=EXCLUDE */
   struct list      *group_source_list;        /* list of struct igmp_source */
@@ -175,4 +181,18 @@ void igmp_group_timer_on(struct igmp_group *group,
 struct igmp_source *
 source_new (struct igmp_group *group,
            struct in_addr src_addr);
+
+void igmp_send_query(int igmp_version,
+                     struct igmp_group *group,
+                     int fd,
+                     const char *ifname,
+                     char *query_buf,
+                     int query_buf_size,
+                     int num_sources,
+                     struct in_addr dst_addr,
+                     struct in_addr group_addr,
+                     int query_max_response_time_dsec,
+                     uint8_t s_flag,
+                     uint8_t querier_robustness_variable,
+                     uint16_t querier_query_interval);
 #endif /* PIM_IGMP_H */
index ba80db0696c801d3ac137faa3071fe78b56217ad..d9542732b815c754d9a0cc81aed6a2a16f0032c5 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_IGMP_JOIN_H
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
new file mode 100644 (file)
index 0000000..975ff9f
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * PIM for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Daniel Walton
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "pimd.h"
+#include "pim_igmp.h"
+#include "pim_igmpv2.h"
+#include "pim_igmpv3.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_util.h"
+
+
+static void
+on_trace (const char *label,
+          struct interface *ifp, struct in_addr from)
+{
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char from_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
+    zlog_debug("%s: from %s on %s",
+               label, from_str, ifp->name);
+  }
+}
+
+void
+igmp_v2_send_query (struct igmp_group *group,
+                    int fd,
+                    const char *ifname,
+                    char *query_buf,
+                    struct in_addr dst_addr,
+                    struct in_addr group_addr,
+                    int query_max_response_time_dsec)
+{
+  ssize_t             msg_size = 8;
+  uint8_t             max_resp_code;
+  ssize_t             sent;
+  struct sockaddr_in  to;
+  socklen_t           tolen;
+  uint16_t            checksum;
+
+  /* max_resp_code must be non-zero else this will look like an IGMP v1 query */
+  max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
+  zassert(max_resp_code > 0);
+
+  query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
+  query_buf[1] = max_resp_code;
+  *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET)   = 0; /* for computing checksum */
+  memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
+
+  checksum = in_cksum(query_buf, msg_size);
+  *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    char dst_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+    zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
+              dst_str, ifname, group_str);
+  }
+
+  memset(&to, 0, sizeof(to));
+  to.sin_family = AF_INET;
+  to.sin_addr = dst_addr;
+  tolen = sizeof(to);
+
+  sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
+                (struct sockaddr *)&to, tolen);
+  if (sent != (ssize_t) msg_size) {
+    char dst_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+    if (sent < 0) {
+      zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+               dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
+    }
+    else {
+      zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+               dst_str, ifname, group_str, msg_size, sent);
+    }
+    return;
+  }
+}
+
+int
+igmp_v2_recv_report (struct igmp_sock *igmp,
+                     struct in_addr from, const char *from_str,
+                     char *igmp_msg, int igmp_msg_len)
+{
+  struct interface *ifp = igmp->interface;
+  struct in_addr group_addr;
+  char group_str[INET_ADDRSTRLEN];
+
+  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+    zlog_warn("Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
+              from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+    return -1;
+  }
+
+  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
+    zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s",
+               from_str, ifp->name, group_str);
+  }
+
+  /*
+   * RFC 3376
+   * 7.3.2. In the Presence of Older Version Group Members
+   *
+   * When Group Compatibility Mode is IGMPv2, a router internally
+   * translates the following IGMPv2 messages for that group to their
+   * IGMPv3 equivalents:
+   *
+   * IGMPv2 Message                IGMPv3 Equivalent
+   * --------------                -----------------
+   * Report                        IS_EX( {} )
+   * Leave                         TO_IN( {} )
+   */
+  igmpv3_report_isex (igmp, from, group_addr, 0, NULL, 1);
+
+  return 0;
+}
+
+int
+igmp_v2_recv_leave (struct igmp_sock *igmp,
+                    struct in_addr from, const char *from_str,
+                    char *igmp_msg, int igmp_msg_len)
+{
+  struct interface *ifp = igmp->interface;
+  struct in_addr group_addr;
+  char group_str[INET_ADDRSTRLEN];
+
+  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+    zlog_warn("Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
+              from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+    return -1;
+  }
+
+  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
+    zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s",
+               from_str, ifp->name, group_str);
+  }
+
+  /*
+   * RFC 3376
+   * 7.3.2. In the Presence of Older Version Group Members
+   *
+   * When Group Compatibility Mode is IGMPv2, a router internally
+   * translates the following IGMPv2 messages for that group to their
+   * IGMPv3 equivalents:
+   *
+   * IGMPv2 Message                IGMPv3 Equivalent
+   * --------------                -----------------
+   * Report                        IS_EX( {} )
+   * Leave                         TO_IN( {} )
+   */
+  igmpv3_report_toin (igmp, from, group_addr, 0, NULL);
+
+  return 0;
+}
diff --git a/pimd/pim_igmpv2.h b/pimd/pim_igmpv2.h
new file mode 100644 (file)
index 0000000..10a2477
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * PIM for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Daniel Walton
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef PIM_IGMPV2_H
+#define PIM_IGMPV2_H
+
+void igmp_v2_send_query (struct igmp_group *group,
+                         int fd,
+                         const char *ifname,
+                         char *query_buf,
+                         struct in_addr dst_addr,
+                         struct in_addr group_addr,
+                         int query_max_response_time_dsec);
+
+int igmp_v2_recv_report (struct igmp_sock *igmp,
+                         struct in_addr from, const char *from_str,
+                         char *igmp_msg, int igmp_msg_len);
+
+int igmp_v2_recv_leave (struct igmp_sock *igmp,
+                        struct in_addr from, const char *from_str,
+                        char *igmp_msg, int igmp_msg_len);
+
+#endif /* PIM_IGMPV2_H */
index bdaf2bb27010af0c3bfc396b559203d4e79867ad..4de3b65ec5f79d2ddba83d0a8f9521102a6e50b5 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -46,8 +50,8 @@ static void on_trace(const char *label,
                     int num_sources, struct in_addr *sources)
 {
   if (PIM_DEBUG_IGMP_TRACE) {
-    char from_str[100];
-    char group_str[100];
+    char from_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN];
 
     pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
@@ -57,50 +61,6 @@ static void on_trace(const char *label,
   }
 }
 
-int igmp_group_compat_mode(const struct igmp_sock *igmp,
-                          const struct igmp_group *group)
-{
-  struct pim_interface *pim_ifp;
-  int64_t               now_dsec;
-  long                  older_host_present_interval_dsec;
-
-  zassert(igmp);
-  zassert(igmp->interface);
-  zassert(igmp->interface->info);
-
-  pim_ifp = igmp->interface->info;
-
-  /*
-    RFC 3376: 8.13. Older Host Present Interval
-
-    This value MUST be ((the Robustness Variable) times (the Query
-    Interval)) plus (one Query Response Interval).
-
-    older_host_present_interval_dsec = \
-      igmp->querier_robustness_variable * \
-      10 * igmp->querier_query_interval + \
-      pim_ifp->query_max_response_time_dsec;
-  */
-  older_host_present_interval_dsec =
-    PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
-                      igmp->querier_query_interval,
-                      pim_ifp->igmp_query_max_response_time_dsec);
-
-  now_dsec = pim_time_monotonic_dsec();
-  if (now_dsec < 1) {
-    /* broken timer logged by pim_time_monotonic_dsec() */
-    return 3;
-  }
-
-  if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
-    return 1; /* IGMPv1 */
-
-  if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
-    return 2; /* IGMPv2 */
-
-  return 3; /* IGMPv3 */
-}
-
 void igmp_group_reset_gmi(struct igmp_group *group)
 {
   long group_membership_interval_msec;
@@ -132,7 +92,7 @@ void igmp_group_reset_gmi(struct igmp_group *group)
                      pim_ifp->igmp_query_max_response_time_dsec);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
               group_str,
@@ -158,15 +118,13 @@ static int igmp_source_timer(struct thread *t)
   struct igmp_source *source;
   struct igmp_group *group;
 
-  zassert(t);
   source = THREAD_ARG(t);
-  zassert(source);
 
   group = source->source_group;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
     zlog_debug("%s: Source timer expired for group %s source %s on %s",
@@ -176,7 +134,7 @@ static int igmp_source_timer(struct thread *t)
   }
 
   zassert(source->t_source_timer);
-  source->t_source_timer = 0;
+  source->t_source_timer = NULL;
 
   /*
     RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
@@ -230,8 +188,8 @@ static void source_timer_off(struct igmp_group *group,
     return;
   
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
     zlog_debug("Cancelling TIMER event for group %s source %s on %s",
@@ -250,8 +208,8 @@ static void igmp_source_timer_on(struct igmp_group *group,
   source_timer_off(group, source);
 
   if (PIM_DEBUG_IGMP_EVENTS) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
     zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
@@ -291,8 +249,8 @@ void igmp_source_reset_gmi(struct igmp_sock *igmp,
                      pim_ifp->igmp_query_max_response_time_dsec);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
 
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
@@ -383,7 +341,7 @@ static void source_channel_oil_detach(struct igmp_source *source)
 {
   if (source->source_channel_oil) {
     pim_channel_oil_del(source->source_channel_oil);
-    source->source_channel_oil = 0;
+    source->source_channel_oil = NULL;
   }
 }
 
@@ -398,8 +356,8 @@ void igmp_source_delete(struct igmp_source *source)
   group = source->source_group;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
     zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
@@ -413,8 +371,8 @@ void igmp_source_delete(struct igmp_source *source)
 
   /* sanity check that forwarding has been disabled */
   if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
@@ -483,8 +441,8 @@ source_new (struct igmp_group *group,
   struct igmp_source *src;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
     zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
@@ -493,9 +451,9 @@ source_new (struct igmp_group *group,
               group->group_igmp_sock->interface->name);
   }
 
-  src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
+  src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
   if (!src) {
-    zlog_warn("%s %s: XMALLOC() failure",
+    zlog_warn("%s %s: XCALLOC() failure",
              __FILE__, __PRETTY_FUNCTION__);
     return 0; /* error, not found, could not create */
   }
@@ -646,8 +604,10 @@ static void isex_excl(struct igmp_group *group,
        struct in_addr star = { .s_addr = INADDR_ANY };
        source = igmp_find_source_by_addr (group, star);
        if (source)
-         IGMP_SOURCE_DONT_DELETE(source->source_flags);
-       igmp_source_reset_gmi (group->group_igmp_sock, group, source);
+         {
+           IGMP_SOURCE_DONT_DELETE(source->source_flags);
+           igmp_source_reset_gmi (group->group_igmp_sock, group, source);
+         }
     }
 
   /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
@@ -702,7 +662,8 @@ static void isex_incl(struct igmp_group *group,
 
 void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
                        struct in_addr group_addr,
-                       int num_sources, struct in_addr *sources)
+                       int num_sources, struct in_addr *sources,
+                        int from_igmp_v2_report)
 {
   struct interface *ifp = igmp->interface;
   struct igmp_group *group;
@@ -716,6 +677,10 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
     return;
   }
 
+  /* So we can display how we learned the group in our show command output */
+  if (from_igmp_v2_report)
+    group->igmp_version = 2;
+
   if (group->group_filtermode_isexcl) {
     /* EXCLUDE mode */
     isex_excl(group, num_sources, sources);
@@ -932,6 +897,16 @@ static void toex_excl(struct igmp_group *group,
   /* clear off SEND flag from all known sources (X,Y) */
   source_clear_send_flag(group->group_source_list);
 
+  if (num_sources == 0)
+    {
+      struct igmp_source *source;
+      struct in_addr any = { .s_addr = INADDR_ANY };
+
+      source = igmp_find_source_by_addr (group, any);
+      if (source)
+        IGMP_SOURCE_DONT_DELETE(source->source_flags);
+    }
+
   /* scan received sources (A) */
   for (i = 0; i < num_sources; ++i) {
     struct igmp_source *source;
@@ -1037,17 +1012,25 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
 */
 static void group_retransmit_group(struct igmp_group *group)
 {
-  char                  query_buf[PIM_IGMP_BUFSIZE_WRITE];
   struct igmp_sock     *igmp;
   struct pim_interface *pim_ifp;
   long                  lmqc;      /* Last Member Query Count */
   long                  lmqi_msec; /* Last Member Query Interval */
   long                  lmqt_msec; /* Last Member Query Time */
   int                   s_flag;
+  int                   query_buf_size;
 
   igmp = group->group_igmp_sock;
   pim_ifp = igmp->interface->info;
 
+  if (pim_ifp->igmp_version == 3) {
+    query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+  } else {
+    query_buf_size = IGMP_V12_MSG_SIZE;
+  }
+
+  char query_buf[query_buf_size];
+
   lmqc      = igmp->querier_robustness_variable;
   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
   lmqt_msec = lmqc * lmqi_msec;
@@ -1062,7 +1045,7 @@ static void group_retransmit_group(struct igmp_group *group)
   s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
               group_str, igmp->interface->name, s_flag,
@@ -1077,18 +1060,19 @@ static void group_retransmit_group(struct igmp_group *group)
     interest.
   */
 
-  pim_igmp_send_membership_query(group,
-                                igmp->fd,
-                                igmp->interface->name,
-                                query_buf,
-                                sizeof(query_buf),
-                                0 /* num_sources_tosend */,
-                                group->group_addr /* dst_addr */,
-                                group->group_addr /* group_addr */,
-                                pim_ifp->igmp_specific_query_max_response_time_dsec,
-                                s_flag,
-                                igmp->querier_robustness_variable,
-                                igmp->querier_query_interval);
+  igmp_send_query(pim_ifp->igmp_version,
+                  group,
+                  igmp->fd,
+                  igmp->interface->name,
+                  query_buf,
+                  sizeof(query_buf),
+                  0 /* num_sources_tosend */,
+                  group->group_addr /* dst_addr */,
+                  group->group_addr /* group_addr */,
+                  pim_ifp->igmp_specific_query_max_response_time_dsec,
+                  s_flag,
+                  igmp->querier_robustness_variable,
+                  igmp->querier_query_interval);
 }
 
 /*
@@ -1123,9 +1107,6 @@ static int group_retransmit_sources(struct igmp_group *group,
   struct igmp_source   *src;
   int                   num_retransmit_sources_left = 0;
   
-  query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
-  query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
-  
   source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
   source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
 
@@ -1163,7 +1144,7 @@ static int group_retransmit_sources(struct igmp_group *group,
   num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
               group_str, igmp->interface->name,
@@ -1183,7 +1164,7 @@ static int group_retransmit_sources(struct igmp_group *group,
 
       query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
       if (num_sources_tosend1 > query_buf1_max_sources) {
-       char group_str[100];
+       char group_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
        zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
                  __PRETTY_FUNCTION__, group_str, igmp->interface->name,
@@ -1198,19 +1179,19 @@ static int group_retransmit_sources(struct igmp_group *group,
          interest.
        */
     
-       pim_igmp_send_membership_query(group,
-                                      igmp->fd,
-                                      igmp->interface->name,
-                                      query_buf1,
-                                      sizeof(query_buf1),
-                                      num_sources_tosend1,
-                                      group->group_addr,
-                                      group->group_addr,
-                                      pim_ifp->igmp_specific_query_max_response_time_dsec,
-                                      1 /* s_flag */,
-                                      igmp->querier_robustness_variable,
-                                      igmp->querier_query_interval);
-    
+        igmp_send_query(pim_ifp->igmp_version,
+                        group,
+                        igmp->fd,
+                        igmp->interface->name,
+                        query_buf1,
+                        sizeof(query_buf1),
+                        num_sources_tosend1,
+                        group->group_addr,
+                        group->group_addr,
+                        pim_ifp->igmp_specific_query_max_response_time_dsec,
+                        1 /* s_flag */,
+                        igmp->querier_robustness_variable,
+                        igmp->querier_query_interval);
       }
 
     } /* send_with_sflag_set */
@@ -1225,7 +1206,7 @@ static int group_retransmit_sources(struct igmp_group *group,
   
     query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
     if (num_sources_tosend2 > query_buf2_max_sources) {
-      char group_str[100];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
       zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
                __PRETTY_FUNCTION__, group_str, igmp->interface->name,
@@ -1240,19 +1221,19 @@ static int group_retransmit_sources(struct igmp_group *group,
        interest.
       */
 
-      pim_igmp_send_membership_query(group,
-                                    igmp->fd,
-                                    igmp->interface->name,
-                                    query_buf2,
-                                    sizeof(query_buf2),
-                                    num_sources_tosend2,
-                                    group->group_addr,
-                                    group->group_addr,
-                                    pim_ifp->igmp_specific_query_max_response_time_dsec,
-                                    0 /* s_flag */,
-                                    igmp->querier_robustness_variable,
-                                    igmp->querier_query_interval);
-
+      igmp_send_query(pim_ifp->igmp_version,
+                      group,
+                      igmp->fd,
+                      igmp->interface->name,
+                      query_buf2,
+                      sizeof(query_buf2),
+                      num_sources_tosend2,
+                      group->group_addr,
+                      group->group_addr,
+                      pim_ifp->igmp_specific_query_max_response_time_dsec,
+                      0 /* s_flag */,
+                      igmp->querier_robustness_variable,
+                      igmp->querier_query_interval);
     }
   }
 
@@ -1265,12 +1246,10 @@ static int igmp_group_retransmit(struct thread *t)
   int num_retransmit_sources_left;
   int send_with_sflag_set; /* boolean */
 
-  zassert(t);
   group = THREAD_ARG(t);
-  zassert(group);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("group_retransmit_timer: group %s on %s",
               group_str, group->group_igmp_sock->interface->name);
@@ -1300,7 +1279,7 @@ static int igmp_group_retransmit(struct thread *t)
   num_retransmit_sources_left = group_retransmit_sources(group,
                                                         send_with_sflag_set);
 
-  group->t_group_query_retransmit_timer = 0;
+  group->t_group_query_retransmit_timer = NULL;
 
   /*
     Keep group retransmit timer running if there is any retransmit
@@ -1336,7 +1315,7 @@ static void group_retransmit_timer_on(struct igmp_group *group)
   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
               lmqi_msec / 1000,
@@ -1565,7 +1544,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
   lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
               __PRETTY_FUNCTION__,
@@ -1600,8 +1579,8 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
   lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
     zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
@@ -1613,32 +1592,19 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
   igmp_source_timer_on(group, source, lmqt_msec);
 }
 
-/*
-  Copy sources to message:
-    
-  struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
-  if (num_sources > 0) {
-  struct listnode    *node;
-  struct igmp_source *src;
-  int                 i = 0;
-
-  for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
-  sources[i++] = src->source_addr;
-  }
-  }
-*/
-void pim_igmp_send_membership_query(struct igmp_group *group,
-                                   int fd,
-                                   const char *ifname,
-                                   char *query_buf,
-                                   int query_buf_size,
-                                   int num_sources,
-                                   struct in_addr dst_addr,
-                                   struct in_addr group_addr,
-                                   int query_max_response_time_dsec,
-                                   uint8_t s_flag,
-                                   uint8_t querier_robustness_variable,
-                                   uint16_t querier_query_interval)
+void
+igmp_v3_send_query (struct igmp_group *group,
+                    int fd,
+                    const char *ifname,
+                    char *query_buf,
+                    int query_buf_size,
+                    int num_sources,
+                    struct in_addr dst_addr,
+                    struct in_addr group_addr,
+                    int query_max_response_time_dsec,
+                    uint8_t s_flag,
+                    uint8_t querier_robustness_variable,
+                    uint16_t querier_query_interval)
 {
   ssize_t             msg_size;
   uint8_t             max_resp_code;
@@ -1678,7 +1644,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
 
   query_buf[0]                                         = PIM_IGMP_MEMBERSHIP_QUERY;
   query_buf[1]                                         = max_resp_code;
-  *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET)   = 0; /* for computing checksum */
+  *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET)   = 0; /* for computing checksum */
   memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
 
   query_buf[8]                                         = (s_flag << 3) | querier_robustness_variable;
@@ -1686,18 +1652,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
   *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
 
   checksum = in_cksum(query_buf, msg_size);
-  *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
+  *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
 
   if (PIM_DEBUG_IGMP_PACKETS) {
-    char dst_str[100];
-    char group_str[100];
+    char dst_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-    zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
-              __PRETTY_FUNCTION__,
-              dst_str, ifname, group_str, num_sources,
-              msg_size, s_flag, querier_robustness_variable,
-              querier_query_interval, qqic, checksum);
+    zlog_debug("Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
+              dst_str, ifname, group_str,
+               num_sources, msg_size, s_flag, querier_robustness_variable,
+               querier_query_interval, qqic);
   }
 
   memset(&to, 0, sizeof(to));
@@ -1708,22 +1673,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
   sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
                 (struct sockaddr *)&to, tolen);
   if (sent != (ssize_t) msg_size) {
-    int e = errno;
-    char dst_str[100];
-    char group_str[100];
+    char dst_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
     if (sent < 0) {
-      zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
-               __PRETTY_FUNCTION__,
-               dst_str, ifname, group_str, msg_size,
-               e, safe_strerror(e));
+      zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+               dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
     }
     else {
-      zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
-               __PRETTY_FUNCTION__,
-               dst_str, ifname, group_str,
-               msg_size, sent);
+      zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+               dst_str, ifname, group_str, msg_size, sent);
     }
     return;
   }
@@ -1742,8 +1702,8 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
   if (!s_flag) {
     /* general query? */
     if (PIM_INADDR_IS_ANY(group_addr)) {
-      char dst_str[100];
-      char group_str[100];
+      char dst_str[INET_ADDRSTRLEN];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
       pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
       zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
@@ -1751,5 +1711,268 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
                dst_str, ifname, group_str, num_sources);
     }
   }
+}
 
+void
+igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg)
+{
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct in_addr        group_addr;
+  uint8_t resv_s_qrv = 0;
+  uint8_t s_flag = 0;
+  uint8_t qrv = 0;
+  int     i;
+
+  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+  ifp = igmp->interface;
+  pim_ifp = ifp->info;
+
+  /*
+   * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+   *
+   * Routers adopt the QRV value from the most recently received Query
+   * as their own [Robustness Variable] value, unless that most
+   * recently received QRV was zero, in which case the receivers use
+   * the default [Robustness Variable] value specified in section 8.1
+   * or a statically configured value.
+   */
+  resv_s_qrv = igmp_msg[8];
+  qrv = 7 & resv_s_qrv;
+  igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
+
+  /*
+   * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+   *
+   * Multicast routers that are not the current querier adopt the QQI
+   * value from the most recently received Query as their own [Query
+   * Interval] value, unless that most recently received QQI was zero,
+   * in which case the receiving routers use the default.
+   */
+  if (igmp->t_other_querier_timer) {
+    /* other querier present */
+    uint8_t  qqic;
+    uint16_t qqi;
+    qqic = igmp_msg[9];
+    qqi = igmp_msg_decode8to16(qqic);
+    igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
+
+    if (PIM_DEBUG_IGMP_TRACE) {
+      char ifaddr_str[INET_ADDRSTRLEN];
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+      zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
+                 ifaddr_str,
+                 qqi ? "recv-non-default" : "default",
+                 igmp->querier_query_interval,
+                 qqic,
+                 from_str);
+    }
+  }
+
+  /*
+   * RFC 3376: 6.6.1. Timer Updates
+   *
+   * When a router sends or receives a query with a clear Suppress
+   * Router-Side Processing flag, it must update its timers to reflect
+   * the correct timeout values for the group or sources being queried.
+   *
+   * General queries don't trigger timer update.
+   */
+  s_flag = (1 << 3) & resv_s_qrv;
+
+  if (!s_flag) {
+    /* s_flag is clear */
+
+    if (PIM_INADDR_IS_ANY(group_addr)) {
+      /* this is a general query */
+      /* log that general query should have the s_flag set */
+      zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
+                from_str, ifp->name);
+    } else {
+      struct igmp_group *group;
+
+      /* this is a non-general query: perform timer updates */
+
+      group = find_group_by_addr(igmp, group_addr);
+      if (group) {
+        int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
+
+        /*
+         * RFC 3376: 6.6.1. Timer Updates
+         * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
+         * Query Q(G): Group Timer is lowered to LMQT
+         */
+        if (recv_num_sources < 1) {
+          /* Query Q(G): Group Timer is lowered to LMQT */
+
+          igmp_group_timer_lower_to_lmqt(group);
+        } else {
+          /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
+
+          /* Scan sources in query and lower their timers to LMQT */
+          struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
+          for (i = 0; i < recv_num_sources; ++i) {
+            struct in_addr src_addr;
+            struct igmp_source *src;
+            memcpy(&src_addr, sources + i, sizeof(struct in_addr));
+            src = igmp_find_source_by_addr(group, src_addr);
+            if (src) {
+              igmp_source_timer_lower_to_lmqt(src);
+            }
+          }
+        }
+      } else {
+        char group_str[INET_ADDRSTRLEN];
+        pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+        zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
+                  from_str, ifp->name, group_str);
+      }
+    }
+  } /* s_flag is clear: timer updates */
+}
+
+int
+igmp_v3_recv_report (struct igmp_sock *igmp,
+                     struct in_addr from, const char *from_str,
+                     char *igmp_msg, int igmp_msg_len)
+{
+  uint16_t          recv_checksum;
+  uint16_t          checksum;
+  int               num_groups;
+  uint8_t          *group_record;
+  uint8_t          *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
+  struct interface *ifp = igmp->interface;
+  int               i;
+  int               local_ncb = 0;
+
+  if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
+    zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
+              from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
+    return -1;
+  }
+
+  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
+
+  /* for computing checksum */
+  *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
+
+  checksum = in_cksum(igmp_msg, igmp_msg_len);
+  if (checksum != recv_checksum) {
+    zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
+              from_str, ifp->name, recv_checksum, checksum);
+    return -1;
+  }
+
+  num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
+  if (num_groups < 1) {
+    zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
+              from_str, ifp->name);
+    return -1;
+  }
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
+               from_str, ifp->name, igmp_msg_len, checksum, num_groups);
+  }
+
+  group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+
+  /* Scan groups */
+  for (i = 0; i < num_groups; ++i) {
+    struct in_addr  rec_group;
+    uint8_t        *sources;
+    uint8_t        *src;
+    int             rec_type;
+    int             rec_auxdatalen;
+    int             rec_num_sources;
+    int             j;
+    struct prefix   lncb;
+    struct prefix   g;
+
+    if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
+      zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
+                from_str, ifp->name);
+      return -1;
+    }
+
+    rec_type        = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
+    rec_auxdatalen  = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
+    rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
+
+    memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
+
+    if (PIM_DEBUG_IGMP_PACKETS) {
+      zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
+                 from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
+    }
+
+    /* Scan sources */
+
+    sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
+
+    for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
+
+      if ((src + 4) > report_pastend) {
+        zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
+                  from_str, ifp->name);
+        return -1;
+      }
+
+      if (PIM_DEBUG_IGMP_PACKETS) {
+        char src_str[200];
+
+        if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
+          sprintf(src_str, "<source?>");
+
+        zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
+                   from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
+      }
+    } /* for (sources) */
+
+
+    lncb.family = AF_INET;
+    lncb.u.prefix4.s_addr = 0x000000E0;
+    lncb.prefixlen = 24;
+
+    g.family = AF_INET;
+    g.u.prefix4 = rec_group;
+    g.prefixlen = 32;
+    /*
+     * If we receive a igmp report with the group in 224.0.0.0/24
+     * then we should ignore it
+     */
+    if (prefix_match(&lncb, &g))
+      local_ncb = 1;
+
+    if (!local_ncb)
+      switch (rec_type) {
+      case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
+        igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+        break;
+      case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+        igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources, 0);
+        break;
+      case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
+        igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+        break;
+      case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+        igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+        break;
+      case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
+        igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+        break;
+      case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
+        igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+        break;
+      default:
+        zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
+                  from_str, ifp->name, rec_type);
+      }
+
+    group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
+    local_ncb = 0;
+
+  } /* for (group records) */
+
+  return 0;
 }
index db7895f9be8a82880a7e6db4e38565c1268dbcf4..893fba60f2137272948c26e127ce399ab6d8ba06 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_IGMPV3_H
 #define IGMP_V3_NUMSOURCES_OFFSET          (10)
 #define IGMP_V3_SOURCES_OFFSET             (12)
 
+#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE        (1)
+#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE        (2)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
+#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES      (5)
+#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES      (6)
+
 /* GMI: Group Membership Interval */
 #define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec))
 
@@ -54,15 +65,13 @@ void igmp_source_free(struct igmp_source *source);
 void igmp_source_delete(struct igmp_source *source);
 void igmp_source_delete_expired(struct list *source_list);
 
-int igmp_group_compat_mode(const struct igmp_sock *igmp,
-                          const struct igmp_group *group);
-
 void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
                        struct in_addr group_addr,
                        int num_sources, struct in_addr *sources);
 void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
                        struct in_addr group_addr,
-                       int num_sources, struct in_addr *sources);
+                        int num_sources, struct in_addr *sources,
+                        int from_igmp_v2_report);
 void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
                        struct in_addr group_addr,
                        int num_sources, struct in_addr *sources);
@@ -82,17 +91,24 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source);
 struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
                                             struct in_addr src_addr);
 
-void pim_igmp_send_membership_query(struct igmp_group *group,
-                                   int fd,
-                                   const char *ifname,
-                                   char *query_buf,
-                                   int query_buf_size,
-                                   int num_sources,
-                                   struct in_addr dst_addr,
-                                   struct in_addr group_addr,
-                                   int query_max_response_time_dsec,
-                                   uint8_t s_flag,
-                                   uint8_t querier_robustness_variable,
-                                   uint16_t querier_query_interval);
+void igmp_v3_send_query (struct igmp_group *group,
+                         int fd,
+                         const char *ifname,
+                         char *query_buf,
+                         int query_buf_size,
+                         int num_sources,
+                         struct in_addr dst_addr,
+                         struct in_addr group_addr,
+                         int query_max_response_time_dsec,
+                         uint8_t s_flag,
+                         uint8_t querier_robustness_variable,
+                         uint16_t querier_query_interval);
+
+void igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str,
+                         char *igmp_msg);
+
+int igmp_v3_recv_report (struct igmp_sock *igmp,
+                         struct in_addr from, const char *from_str,
+                         char *igmp_msg, int igmp_msg_len);
 
 #endif /* PIM_IGMPV3_H */
index 6a5fb851d6ec86ddba1195686b328d64544d1b80..db17c3faec08440a31e435b8216f9bc55584c8aa 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -23,6 +27,8 @@
 #include "log.h"
 #include "prefix.h"
 #include "if.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_str.h"
 #include "pim_msg.h"
 #include "pim_pim.h"
 #include "pim_join.h"
+#include "pim_oil.h"
 #include "pim_iface.h"
 #include "pim_hello.h"
 #include "pim_ifchannel.h"
+#include "pim_rpf.h"
+#include "pim_rp.h"
 
-static void on_trace(const char *label,
-                    struct interface *ifp, struct in_addr src)
+static void
+on_trace (const char *label,
+         struct interface *ifp, struct in_addr src)
 {
   if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
     zlog_debug("%s: from %s on %s",
               label, src_str, ifp->name);
@@ -49,58 +59,81 @@ static void recv_join(struct interface *ifp,
                      struct pim_neighbor *neigh,
                      uint16_t holdtime,
                      struct in_addr upstream,
-                     struct in_addr group,
-                     struct in_addr source,
+                     struct prefix_sg *sg,
                      uint8_t source_flags)
 {
   if (PIM_DEBUG_PIM_TRACE) {
-    char up_str[100];
-    char src_str[100];
-    char grp_str[100];
-    char neigh_str[100];
+    char up_str[INET_ADDRSTRLEN];
+    char neigh_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
-    pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
     pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
-    zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+    zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str,
+             pim_str_sg_dump (sg),
              source_flags & PIM_RPT_BIT_MASK,
              source_flags & PIM_WILDCARD_BIT_MASK,
              up_str, holdtime, neigh_str, ifp->name);
   }
-  
+
+  /*
+   * If the RPT and WC are set it's a (*,G)
+   * and the source is the RP
+   */
+  if ((source_flags & PIM_RPT_BIT_MASK) &&
+      (source_flags & PIM_WILDCARD_BIT_MASK))
+    {
+      struct pim_rpf *rp = RP (sg->grp);
+
+      /*
+       * If the RP sent in the message is not
+       * our RP for the group, drop the message
+       */
+      if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr)
+       return;
+
+      sg->src.s_addr = INADDR_ANY;
+    }
+
   /* Restart join expiry timer */
   pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
-                        source, group, source_flags, holdtime);
+                        sg, source_flags, holdtime);
+
 }
 
 static void recv_prune(struct interface *ifp,
                       struct pim_neighbor *neigh,
                       uint16_t holdtime,
                       struct in_addr upstream,
-                      struct in_addr group,
-                      struct in_addr source,
+                      struct prefix_sg *sg,
                       uint8_t source_flags)
 {
   if (PIM_DEBUG_PIM_TRACE) {
-    char up_str[100];
-    char src_str[100];
-    char grp_str[100];
-    char neigh_str[100];
+    char up_str[INET_ADDRSTRLEN];
+    char neigh_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
-    pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
     pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
-    zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+    zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str,
+             pim_str_sg_dump (sg),
              source_flags & PIM_RPT_BIT_MASK,
              source_flags & PIM_WILDCARD_BIT_MASK,
              up_str, holdtime, neigh_str, ifp->name);
   }
-  
-  pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
+
+  if ((source_flags & PIM_RPT_BIT_MASK) &&
+      (source_flags & PIM_WILDCARD_BIT_MASK))
+    {
+      struct pim_rpf *rp = RP (sg->grp);
+
+      // Ignoring Prune *,G's at the moment.
+      if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr)
+       return;
+
+      sg->src.s_addr = INADDR_ANY;
+    }
+
+  pim_ifchannel_prune(ifp, upstream, sg, source_flags, holdtime);
+
 }
 
 int pim_joinprune_recv(struct interface *ifp,
@@ -117,8 +150,6 @@ int pim_joinprune_recv(struct interface *ifp,
   int             remain;
   int             group;
 
-  on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
-
   buf     = tlv_buf;
   pastend = tlv_buf + tlv_buf_size;
 
@@ -128,7 +159,7 @@ int pim_joinprune_recv(struct interface *ifp,
   addr_offset = pim_parse_addr_ucast (&msg_upstream_addr,
                                      buf, pastend - buf);
   if (addr_offset < 1) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
              __PRETTY_FUNCTION__,
@@ -141,8 +172,8 @@ int pim_joinprune_recv(struct interface *ifp,
     Check upstream address family
    */
   if (msg_upstream_addr.family != AF_INET) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      char src_str[100];
+    if (PIM_DEBUG_PIM_J_P) {
+      char src_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
       zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
                __PRETTY_FUNCTION__,
@@ -153,7 +184,7 @@ int pim_joinprune_recv(struct interface *ifp,
 
   remain = pastend - buf;
   if (remain < 4) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
              __PRETTY_FUNCTION__,
@@ -168,28 +199,29 @@ int pim_joinprune_recv(struct interface *ifp,
   ++buf;
   ++buf;
 
-  if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
-    char upstream_str[100];
+  if (PIM_DEBUG_PIM_J_P) {
+    char src_str[INET_ADDRSTRLEN];
+    char upstream_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
                   upstream_str, sizeof(upstream_str));
-    zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
-             __PRETTY_FUNCTION__,
-             upstream_str, msg_num_groups, msg_holdtime,
-             src_str, ifp->name);
+    zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               upstream_str, msg_num_groups, msg_holdtime,
+               src_str, ifp->name);
   }
 
   /* Scan groups */
   for (group = 0; group < msg_num_groups; ++group) {
-    struct prefix msg_group_addr;
-    struct prefix msg_source_addr;
+    struct prefix_sg sg;
     uint8_t       msg_source_flags;
     uint16_t      msg_num_joined_sources;
     uint16_t      msg_num_pruned_sources;
     int           source;
+    struct        pim_ifchannel *ch = NULL;
 
-    addr_offset = pim_parse_addr_group (&msg_group_addr,
+    memset (&sg, 0, sizeof (struct prefix_sg));
+    addr_offset = pim_parse_addr_group (&sg,
                                        buf, pastend - buf);
     if (addr_offset < 1) {
       return -5;
@@ -198,7 +230,7 @@ int pim_joinprune_recv(struct interface *ifp,
 
     remain = pastend - buf;
     if (remain < 4) {
-      char src_str[100];
+      char src_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
       zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
                __PRETTY_FUNCTION__,
@@ -211,25 +243,25 @@ int pim_joinprune_recv(struct interface *ifp,
     msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
     buf += 2;
 
-    if (PIM_DEBUG_PIM_TRACE) {
-      char src_str[100];
-      char upstream_str[100];
-      char group_str[100];
+    if (PIM_DEBUG_PIM_J_P) {
+      char src_str[INET_ADDRSTRLEN];
+      char upstream_str[INET_ADDRSTRLEN];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
       pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
                     upstream_str, sizeof(upstream_str));
-      pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
+      pim_inet4_dump("<grp?>", sg.grp,
                     group_str, sizeof(group_str));
-      zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
+      zlog_warn("%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
                __PRETTY_FUNCTION__,
-               upstream_str, group_str, msg_group_addr.prefixlen,
+               upstream_str, group_str,
                msg_num_joined_sources, msg_num_pruned_sources,
                src_str, ifp->name);
     }
 
     /* Scan joined sources */
     for (source = 0; source < msg_num_joined_sources; ++source) {
-      addr_offset = pim_parse_addr_source (&msg_source_addr,
+      addr_offset = pim_parse_addr_source (&sg,
                                           &msg_source_flags,
                                           buf, pastend - buf);
       if (addr_offset < 1) {
@@ -240,14 +272,20 @@ int pim_joinprune_recv(struct interface *ifp,
 
       recv_join(ifp, neigh, msg_holdtime,
                msg_upstream_addr.u.prefix4,
-               msg_group_addr.u.prefix4,
-               msg_source_addr.u.prefix4,
+               &sg,
                msg_source_flags);
+
+      if (sg.src.s_addr == INADDR_ANY)
+        {
+          ch = pim_ifchannel_find (ifp, &sg);
+         if (ch)
+           pim_ifchannel_set_star_g_join_state (ch, 0);
+        }
     }
 
     /* Scan pruned sources */
     for (source = 0; source < msg_num_pruned_sources; ++source) {
-      addr_offset = pim_parse_addr_source (&msg_source_addr,
+      addr_offset = pim_parse_addr_source (&sg,
                                           &msg_source_flags,
                                           buf, pastend - buf);
       if (addr_offset < 1) {
@@ -258,11 +296,12 @@ int pim_joinprune_recv(struct interface *ifp,
 
       recv_prune(ifp, neigh, msg_holdtime,
                 msg_upstream_addr.u.prefix4,
-                msg_group_addr.u.prefix4,
-                msg_source_addr.u.prefix4,
+                &sg,
                 msg_source_flags);
     }
-
+    if (ch)
+      pim_ifchannel_set_star_g_join_state (ch, 1);
+    ch = NULL;
   } /* scan groups */
 
   return 0;
@@ -270,16 +309,14 @@ int pim_joinprune_recv(struct interface *ifp,
 
 int pim_joinprune_send(struct interface *ifp,
                       struct in_addr upstream_addr,
-                      struct in_addr source_addr,
-                      struct in_addr group_addr,
+                      struct pim_upstream *up,
                       int send_join)
 {
   struct pim_interface *pim_ifp;
-  uint8_t pim_msg[1000];
-  const uint8_t *pastend = pim_msg + sizeof(pim_msg);
-  uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
+  uint8_t pim_msg[9000];
   int pim_msg_size;
-  int remain;
+
+  on_trace (__PRETTY_FUNCTION__, ifp, upstream_addr);
 
   zassert(ifp);
 
@@ -292,31 +329,23 @@ int pim_joinprune_send(struct interface *ifp,
     return -1;
   }
 
-  if (PIM_DEBUG_PIM_TRACE) {
-    char source_str[100];
-    char group_str[100];
-    char dst_str[100];
-    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+  if (PIM_DEBUG_PIM_J_P) {
+    char dst_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
-    zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
+    zlog_debug("%s: sending %s(S,G)=%s to upstream=%s on interface %s",
               __PRETTY_FUNCTION__,
               send_join ? "Join" : "Prune",
-              source_str, group_str, dst_str, ifp->name);
+              up->sg_str, dst_str, ifp->name);
   }
 
   if (PIM_INADDR_IS_ANY(upstream_addr)) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      char source_str[100];
-      char group_str[100];
-      char dst_str[100];
-      pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-      pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    if (PIM_DEBUG_PIM_J_P) {
+      char dst_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
-      zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
+      zlog_debug("%s: %s(S,G)=%s: upstream=%s is myself on interface %s",
                 __PRETTY_FUNCTION__,
                 send_join ? "Join" : "Prune",
-                source_str, group_str, dst_str, ifp->name);
+                up->sg_str, dst_str, ifp->name);
     }
     return 0;
   }
@@ -335,83 +364,14 @@ int pim_joinprune_send(struct interface *ifp,
   /*
     Build PIM message
   */
+  pim_msg_size = pim_msg_join_prune_encode (pim_msg, 9000, send_join,
+                                           up, upstream_addr, PIM_JP_HOLDTIME);
 
-  remain = pastend - pim_msg_curr;
-  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
-                                               remain,
-                                               upstream_addr);
-  if (!pim_msg_curr) {
-    char dst_str[100];
-    pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
-    zlog_warn("%s: failure encoding destination address %s: space left=%d",
-             __PRETTY_FUNCTION__, dst_str, remain);
-    return -3;
-  }
-
-  remain = pastend - pim_msg_curr;
-  if (remain < 4) {
-    zlog_warn("%s: group will not fit: space left=%d",
-           __PRETTY_FUNCTION__, remain);
-    return -4;
-  }
-
-  *pim_msg_curr = 0; /* reserved */
-  ++pim_msg_curr;
-  *pim_msg_curr = 1; /* number of groups */
-  ++pim_msg_curr;
-  *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
-  ++pim_msg_curr;
-  ++pim_msg_curr;
-
-  remain = pastend - pim_msg_curr;
-  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
-                                               remain,
-                                               group_addr);
-  if (!pim_msg_curr) {
-    char group_str[100];
-    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
-    zlog_warn("%s: failure encoding group address %s: space left=%d",
-             __PRETTY_FUNCTION__, group_str, remain);
-    return -5;
-  }
-
-  remain = pastend - pim_msg_curr;
-  if (remain < 4) {
-    zlog_warn("%s: sources will not fit: space left=%d",
-             __PRETTY_FUNCTION__, remain);
-    return -6;
-  }
-
-  /* number of joined sources */
-  *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
-  ++pim_msg_curr;
-  ++pim_msg_curr;
-
-  /* number of pruned sources */
-  *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
-  ++pim_msg_curr;
-  ++pim_msg_curr;
-
-  remain = pastend - pim_msg_curr;
-  pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
-                                                remain,
-                                                source_addr);
-  if (!pim_msg_curr) {
-    char source_str[100];
-    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-    zlog_warn("%s: failure encoding source address %s: space left=%d",
-             __PRETTY_FUNCTION__, source_str, remain);
-    return -7;
-  }
-
-  /* Add PIM header */
-
-  pim_msg_size = pim_msg_curr - pim_msg;
-
-  pim_msg_build_header(pim_msg, pim_msg_size,
-                      PIM_MSG_TYPE_JOIN_PRUNE);
+  if (pim_msg_size < 0)
+    return pim_msg_size;
 
   if (pim_msg_send(pim_ifp->pim_sock_fd,
+                  pim_ifp->primary_address,
                   qpim_all_pim_routers_addr,
                   pim_msg,
                   pim_msg_size,
index dcdca00359b84baddee75a69bb92cf0a8a3ce642..27333243db41fe565013ab8a75beab1320407569 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_JOIN_H
@@ -34,8 +38,7 @@ int pim_joinprune_recv(struct interface *ifp,
 
 int pim_joinprune_send(struct interface *ifp,
                       struct in_addr upstream_addr,
-                      struct in_addr source_addr,
-                      struct in_addr group_addr,
+                      struct pim_upstream *up,
                       int send_join);
 
 #endif /* PIM_JOIN_H */
index 622bef44393ad73597a9097157ed2671261085f6..127e0f6252d6181328e01589b8e4e8b8da0cf2d7 100644 (file)
 #include <zebra.h>
 
 #include "log.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
 
-#include "pim_macro.h"
 #include "pimd.h"
-#include "pim_str.h"
+#include "pim_macro.h"
 #include "pim_iface.h"
 #include "pim_ifchannel.h"
+#include "pim_rp.h"
 
 /*
   DownstreamJPState(S,G,I) is the per-interface state machine for
   receiving (S,G) Join/Prune messages.
 
-  DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+  DownstreamJPState(S,G,I) is either Join or Prune-Pending
+  DownstreamJPState(*,G,I) is either Join or Prune-Pending
 */
 static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
 {
-  return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
+  switch (ch->ifjoin_state)
+    {
+    case PIM_IFJOIN_NOINFO:
+    case PIM_IFJOIN_PRUNE:
+    case PIM_IFJOIN_PRUNE_TMP:
+    case PIM_IFJOIN_PRUNE_PENDING_TMP:
+      return 0;
+     break;
+    case PIM_IFJOIN_JOIN:
+    case PIM_IFJOIN_PRUNE_PENDING:
+      return 1;
+      break;
+    }
+  return 0;
 }
 
 /*
@@ -100,13 +117,9 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
 
   ifp = ch->interface;
   if (!ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): null interface",
+    zlog_warn("%s: (S,G)=%s: null interface",
              __PRETTY_FUNCTION__,
-             src_str, grp_str);
+             ch->sg_str);
     return 0; /* false */
   }
 
@@ -116,13 +129,9 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
 
   pim_ifp = ifp->info;
   if (!pim_ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+    zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, ifp->name);
+             ch->sg_str, ifp->name);
     return 0; /* false */
   }
 
@@ -157,13 +166,9 @@ int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
   struct pim_interface *pim_ifp = ch->interface->info;
 
   if (!pim_ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+    zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str, ch->interface->name);
+             ch->sg_str, ch->interface->name);
     return 0; /* false */
   }
 
@@ -225,13 +230,8 @@ int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
 
   ifp = ch->interface;
   if (!ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): null interface",
-             __PRETTY_FUNCTION__,
-             src_str, grp_str);
+    zlog_warn("%s: (S,G)=%s: null interface",
+             __PRETTY_FUNCTION__, ch->sg_str);
     return 0; /* false */
   }
 
@@ -350,7 +350,7 @@ static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
 */
 int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
 {
-  if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) {
+  if (ch->upstream->join_state == PIM_UPSTREAM_NOTJOINED) {
     /* oiflist is NULL */
     return 0; /* false */
   }
@@ -386,25 +386,15 @@ int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
 
   ifp = ch->interface;
   if (!ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): null interface",
-             __PRETTY_FUNCTION__,
-             src_str, grp_str);
+    zlog_warn("%s: (S,G)=%s: null interface",
+             __PRETTY_FUNCTION__, ch->sg_str);
     return 0; /* false */
   }
 
   pim_ifp = ifp->info;
   if (!pim_ifp) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
-             __PRETTY_FUNCTION__,
-             src_str, grp_str, ch->interface->name);
+    zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name);
     return 0; /* false */
   }
 
index ed8f69b5172b4975d5f7bed54168ebdaf98433aa..e7b86a04042f1020866839c4b51afe05a8e5d5f1 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 #include "pim_version.h"
 #include "pim_signals.h"
 #include "pim_zebra.h"
-
-#ifdef PIM_ZCLIENT_DEBUG
-extern int zclient_debug;
-#endif
+#include "pim_msdp.h"
+#include "pim_iface.h"
+#include "pim_rp.h"
 
 extern struct host host;
 
@@ -70,6 +73,7 @@ zebra_capabilities_t _caps_p [] =
   ZCAP_NET_ADMIN,
   ZCAP_SYS_ADMIN,
   ZCAP_NET_RAW,
+  ZCAP_BIND,
 };
 
 /* pimd privileges to run with */
@@ -104,18 +108,9 @@ Daemon which manages PIM.\n\n\
 -A, --vty_addr       Set vty's bind address\n\
 -P, --vty_port       Set vty's port number\n\
 -v, --version        Print program version\n\
-"
-
-#ifdef PIM_ZCLIENT_DEBUG
-"\
--Z, --debug_zclient  Enable zclient debugging\n\
-"
-#endif
-
-"\
 -h, --help           Display this help and exit\n\
 \n\
-Report bugs to %s\n", progname, PIMD_BUG_ADDRESS);
+Report bugs to %s\n", progname, PACKAGE_BUGREPORT);
   }
 
   exit (status);
@@ -177,11 +172,6 @@ int main(int argc, char** argv, char** envp) {
       print_version(progname);
       exit (0);
       break;
-#ifdef PIM_ZCLIENT_DEBUG
-    case 'Z':
-      zclient_debug = 1;
-      break;
-#endif
     case 'h':
       usage (0);
       break;
@@ -206,8 +196,12 @@ int main(int argc, char** argv, char** envp) {
   vrf_init ();
   access_list_init();
   prefix_list_init ();
+  prefix_list_add_hook (pim_rp_prefix_list_update);
+  prefix_list_delete_hook (pim_rp_prefix_list_update);
+
   pim_route_map_init ();
   pim_init();
+  pim_msdp_init (master);
 
   /*
    * Initialize zclient "update" and "lookup" sockets
@@ -254,11 +248,6 @@ int main(int argc, char** argv, char** envp) {
   PIM_DO_DEBUG_ZEBRA;
 #endif
 
-#ifdef PIM_ZCLIENT_DEBUG
-  zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)",
-             zclient_debug ? "ON" : "OFF");
-#endif
-
 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
   zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex");
 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
index 601472502081495a9b51139816630fff496714a6..ccd0fa81ac2292d112142652df5c7d4289de6f26 100644 (file)
@@ -39,3 +39,11 @@ DEFINE_MTYPE(PIMD, PIM_UPSTREAM,          "PIM upstream (S,G) state")
 DEFINE_MTYPE(PIMD, PIM_SSMPINGD,          "PIM sspimgd socket")
 DEFINE_MTYPE(PIMD, PIM_STATIC_ROUTE,      "PIM Static Route")
 DEFINE_MTYPE(PIMD, PIM_BR,                "PIM Bridge Router info")
+DEFINE_MTYPE(PIMD, PIM_RP,                "PIM RP info")
+DEFINE_MTYPE(PIMD, PIM_FILTER_NAME,       "PIM RP filter info")
+DEFINE_MTYPE(PIMD, PIM_MSDP_PEER,         "PIM MSDP peer")
+DEFINE_MTYPE(PIMD, PIM_MSDP_MG_NAME,      "PIM MSDP mesh-group name")
+DEFINE_MTYPE(PIMD, PIM_MSDP_SA,           "PIM MSDP source-active cache")
+DEFINE_MTYPE(PIMD, PIM_MSDP_MG,           "PIM MSDP mesh group")
+DEFINE_MTYPE(PIMD, PIM_MSDP_MG_MBR,       "PIM MSDP mesh group mbr")
+DEFINE_MTYPE(PIMD, PIM_SEC_ADDR,          "PIM secondary address")
index 81841e58b6dcd62623f6651aa4e7ad00eac12619..b6b9b23239049a015a6ee1e391f6f56a18c51377 100644 (file)
@@ -38,5 +38,13 @@ DECLARE_MTYPE(PIM_UPSTREAM)
 DECLARE_MTYPE(PIM_SSMPINGD)
 DECLARE_MTYPE(PIM_STATIC_ROUTE)
 DECLARE_MTYPE(PIM_BR)
+DECLARE_MTYPE(PIM_RP)
+DECLARE_MTYPE(PIM_FILTER_NAME)
+DECLARE_MTYPE(PIM_MSDP_PEER)
+DECLARE_MTYPE(PIM_MSDP_MG_NAME)
+DECLARE_MTYPE(PIM_MSDP_SA)
+DECLARE_MTYPE(PIM_MSDP_MG)
+DECLARE_MTYPE(PIM_MSDP_MG_MBR)
+DECLARE_MTYPE(PIM_SEC_ADDR)
 
 #endif /* _QUAGGA_PIM_MEMORY_H */
index 3fbed88800ee3490909c619f1d41489ac53470a8..8e97233d4b207e4f0397c44bcda32d8210d6b575 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 #include "privs.h"
 #include "if.h"
 #include "prefix.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
+#include "pim_rpf.h"
 #include "pim_mroute.h"
 #include "pim_oil.h"
 #include "pim_str.h"
 #include "pim_rp.h"
 #include "pim_oil.h"
 #include "pim_register.h"
+#include "pim_ifchannel.h"
+#include "pim_zlookup.h"
 
 /* GLOBAL VARS */
 extern struct zebra_privs_t pimd_privs;
 
+static struct thread *qpim_mroute_socket_reader = NULL;
+
 static void mroute_read_on(void);
 
 static int pim_mroute_set(int fd, int enable)
@@ -45,62 +56,71 @@ static int pim_mroute_set(int fd, int enable)
   int err;
   int opt = enable ? MRT_INIT : MRT_DONE;
   socklen_t opt_len = sizeof(opt);
+  int rcvbuf = 1024 * 1024 * 8;
+  long flags;
 
   err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
   if (err) {
-    int e = errno;
     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
              __FILE__, __PRETTY_FUNCTION__,
-             fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
-    errno = e;
+             fd, enable ? "MRT_INIT" : "MRT_DONE", opt, errno, safe_strerror(errno));
     return -1;
   }
 
-#if 0
-  zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
-           __FILE__, __PRETTY_FUNCTION__,
-           fd, opt);
-#endif
-
-  return 0;
-}
-
-static int
-pim_mroute_connected_to_source (struct interface *ifp, struct in_addr src)
-{
-  struct listnode *cnode;
-  struct connected *c;
-  struct prefix p;
-
-  p.family = AF_INET;
-  p.u.prefix4 = src;
-  p.prefixlen = IPV4_MAX_BITLEN;
+  err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+  if (err) {
+    zlog_warn("%s: failure: setsockopt(fd=%d, SOL_SOCKET, %d): errno=%d: %s",
+             __PRETTY_FUNCTION__, fd, rcvbuf, errno, safe_strerror(errno));
+  }
 
-  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+  flags = fcntl(fd, F_GETFL, 0);
+  if (flags < 0)
     {
-      if ((c->address->family == AF_INET) &&
-         prefix_match (CONNECTED_PREFIX (c), &p))
-       {
-        return 1;
-       }
+      zlog_warn("Could not get flags on socket fd:%d %d %s",
+               fd, errno, safe_strerror(errno));
+      close (fd);
+      return -1;
+    }
+  if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+    {
+      zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
+               fd, errno, safe_strerror(errno));
+      close(fd);
+      return -1;
     }
 
+  if (enable)
+    {
+      int upcalls = IGMPMSG_WRVIFWHOLE;
+      opt = MRT_PIM;
+    
+      err = setsockopt (fd, IPPROTO_IP, opt, &upcalls, sizeof (upcalls));
+      if (err)
+        {
+          zlog_warn ("Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
+                    errno, safe_strerror (errno));
+          return -1;
+        }
+    }
+  
   return 0;
 }
 
-static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = {
+static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = {
   "<unknown_upcall?>",
   "NOCACHE",
   "WRONGVIF",
-  "WHOLEPKT", };
+  "WHOLEPKT",
+  "WRVIFWHOLE" };
 
 static int
-pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg,
-                       const char *src_str, const char *grp_str)
+pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg)
 {
   struct pim_interface *pim_ifp = ifp->info;
   struct pim_upstream *up;
   struct pim_rpf *rpg;
+  struct prefix_sg sg;
+  struct channel_oil *oil;
 
   rpg = RP(msg->im_dst);
   /*
@@ -108,108 +128,131 @@ pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg
    * the Interface type is SSM we don't need to
    * do anything here
    */
-  if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
+  if ((pim_rpf_addr_is_inaddr_none (rpg)) ||
       (!pim_ifp) ||
       (!(PIM_I_am_DR(pim_ifp))) ||
       (pim_ifp->itype == PIM_INTERFACE_SSM))
-    return 0;
+    {
+      if (PIM_DEBUG_MROUTE_DETAIL)
+       zlog_debug ("%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
+                   __PRETTY_FUNCTION__);
+      return 0;
+    }
 
   /*
    * If we've received a multicast packet that isn't connected to
    * us
    */
-  if (!pim_mroute_connected_to_source (ifp, msg->im_src))
+  if (!pim_if_connected_to_source (ifp, msg->im_src))
     {
-      if (PIM_DEBUG_PIM_TRACE)
-       zlog_debug ("%s: Received incoming packet that does originate on our seg",
+      if (PIM_DEBUG_MROUTE_DETAIL)
+       zlog_debug ("%s: Received incoming packet that doesn't originate on our seg",
                   __PRETTY_FUNCTION__);
       return 0;
     }
 
-  if (PIM_DEBUG_PIM_TRACE) {
-    zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
-              __PRETTY_FUNCTION__, grp_str, src_str);
-  }
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = msg->im_src;
+  sg.grp = msg->im_dst;
 
-  up = pim_upstream_add(msg->im_src, msg->im_dst, ifp);
-  if (!up) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: Failure to add upstream information for (%s,%s)",
+  oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
+  if (!oil) {
+    if (PIM_DEBUG_MROUTE) {
+      zlog_debug("%s: Failure to add channel oil for %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str);
+                pim_str_sg_dump (&sg));
     }
     return 0;
   }
 
-  pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
-
-  up->channel_oil = pim_channel_oil_add(msg->im_dst,
-                                       msg->im_src,
-                                       pim_ifp->mroute_vif_index);
-  if (!up->channel_oil) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: Failure to add channel oil for (%s,%s)",
+  up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
+  if (!up) {
+    if (PIM_DEBUG_MROUTE) {
+      zlog_debug("%s: Failure to add upstream information for %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str);
+                pim_str_sg_dump (&sg));
     }
     return 0;
   }
-  up->channel_oil->cc.pktcnt++;
 
-  pim_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE);
+  /*
+   * I moved this debug till after the actual add because
+   * I want to take advantage of the up->sg_str being filled in.
+   */
+  if (PIM_DEBUG_MROUTE) {
+    zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
+              __PRETTY_FUNCTION__, up->sg_str);
+  }
+
+  PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
+  pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+
+  up->channel_oil = oil;
+  up->channel_oil->cc.pktcnt++;
+  PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+  pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+  up->join_state = PIM_UPSTREAM_JOINED;
 
   return 0;
 }
 
 static int
-pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf,
-                        const char *src_str, const char *grp_str)
+pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf)
 {
   struct pim_interface *pim_ifp;
-  struct in_addr group;
-  struct in_addr src;
+  struct prefix_sg sg;
   struct pim_rpf *rpg;
   const struct ip *ip_hdr;
   struct pim_upstream *up;
 
   ip_hdr = (const struct ip *)buf;
 
-  src = ip_hdr->ip_src;
-  group = ip_hdr->ip_dst;
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = ip_hdr->ip_src;
+  sg.grp = ip_hdr->ip_dst;
 
-  up = pim_upstream_find(src, group);
+  up = pim_upstream_find(&sg);
   if (!up) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
-                __PRETTY_FUNCTION__, src_str, grp_str);
+    if (PIM_DEBUG_MROUTE_DETAIL) {
+      zlog_debug("%s: Unable to find upstream channel WHOLEPKT%s",
+                __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
     }
     return 0;
   }
 
   pim_ifp = up->rpf.source_nexthop.interface->info;
 
-  rpg = RP(group);
+  rpg = RP(sg.grp);
 
-  if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
+  if ((pim_rpf_addr_is_inaddr_none (rpg)) ||
       (!pim_ifp) ||
       (!(PIM_I_am_DR(pim_ifp))) ||
       (pim_ifp->itype == PIM_INTERFACE_SSM)) {
-    if (PIM_DEBUG_PIM_TRACE) {
+    if (PIM_DEBUG_MROUTE) {
       zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__);
     }
     return 0;
   }
 
-  pim_register_send((const struct ip *)(buf + sizeof(struct ip)), rpg);
+  /*
+   * If we've received a register suppress
+   */
+  if (!up->t_rs_timer)
+    pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs (ip_hdr->ip_len),
+                     pim_ifp->primary_address, rpg, 0, up);
   return 0;
 }
 
 static int
-pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg,
-                        const char *src_str, const char *grp_str)
+pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg)
 {
   struct pim_ifchannel *ch;
   struct pim_interface *pim_ifp;
+  struct prefix_sg sg;
+
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = msg->im_src;
+  sg.grp = msg->im_dst;
 
   /*
     Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
@@ -223,32 +266,39 @@ pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *ms
   */
 
   if (!ifp) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+    if (PIM_DEBUG_MROUTE)
+      zlog_debug("%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, msg->im_vif);
-    }
+                pim_str_sg_dump (&sg), msg->im_vif);
     return -1;
   }
 
   pim_ifp = ifp->info;
   if (!pim_ifp) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
+    if (PIM_DEBUG_MROUTE)
+      zlog_debug("%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, ifp->name);
-    }
+                pim_str_sg_dump (&sg), ifp->name);
     return -2;
   }
 
-  ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
+  ch = pim_ifchannel_find(ifp, &sg);
   if (!ch) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
+    struct prefix_sg star_g = sg;
+    if (PIM_DEBUG_MROUTE)
+      zlog_debug("%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, ifp->name);
+                pim_str_sg_dump(&sg), ifp->name);
+
+    star_g.src.s_addr = INADDR_ANY;
+    ch = pim_ifchannel_find(ifp, &star_g);
+    if (!ch) {
+      if (PIM_DEBUG_MROUTE)
+       zlog_debug("%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
+                  __PRETTY_FUNCTION__,
+                  pim_str_sg_dump(&star_g), ifp->name);
+      return -3;
     }
-    return -3;
   }
 
   /*
@@ -266,28 +316,28 @@ pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *ms
   */
 
   if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
+    if (PIM_DEBUG_MROUTE) {
+      zlog_debug("%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, ifp->name);
+                ch->sg_str, ifp->name);
     }
     return -4;
   }
 
   if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
+    if (PIM_DEBUG_MROUTE) {
+      zlog_debug("%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, ifp->name);
+                ch->sg_str, ifp->name);
     }
     return -5;
   }
 
   if (assert_action_a1(ch)) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+    if (PIM_DEBUG_MROUTE) {
+      zlog_debug("%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
                 __PRETTY_FUNCTION__,
-                src_str, grp_str, ifp->name);
+                ch->sg_str, ifp->name);
     }
     return -6;
   }
@@ -295,107 +345,250 @@ pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *ms
   return 0;
 }
 
+static int
+pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf)
+{
+  const struct ip *ip_hdr = (const struct ip *)buf;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+  struct pim_upstream *up;
+  //struct prefix_sg star_g;
+  struct prefix_sg sg;
+  struct channel_oil *oil;
+
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = ip_hdr->ip_src;
+  sg.grp = ip_hdr->ip_dst;
+
+  ch = pim_ifchannel_find(ifp, &sg);
+  if (ch)
+    {
+      if (PIM_DEBUG_MROUTE)
+       zlog_debug ("WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
+                   ch->sg_str, ifp->name);
+      return -1;
+    }
+#if 0
+  star_g = sg;
+  star_g.src.s_addr = INADDR_ANY;
+  ch = pim_ifchannel_find(ifp, &star_g);
+  if (ch)
+    {
+      if (PIM_DEBUG_MROUTE)
+       zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
+                   pim_str_sg_dump (&star_g), ifp->name);
+      return -1;
+    }
+#endif
+
+  up = pim_upstream_find (&sg);
+  if (up)
+    {
+      struct pim_nexthop source;
+      struct pim_rpf *rpf = RP (sg.grp);
+      if (!rpf || !rpf->source_nexthop.interface)
+        return 0;
+
+      pim_ifp = rpf->source_nexthop.interface->info;
+
+      memset (&source, 0, sizeof (source));
+      /*
+       * If we are the fhr that means we are getting a callback during
+       * the pimreg period, so I believe we can ignore this packet
+       */
+      if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+       {
+         //No if channel, but upstream we are at the RP.
+         if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
+           pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
+          if (!up->channel_oil)
+            up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
+          pim_upstream_inherited_olist (up);
+          if (!up->channel_oil->installed)
+            pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
+         pim_upstream_set_sptbit (up, ifp);
+       }
+      else
+       {
+         if (I_am_RP (up->sg.grp))
+           {
+             if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
+               pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
+             up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
+           }
+         pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+         pim_upstream_inherited_olist (up);
+         pim_mroute_msg_wholepkt (fd, ifp, buf);
+       }
+      return 0;
+    }
+
+  pim_ifp = ifp->info;
+  oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
+  if (!oil->installed)
+    pim_mroute_add (oil, __PRETTY_FUNCTION__);
+  if (pim_if_connected_to_source (ifp, sg.src))
+    {
+      up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
+      if (!up)
+       {
+         if (PIM_DEBUG_MROUTE)
+           zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface",
+                       pim_str_sg_dump (&sg), ifp->name);
+         return -2;
+       }
+      PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
+      pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+      up->channel_oil = oil;
+      up->channel_oil->cc.pktcnt++;
+      pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+      up->join_state = PIM_UPSTREAM_JOINED;
+      pim_upstream_inherited_olist (up);
+
+      // Send the packet to the RP
+      pim_mroute_msg_wholepkt (fd, ifp, buf);
+    }
+
+  return 0;
+}
+
 int pim_mroute_msg(int fd, const char *buf, int buf_size)
 {
   struct interface     *ifp;
+  struct pim_interface *pim_ifp;
   const struct ip      *ip_hdr;
   const struct igmpmsg *msg;
-  char src_str[100] = "<src?>";
-  char grp_str[100] = "<grp?>";
+  char ip_src_str[INET_ADDRSTRLEN] = "";
+  char ip_dst_str[INET_ADDRSTRLEN] = "";
+  char src_str[INET_ADDRSTRLEN] = "<src?>";
+  char grp_str[INET_ADDRSTRLEN] = "<grp?>";
+  struct in_addr ifaddr;
+  struct igmp_sock *igmp;
 
   ip_hdr = (const struct ip *) buf;
 
-  /* kernel upcall must have protocol=0 */
-  if (ip_hdr->ip_p) {
-    /* this is not a kernel upcall */
-    if (PIM_DEBUG_PIM_TRACE) {
-      pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
-      zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
-                __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
+  if (ip_hdr->ip_p == IPPROTO_IGMP) {
+
+    /* We have the IP packet but we do not know which interface this packet was
+     * received on. Find the interface that is on the same subnet as the source
+     * of the IP packet.
+     */
+    ifp = pim_if_lookup_address_vrf (ip_hdr->ip_src, VRF_DEFAULT);
+
+    if (!ifp) {
+      if (PIM_DEBUG_MROUTE_DETAIL) {
+        pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
+        pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
+
+        zlog_warn("%s: igmp kernel upcall could not find usable interface for %s -> %s",
+                __PRETTY_FUNCTION__,
+                ip_src_str,
+                ip_dst_str);
+      }
+      return 0;
     }
-    return 0;
-  }
+    pim_ifp = ifp->info;
+    ifaddr = pim_find_primary_addr(ifp);
+    igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
 
-  msg = (const struct igmpmsg *) buf;
-
-  ifp = pim_if_find_by_vif_index(msg->im_vif);
-
-  if (PIM_DEBUG_PIM_TRACE) {
-    pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
-    zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
-             __PRETTY_FUNCTION__,
-             igmpmsgtype2str[msg->im_msgtype],
-             msg->im_msgtype,
-             ip_hdr->ip_p,
-             fd,
-             src_str,
-             grp_str,
-             ifp ? ifp->name : "<ifname?>",
-             msg->im_vif);
-  }
+    if (PIM_DEBUG_MROUTE) {
+      pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
+      pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
 
-  switch (msg->im_msgtype) {
-  case IGMPMSG_WRONGVIF:
-    return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str);
-    break;
-  case IGMPMSG_NOCACHE:
-    return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str);
-    break;
-  case IGMPMSG_WHOLEPKT:
-    return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg, src_str, grp_str);
-    break;
-  default:
-    break;
-  }
+      zlog_warn("%s: igmp kernel upcall on %s(%p) for %s -> %s",
+                __PRETTY_FUNCTION__, ifp->name, igmp, ip_src_str, ip_dst_str);
+    }
+    if (igmp)
+      pim_igmp_packet(igmp, (char *)buf, buf_size);
 
-  return 0;
-}
+  } else if (ip_hdr->ip_p) {
+    if (PIM_DEBUG_MROUTE_DETAIL) {
+      pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
+      zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
+                 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
+    }
 
-static int mroute_read_msg(int fd)
-{
-  const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
-  char buf[1000];
-  int rd;
+  } else {
+    msg = (const struct igmpmsg *) buf;
 
-  if (((int) sizeof(buf)) < msg_min_size) {
-    zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
-            __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
-    return -1;
-  }
+    ifp = pim_if_find_by_vif_index(msg->im_vif);
 
-  rd = read(fd, buf, sizeof(buf));
-  if (rd < 0) {
-    zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
-             __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
-    return -2;
-  }
+    if (!ifp)
+      return 0;
+    if (PIM_DEBUG_MROUTE) {
+      pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
+      zlog_warn("%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d  size=%d",
+                __PRETTY_FUNCTION__,
+                igmpmsgtype2str[msg->im_msgtype],
+                msg->im_msgtype,
+                ip_hdr->ip_p,
+                fd,
+                src_str,
+                grp_str,
+                ifp->name,
+                msg->im_vif, buf_size);
+    }
 
-  if (rd < msg_min_size) {
-    zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
-             __PRETTY_FUNCTION__, fd, rd, msg_min_size);
-    return -3;
+    switch (msg->im_msgtype) {
+    case IGMPMSG_WRONGVIF:
+      return pim_mroute_msg_wrongvif(fd, ifp, msg);
+      break;
+    case IGMPMSG_NOCACHE:
+      return pim_mroute_msg_nocache(fd, ifp, msg);
+      break;
+    case IGMPMSG_WHOLEPKT:
+      return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
+      break;
+    case IGMPMSG_WRVIFWHOLE:
+      return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
+      break;
+    default:
+      break;
+    }
   }
 
-  return pim_mroute_msg(fd, buf, rd);
+  return 0;
 }
 
 static int mroute_read(struct thread *t)
 {
+  static long long count;
+  char buf[10000];
+  int result = 0;
+  int cont = 1;
   int fd;
-  int result;
-
-  zassert(t);
-  zassert(!THREAD_ARG(t));
+  int rd;
 
   fd = THREAD_FD(t);
-  zassert(fd == qpim_mroute_socket_fd);
-
-  result = mroute_read_msg(fd);
 
+  while (cont)
+    {
+      rd = read(fd, buf, sizeof(buf));
+      if (rd < 0) {
+       if (errno == EINTR)
+         continue;
+       if (errno == EWOULDBLOCK || errno == EAGAIN)
+         {
+           cont = 0;
+           break;
+         }
+       if (PIM_DEBUG_MROUTE)
+         zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
+                   __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+       goto done;
+      }
+
+      result = pim_mroute_msg(fd, buf, rd);
+
+      count++;
+      if (count % qpim_packet_process == 0)
+       cont = 0;
+    }
   /* Keep reading */
-  qpim_mroute_socket_reader = 0;
+ done:
+  qpim_mroute_socket_reader = NULL;
   mroute_read_on();
 
   return result;
@@ -450,8 +643,6 @@ int pim_mroute_socket_enable()
   qpim_mroute_socket_creation = pim_time_monotonic_sec();
   mroute_read_on();
 
-  zassert(PIM_MROUTE_IS_ENABLED);
-
   return 0;
 }
 
@@ -475,8 +666,6 @@ int pim_mroute_socket_disable()
   mroute_read_off();
   qpim_mroute_socket_fd = -1;
 
-  zassert(PIM_MROUTE_IS_DISABLED);
-
   return 0;
 }
 
@@ -521,16 +710,14 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned ch
 
   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
   if (err) {
-    char ifaddr_str[100];
-    int e = errno;
+    char ifaddr_str[INET_ADDRSTRLEN];
 
     pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
 
     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
              __FILE__, __PRETTY_FUNCTION__,
              qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
-             e, safe_strerror(e));
-    errno = e;
+             errno, safe_strerror(errno));
     return -2;
   }
 
@@ -553,22 +740,21 @@ int pim_mroute_del_vif(int vif_index)
 
   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); 
   if (err) {
-    int e = errno;
     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
              __FILE__, __PRETTY_FUNCTION__,
              qpim_mroute_socket_fd, vif_index,
-             e, safe_strerror(e));
-    errno = e;
+             errno, safe_strerror(errno));
     return -2;
   }
 
   return 0;
 }
 
-int pim_mroute_add(struct channel_oil *c_oil)
+int pim_mroute_add(struct channel_oil *c_oil, const char *name)
 {
   int err;
   int orig = 0;
+  int orig_iif_vif = 0;
 
   qpim_mroute_add_last = pim_time_monotonic_sec();
   ++qpim_mroute_add_events;
@@ -589,27 +775,57 @@ int pim_mroute_add(struct channel_oil *c_oil)
       c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
     }
 
+  /*
+   * If we have an unresolved cache entry for the S,G
+   * it is owned by the pimreg for the incoming IIF
+   * So set pimreg as the IIF temporarily to cause
+   * the packets to be forwarded.  Then set it
+   * to the correct IIF afterwords.
+   */
+  if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
+      c_oil->oil.mfcc_parent != 0)
+    {
+      orig_iif_vif = c_oil->oil.mfcc_parent;
+      c_oil->oil.mfcc_parent = 0;
+    }
   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
                   &c_oil->oil, sizeof(c_oil->oil));
 
+  if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
+      orig_iif_vif != 0)
+    {
+      c_oil->oil.mfcc_parent = orig_iif_vif;
+      err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
+                       &c_oil->oil, sizeof (c_oil->oil));
+    }
+
   if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
       c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
 
   if (err) {
-    int e = errno;
     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
              __FILE__, __PRETTY_FUNCTION__,
              qpim_mroute_socket_fd,
-             e, safe_strerror(e));
-    errno = e;
+             errno, safe_strerror(errno));
     return -2;
   }
 
+  if (PIM_DEBUG_MROUTE)
+    {
+      struct prefix_sg sg;
+
+      sg.src = c_oil->oil.mfcc_origin;
+      sg.grp = c_oil->oil.mfcc_mcastgrp;
+
+      zlog_debug("%s(%s), Added Route: %s to mroute table",
+                __PRETTY_FUNCTION__, name, pim_str_sg_dump(&sg));
+    }
+
   c_oil->installed = 1;
   return 0;
 }
 
-int pim_mroute_del (struct channel_oil *c_oil)
+int pim_mroute_del (struct channel_oil *c_oil, const char *name)
 {
   int err;
 
@@ -624,15 +840,24 @@ int pim_mroute_del (struct channel_oil *c_oil)
 
   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
   if (err) {
-    int e = errno;
-    zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
-             __FILE__, __PRETTY_FUNCTION__,
-             qpim_mroute_socket_fd,
-             e, safe_strerror(e));
-    errno = e;
+    if (PIM_DEBUG_MROUTE)
+      zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
+               __FILE__, __PRETTY_FUNCTION__,
+               qpim_mroute_socket_fd,
+               errno, safe_strerror(errno));
     return -2;
   }
 
+  if (PIM_DEBUG_MROUTE)
+    {
+      struct prefix_sg sg;
+
+      sg.src = c_oil->oil.mfcc_origin;
+      sg.grp = c_oil->oil.mfcc_mcastgrp;
+
+      zlog_debug("%s(%s), Deleted Route: %s from mroute table",
+                __PRETTY_FUNCTION__, name, pim_str_sg_dump(&sg));
+    }
   c_oil->installed = 0;
 
   return 0;
@@ -643,28 +868,46 @@ pim_mroute_update_counters (struct channel_oil *c_oil)
 {
   struct sioc_sg_req sgreq;
 
-  memset (&sgreq, 0, sizeof(sgreq));
-  sgreq.src = c_oil->oil.mfcc_origin;
-  sgreq.grp = c_oil->oil.mfcc_mcastgrp;
-
   c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
   c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
   c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
 
+  if (!c_oil->installed)
+    {
+      c_oil->cc.lastused = 100 * qpim_keep_alive_time;
+      if (PIM_DEBUG_MROUTE)
+       {
+         struct prefix_sg sg;
+
+         sg.src = c_oil->oil.mfcc_origin;
+         sg.grp = c_oil->oil.mfcc_mcastgrp;
+         if (PIM_DEBUG_MROUTE)
+           zlog_debug("Channel(%s) is not installed no need to collect data from kernel",
+                      pim_str_sg_dump (&sg));
+       }
+      return;
+    }
+
+  memset (&sgreq, 0, sizeof(sgreq));
+  sgreq.src = c_oil->oil.mfcc_origin;
+  sgreq.grp = c_oil->oil.mfcc_mcastgrp;
+
+  pim_zlookup_sg_statistics (c_oil);
   if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
     {
-      char group_str[100];
-      char source_str[100];
-
-      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-
-      zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
-                (unsigned long)SIOCGETSGCNT,
-                source_str,
-                group_str,
-                errno,
-                safe_strerror(errno));
+      if (PIM_DEBUG_MROUTE)
+       {
+         struct prefix_sg sg;
+
+         sg.src = c_oil->oil.mfcc_origin;
+         sg.grp = c_oil->oil.mfcc_mcastgrp;
+
+         zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
+                    (unsigned long)SIOCGETSGCNT,
+                    pim_str_sg_dump (&sg),
+                    errno,
+                    safe_strerror(errno));
+       }
       return;
     }
 
index f385ce09f4316e112d327f9fb05caedf8c387d51..3c15c800daf1915c6c53e4a4756bbf4805d22bd8 100644 (file)
@@ -157,6 +157,10 @@ struct igmpmsg
 #endif
 #endif
 
+#ifndef IGMPMSG_WRVIFWHOLE
+#define IGMPMSG_WRVIFWHOLE      4               /* For PIM processing */
+#endif
+
 /*
   Above: from <linux/mroute.h>
 */
@@ -167,8 +171,8 @@ int pim_mroute_socket_disable(void);
 int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags);
 int pim_mroute_del_vif(int vif_index);
 
-int pim_mroute_add(struct channel_oil *c_oil);
-int pim_mroute_del(struct channel_oil *c_oil);
+int pim_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_mroute_del(struct channel_oil *c_oil, const char *name);
 
 int pim_mroute_msg(int fd, const char *buf, int buf_size);
 
diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c
new file mode 100644 (file)
index 0000000..93141f3
--- /dev/null
@@ -0,0 +1,1606 @@
+/*
+ * IP MSDP for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <lib/hash.h>
+#include <lib/jhash.h>
+#include <lib/log.h>
+#include <lib/prefix.h>
+#include <lib/sockunion.h>
+#include <lib/stream.h>
+#include <lib/thread.h>
+#include <lib/vty.h>
+#include <lib/plist.h>
+
+#include "pimd.h"
+#include "pim_cmd.h"
+#include "pim_memory.h"
+#include "pim_iface.h"
+#include "pim_rp.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_upstream.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_packet.h"
+#include "pim_msdp_socket.h"
+
+struct pim_msdp pim_msdp, *msdp = &pim_msdp;
+
+static void pim_msdp_peer_listen(struct pim_msdp_peer *mp);
+static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start);
+static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start);
+static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start);
+static void pim_msdp_peer_free(struct pim_msdp_peer *mp);
+static void pim_msdp_enable(void);
+static void pim_msdp_sa_adv_timer_setup(bool start);
+static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags);
+static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2);
+static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr);
+static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr);
+
+/************************ SA cache management ******************************/
+static void
+pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, const char *timer_str)
+{
+  zlog_debug("MSDP SA %s %s timer expired", sa->sg_str, timer_str);
+}
+
+/* RFC-3618:Sec-5.1 - global active source advertisement timer */
+static int
+pim_msdp_sa_adv_timer_cb(struct thread *t)
+{
+  msdp->sa_adv_timer = NULL;
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP SA advertisment timer expired");
+  }
+
+  pim_msdp_sa_adv_timer_setup(true /* start */);
+  pim_msdp_pkt_sa_tx();
+  return 0;
+}
+static void
+pim_msdp_sa_adv_timer_setup(bool start)
+{
+  THREAD_OFF(msdp->sa_adv_timer);
+  if (start) {
+    THREAD_TIMER_ON(msdp->master, msdp->sa_adv_timer,
+        pim_msdp_sa_adv_timer_cb, NULL, PIM_MSDP_SA_ADVERTISMENT_TIME);
+  }
+}
+
+/* RFC-3618:Sec-5.3 - SA cache state timer */
+static int
+pim_msdp_sa_state_timer_cb(struct thread *t)
+{
+  struct pim_msdp_sa *sa;
+
+  sa = THREAD_ARG(t);
+  sa->sa_state_timer = NULL;
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_sa_timer_expiry_log(sa, "state");
+  }
+
+  pim_msdp_sa_deref(sa, PIM_MSDP_SAF_PEER);
+  return 0;
+}
+static void
+pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start)
+{
+  THREAD_OFF(sa->sa_state_timer);
+  if (start) {
+    THREAD_TIMER_ON(msdp->master, sa->sa_state_timer,
+        pim_msdp_sa_state_timer_cb, sa, PIM_MSDP_SA_HOLD_TIME);
+  }
+}
+
+static void
+pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa)
+{
+  struct pim_upstream *up = sa->up;
+  if (!up) {
+    return;
+  }
+
+  sa->up = NULL;
+  if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
+    PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
+    sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG;
+    pim_upstream_del(up, __PRETTY_FUNCTION__);
+    sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG;
+  }
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP SA %s de-referenced SPT", sa->sg_str);
+  }
+}
+
+static bool
+pim_msdp_sa_upstream_add_ok(struct pim_msdp_sa *sa, struct pim_upstream *xg_up)
+{
+  if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
+    /* SA should have been rxed from a peer */
+    return false;
+  }
+  /* check if we are RP */
+  if (!I_am_RP(sa->sg.grp)) {
+    return false;
+  }
+
+  /* check if we have a (*, G) with a non-empty immediate OIL */
+  if (!xg_up) {
+    struct prefix_sg sg;
+
+    memset(&sg, 0, sizeof(sg));
+    sg.grp = sa->sg.grp;
+
+    xg_up = pim_upstream_find(&sg);
+  }
+  if (!xg_up || (xg_up->join_state != PIM_UPSTREAM_JOINED)) {
+    /* join desired will be true for such (*, G) entries so we will
+     * just look at join_state and let the PIM state machine do the rest of
+     * the magic */
+    return false;
+  }
+
+  return true;
+}
+
+/* Upstream add evaluation needs to happen everytime -
+ * 1. Peer reference is added or removed.
+ * 2. The RP for a group changes.
+ * 3. joinDesired for the associated (*, G) changes
+ * 4. associated (*, G) is removed - this seems like a bit redundant
+ *    (considering #4); but just in case an entry gets nuked without
+ *    upstream state transition
+ *    */
+static void
+pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa,
+                            struct pim_upstream *xg_up, const char *ctx)
+{
+  struct pim_upstream *up;
+
+  if (!pim_msdp_sa_upstream_add_ok(sa, xg_up)) {
+    pim_msdp_sa_upstream_del(sa);
+    return;
+  }
+
+  if (sa->up) {
+    /* nothing to do */
+    return;
+  }
+
+  up = pim_upstream_find(&sa->sg);
+  if (up && (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags))) {
+    /* somehow we lost track of the upstream ptr? best log it */
+    sa->up = up;
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      zlog_debug("MSDP SA %s SPT reference missing", sa->sg_str);
+    }
+    return;
+  }
+
+  /* RFC3618: "RP triggers a (S, G) join event towards the data source
+   * as if a JP message was rxed addressed to the RP itself." */
+  up = pim_upstream_add(&sa->sg, NULL /* iif */,
+                        PIM_UPSTREAM_FLAG_MASK_SRC_MSDP,
+                        __PRETTY_FUNCTION__);
+
+  sa->up = up;
+  if (up) {
+    /* update inherited oil */
+    pim_upstream_inherited_olist(up);
+    /* should we also start the kat in parallel? we will need it when the
+     * SA ages out */
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      zlog_debug("MSDP SA %s referenced SPT", sa->sg_str);
+    }
+  } else {
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      zlog_debug("MSDP SA %s SPT reference failed", sa->sg_str);
+    }
+  }
+}
+
+/* release all mem associated with a sa */
+static void
+pim_msdp_sa_free(struct pim_msdp_sa *sa)
+{
+  XFREE(MTYPE_PIM_MSDP_SA, sa);
+}
+
+static struct pim_msdp_sa *
+pim_msdp_sa_new(struct prefix_sg *sg, struct in_addr rp)
+{
+  struct pim_msdp_sa *sa;
+
+  sa = XCALLOC(MTYPE_PIM_MSDP_SA, sizeof(*sa));
+  if (!sa) {
+    zlog_err("%s: PIM XCALLOC(%zu) failure",
+            __PRETTY_FUNCTION__, sizeof(*sa));
+    return NULL;
+  }
+
+  sa->sg = *sg;
+  pim_str_sg_set(sg, sa->sg_str);
+  sa->rp = rp;
+  sa->uptime = pim_time_monotonic_sec();
+
+  /* insert into misc tables for easy access */
+  sa = hash_get(msdp->sa_hash, sa, hash_alloc_intern);
+  if (!sa) {
+    zlog_err("%s: PIM hash get failure", __PRETTY_FUNCTION__);
+    pim_msdp_sa_free(sa);
+    return NULL;
+  }
+  listnode_add_sort(msdp->sa_list, sa);
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP SA %s created", sa->sg_str);
+  }
+
+  return sa;
+}
+
+static struct pim_msdp_sa *
+pim_msdp_sa_find(struct prefix_sg *sg)
+{
+  struct pim_msdp_sa lookup;
+
+  lookup.sg = *sg;
+  return hash_lookup(msdp->sa_hash, &lookup);
+}
+
+static struct pim_msdp_sa *
+pim_msdp_sa_add(struct prefix_sg *sg, struct in_addr rp)
+{
+  struct pim_msdp_sa *sa;
+
+  sa = pim_msdp_sa_find(sg);
+  if (sa) {
+    return sa;
+  }
+
+  return pim_msdp_sa_new(sg, rp);
+}
+
+static void
+pim_msdp_sa_del(struct pim_msdp_sa * sa)
+{
+  /* this is somewhat redundant - still want to be careful not to leave
+   * stale upstream references */
+  pim_msdp_sa_upstream_del(sa);
+
+  /* stop timers */
+  pim_msdp_sa_state_timer_setup(sa, false /* start */);
+
+  /* remove the entry from various tables */
+  listnode_delete(msdp->sa_list, sa);
+  hash_release(msdp->sa_hash, sa);
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP SA %s deleted", sa->sg_str);
+  }
+
+  /* free up any associated memory */
+  pim_msdp_sa_free(sa);
+}
+
+static void
+pim_msdp_sa_peer_ip_set(struct pim_msdp_sa *sa, struct pim_msdp_peer *mp, struct in_addr rp)
+{
+  struct pim_msdp_peer *old_mp;
+
+  /* optimize the "no change" case as it will happen
+   * frequently/periodically */
+  if (mp && (sa->peer.s_addr == mp->peer.s_addr)) {
+    return;
+  }
+
+  /* any time the peer ip changes also update the rp address */
+  if (PIM_INADDR_ISNOT_ANY(sa->peer)) {
+    old_mp = pim_msdp_peer_find(sa->peer);
+    if (old_mp && old_mp->sa_cnt) {
+      --old_mp->sa_cnt;
+    }
+  }
+
+  if (mp) {
+    ++mp->sa_cnt;
+    sa->peer = mp->peer;
+  } else {
+    sa->peer.s_addr = PIM_NET_INADDR_ANY;
+  }
+  sa->rp = rp;
+}
+
+/* When a local active-source is removed there is no way to withdraw the
+ * source from peers. We will simply remove it from the SA cache so it will
+ * not be sent in supsequent SA updates. Peers will consequently timeout the
+ * SA.
+ * Similarly a "peer-added" SA is never explicitly deleted. It is simply
+ * aged out overtime if not seen in the SA updates from the peers. 
+ * XXX: should we provide a knob to drop entries learnt from a peer when the
+ * peer goes down? */
+static void
+pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags)
+{
+  bool update_up = false;
+
+  if ((sa->flags &PIM_MSDP_SAF_LOCAL)) {
+    if (flags & PIM_MSDP_SAF_LOCAL) {
+      if (PIM_DEBUG_MSDP_EVENTS) {
+        zlog_debug("MSDP SA %s local reference removed", sa->sg_str);
+      }
+      if (msdp->local_cnt)
+        --msdp->local_cnt;
+    }
+  }
+
+  if ((sa->flags &PIM_MSDP_SAF_PEER)) {
+    if (flags & PIM_MSDP_SAF_PEER) {
+      struct in_addr rp;
+
+      if (PIM_DEBUG_MSDP_EVENTS) {
+        zlog_debug("MSDP SA %s peer reference removed", sa->sg_str);
+      }
+      pim_msdp_sa_state_timer_setup(sa, false /* start */);
+      rp.s_addr = INADDR_ANY;
+      pim_msdp_sa_peer_ip_set(sa, NULL /* mp */, rp);
+      /* if peer ref was removed we need to remove the msdp reference on the
+       * msdp entry */
+      update_up = true;
+    }
+  }
+
+  sa->flags &= ~flags;
+  if (update_up) {
+    pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "sa-deref");
+  }
+
+  if (!(sa->flags & PIM_MSDP_SAF_REF)) {
+    pim_msdp_sa_del(sa);
+  }
+}
+
+void
+pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg,
+                struct in_addr rp)
+{
+  struct pim_msdp_sa *sa;
+
+  sa = pim_msdp_sa_add(sg, rp);
+  if (!sa) {
+    return;
+  }
+
+  /* reference it */
+  if (mp) {
+    if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
+      sa->flags |= PIM_MSDP_SAF_PEER;
+      if (PIM_DEBUG_MSDP_EVENTS) {
+        zlog_debug("MSDP SA %s added by peer", sa->sg_str);
+      }
+    }
+    pim_msdp_sa_peer_ip_set(sa, mp, rp);
+    /* start/re-start the state timer to prevent cache expiry */
+    pim_msdp_sa_state_timer_setup(sa, true /* start */);
+    /* We re-evaluate SA "SPT-trigger" everytime we hear abt it from a
+     * peer. XXX: If this becomes too much of a periodic overhead we
+     * can make it event based */
+    pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "peer-ref");
+  } else {
+    if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
+      sa->flags |= PIM_MSDP_SAF_LOCAL;
+      ++msdp->local_cnt;
+      if (PIM_DEBUG_MSDP_EVENTS) {
+        zlog_debug("MSDP SA %s added locally", sa->sg_str);
+      }
+      /* send an immediate SA update to peers */
+      pim_msdp_pkt_sa_tx_one(sa);
+    }
+    sa->flags &= ~PIM_MSDP_SAF_STALE;
+  }
+}
+
+/* The following criteria must be met to originate an SA from the MSDP
+ * speaker -
+ * 1. KAT must be running i.e. source is active.
+ * 2. We must be RP for the group.
+ * 3. Source must be registrable to the RP (this is where the RFC is vague
+ *    and especially ambiguous in CLOS networks; with anycast RP all sources
+ *    are potentially registrable to all RPs in the domain). We assume #3 is
+ *    satisfied if -
+ *    a. We are also the FHR-DR for the source (OR)
+ *    b. We rxed a pim register (null or data encapsulated) within the last
+ *       (3 * (1.5 * register_suppression_timer))).
+ */
+static bool
+pim_msdp_sa_local_add_ok(struct pim_upstream *up)
+{
+  if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
+    return false;
+  }
+
+  if (!up->t_ka_timer) {
+    /* stream is not active */
+    return false;
+  }
+
+  if (!I_am_RP(up->sg.grp)) {
+    /* we are not RP for the group */
+    return false;
+  }
+
+  /* we are the FHR-DR for this stream  or we are RP and have seen registers
+   * from a FHR for this source */
+  if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || up->t_msdp_reg_timer) {
+    return true;
+  }
+
+  return false;
+}
+
+static void
+pim_msdp_sa_local_add(struct prefix_sg *sg)
+{
+  struct in_addr rp;
+  rp.s_addr = 0;
+  pim_msdp_sa_ref(NULL /* mp */, sg, rp);
+}
+
+void
+pim_msdp_sa_local_del(struct prefix_sg *sg)
+{
+  struct pim_msdp_sa *sa;
+
+  sa = pim_msdp_sa_find(sg);
+  if (sa) {
+    pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+  }
+}
+
+/* we need to be very cautious with this API as SA del too can trigger an
+ * upstream del and we will get stuck in a simple loop */
+static void
+pim_msdp_sa_local_del_on_up_del(struct prefix_sg *sg)
+{
+  struct pim_msdp_sa *sa;
+
+  sa = pim_msdp_sa_find(sg);
+  if (sa) {
+    if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP local sa %s del on up del", sa->sg_str);
+    }
+
+    /* if there is no local reference escape */
+    if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
+      if (PIM_DEBUG_MSDP_INTERNAL) {
+        zlog_debug("MSDP local sa %s del; no local ref", sa->sg_str);
+      }
+      return;
+    }
+
+    if (sa->flags & PIM_MSDP_SAF_UP_DEL_IN_PROG) {
+      /* MSDP is the one that triggered the upstream del. if this happens
+       * we most certainly have a bug in the PIM upstream state machine. We
+       * will not have a local reference unless the KAT is running. And if the
+       * KAT is running there MUST be an additional source-stream reference to
+       * the flow. Accounting for such cases requires lot of changes; perhaps
+       * address this in the next release? - XXX  */
+      zlog_err("MSDP sa %s SPT teardown is causing the local entry to be removed", sa->sg_str);
+      return;
+    }
+
+    /* we are dropping the sa on upstream del we should not have an
+     * upstream reference */
+    if (sa->up) {
+      if (PIM_DEBUG_MSDP_INTERNAL) {
+        zlog_debug("MSDP local sa %s del; up non-NULL", sa->sg_str);
+      }
+      sa->up = NULL;
+    }
+    pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+  }
+}
+
+/* Local SA qualification needs to be re-evaluated when -
+ * 1. KAT is started or stopped
+ * 2. on RP changes
+ * 3. Whenever FHR status changes for a (S,G) - XXX - currently there
+ *    is no clear path to transition an entry out of "MASK_FHR" need
+ *    to discuss this with Donald. May result in some strangeness if the
+ *    FHR is also the RP.
+ * 4. When msdp_reg timer is started or stopped
+ */
+void
+pim_msdp_sa_local_update(struct pim_upstream *up)
+{
+  if (pim_msdp_sa_local_add_ok(up)) {
+    pim_msdp_sa_local_add(&up->sg);
+  } else {
+    pim_msdp_sa_local_del(&up->sg);
+  }
+}
+
+static void
+pim_msdp_sa_local_setup(void)
+{
+  struct pim_upstream *up;
+  struct listnode *up_node;
+
+  for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) {
+    pim_msdp_sa_local_update(up);
+  }
+}
+
+/* whenever the RP changes we need to re-evaluate the "local" SA-cache */
+/* XXX: needs to be tested */
+void
+pim_msdp_i_am_rp_changed(void)
+{
+  struct listnode *sanode;
+  struct listnode *nextnode;
+  struct pim_msdp_sa *sa;
+
+  if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
+    /* if the feature is not enabled do nothing */
+    return;
+  }
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP i_am_rp changed"); 
+  }
+
+  /* mark all local entries as stale */
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    if (sa->flags & PIM_MSDP_SAF_LOCAL) {
+      sa->flags |= PIM_MSDP_SAF_STALE;
+    }
+  }
+
+  /* re-setup local SA entries */
+  pim_msdp_sa_local_setup();
+
+  for (ALL_LIST_ELEMENTS(msdp->sa_list, sanode, nextnode, sa)) {
+    /* purge stale SA entries */
+    if (sa->flags & PIM_MSDP_SAF_STALE) {
+      /* clear the stale flag; the entry may be kept even after
+       * "local-deref" */
+      sa->flags &= ~PIM_MSDP_SAF_STALE;
+      /* sa_deref can end up freeing the sa; so don't access contents after */
+      pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+    } else {
+      /* if the souce is still active check if we can influence SPT */
+      pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change");
+    }
+  }
+}
+
+/* We track the join state of (*, G) entries. If G has sources in the SA-cache
+ * we need to setup or teardown SPT when the JoinDesired status changes for
+ * (*, G) */
+void
+pim_msdp_up_join_state_changed(struct pim_upstream *xg_up)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP join state changed for %s", xg_up->sg_str);
+  }
+
+  /* If this is not really an XG entry just move on */
+  if ((xg_up->sg.src.s_addr != INADDR_ANY) ||
+      (xg_up->sg.grp.s_addr == INADDR_ANY)) {
+    return;
+  }
+
+  /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
+   * walking */
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    if (sa->sg.grp.s_addr != xg_up->sg.grp.s_addr) {
+      continue;
+    }
+    pim_msdp_sa_upstream_update(sa, xg_up, "up-jp-change");
+  }
+}
+
+static void
+pim_msdp_up_xg_del(struct prefix_sg *sg)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP %s del", pim_str_sg_dump(sg)); 
+  }
+
+  /* If this is not really an XG entry just move on */
+  if ((sg->src.s_addr != INADDR_ANY) ||
+      (sg->grp.s_addr == INADDR_ANY)) {
+    return;
+  }
+
+  /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
+   * walking */
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    if (sa->sg.grp.s_addr != sg->grp.s_addr) {
+      continue;
+    }
+    pim_msdp_sa_upstream_update(sa, NULL /* xg */, "up-jp-change");
+  }
+}
+
+void
+pim_msdp_up_del(struct prefix_sg *sg)
+{
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP up %s del", pim_str_sg_dump(sg));
+  }
+  if (sg->src.s_addr == INADDR_ANY) {
+    pim_msdp_up_xg_del(sg);
+  } else {
+    pim_msdp_sa_local_del_on_up_del(sg);
+  }
+}
+
+/* sa hash and peer list helpers */
+static unsigned int
+pim_msdp_sa_hash_key_make(void *p)
+{
+  struct pim_msdp_sa *sa = p;
+
+  return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0));
+}
+
+static int
+pim_msdp_sa_hash_eq(const void *p1, const void *p2)
+{
+  const struct pim_msdp_sa *sa1 = p1;
+  const struct pim_msdp_sa *sa2 = p2;
+
+  return ((sa1->sg.src.s_addr == sa2->sg.src.s_addr) &&
+          (sa1->sg.grp.s_addr == sa2->sg.grp.s_addr));
+}
+
+static int
+pim_msdp_sa_comp(const void *p1, const void *p2)
+{
+  const struct pim_msdp_sa *sa1 = p1;
+  const struct pim_msdp_sa *sa2 = p2;
+
+  if (ntohl(sa1->sg.grp.s_addr) < ntohl(sa2->sg.grp.s_addr))
+    return -1;
+
+  if (ntohl(sa1->sg.grp.s_addr) > ntohl(sa2->sg.grp.s_addr))
+    return 1;
+
+  if (ntohl(sa1->sg.src.s_addr) < ntohl(sa2->sg.src.s_addr))
+    return -1;
+
+  if (ntohl(sa1->sg.src.s_addr) > ntohl(sa2->sg.src.s_addr))
+    return 1;
+
+  return 0;
+}
+
+/* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */
+/* XXX: this can use a bit of refining and extensions */
+bool
+pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
+{
+  if (mp->peer.s_addr == rp.s_addr) {
+    return true;
+  }
+
+  return false;
+}
+  
+/************************ Peer session management **************************/
+char *
+pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size)
+{
+  switch (state) {
+    case PIM_MSDP_DISABLED:
+      snprintf(buf, buf_size, "%s", "disabled");
+      break;
+    case PIM_MSDP_INACTIVE:
+      snprintf(buf, buf_size, "%s", "inactive");
+      break;
+    case PIM_MSDP_LISTEN:
+      snprintf(buf, buf_size, "%s", "listen");
+      break;
+    case PIM_MSDP_CONNECTING:
+      snprintf(buf, buf_size, "%s", "connecting");
+      break;
+    case PIM_MSDP_ESTABLISHED:
+      snprintf(buf, buf_size, "%s", "established");
+      break;
+    default:
+      snprintf(buf, buf_size, "unk-%d", state);
+  }
+  return buf;
+}
+
+char *
+pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format)
+{
+  char peer_str[INET_ADDRSTRLEN];
+  char local_str[INET_ADDRSTRLEN];
+
+  pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
+  if (long_format) {
+    pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
+    snprintf(buf, buf_size, "MSDP peer %s local %s mg %s",
+        peer_str, local_str, mp->mesh_group_name);
+  } else {
+    snprintf(buf, buf_size, "MSDP peer %s", peer_str);
+  }
+
+  return buf;
+}
+
+static void
+pim_msdp_peer_state_chg_log(struct pim_msdp_peer *mp)
+{
+  char state_str[PIM_MSDP_STATE_STRLEN];
+
+  pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
+  zlog_debug("MSDP peer %s state chg to %s", mp->key_str, state_str);
+}
+
+/* MSDP Connection State Machine actions (defined in RFC-3618:Sec-11.2) */
+/* 11.2.A2: active peer - start connect retry timer; when the timer fires
+ * a tcp connection will be made */
+static void
+pim_msdp_peer_connect(struct pim_msdp_peer *mp)
+{
+  mp->state = PIM_MSDP_CONNECTING;
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_state_chg_log(mp);
+  }
+
+  pim_msdp_peer_cr_timer_setup(mp, true /* start */);
+}
+
+/* 11.2.A3: passive peer - just listen for connections */
+static void
+pim_msdp_peer_listen(struct pim_msdp_peer *mp)
+{
+  mp->state = PIM_MSDP_LISTEN;
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_state_chg_log(mp);
+  }
+
+  /* this is interntionally asymmetric i.e. we set up listen-socket when the
+  * first listening peer is configured; but don't bother tearing it down when
+  * all the peers go down */
+  pim_msdp_sock_listen();
+}
+
+/* 11.2.A4 and 11.2.A5: transition active or passive peer to
+ * established state */
+void
+pim_msdp_peer_established(struct pim_msdp_peer *mp)
+{
+  if (mp->state != PIM_MSDP_ESTABLISHED) {
+    ++mp->est_flaps;
+  }
+
+  mp->state = PIM_MSDP_ESTABLISHED;
+  mp->uptime = pim_time_monotonic_sec();
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_state_chg_log(mp);
+  }
+
+  /* stop retry timer on active peers */
+  pim_msdp_peer_cr_timer_setup(mp, false /* start */);
+
+  /* send KA; start KA and hold timers */
+  pim_msdp_pkt_ka_tx(mp);
+  pim_msdp_peer_ka_timer_setup(mp, true /* start */);
+  pim_msdp_peer_hold_timer_setup(mp, true /* start */);
+
+  pim_msdp_pkt_sa_tx_to_one_peer(mp);
+
+  PIM_MSDP_PEER_WRITE_ON(mp);
+  PIM_MSDP_PEER_READ_ON(mp);
+}
+
+/* 11.2.A6, 11.2.A7 and 11.2.A8: shutdown the peer tcp connection */
+void
+pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state)
+{
+  if (chg_state) {
+    if (mp->state == PIM_MSDP_ESTABLISHED) {
+      ++mp->est_flaps;
+    }
+    mp->state = PIM_MSDP_INACTIVE;
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      pim_msdp_peer_state_chg_log(mp);
+    }
+  }
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s pim_msdp_peer_stop_tcp_conn", mp->key_str);
+  }
+  /* stop read and write threads */
+  PIM_MSDP_PEER_READ_OFF(mp);
+  PIM_MSDP_PEER_WRITE_OFF(mp);
+
+  /* reset buffers */
+  mp->packet_size = 0;
+  if (mp->ibuf)
+    stream_reset(mp->ibuf);
+  if (mp->obuf)
+    stream_fifo_clean(mp->obuf);
+
+  /* stop all peer timers */
+  pim_msdp_peer_ka_timer_setup(mp, false /* start */);
+  pim_msdp_peer_cr_timer_setup(mp, false /* start */);
+  pim_msdp_peer_hold_timer_setup(mp, false /* start */);
+
+  /* close connection */
+  if (mp->fd >= 0) {
+    close(mp->fd);
+    mp->fd = -1;
+  }
+}
+
+/* RFC-3618:Sec-5.6 - stop the peer tcp connection and startover */
+void
+pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str)
+{
+  if (PIM_DEBUG_EVENTS) {
+    zlog_debug("MSDP peer %s tcp reset %s", mp->key_str, rc_str);
+    snprintf(mp->last_reset, sizeof(mp->last_reset), "%s", rc_str);
+  }
+
+  /* close the connection and transition to listening or connecting */
+  pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
+  if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
+    pim_msdp_peer_listen(mp);
+  } else {
+    pim_msdp_peer_connect(mp);
+  }
+}
+
+static void
+pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp, const char *timer_str)
+{
+  zlog_debug("MSDP peer %s %s timer expired", mp->key_str, timer_str);
+}
+
+/* RFC-3618:Sec-5.4 - peer hold timer */
+static int
+pim_msdp_peer_hold_timer_cb(struct thread *t)
+{
+  struct pim_msdp_peer *mp;
+
+  mp = THREAD_ARG(t);
+  mp->hold_timer = NULL;
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_timer_expiry_log(mp, "hold");
+  }
+
+  if (mp->state != PIM_MSDP_ESTABLISHED) {
+    return 0;
+  }
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_state_chg_log(mp);
+  }
+  pim_msdp_peer_reset_tcp_conn(mp, "ht-expired");
+  return 0;
+}
+static void
+pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start)
+{
+  THREAD_OFF(mp->hold_timer);
+  if (start) {
+    THREAD_TIMER_ON(msdp->master, mp->hold_timer,
+        pim_msdp_peer_hold_timer_cb, mp, PIM_MSDP_PEER_HOLD_TIME);
+  }
+}
+
+
+/* RFC-3618:Sec-5.5 - peer keepalive timer */
+static int
+pim_msdp_peer_ka_timer_cb(struct thread *t)
+{
+  struct pim_msdp_peer *mp;
+
+  mp = THREAD_ARG(t);
+  mp->ka_timer = NULL;
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_timer_expiry_log(mp, "ka");
+  }
+
+  pim_msdp_pkt_ka_tx(mp);
+  pim_msdp_peer_ka_timer_setup(mp, true /* start */);
+  return 0;
+}
+static void
+pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start)
+{
+  THREAD_OFF(mp->ka_timer);
+  if (start) {
+    THREAD_TIMER_ON(msdp->master, mp->ka_timer,
+        pim_msdp_peer_ka_timer_cb, mp, PIM_MSDP_PEER_KA_TIME);
+  }
+}
+
+static void
+pim_msdp_peer_active_connect(struct pim_msdp_peer *mp)
+{
+  int rc;
+  ++mp->conn_attempts;
+  rc = pim_msdp_sock_connect(mp);
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s pim_msdp_peer_active_connect: %d", mp->key_str, rc);
+  }
+
+  switch (rc) {
+    case connect_error:
+    case -1:
+      /* connect failed restart the connect-retry timer */
+      pim_msdp_peer_cr_timer_setup(mp, true /* start */);
+      break;
+
+    case connect_success:
+      /* connect was sucessful move to established */
+      pim_msdp_peer_established(mp);
+      break;
+
+    case connect_in_progress:
+      /* for NB content we need to wait till sock is readable or
+       * writeable */
+      PIM_MSDP_PEER_WRITE_ON(mp);
+      PIM_MSDP_PEER_READ_ON(mp);
+      /* also restart connect-retry timer to reset the socket if connect is
+       * not sucessful */
+      pim_msdp_peer_cr_timer_setup(mp, true /* start */);
+      break;
+  }
+}
+
+/* RFC-3618:Sec-5.6 - connection retry on active peer */
+static int
+pim_msdp_peer_cr_timer_cb(struct thread *t)
+{
+  struct pim_msdp_peer *mp;
+
+  mp = THREAD_ARG(t);
+  mp->cr_timer = NULL;
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    pim_msdp_peer_timer_expiry_log(mp, "connect-retry");
+  }
+
+  if (mp->state != PIM_MSDP_CONNECTING || PIM_MSDP_PEER_IS_LISTENER(mp)) {
+    return 0;
+  }
+
+  pim_msdp_peer_active_connect(mp);
+  return 0;
+}
+static void
+pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start)
+{
+  THREAD_OFF(mp->cr_timer);
+  if (start) {
+    THREAD_TIMER_ON(msdp->master, mp->cr_timer,
+        pim_msdp_peer_cr_timer_cb, mp, PIM_MSDP_PEER_CONNECT_RETRY_TIME);
+  }
+}
+
+/* if a valid packet is rxed from the peer we can restart hold timer */
+void
+pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp)
+{
+  if (mp->state == PIM_MSDP_ESTABLISHED) {
+    pim_msdp_peer_hold_timer_setup(mp, true /* start */);
+  }
+}
+
+/* if a valid packet is txed to the peer we can restart ka timer and avoid
+ * unnecessary ka noise in the network */
+void
+pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp)
+{
+  if (mp->state == PIM_MSDP_ESTABLISHED) {
+    pim_msdp_peer_ka_timer_setup(mp, true /* start */);
+    if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP ka timer restart on pkt tx to %s", mp->key_str);
+    }
+  }
+}
+
+static void pim_msdp_addr2su(union sockunion *su, struct in_addr addr)
+{
+  sockunion_init(su);
+  su->sin.sin_addr = addr;
+  su->sin.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  su->sin.sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+}
+
+/* 11.2.A1: create a new peer and transition state to listen or connecting */
+static enum pim_msdp_err
+pim_msdp_peer_new(struct in_addr peer_addr, struct in_addr local_addr,
+                  const char *mesh_group_name, struct pim_msdp_peer **mp_p)
+{
+  struct pim_msdp_peer *mp;
+
+  pim_msdp_enable();
+
+  mp = XCALLOC(MTYPE_PIM_MSDP_PEER, sizeof(*mp));
+  if (!mp) {
+    zlog_err("%s: PIM XCALLOC(%zu) failure",
+             __PRETTY_FUNCTION__, sizeof(*mp));
+    return PIM_MSDP_ERR_OOM;
+  }
+
+  mp->peer = peer_addr;
+  pim_inet4_dump("<peer?>", mp->peer, mp->key_str, sizeof(mp->key_str));
+  pim_msdp_addr2su(&mp->su_peer, mp->peer);
+  mp->local = local_addr;
+  /* XXX: originator_id setting needs to move to the mesh group */
+  msdp->originator_id = local_addr;
+  pim_msdp_addr2su(&mp->su_local, mp->local);
+  mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
+  mp->state = PIM_MSDP_INACTIVE;
+  mp->fd = -1;
+  strcpy(mp->last_reset, "-");
+  /* higher IP address is listener */
+  if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) {
+    mp->flags |= PIM_MSDP_PEERF_LISTENER;
+  }
+
+  /* setup packet buffers */
+  mp->ibuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
+  mp->obuf = stream_fifo_new();
+
+  /* insert into misc tables for easy access */
+  mp = hash_get(msdp->peer_hash, mp, hash_alloc_intern);
+  listnode_add_sort(msdp->peer_list, mp);
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP peer %s created", mp->key_str);
+
+    pim_msdp_peer_state_chg_log(mp);
+  }
+
+  /* fireup the connect state machine */
+  if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
+    pim_msdp_peer_listen(mp);
+  } else {
+    pim_msdp_peer_connect(mp);
+  }
+  if (mp_p) {
+    *mp_p = mp;
+  }
+  return PIM_MSDP_ERR_NONE;
+}
+
+struct pim_msdp_peer *
+pim_msdp_peer_find(struct in_addr peer_addr)
+{
+  struct pim_msdp_peer lookup;
+
+  lookup.peer = peer_addr;
+  return hash_lookup(msdp->peer_hash, &lookup);
+}
+
+/* add peer configuration if it doesn't already exist */
+enum pim_msdp_err
+pim_msdp_peer_add(struct in_addr peer_addr, struct in_addr local_addr,
+                  const char *mesh_group_name, struct pim_msdp_peer **mp_p)
+{
+  struct pim_msdp_peer *mp;
+
+  if (mp_p) {
+    *mp_p = NULL;
+  }
+
+  if (peer_addr.s_addr == local_addr.s_addr) {
+    /* skip session setup if config is invalid */
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      char peer_str[INET_ADDRSTRLEN];
+
+      pim_inet4_dump("<peer?>", peer_addr, peer_str, sizeof(peer_str));
+      zlog_debug("%s add skipped as DIP=SIP", peer_str);
+    }
+    return PIM_MSDP_ERR_SIP_EQ_DIP;
+  }
+
+  mp = pim_msdp_peer_find(peer_addr);
+  if (mp) {
+    if (mp_p) {
+      *mp_p = mp;
+    }
+    return PIM_MSDP_ERR_PEER_EXISTS;
+  }
+
+  return pim_msdp_peer_new(peer_addr, local_addr, mesh_group_name, mp_p);
+}
+
+/* release all mem associated with a peer */
+static void
+pim_msdp_peer_free(struct pim_msdp_peer *mp)
+{
+  if (mp->ibuf) {
+    stream_free(mp->ibuf);
+  }
+
+  if (mp->obuf) {
+    stream_fifo_free(mp->obuf);
+  }
+
+  if (mp->mesh_group_name) {
+    XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name);
+  }
+  XFREE(MTYPE_PIM_MSDP_PEER, mp);
+}
+
+/* delete the peer config */
+static enum pim_msdp_err
+pim_msdp_peer_do_del(struct pim_msdp_peer *mp)
+{
+  /* stop the tcp connection and shutdown all timers */
+  pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
+
+  /* remove the session from various tables */
+  listnode_delete(msdp->peer_list, mp);
+  hash_release(msdp->peer_hash, mp);
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP peer %s deleted", mp->key_str);
+  }
+
+  /* free up any associated memory */
+  pim_msdp_peer_free(mp);
+
+  return PIM_MSDP_ERR_NONE;
+}
+
+enum pim_msdp_err
+pim_msdp_peer_del(struct in_addr peer_addr)
+{
+  struct pim_msdp_peer *mp;
+
+  mp = pim_msdp_peer_find(peer_addr);
+  if (!mp) {
+    return PIM_MSDP_ERR_NO_PEER;
+  }
+
+  return pim_msdp_peer_do_del(mp);
+}
+
+/* peer hash and peer list helpers */
+static unsigned int
+pim_msdp_peer_hash_key_make(void *p)
+{
+  struct pim_msdp_peer *mp = p;
+  return (jhash_1word(mp->peer.s_addr, 0));
+}
+
+static int
+pim_msdp_peer_hash_eq(const void *p1, const void *p2)
+{
+  const struct pim_msdp_peer *mp1 = p1;
+  const struct pim_msdp_peer *mp2 = p2;
+
+  return (mp1->peer.s_addr == mp2->peer.s_addr);
+}
+
+static int
+pim_msdp_peer_comp(const void *p1, const void *p2)
+{
+  const struct pim_msdp_peer *mp1 = p1;
+  const struct pim_msdp_peer *mp2 = p2;
+
+  if (ntohl(mp1->peer.s_addr) < ntohl(mp2->peer.s_addr))
+    return -1;
+
+  if (ntohl(mp1->peer.s_addr) > ntohl(mp2->peer.s_addr))
+    return 1;
+
+  return 0;
+}
+
+/************************** Mesh group management **************************/
+static void
+pim_msdp_mg_free(struct pim_msdp_mg *mg)
+{
+  /* If the mesh-group has valid member or src_ip don't delete it */
+  if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) {
+    return;
+  }
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name);
+  }
+  if (mg->mesh_group_name)
+    XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name);
+
+  if (mg->mbr_list)
+    list_free(mg->mbr_list);
+
+  XFREE(MTYPE_PIM_MSDP_MG, mg);
+  msdp->mg = NULL;
+}
+
+static struct pim_msdp_mg *
+pim_msdp_mg_new(const char *mesh_group_name)
+{
+  struct pim_msdp_mg *mg;
+
+  mg = XCALLOC(MTYPE_PIM_MSDP_MG, sizeof(*mg));
+  if (!mg) {
+    zlog_err("%s: PIM XCALLOC(%zu) failure",
+             __PRETTY_FUNCTION__, sizeof(*mg));
+    return NULL;
+  }
+
+  mg->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
+  mg->mbr_list = list_new();
+  mg->mbr_list->del = (void (*)(void *))pim_msdp_mg_mbr_free;
+  mg->mbr_list->cmp = (int (*)(void *, void *))pim_msdp_mg_mbr_comp;
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name);
+  }
+  return mg;
+}
+
+enum pim_msdp_err
+pim_msdp_mg_del(const char *mesh_group_name)
+{
+  struct pim_msdp_mg *mg = msdp->mg;
+  struct pim_msdp_mg_mbr *mbr;
+
+  if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+    return PIM_MSDP_ERR_NO_MG;
+  }
+
+  /* delete all the mesh-group members */
+  while (!list_isempty(mg->mbr_list)) {
+    mbr = listnode_head(mg->mbr_list);
+    pim_msdp_mg_mbr_do_del(mg, mbr);
+  }
+
+  /* clear src ip */
+  mg->src_ip.s_addr = INADDR_ANY;
+
+  /* free up the mesh-group */
+  pim_msdp_mg_free(mg);
+  return PIM_MSDP_ERR_NONE;
+}
+
+static enum pim_msdp_err
+pim_msdp_mg_add(const char *mesh_group_name)
+{
+  if (msdp->mg) {
+    if (!strcmp(msdp->mg->mesh_group_name, mesh_group_name)) {
+      return PIM_MSDP_ERR_NONE;
+    }
+    /* currently only one mesh-group can exist at a time */
+    return PIM_MSDP_ERR_MAX_MESH_GROUPS;
+  }
+
+  msdp->mg = pim_msdp_mg_new(mesh_group_name);
+  if (!msdp->mg) {
+    return PIM_MSDP_ERR_OOM;
+  }
+
+  return PIM_MSDP_ERR_NONE;
+}
+
+static int
+pim_msdp_mg_mbr_comp(const void *p1, const void *p2)
+{
+  const struct pim_msdp_mg_mbr *mbr1 = p1;
+  const struct pim_msdp_mg_mbr *mbr2 = p2;
+
+  if (ntohl(mbr1->mbr_ip.s_addr) < ntohl(mbr2->mbr_ip.s_addr))
+    return -1;
+
+  if (ntohl(mbr1->mbr_ip.s_addr) > ntohl(mbr2->mbr_ip.s_addr))
+    return 1;
+
+  return 0;
+}
+
+static void
+pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr)
+{
+  XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr);
+}
+
+static struct pim_msdp_mg_mbr *
+pim_msdp_mg_mbr_find(struct in_addr mbr_ip)
+{
+  struct pim_msdp_mg_mbr *mbr;
+  struct listnode *mbr_node;
+
+  if (!msdp->mg) {
+    return NULL;
+  }
+  /* we can move this to a hash but considering that number of peers in
+   * a mesh-group that seems like bit of an overkill */
+  for (ALL_LIST_ELEMENTS_RO(msdp->mg->mbr_list, mbr_node, mbr)) {
+    if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) {
+      return mbr;
+    }
+  }
+  return mbr;
+}
+
+enum pim_msdp_err
+pim_msdp_mg_mbr_add(const char *mesh_group_name, struct in_addr mbr_ip)
+{
+  int rc;
+  struct pim_msdp_mg_mbr *mbr;
+  struct pim_msdp_mg *mg;
+
+  rc = pim_msdp_mg_add(mesh_group_name);
+  if (rc != PIM_MSDP_ERR_NONE) {
+    return rc;
+  }
+
+  mg = msdp->mg;
+  mbr = pim_msdp_mg_mbr_find(mbr_ip);
+  if (mbr) {
+    return PIM_MSDP_ERR_MG_MBR_EXISTS;
+  }
+
+  mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr));
+  if (!mbr) {
+    zlog_err("%s: PIM XCALLOC(%zu) failure",
+             __PRETTY_FUNCTION__, sizeof(*mbr));
+    /* if there are no references to the mg free it */
+    pim_msdp_mg_free(mg);
+    return PIM_MSDP_ERR_OOM;
+  }
+  mbr->mbr_ip = mbr_ip;
+  listnode_add_sort(mg->mbr_list, mbr);
+
+  /* if valid SIP has been configured add peer session */
+  if (mg->src_ip.s_addr != INADDR_ANY) {
+    pim_msdp_peer_add(mbr_ip, mg->src_ip, mesh_group_name,
+                           &mbr->mp);
+  }
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    char ip_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
+    zlog_debug("MSDP mesh-group %s mbr %s created", mg->mesh_group_name, ip_str);
+  }
+  ++mg->mbr_cnt;
+  return PIM_MSDP_ERR_NONE;
+}
+
+static void
+pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr)
+{
+  /* Delete active peer session if any */
+  if (mbr->mp) {
+    pim_msdp_peer_do_del(mbr->mp);
+  }
+
+  listnode_delete(mg->mbr_list, mbr);
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    char ip_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
+    zlog_debug("MSDP mesh-group %s mbr %s deleted", mg->mesh_group_name, ip_str);
+  }
+  pim_msdp_mg_mbr_free(mbr);
+  if (mg->mbr_cnt) {
+    --mg->mbr_cnt;
+  }
+}
+
+enum pim_msdp_err
+pim_msdp_mg_mbr_del(const char *mesh_group_name, struct in_addr mbr_ip)
+{
+  struct pim_msdp_mg_mbr *mbr;
+  struct pim_msdp_mg *mg = msdp->mg;
+
+  if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+    return PIM_MSDP_ERR_NO_MG;
+  }
+
+  mbr = pim_msdp_mg_mbr_find(mbr_ip);
+  if (!mbr) {
+    return PIM_MSDP_ERR_NO_MG_MBR;
+  }
+
+  pim_msdp_mg_mbr_do_del(mg, mbr);
+  /* if there are no references to the mg free it */
+  pim_msdp_mg_free(mg);
+
+  return PIM_MSDP_ERR_NONE;
+}
+
+static void
+pim_msdp_mg_src_do_del(void)
+{
+  struct pim_msdp_mg_mbr *mbr;
+  struct listnode *mbr_node;
+  struct pim_msdp_mg *mg = msdp->mg;
+
+  /* SIP is being removed - tear down all active peer sessions */
+  for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
+    if (mbr->mp) {
+      pim_msdp_peer_do_del(mbr->mp);
+      mbr->mp = NULL;
+    }
+  }
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    zlog_debug("MSDP mesh-group %s src cleared", mg->mesh_group_name);
+  }
+}
+
+enum pim_msdp_err
+pim_msdp_mg_src_del(const char *mesh_group_name)
+{
+  struct pim_msdp_mg *mg = msdp->mg;
+
+  if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+    return PIM_MSDP_ERR_NO_MG;
+  }
+
+  if (mg->src_ip.s_addr != INADDR_ANY) {
+    mg->src_ip.s_addr = INADDR_ANY;
+    pim_msdp_mg_src_do_del();
+    /* if there are no references to the mg free it */
+    pim_msdp_mg_free(mg);
+  }
+  return PIM_MSDP_ERR_NONE;
+}
+
+enum pim_msdp_err
+pim_msdp_mg_src_add(const char *mesh_group_name, struct in_addr src_ip)
+{
+  int rc;
+  struct pim_msdp_mg_mbr *mbr;
+  struct listnode *mbr_node;
+  struct pim_msdp_mg *mg;
+
+  if (src_ip.s_addr == INADDR_ANY) {
+    pim_msdp_mg_src_del(mesh_group_name);
+    return PIM_MSDP_ERR_NONE;
+  }
+
+  rc = pim_msdp_mg_add(mesh_group_name);
+  if (rc != PIM_MSDP_ERR_NONE) {
+    return rc;
+  }
+
+  mg = msdp->mg;
+  if (mg->src_ip.s_addr != INADDR_ANY) {
+    pim_msdp_mg_src_do_del();
+  }
+  mg->src_ip = src_ip;
+
+  for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
+    pim_msdp_peer_add(mbr->mbr_ip, mg->src_ip, mesh_group_name,
+                           &mbr->mp);
+  }
+
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    char ip_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<src?>", mg->src_ip, ip_str, sizeof(ip_str));
+    zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name, ip_str);
+  }
+  return PIM_MSDP_ERR_NONE;
+}
+
+/*********************** MSDP feature APIs *********************************/
+int
+pim_msdp_config_write(struct vty *vty)
+{
+  struct listnode *mbrnode;
+  struct pim_msdp_mg_mbr *mbr;
+  struct pim_msdp_mg *mg = msdp->mg;
+  char mbr_str[INET_ADDRSTRLEN];
+  char src_str[INET_ADDRSTRLEN];
+  int count = 0;
+
+  if (!mg) {
+    return count;
+  }
+
+  if (mg->src_ip.s_addr != INADDR_ANY) {
+    pim_inet4_dump("<src?>", mg->src_ip, src_str, sizeof(src_str));
+    vty_out(vty, "ip msdp mesh-group %s source %s%s",
+        mg->mesh_group_name, src_str, VTY_NEWLINE);
+    ++count;
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
+    pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
+    vty_out(vty, "ip msdp mesh-group %s member %s%s",
+        mg->mesh_group_name, mbr_str, VTY_NEWLINE);
+    ++count;
+  }
+  return count;
+}
+
+/* Enable feature including active/periodic timers etc. on the first peer
+ * config. Till then MSDP should just stay quiet. */
+static void
+pim_msdp_enable(void)
+{
+  if (msdp->flags & PIM_MSDPF_ENABLE) {
+    /* feature is already enabled */
+    return;
+  }
+  msdp->flags |= PIM_MSDPF_ENABLE;
+  msdp->work_obuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
+  pim_msdp_sa_adv_timer_setup(true /* start */);
+  /* setup sa cache based on local sources */
+  pim_msdp_sa_local_setup();
+}
+
+/* MSDP init */
+void
+pim_msdp_init(struct thread_master *master)
+{
+  msdp->master = master;
+
+  msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make,
+                                 pim_msdp_peer_hash_eq);
+  msdp->peer_list = list_new();
+  msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free;
+  msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp;
+
+  msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make,
+                                 pim_msdp_sa_hash_eq);
+  msdp->sa_list = list_new();
+  msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free;
+  msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp;
+}
+
+/* counterpart to MSDP init; XXX: unused currently */
+void
+pim_msdp_exit(void)
+{
+  /* XXX: stop listener and delete all peer sessions */
+
+  if (msdp->peer_hash) {
+    hash_free(msdp->peer_hash);
+    msdp->peer_hash = NULL;
+  }
+
+  if (msdp->peer_list) {
+    list_free(msdp->peer_list);
+    msdp->peer_list = NULL;
+  }
+}
diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h
new file mode 100644 (file)
index 0000000..33c1d88
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * IP MSDP for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef PIM_MSDP_H
+#define PIM_MSDP_H
+
+enum pim_msdp_peer_state {
+  PIM_MSDP_DISABLED,
+  PIM_MSDP_INACTIVE,
+  PIM_MSDP_LISTEN,
+  PIM_MSDP_CONNECTING,
+  PIM_MSDP_ESTABLISHED
+};
+
+/* SA and KA TLVs are processed; rest ignored */
+enum pim_msdp_tlv {
+  PIM_MSDP_V4_SOURCE_ACTIVE = 1,
+  PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST,
+  PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE,
+  PIM_MSDP_KEEPALIVE,
+  PIM_MSDP_RESERVED,
+  PIM_MSDP_TRACEROUTE_PROGRESS,
+  PIM_MSDP_TRACEROUTE_REPLY,
+};
+
+/* MSDP error codes */
+enum pim_msdp_err {
+  PIM_MSDP_ERR_NONE = 0,
+  PIM_MSDP_ERR_OOM = -1,
+  PIM_MSDP_ERR_PEER_EXISTS = -2,
+  PIM_MSDP_ERR_MAX_MESH_GROUPS = -3,
+  PIM_MSDP_ERR_NO_PEER = -4,
+  PIM_MSDP_ERR_MG_MBR_EXISTS = -5,
+  PIM_MSDP_ERR_NO_MG = -6,
+  PIM_MSDP_ERR_NO_MG_MBR = -7,
+  PIM_MSDP_ERR_SIP_EQ_DIP = -8,
+};
+
+#define PIM_MSDP_STATE_STRLEN 16
+#define PIM_MSDP_UPTIME_STRLEN 80
+#define PIM_MSDP_TIMER_STRLEN 12
+#define PIM_MSDP_TCP_PORT 639
+#define PIM_MSDP_SOCKET_SNDBUF_SIZE 65536
+
+enum pim_msdp_sa_flags {
+  PIM_MSDP_SAF_NONE = 0,
+  /* There are two cases where we can pickup an active source locally -
+   * 1. We are RP and got a source-register from the FHR
+   * 2. We are RP and FHR and learnt a new directly connected source on a
+   * DR interface */
+  PIM_MSDP_SAF_LOCAL = (1 << 0),
+  /* We got this in the MSDP SA TLV from a peer (and this passed peer-RPF
+   * checks) */
+  PIM_MSDP_SAF_PEER = (1 << 1),
+  PIM_MSDP_SAF_REF = (PIM_MSDP_SAF_LOCAL | PIM_MSDP_SAF_PEER),
+  PIM_MSDP_SAF_STALE = (1 << 2), /* local entries can get kicked out on
+                                 * misc pim events such as RP change */
+  PIM_MSDP_SAF_UP_DEL_IN_PROG = (1 << 3)
+};
+
+struct pim_msdp_sa {
+  struct prefix_sg sg;
+  char sg_str[PIM_SG_LEN];
+  struct in_addr rp; /* Last RP address associated with this SA */
+  struct in_addr peer; /* last peer from who we heard this SA */
+  enum pim_msdp_sa_flags flags;
+
+  /* rfc-3618 is missing default value for SA-hold-down-Period. pulled
+   * this number from industry-standards */
+#define PIM_MSDP_SA_HOLD_TIME ((3*60)+30)
+  struct thread *sa_state_timer;  // 5.6
+  int64_t uptime;
+
+  struct pim_upstream *up;
+};
+
+enum pim_msdp_peer_flags {
+  PIM_MSDP_PEERF_NONE = 0,
+  PIM_MSDP_PEERF_LISTENER = (1 << 0),
+#define PIM_MSDP_PEER_IS_LISTENER(mp) (mp->flags & PIM_MSDP_PEERF_LISTENER)
+  PIM_MSDP_PEERF_SA_JUST_SENT = (1 << 1)
+};
+
+struct pim_msdp_peer {
+  /* configuration */
+  struct in_addr local;
+  struct in_addr peer;
+  char *mesh_group_name;
+  char key_str[INET_ADDRSTRLEN];
+
+  /* state */
+  enum pim_msdp_peer_state state;
+  enum pim_msdp_peer_flags flags;
+
+  /* TCP socket info */
+  union sockunion su_local;
+  union sockunion su_peer;
+  int fd;
+
+  /* protocol timers */
+#define PIM_MSDP_PEER_HOLD_TIME 75
+  struct thread *hold_timer;   // 5.4
+#define PIM_MSDP_PEER_KA_TIME 60
+  struct thread *ka_timer;  // 5.5
+#define PIM_MSDP_PEER_CONNECT_RETRY_TIME 30
+  struct thread *cr_timer;  // 5.6
+
+  /* packet thread and buffers */
+  uint32_t packet_size;
+  struct stream *ibuf;
+  struct stream_fifo *obuf;
+  struct thread *t_read;
+  struct thread *t_write;
+
+  /* stats */
+  uint32_t conn_attempts;
+  uint32_t est_flaps;
+  uint32_t sa_cnt; /* number of SAs attributed to this peer */
+#define PIM_MSDP_PEER_LAST_RESET_STR 20
+  char last_reset[PIM_MSDP_PEER_LAST_RESET_STR];
+
+  /* packet stats */
+  uint32_t ka_tx_cnt;
+  uint32_t sa_tx_cnt;
+  uint32_t ka_rx_cnt;
+  uint32_t sa_rx_cnt;
+  uint32_t unk_rx_cnt;
+
+  /* timestamps */
+  int64_t uptime;
+};
+
+struct pim_msdp_mg_mbr {
+  struct in_addr mbr_ip;
+  struct pim_msdp_peer *mp;
+};
+
+/* PIM MSDP mesh-group */
+struct pim_msdp_mg {
+  char *mesh_group_name;
+  struct in_addr src_ip;
+  uint32_t mbr_cnt;
+  struct list *mbr_list;
+};
+
+enum pim_msdp_flags {
+  PIM_MSDPF_NONE = 0,
+  PIM_MSDPF_ENABLE = (1 << 0),
+  PIM_MSDPF_LISTENER = (1 << 1)
+};
+
+struct pim_msdp_listener {
+  int fd;
+  union sockunion su;
+  struct thread *thread;
+};
+
+struct pim_msdp {
+  enum pim_msdp_flags flags;
+  struct thread_master *master;
+  struct pim_msdp_listener listener;
+  uint32_t rejected_accepts;
+
+  /* MSDP peer info */
+  struct hash *peer_hash;
+  struct list *peer_list;
+
+  /* MSDP active-source info */
+#define PIM_MSDP_SA_ADVERTISMENT_TIME 60
+  struct thread *sa_adv_timer;  // 5.6
+  struct hash *sa_hash;
+  struct list *sa_list;
+  uint32_t local_cnt;
+
+  /* keep a scratch pad for building SA TLVs */
+  struct stream *work_obuf;
+
+  struct in_addr originator_id;
+
+  /* currently only one mesh-group is supported - so just stash it here */
+  struct pim_msdp_mg *mg;
+};
+
+#define PIM_MSDP_PEER_READ_ON(mp) THREAD_READ_ON(msdp->master, mp->t_read, pim_msdp_read, mp, mp->fd);
+#define PIM_MSDP_PEER_WRITE_ON(mp) THREAD_WRITE_ON(msdp->master, mp->t_write, pim_msdp_write, mp, mp->fd);
+
+#define PIM_MSDP_PEER_READ_OFF(mp) THREAD_READ_OFF(mp->t_read)
+#define PIM_MSDP_PEER_WRITE_OFF(mp) THREAD_WRITE_OFF(mp->t_write)
+
+extern struct pim_msdp *msdp;
+void pim_msdp_init(struct thread_master *master);
+void pim_msdp_exit(void);
+enum pim_msdp_err pim_msdp_peer_add(struct in_addr peer, struct in_addr local, const char *mesh_group_name, struct pim_msdp_peer **mp_p);
+enum pim_msdp_err pim_msdp_peer_del(struct in_addr peer_addr);
+char *pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size);
+struct pim_msdp_peer *pim_msdp_peer_find(struct in_addr peer_addr);
+void pim_msdp_peer_established(struct pim_msdp_peer *mp);
+void pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp);
+void pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state);
+void pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str);
+int pim_msdp_write(struct thread *thread);
+char *pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format);
+int pim_msdp_config_write(struct vty *vty);
+void pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp);
+void pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg, struct in_addr rp);
+void pim_msdp_sa_local_update(struct pim_upstream *up);
+void pim_msdp_sa_local_del(struct prefix_sg *sg);
+void pim_msdp_i_am_rp_changed(void);
+bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp);
+void pim_msdp_up_join_state_changed(struct pim_upstream *xg_up);
+void pim_msdp_up_del(struct prefix_sg *sg);
+enum pim_msdp_err pim_msdp_mg_mbr_add(const char *mesh_group_name, struct in_addr mbr_ip);
+enum pim_msdp_err pim_msdp_mg_mbr_del(const char *mesh_group_name, struct in_addr mbr_ip);
+enum pim_msdp_err pim_msdp_mg_src_del(const char *mesh_group_name);
+enum pim_msdp_err pim_msdp_mg_src_add(const char *mesh_group_name, struct in_addr src_ip);
+enum pim_msdp_err pim_msdp_mg_del(const char *mesh_group_name);
+#endif
diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c
new file mode 100644 (file)
index 0000000..fbf34cd
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * IP MSDP packet helper
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <lib/log.h>
+#include <lib/network.h>
+#include <lib/stream.h>
+#include <lib/thread.h>
+#include <lib/vty.h>
+
+#include "pimd.h"
+#include "pim_str.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_packet.h"
+#include "pim_msdp_socket.h"
+
+static char *
+pim_msdp_pkt_type_dump(enum pim_msdp_tlv type, char *buf, int buf_size)
+{
+  switch (type) {
+    case PIM_MSDP_V4_SOURCE_ACTIVE:
+      snprintf(buf, buf_size, "%s", "SA");
+      break;
+    case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST:
+      snprintf(buf, buf_size, "%s", "SA_REQ");
+      break;
+    case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE:
+      snprintf(buf, buf_size, "%s", "SA_RESP");
+      break;
+    case PIM_MSDP_KEEPALIVE:
+      snprintf(buf, buf_size, "%s", "KA");
+      break;
+    case PIM_MSDP_RESERVED:
+      snprintf(buf, buf_size, "%s", "RSVD");
+      break;
+    case PIM_MSDP_TRACEROUTE_PROGRESS:
+      snprintf(buf, buf_size, "%s", "TRACE_PROG");
+      break;
+    case PIM_MSDP_TRACEROUTE_REPLY:
+      snprintf(buf, buf_size, "%s", "TRACE_REPLY");
+      break;
+    default:
+      snprintf(buf, buf_size, "UNK-%d", type);
+  }
+  return buf;
+}
+
+static void
+pim_msdp_pkt_sa_dump_one(struct stream *s)
+{
+  struct prefix_sg sg;
+
+  /* just throw away the three reserved bytes */
+  stream_get3(s);
+  /* throw away the prefix length also */
+  stream_getc(s);
+
+  memset(&sg, 0, sizeof (struct prefix_sg));
+  sg.grp.s_addr = stream_get_ipv4(s);
+  sg.src.s_addr = stream_get_ipv4(s);
+
+  zlog_debug("  sg %s", pim_str_sg_dump(&sg));
+}
+
+static void
+pim_msdp_pkt_sa_dump(struct stream *s)
+{
+  int entry_cnt;
+  int i;
+  struct in_addr rp; /* Last RP address associated with this SA */
+
+  entry_cnt = stream_getc(s);
+  rp.s_addr = stream_get_ipv4(s);
+
+  if (PIM_DEBUG_MSDP_PACKETS) {
+    char rp_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<rp?>", rp, rp_str, sizeof(rp_str));
+    zlog_debug("  entry_cnt %d rp %s", entry_cnt, rp_str);
+  }
+
+  /* dump SAs */
+  for (i = 0; i < entry_cnt; ++i) {
+    pim_msdp_pkt_sa_dump_one(s);
+  }
+}
+
+static void
+pim_msdp_pkt_dump(struct pim_msdp_peer *mp, int type, int len, bool rx,
+                  struct stream *s)
+{
+  char type_str[PIM_MSDP_PKT_TYPE_STRLEN];
+
+  pim_msdp_pkt_type_dump(type, type_str, sizeof(type_str));
+
+  zlog_debug("MSDP peer %s pkt %s type %s len %d",
+      mp->key_str, rx?"rx":"tx", type_str, len);
+
+  if (!s) {
+    return;
+  }
+
+  switch(type) {
+      case PIM_MSDP_V4_SOURCE_ACTIVE:
+        pim_msdp_pkt_sa_dump(s);
+        break;
+      default:;
+  }
+}
+
+/* Check file descriptor whether connect is established. */
+static void
+pim_msdp_connect_check(struct pim_msdp_peer *mp)
+{
+  int status;
+  socklen_t slen;
+  int ret;
+
+  if (mp->state != PIM_MSDP_CONNECTING) {
+    /* if we are here it means we are not in a connecting or established state
+     * for now treat this as a fatal error */
+    pim_msdp_peer_reset_tcp_conn(mp, "invalid-state");
+    return;
+  }
+
+  PIM_MSDP_PEER_READ_OFF(mp);
+  PIM_MSDP_PEER_WRITE_OFF(mp);
+
+  /* Check file descriptor. */
+  slen = sizeof(status);
+  ret = getsockopt(mp->fd, SOL_SOCKET, SO_ERROR, (void *)&status, &slen);
+
+  /* If getsockopt is fail, this is fatal error. */
+  if (ret < 0) {
+    zlog_err("can't get sockopt for nonblocking connect");
+    pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
+    return;
+  }
+
+  /* When status is 0 then TCP connection is established. */
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s pim_connect_check %s", mp->key_str, status?"fail":"success");
+  }
+  if (status == 0) {
+    pim_msdp_peer_established(mp);
+  } else {
+    pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
+  }
+}
+
+static void
+pim_msdp_pkt_delete(struct pim_msdp_peer *mp)
+{
+  stream_free(stream_fifo_pop(mp->obuf));
+}
+
+static void
+pim_msdp_pkt_add(struct pim_msdp_peer *mp, struct stream *s)
+{
+  stream_fifo_push(mp->obuf, s);
+}
+
+static void
+pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp)
+{
+  if (stream_fifo_head(mp->obuf)) {
+    PIM_MSDP_PEER_WRITE_ON(mp);
+  }
+}
+
+int
+pim_msdp_write(struct thread *thread)
+{
+  struct pim_msdp_peer *mp;
+  struct stream *s;
+  int num;
+  enum pim_msdp_tlv type;
+  int len;
+  int work_cnt = 0;
+  int work_max_cnt = 100;
+
+  mp = THREAD_ARG(thread);
+  mp->t_write = NULL;
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s pim_msdp_write", mp->key_str);
+  }
+  if (mp->fd < 0) {
+    return -1;
+  }
+
+  /* check if TCP connection is established */
+  if (mp->state != PIM_MSDP_ESTABLISHED) {
+    pim_msdp_connect_check(mp);
+    return 0;
+  }
+
+  s = stream_fifo_head(mp->obuf);
+  if (!s) {
+    pim_msdp_write_proceed_actions(mp);
+    return 0;
+  }
+
+  sockopt_cork(mp->fd, 1);
+
+  /* Nonblocking write until TCP output buffer is full  */
+  do
+  {
+    int writenum;
+
+    /* Number of bytes to be sent */
+    writenum = stream_get_endp(s) - stream_get_getp(s);
+
+    /* Call write() system call */
+    num = write(mp->fd, STREAM_PNT(s), writenum);
+    if (num < 0) {
+      /* write failed either retry needed or error */
+      if (ERRNO_IO_RETRY(errno)) {
+        if (PIM_DEBUG_MSDP_INTERNAL) {
+          zlog_debug("MSDP peer %s pim_msdp_write io retry", mp->key_str);
+        }
+        break;
+      }
+
+      pim_msdp_peer_reset_tcp_conn(mp, "pkt-tx-failed");
+      return 0;
+    }
+
+    if (num != writenum) {
+      /* Partial write */
+      stream_forward_getp(s, num);
+      if (PIM_DEBUG_MSDP_INTERNAL) {
+        zlog_debug("MSDP peer %s pim_msdp_partial_write", mp->key_str);
+      }
+      break;
+    }
+
+    /* Retrieve msdp packet type. */
+    stream_set_getp(s,0);
+    type = stream_getc(s);
+    len = stream_getw(s);
+    switch (type)
+    {
+      case PIM_MSDP_KEEPALIVE:
+        mp->ka_tx_cnt++;
+        break;
+      case PIM_MSDP_V4_SOURCE_ACTIVE:
+        mp->sa_tx_cnt++;
+        break;
+      default:;
+    }
+    if (PIM_DEBUG_MSDP_PACKETS) {
+      pim_msdp_pkt_dump(mp, type, len, false /*rx*/, s);
+    }
+
+    /* packet sent delete it. */
+    pim_msdp_pkt_delete(mp);
+
+    ++work_cnt;
+    /* may need to pause if we have done too much work in this
+     * loop */
+    if (work_cnt >= work_max_cnt) {
+      break;
+    }
+  } while ((s = stream_fifo_head(mp->obuf)) != NULL);
+  pim_msdp_write_proceed_actions(mp);
+
+  sockopt_cork(mp->fd, 0);
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets", mp->key_str, work_cnt);
+  }
+
+  return 0;
+}
+
+static void
+pim_msdp_pkt_send(struct pim_msdp_peer *mp, struct stream *s)
+{
+  /* Add packet to the end of list. */
+  pim_msdp_pkt_add(mp, s);
+
+  PIM_MSDP_PEER_WRITE_ON(mp);
+}
+
+void
+pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp)
+{
+  struct stream *s;
+
+  if (mp->state != PIM_MSDP_ESTABLISHED) {
+    /* don't tx anything unless a session is established */
+    return;
+  }
+  s = stream_new(PIM_MSDP_KA_TLV_MAX_SIZE);
+  stream_putc(s, PIM_MSDP_KEEPALIVE);
+  stream_putw(s, PIM_MSDP_KA_TLV_MAX_SIZE);
+
+  pim_msdp_pkt_send(mp, s);
+}
+
+static void
+pim_msdp_pkt_sa_push_to_one_peer(struct pim_msdp_peer *mp)
+{
+  struct stream *s;
+
+  if (mp->state != PIM_MSDP_ESTABLISHED) {
+    /* don't tx anything unless a session is established */
+    return;
+  }
+  s = stream_dup(msdp->work_obuf);
+  if (s) {
+    pim_msdp_pkt_send(mp, s);
+    mp->flags |= PIM_MSDP_PEERF_SA_JUST_SENT;
+  }
+}
+
+/* push the stream into the obuf fifo of all the peers */
+static void
+pim_msdp_pkt_sa_push(struct pim_msdp_peer *mp)
+{
+  struct listnode *mpnode;
+
+  if (mp) {
+    pim_msdp_pkt_sa_push_to_one_peer(mp);
+  } else {
+    for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+      if (PIM_DEBUG_MSDP_INTERNAL) {
+        zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push", mp->key_str);
+      }
+      pim_msdp_pkt_sa_push_to_one_peer(mp);
+    }
+  }
+}
+
+static int
+pim_msdp_pkt_sa_fill_hdr(int local_cnt)
+{
+  int curr_tlv_ecnt;
+
+  stream_reset(msdp->work_obuf);
+  curr_tlv_ecnt = local_cnt>PIM_MSDP_SA_MAX_ENTRY_CNT?PIM_MSDP_SA_MAX_ENTRY_CNT:local_cnt;
+  local_cnt -= curr_tlv_ecnt;
+  stream_putc(msdp->work_obuf, PIM_MSDP_V4_SOURCE_ACTIVE);
+  stream_putw(msdp->work_obuf, PIM_MSDP_SA_ENTRY_CNT2SIZE(curr_tlv_ecnt));
+  stream_putc(msdp->work_obuf, curr_tlv_ecnt);
+  stream_put_ipv4(msdp->work_obuf, msdp->originator_id.s_addr);
+
+  return local_cnt;
+}
+
+static void
+pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa *sa)
+{
+  stream_put3(msdp->work_obuf, 0 /* reserved */);
+  stream_putc(msdp->work_obuf, 32 /* sprefix len */);
+  stream_put_ipv4(msdp->work_obuf, sa->sg.grp.s_addr);
+  stream_put_ipv4(msdp->work_obuf, sa->sg.src.s_addr);
+}
+
+static void
+pim_msdp_pkt_sa_gen(struct pim_msdp_peer *mp)
+{
+  struct listnode *sanode;
+  struct pim_msdp_sa *sa;
+  int sa_count;
+  int local_cnt = msdp->local_cnt;
+
+  sa_count = 0;
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("  sa gen  %d", local_cnt);
+  }
+
+  local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt);
+
+  for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+    if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
+      /* current implementation of MSDP is for anycast i.e. full mesh. so
+       * no re-forwarding of SAs that we learnt from other peers */
+      continue;
+    }
+    /* add sa into scratch pad */
+    pim_msdp_pkt_sa_fill_one(sa);
+    ++sa_count;
+    if (sa_count >= PIM_MSDP_SA_MAX_ENTRY_CNT) {
+      pim_msdp_pkt_sa_push(mp);
+      /* reset headers */
+      sa_count = 0;
+      if (PIM_DEBUG_MSDP_INTERNAL) {
+          zlog_debug("  sa gen for remainder %d", local_cnt);
+      }
+      local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt);
+    }
+  }
+
+  if (sa_count) {
+    pim_msdp_pkt_sa_push(mp);
+  }
+  return;
+}
+
+static void
+pim_msdp_pkt_sa_tx_done(void)
+{
+  struct listnode *mpnode;
+  struct pim_msdp_peer *mp;
+
+  /* if SA were sent to the peers we restart ka timer and avoid
+   * unnecessary ka noise */
+  for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+    if (mp->flags & PIM_MSDP_PEERF_SA_JUST_SENT) {
+      mp->flags &= ~PIM_MSDP_PEERF_SA_JUST_SENT;
+      pim_msdp_peer_pkt_txed(mp);
+    }
+  }
+}
+
+void
+pim_msdp_pkt_sa_tx(void)
+{
+  pim_msdp_pkt_sa_gen(NULL /* mp */);
+  pim_msdp_pkt_sa_tx_done();
+}
+
+void
+pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa)
+{
+  pim_msdp_pkt_sa_fill_hdr(1 /* cnt */);
+  pim_msdp_pkt_sa_fill_one(sa);
+  pim_msdp_pkt_sa_push(NULL);
+  pim_msdp_pkt_sa_tx_done();
+}
+
+/* when a connection is first established we push all SAs immediately */
+void
+pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp)
+{
+  pim_msdp_pkt_sa_gen(mp);
+  pim_msdp_pkt_sa_tx_done();
+}
+
+static void
+pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer *mp)
+{
+  pim_msdp_peer_reset_tcp_conn(mp, "invalid-pkt-rx");
+}
+
+static void
+pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len)
+{
+  mp->ka_rx_cnt++;
+  if (len !=  PIM_MSDP_KA_TLV_MAX_SIZE) {
+    pim_msdp_pkt_rxed_with_fatal_error(mp);
+    return;
+  }
+  pim_msdp_peer_pkt_rxed(mp);
+}
+
+static void
+pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp)
+{
+  int prefix_len;
+  struct prefix_sg sg;
+
+  /* just throw away the three reserved bytes */
+  stream_get3(mp->ibuf);
+  prefix_len = stream_getc(mp->ibuf);
+
+  memset(&sg, 0, sizeof (struct prefix_sg));
+  sg.grp.s_addr = stream_get_ipv4(mp->ibuf);
+  sg.src.s_addr = stream_get_ipv4(mp->ibuf);
+
+  if (prefix_len != 32) {
+    /* ignore SA update if the prefix length is not 32 */
+    zlog_err("rxed sa update with invalid prefix length %d", prefix_len);
+    return;
+  }
+  if (PIM_DEBUG_MSDP_PACKETS) {
+    zlog_debug("  sg %s", pim_str_sg_dump(&sg));
+  }
+  pim_msdp_sa_ref(mp, &sg, rp);
+}
+
+static void
+pim_msdp_pkt_sa_rx(struct pim_msdp_peer *mp, int len)
+{
+  int entry_cnt;
+  int i;
+  struct in_addr rp; /* Last RP address associated with this SA */
+
+  mp->sa_rx_cnt++;
+
+  if (len <  PIM_MSDP_SA_TLV_MIN_SIZE) {
+    pim_msdp_pkt_rxed_with_fatal_error(mp);
+    return;
+  }
+
+  entry_cnt = stream_getc(mp->ibuf);
+  /* some vendors include the actual multicast data in the tlv (at the end).
+   * we will ignore such data. in the future we may consider pushing it down
+   * the RPT */
+  if (len < PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt)) {
+    pim_msdp_pkt_rxed_with_fatal_error(mp);
+    return;
+  }
+  rp.s_addr = stream_get_ipv4(mp->ibuf);
+
+  if (PIM_DEBUG_MSDP_PACKETS) {
+    char rp_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<rp?>", rp, rp_str, sizeof(rp_str));
+    zlog_debug("  entry_cnt %d rp %s", entry_cnt, rp_str);
+  }
+
+  if (!pim_msdp_peer_rpf_check(mp, rp)) {
+    /* if peer-RPF check fails don't process the packet any further */
+    if (PIM_DEBUG_MSDP_PACKETS) {
+      zlog_debug("  peer RPF check failed");
+    }
+    return;
+  }
+
+  pim_msdp_peer_pkt_rxed(mp);
+
+  /* update SA cache */
+  for (i = 0; i < entry_cnt; ++i) {
+    pim_msdp_pkt_sa_rx_one(mp, rp);
+  }
+}
+
+static void
+pim_msdp_pkt_rx(struct pim_msdp_peer *mp)
+{
+  enum pim_msdp_tlv type;
+  int len;
+
+  /* re-read type and len */
+  type = stream_getc_from(mp->ibuf, 0);
+  len = stream_getw_from(mp->ibuf, 1);
+  if (len <  PIM_MSDP_HEADER_SIZE) {
+    pim_msdp_pkt_rxed_with_fatal_error(mp);
+    return;
+  }
+
+  if (len > PIM_MSDP_SA_TLV_MAX_SIZE) {
+    /* if tlv size if greater than max just ignore the tlv */
+    return;
+  }
+
+  if (PIM_DEBUG_MSDP_PACKETS) {
+    pim_msdp_pkt_dump(mp, type, len, true /*rx*/, NULL /*s*/);
+  }
+
+  switch(type) {
+      case PIM_MSDP_KEEPALIVE:
+        pim_msdp_pkt_ka_rx(mp, len);
+        break;
+      case PIM_MSDP_V4_SOURCE_ACTIVE:
+        mp->sa_rx_cnt++;
+        pim_msdp_pkt_sa_rx(mp, len);
+        break;
+      default:
+        mp->unk_rx_cnt++;
+  }
+}
+
+/* pim msdp read utility function. */
+static int
+pim_msdp_read_packet(struct pim_msdp_peer *mp)
+{
+  int nbytes;
+  int readsize;
+  int old_endp;
+  int new_endp;
+
+  old_endp = stream_get_endp(mp->ibuf);
+  readsize = mp->packet_size - old_endp;
+  if (!readsize) {
+    return 0;
+  }
+
+  /* Read packet from fd */
+  nbytes = stream_read_try(mp->ibuf, mp->fd, readsize);
+  new_endp = stream_get_endp(mp->ibuf);
+  if (nbytes < 0) {
+    if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes);
+    }
+    if (nbytes == -2) {
+      if (PIM_DEBUG_MSDP_INTERNAL) {
+        zlog_debug("MSDP peer %s pim_msdp_read io retry old_end: %d new_end: %d", mp->key_str, old_endp, new_endp);
+      }
+      /* transient error retry */
+      return -1;
+    }
+    pim_msdp_pkt_rxed_with_fatal_error(mp);
+    return -1;
+  }
+
+  if (!nbytes) {
+    if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes);
+    }
+    pim_msdp_peer_reset_tcp_conn(mp, "peer-down");
+    return -1;
+  }
+
+  /* We read partial packet. */
+  if (stream_get_endp(mp->ibuf) != mp->packet_size) {
+    if (PIM_DEBUG_MSDP_INTERNAL) {
+      zlog_debug("MSDP peer %s read partial len %d old_endp %d new_endp %d", mp->key_str, mp->packet_size, old_endp, new_endp);
+    }
+    return -1;
+  }
+
+  return 0;
+}
+
+int
+pim_msdp_read(struct thread *thread)
+{
+  struct pim_msdp_peer *mp;
+  int rc;
+  uint32_t len;
+
+  mp = THREAD_ARG(thread);
+  mp->t_read = NULL;
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s pim_msdp_read", mp->key_str);
+  }
+
+  if (mp->fd < 0) {
+    return -1;
+  }
+
+  /* check if TCP connection is established */
+  if (mp->state != PIM_MSDP_ESTABLISHED) {
+    pim_msdp_connect_check(mp);
+    return 0;
+  }
+
+  PIM_MSDP_PEER_READ_ON(mp);
+
+  if (!mp->packet_size) {
+    mp->packet_size = PIM_MSDP_HEADER_SIZE;
+  }
+
+  if (stream_get_endp(mp->ibuf) < PIM_MSDP_HEADER_SIZE) {
+    /* start by reading the TLV header */
+    rc = pim_msdp_read_packet(mp);
+    if (rc < 0) {
+      goto pim_msdp_read_end;
+    }
+
+    /* Find TLV type and len  */
+    stream_getc(mp->ibuf);
+    len = stream_getw(mp->ibuf);
+    if (len < PIM_MSDP_HEADER_SIZE) {
+      pim_msdp_pkt_rxed_with_fatal_error(mp);
+      goto pim_msdp_read_end;
+    }
+    /* read complete TLV */
+    mp->packet_size = len;
+  }
+
+  rc = pim_msdp_read_packet(mp);
+  if (rc < 0) {
+    goto pim_msdp_read_end;
+  }
+
+  pim_msdp_pkt_rx(mp);
+
+  /* reset input buffers and get ready for the next packet */
+  mp->packet_size = 0;
+  stream_reset(mp->ibuf);
+
+pim_msdp_read_end:
+  return 0;
+}
diff --git a/pimd/pim_msdp_packet.h b/pimd/pim_msdp_packet.h
new file mode 100644 (file)
index 0000000..f6fcfee
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * IP MSDP packet helpers
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef PIM_MSDP_PACKET_H
+#define PIM_MSDP_PACKET_H
+
+/* type and length of a single tlv can be consider packet header */
+#define PIM_MSDP_HEADER_SIZE 3
+
+/* Keepalive TLV
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       4      |              3                 |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+#define PIM_MSDP_KA_TLV_MAX_SIZE PIM_MSDP_HEADER_SIZE
+
+/* Source-Active TLV (x=8, y=12xEntryCount)
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       1       |           x + y               |  Entry Count  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                         RP Address                            |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                          Reserved             |  Sprefix Len  | \
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  \
+|                        Group Address                          |   ) z
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  /
+|                        Source Address                         | /
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+#define PIM_MSDP_SA_TLV_MAX_SIZE 9192
+#define PIM_MSDP_SA_X_SIZE 8
+#define PIM_MSDP_SA_ONE_ENTRY_SIZE 12
+#define PIM_MSDP_SA_Y_SIZE(entry_cnt) (PIM_MSDP_SA_ONE_ENTRY_SIZE * entry_cnt)
+#define PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt) (PIM_MSDP_SA_X_SIZE +\
+                                    PIM_MSDP_SA_Y_SIZE(entry_cnt))
+/* SA TLV has to have atleast only one entry in it so x=8 + y=12 */
+#define PIM_MSDP_SA_TLV_MIN_SIZE PIM_MSDP_SA_ENTRY_CNT2SIZE(1)
+/* XXX: theoretically we can fix a max of 255 but that may result in packet
+ * fragmentation */
+#define PIM_MSDP_SA_MAX_ENTRY_CNT 120
+
+#define PIM_MSDP_MAX_PACKET_SIZE max(PIM_MSDP_SA_TLV_MAX_SIZE, PIM_MSDP_KA_TLV_MAX_SIZE)
+
+#define PIM_MSDP_PKT_TYPE_STRLEN 16
+
+void pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp);
+int pim_msdp_read(struct thread *thread);
+void pim_msdp_pkt_sa_tx(void);
+void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa);
+void pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp);
+
+#endif
diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c
new file mode 100644 (file)
index 0000000..bc9720f
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * IP MSDP socket management
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <lib/log.h>
+#include <lib/network.h>
+#include <lib/sockunion.h>
+#include <lib/thread.h>
+#include <lib/vty.h>
+
+#include "pimd.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_socket.h"
+
+extern struct zebra_privs_t pimd_privs;
+
+/* increase socket send buffer size */
+static void
+pim_msdp_update_sock_send_buffer_size (int fd)
+{
+  int size = PIM_MSDP_SOCKET_SNDBUF_SIZE;
+  int optval;
+  socklen_t optlen = sizeof(optval);
+
+  if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) {
+    zlog_err("getsockopt of SO_SNDBUF failed %s\n", safe_strerror(errno));
+    return;
+  }
+
+  if (optval < size) {
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) {
+      zlog_err("Couldn't increase send buffer: %s\n", safe_strerror(errno));
+    }
+  }
+}
+
+/* passive peer socket accept */
+static int
+pim_msdp_sock_accept(struct thread *thread)
+{
+  union sockunion su;
+  struct pim_msdp_listener *listener = THREAD_ARG(thread);
+  int accept_sock;
+  int msdp_sock;
+  struct pim_msdp_peer *mp;
+  char buf[SU_ADDRSTRLEN];
+
+  sockunion_init(&su);
+
+  /* re-register accept thread */
+  accept_sock = THREAD_FD(thread);
+  if (accept_sock < 0) {
+    zlog_err ("accept_sock is negative value %d", accept_sock);
+    return -1;
+  }
+  listener->thread = thread_add_read(master, pim_msdp_sock_accept,
+                                     listener, accept_sock);
+
+  /* accept client connection. */
+  msdp_sock = sockunion_accept(accept_sock, &su);
+  if (msdp_sock < 0) {
+    zlog_err ("pim_msdp_sock_accept failed (%s)", safe_strerror (errno));
+    return -1;
+  }
+
+  /* see if have peer config for this */
+  mp = pim_msdp_peer_find(su.sin.sin_addr);
+  if (!mp || !PIM_MSDP_PEER_IS_LISTENER(mp)) {
+    ++msdp->rejected_accepts;
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      zlog_err("msdp peer connection refused from %s",
+          sockunion2str(&su, buf, SU_ADDRSTRLEN));
+    }
+    close(msdp_sock);
+    return -1;
+  }
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s accept success%s", mp->key_str, mp->fd>=0?"(dup)":"");
+  }
+
+  /* if we have an existing connection we need to kill that one
+   * with this one */
+  if (mp->fd >= 0) {
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      zlog_err("msdp peer new connection from %s stop old connection",
+          sockunion2str(&su, buf, SU_ADDRSTRLEN));
+    }
+    pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
+  }
+  mp->fd = msdp_sock;
+  set_nonblocking(mp->fd);
+  pim_msdp_update_sock_send_buffer_size(mp->fd);
+  pim_msdp_peer_established(mp);
+  return 0;
+}
+
+/* global listener for the MSDP well know TCP port */
+int
+pim_msdp_sock_listen(void)
+{
+  int sock;
+  int socklen;
+  struct sockaddr_in sin;
+  int rc;
+  struct pim_msdp_listener *listener = &msdp->listener;
+
+  if (msdp->flags & PIM_MSDPF_LISTENER) {
+    /* listener already setup */
+    return 0;
+  }
+
+  sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock < 0) {
+    zlog_err ("socket: %s", safe_strerror (errno));
+    return sock;
+  }
+
+  memset(&sin, 0, sizeof(struct sockaddr_in));
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(PIM_MSDP_TCP_PORT);
+  socklen = sizeof(struct sockaddr_in);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  sin.sin_len = socklen;
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  sockopt_reuseaddr(sock);
+  sockopt_reuseport(sock);
+
+  if (pimd_privs.change(ZPRIVS_RAISE)) {
+    zlog_err ("pim_msdp_socket: could not raise privs, %s",
+        safe_strerror (errno));
+  }
+
+  /* bind to well known TCP port */
+  rc = bind(sock, (struct sockaddr *)&sin, socklen);
+
+  if (pimd_privs.change(ZPRIVS_LOWER)) {
+    zlog_err ("pim_msdp_socket: could not lower privs, %s",
+        safe_strerror (errno));
+  }
+
+  if (rc < 0) {
+    zlog_err ("pim_msdp_socket bind to port %d: %s", ntohs(sin.sin_port), safe_strerror (errno));
+    close(sock);
+    return rc;
+  }
+
+  rc = listen(sock, 3 /* backlog */);
+  if (rc < 0) {
+    zlog_err ("pim_msdp_socket listen: %s", safe_strerror (errno));
+    close(sock);
+    return rc;
+  }
+
+  /* add accept thread */
+  listener->fd = sock;
+  memcpy(&listener->su, &sin, socklen);
+  listener->thread = thread_add_read(msdp->master, pim_msdp_sock_accept, listener, sock);
+
+  msdp->flags |= PIM_MSDPF_LISTENER;
+  return 0;
+}
+
+/* active peer socket setup */
+int
+pim_msdp_sock_connect(struct pim_msdp_peer *mp)
+{
+  int rc;
+
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    zlog_debug("MSDP peer %s attempt connect%s", mp->key_str, mp->fd<0?"":"(dup)");
+  }
+
+  /* if we have an existing connection we need to kill that one
+   * with this one */
+  if (mp->fd >= 0) {
+    if (PIM_DEBUG_MSDP_EVENTS) {
+      zlog_err("msdp duplicate connect to %s nuke old connection", mp->key_str);
+    }
+    pim_msdp_peer_stop_tcp_conn(mp, false /* chg_state */);
+  }
+
+  /* Make socket for the peer. */
+  mp->fd = sockunion_socket(&mp->su_peer);
+  if (mp->fd < 0) {
+    zlog_err ("pim_msdp_socket socket failure: %s", safe_strerror (errno));
+    return -1;
+  }
+
+  set_nonblocking(mp->fd);
+
+  /* Set socket send buffer size */
+  pim_msdp_update_sock_send_buffer_size(mp->fd);
+  sockopt_reuseaddr(mp->fd);
+  sockopt_reuseport(mp->fd);
+
+  /* source bind */
+  rc = sockunion_bind(mp->fd, &mp->su_local, 0, &mp->su_local);
+  if (rc < 0) {
+    zlog_err ("pim_msdp_socket connect bind failure: %s", safe_strerror (errno));
+    close(mp->fd);
+    mp->fd = -1;
+    return rc;
+  }
+
+  /* Connect to the remote mp. */
+  return (sockunion_connect(mp->fd, &mp->su_peer, htons(PIM_MSDP_TCP_PORT), 0));
+}
+
diff --git a/pimd/pim_msdp_socket.h b/pimd/pim_msdp_socket.h
new file mode 100644 (file)
index 0000000..bf3d12a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * IP MSDP socket management for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef PIM_MSDP_SOCKET_H
+#define PIM_MSDP_SOCKET_H
+
+int pim_msdp_sock_listen(void);
+int pim_msdp_sock_connect(struct pim_msdp_peer *mp);
+#endif
index 9d0fc0ad8f6f163fc6ccc095b662a5d06cb7a85c..e1f20b14961d05f245f0727552f517e491ded0df 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 
 #include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
+#include "pim_vty.h"
 #include "pim_pim.h"
 #include "pim_msg.h"
 #include "pim_util.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_rp.h"
+#include "pim_rpf.h"
 
 void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size,
                          uint8_t pim_msg_type)
@@ -86,9 +99,9 @@ uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf,
   return buf + ENCODED_IPV4_GROUP_SIZE;
 }
 
-uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
-                                        int buf_size,
-                                        struct in_addr addr)
+uint8_t *
+pim_msg_addr_encode_ipv4_source(uint8_t *buf, int buf_size,
+                               struct in_addr addr, uint8_t bits)
 {
   const int ENCODED_IPV4_SOURCE_SIZE = 8;
 
@@ -98,9 +111,172 @@ uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
 
   buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
   buf[1] = '\0';    /* native encoding */
-  buf[2] = 4;       /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */
+  buf[2] = bits;
   buf[3] = 32;      /* mask len */
   memcpy(buf+4, &addr, sizeof(struct in_addr));
 
   return buf + ENCODED_IPV4_SOURCE_SIZE;
 }
+
+int
+pim_msg_join_prune_encode (uint8_t *buf, int buf_size, int is_join,
+                          struct pim_upstream *up,
+                          struct in_addr upstream, int holdtime)
+{
+  uint8_t *pim_msg = buf;
+  uint8_t *pim_msg_curr = buf + PIM_MSG_HEADER_LEN;
+  uint8_t *end = buf + buf_size;
+  uint16_t *prunes = NULL;
+  uint16_t *joins = NULL;
+  struct in_addr stosend;
+  uint8_t bits;
+  int remain;
+
+  remain = end - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast (pim_msg_curr, buf_size - PIM_MSG_HEADER_LEN, upstream);
+  if (!pim_msg_curr) {
+    char dst_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<dst?>", upstream, dst_str, sizeof(dst_str));
+    zlog_warn("%s: failure encoding destination address %s: space left=%d",
+             __PRETTY_FUNCTION__, dst_str, remain);
+    return -3;
+  }
+
+  remain = end - pim_msg_curr;
+  if (remain < 4) {
+    zlog_warn("%s: group will not fit: space left=%d",
+           __PRETTY_FUNCTION__, remain);
+    return -4;
+  }
+
+  *pim_msg_curr = 0; /* reserved */
+  ++pim_msg_curr;
+  *pim_msg_curr = 1; /* number of groups */
+  ++pim_msg_curr;
+
+  *((uint16_t *) pim_msg_curr) = htons(holdtime);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  remain = end - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_group (pim_msg_curr, remain,
+                                                up->sg.grp);
+  if (!pim_msg_curr) {
+    char group_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<grp?>", up->sg.grp, group_str, sizeof(group_str));
+    zlog_warn("%s: failure encoding group address %s: space left=%d",
+             __PRETTY_FUNCTION__, group_str, remain);
+    return -5;
+  }
+
+  remain = end - pim_msg_curr;
+  if (remain < 4) {
+    zlog_warn("%s: sources will not fit: space left=%d",
+             __PRETTY_FUNCTION__, remain);
+    return -6;
+  }
+
+  /* number of joined sources */
+  joins = (uint16_t *)pim_msg_curr;
+  *joins = htons(is_join ? 1 : 0);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  /* number of pruned sources */
+  prunes = (uint16_t *)pim_msg_curr;
+  *prunes = htons(is_join ? 0 : 1);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  remain = end - pim_msg_curr;
+  if (up->sg.src.s_addr == INADDR_ANY)
+    {
+      struct pim_rpf *rpf = pim_rp_g (up->sg.grp);
+      bits = PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_WC_BIT | PIM_ENCODE_RPT_BIT;
+      stosend = rpf->rpf_addr.u.prefix4;
+    }
+  else
+    {
+      bits = PIM_ENCODE_SPARSE_BIT;
+      stosend = up->sg.src;
+    }
+  pim_msg_curr = pim_msg_addr_encode_ipv4_source (pim_msg_curr, remain, stosend, bits);
+  if (!pim_msg_curr) {
+    char source_str[INET_ADDRSTRLEN];
+    pim_inet4_dump("<src?>", up->sg.src, source_str, sizeof(source_str));
+    zlog_warn("%s: failure encoding source address %s: space left=%d",
+             __PRETTY_FUNCTION__, source_str, remain);
+    return -7;
+  }
+  remain = pim_msg_curr - pim_msg;
+
+  /*
+   * This is not implemented correctly at this point in time
+   * Make it stop.
+   */
+#if 0
+  if (up->sg.src.s_addr == INADDR_ANY)
+    {
+      struct pim_upstream *child;
+      struct listnode *up_node;
+      int send_prune = 0;
+
+      zlog_debug ("%s: Considering (%s) children for (S,G,rpt) prune",
+                 __PRETTY_FUNCTION__, up->sg_str);
+      for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+       {
+         if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
+           {
+             if (!pim_rpf_is_same(&up->rpf, &child->rpf))
+               {
+                 send_prune = 1;
+                 if (PIM_DEBUG_PIM_PACKETS)
+                   zlog_debug ("%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message",
+                               __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+               }
+             else
+               if (PIM_DEBUG_PIM_PACKETS)
+                 zlog_debug ("%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)",
+                             __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+           }
+         else if (pim_upstream_is_sg_rpt (child))
+           {
+             if (pim_upstream_empty_inherited_olist (child))
+               {
+                 send_prune = 1;
+                 if (PIM_DEBUG_PIM_PACKETS)
+                   zlog_debug ("%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message",
+                               __PRETTY_FUNCTION__, child->sg_str);
+               }
+             else if (!pim_rpf_is_same (&up->rpf, &child->rpf))
+               {
+                 send_prune = 1;
+                 if (PIM_DEBUG_PIM_PACKETS)
+                   zlog_debug ("%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message",
+                               __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+               }
+             else
+               if (PIM_DEBUG_PIM_PACKETS)
+                 zlog_debug ("%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message",
+                             __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+           }
+         else
+           if (PIM_DEBUG_PIM_PACKETS)
+             zlog_debug ("%s: SPT bit is not set for (%s)",
+                         __PRETTY_FUNCTION__, child->sg_str);
+         if (send_prune)
+           {
+             pim_msg_curr = pim_msg_addr_encode_ipv4_source (pim_msg_curr, remain,
+                                                             child->sg.src,
+                                                             PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_RPT_BIT);
+             remain = pim_msg_curr - pim_msg;
+             *prunes = htons(ntohs(*prunes) + 1);
+             send_prune = 0;
+           }
+       }
+    }
+#endif
+  pim_msg_build_header (pim_msg, remain, PIM_MSG_TYPE_JOIN_PRUNE);
+
+  return remain;
+}
index 96a89659e671806f393dce0c73efc9067cf85e51..5229f85c72a9a0cac2a7619458ab2b273be98fd0 100644 (file)
@@ -43,8 +43,17 @@ uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf,
 uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf,
                                        int buf_size,
                                        struct in_addr addr);
+
+#define PIM_ENCODE_SPARSE_BIT      0x04
+#define PIM_ENCODE_WC_BIT          0x02
+#define PIM_ENCODE_RPT_BIT         0x01
 uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
                                         int buf_size,
-                                        struct in_addr addr);
+                                        struct in_addr addr,
+                                        uint8_t bits);
+
 
+int pim_msg_join_prune_encode (uint8_t *buf, int buf_size, int is_join,
+                              struct pim_upstream *up,
+                              struct in_addr upstream, int holdtime);
 #endif /* PIM_MSG_H */
index 04792eb35e656672275b10e7a5a782618d6cb6fa..346b911157eca3329868f00274bdb8c34819dc5d 100644 (file)
@@ -24,6 +24,8 @@
 #include "prefix.h"
 #include "memory.h"
 #include "if.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_neighbor.h"
@@ -33,6 +35,8 @@
 #include "pim_pim.h"
 #include "pim_upstream.h"
 #include "pim_ifchannel.h"
+#include "pim_rp.h"
+#include "pim_zebra.h"
 
 static void dr_election_by_addr(struct interface *ifp)
 {
@@ -125,8 +129,8 @@ int pim_if_dr_election(struct interface *ifp)
   if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) {
 
     if (PIM_DEBUG_PIM_EVENTS) {
-      char dr_old_str[100];
-      char dr_new_str[100];
+      char dr_old_str[INET_ADDRSTRLEN];
+      char dr_new_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<old_dr?>", old_dr_addr, dr_old_str, sizeof(dr_old_str));
       pim_inet4_dump("<new_dr?>", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str));
       zlog_debug("%s: DR was %s now is %s on interface %s",
@@ -207,20 +211,18 @@ static int on_neighbor_timer(struct thread *t)
   struct interface *ifp;
   char msg[100];
 
-  zassert(t);
   neigh = THREAD_ARG(t);
-  zassert(neigh);
 
   ifp = neigh->interface;
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
     zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s",
               neigh->holdtime, src_str, ifp->name);
   }
 
-  neigh->t_expire_timer = 0;
+  neigh->t_expire_timer = NULL;
 
   snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime);
   pim_neighbor_delete(ifp, neigh, msg);
@@ -237,26 +239,11 @@ static int on_neighbor_timer(struct thread *t)
   return 0;
 }
 
-static void neighbor_timer_off(struct pim_neighbor *neigh)
-{
-  if (PIM_DEBUG_PIM_TRACE_DETAIL) {
-    if (neigh->t_expire_timer) {
-      char src_str[100];
-      pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
-      zlog_debug("%s: cancelling timer for neighbor %s on %s",
-                __PRETTY_FUNCTION__,
-                src_str, neigh->interface->name);
-    }
-  }
-  THREAD_OFF(neigh->t_expire_timer);
-  zassert(!neigh->t_expire_timer);
-}
-
 void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime)
 {
   neigh->holdtime = holdtime;
 
-  neighbor_timer_off(neigh);
+  THREAD_OFF(neigh->t_expire_timer);
 
   /*
     0xFFFF is request for no holdtime
@@ -266,7 +253,7 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime)
   }
 
   if (PIM_DEBUG_PIM_TRACE_DETAIL) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
     zlog_debug("%s: starting %u sec timer for neighbor %s on %s",
               __PRETTY_FUNCTION__,
@@ -290,15 +277,15 @@ static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
 {
   struct pim_interface *pim_ifp;
   struct pim_neighbor *neigh;
-  char src_str[100];
+  char src_str[INET_ADDRSTRLEN];
 
   zassert(ifp);
   pim_ifp = ifp->info;
   zassert(pim_ifp);
 
-  neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh));
+  neigh = XCALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh));
   if (!neigh) {
-    zlog_err("%s: PIM XMALLOC(%zu) failure",
+    zlog_err("%s: PIM XCALLOC(%zu) failure",
             __PRETTY_FUNCTION__, sizeof(*neigh));
     return 0;
   }
@@ -311,10 +298,18 @@ static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
   neigh->dr_priority            = dr_priority;
   neigh->generation_id          = generation_id;
   neigh->prefix_list            = addr_list;
-  neigh->t_expire_timer         = 0;
+  neigh->t_expire_timer         = NULL;
   neigh->interface              = ifp;
 
   pim_neighbor_timer_reset(neigh, holdtime);
+  /*
+   * The pim_ifstat_hello_sent variable is used to decide if
+   * we should expedite a hello out the interface.  If we
+   * establish a new neighbor, we unfortunately need to
+   * reset the value so that we can know to hurry up and
+   * hello
+   */
+  pim_ifp->pim_ifstat_hello_sent = 0;
 
   pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
 
@@ -391,7 +386,8 @@ struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
   struct pim_neighbor  *neigh;
 
   pim_ifp = ifp->info;
-  zassert(pim_ifp);
+  if (!pim_ifp)
+    return NULL;
 
   for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
     if (source_addr.s_addr == neigh->source_addr.s_addr) {
@@ -399,7 +395,35 @@ struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
     }
   }
 
-  return 0;
+  return NULL;
+}
+
+/*
+ * Find the *one* interface out
+ * this interface.  If more than
+ * one return NULL
+ */
+struct pim_neighbor *
+pim_neighbor_find_if (struct interface *ifp)
+{
+  struct pim_interface *pim_ifp = ifp->info;
+
+  if (!pim_ifp || pim_ifp->pim_neighbor_list->count != 1)
+    return NULL;
+
+  return listnode_head (pim_ifp->pim_neighbor_list);
+}
+
+/* rpf info associated with an upstream entry needs to be re-evaluated
+ * when an RPF neighbor comes or goes */
+static void
+pim_neighbor_rpf_update(void)
+{
+  /* XXX: for the time being piggyback on the timer used on rib changes
+   * to scan and update the rpf nexthop. This is expensive processing
+   * and we should be able to optimize neighbor changes differently than
+   * nexthop changes. */
+  sched_rpf_cache_refresh();
 }
 
 struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
@@ -410,7 +434,8 @@ struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
                                      uint16_t override_interval,
                                      uint32_t dr_priority,
                                      uint32_t generation_id,
-                                     struct list *addr_list)
+                                     struct list *addr_list,
+                                     int send_hello_now)
 {
   struct pim_interface *pim_ifp;
   struct pim_neighbor *neigh;
@@ -449,9 +474,22 @@ struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
     message with a new GenID is received from an existing neighbor, a
     new Hello message should be sent on this interface after a
     randomized delay between 0 and Triggered_Hello_Delay.
+
+    This is a bit silly to do it that way.  If I get a new
+    genid we need to send the hello *now* because we've
+    lined up a bunch of join/prune messages to go out the
+    interface.
   */
-  pim_hello_restart_triggered(neigh->interface);
+  if (send_hello_now)
+    pim_hello_restart_now (ifp);
+  else
+    pim_hello_restart_triggered(neigh->interface);
+
+  pim_upstream_find_new_rpf();
 
+  pim_rp_setup ();
+
+  pim_neighbor_rpf_update();
   return neigh;
 }
 
@@ -508,7 +546,7 @@ void pim_neighbor_delete(struct interface *ifp,
                         const char *delete_message)
 {
   struct pim_interface *pim_ifp;
-  char src_str[100];
+  char src_str[INET_ADDRSTRLEN];
 
   pim_ifp = ifp->info;
   zassert(pim_ifp);
@@ -517,7 +555,7 @@ void pim_neighbor_delete(struct interface *ifp,
   zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
            src_str, ifp->name, delete_message);
 
-  neighbor_timer_off(neigh);
+  THREAD_OFF(neigh->t_expire_timer);
 
   pim_if_assert_on_neighbor_down(ifp, neigh->source_addr);
 
@@ -564,6 +602,8 @@ void pim_neighbor_delete(struct interface *ifp,
   listnode_delete(pim_ifp->pim_neighbor_list, neigh);
 
   pim_neighbor_free(neigh);
+
+  pim_neighbor_rpf_update();
 }
 
 void pim_neighbor_delete_all(struct interface *ifp,
@@ -646,9 +686,9 @@ static void delete_from_neigh_addr(struct interface *ifp,
       {
        struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4);
        if (p) {
-         char addr_str[100];
-         char this_neigh_str[100];
-         char other_neigh_str[100];
+         char addr_str[INET_ADDRSTRLEN];
+         char this_neigh_str[INET_ADDRSTRLEN];
+         char other_neigh_str[INET_ADDRSTRLEN];
          
          pim_inet4_dump("<addr?>", addr->u.prefix4, addr_str, sizeof(addr_str));
          pim_inet4_dump("<neigh1?>", neigh_addr, this_neigh_str, sizeof(this_neigh_str));
index e023a7f1ef784b094cf58cbf4630f4161a737fc0..9e419aec18c85e17df82a3cd99b25fbead0c037a 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_NEIGHBOR_H
@@ -25,6 +29,7 @@
 
 #include "if.h"
 #include "linklist.h"
+#include "prefix.h"
 
 #include "pim_tlv.h"
 
@@ -46,6 +51,12 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime);
 void pim_neighbor_free(struct pim_neighbor *neigh);
 struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
                                       struct in_addr source_addr);
+
+struct pim_neighbor *pim_neighbor_find_if (struct interface *ifp);
+
+
+#define PIM_NEIGHBOR_SEND_DELAY 0
+#define PIM_NEIGHBOR_SEND_NOW   1
 struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
                                      struct in_addr source_addr,
                                      pim_hello_options hello_options,
@@ -54,7 +65,8 @@ struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
                                      uint16_t override_interval,
                                      uint32_t dr_priority,
                                      uint32_t generation_id,
-                                     struct list *addr_list);
+                                     struct list *addr_list,
+                                     int send_hello_now);
 void pim_neighbor_delete(struct interface *ifp,
                         struct pim_neighbor *neigh,
                         const char *delete_message);
index ebbc6e19f96e843cafcbeed25dde6f91dd522dd3..1c0a2f2beab32038fb1db634d3d3a3339bac35dc 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -24,6 +28,8 @@
 #include "memory.h"
 #include "linklist.h"
 #include "if.h"
+#include "hash.h"
+#include "jhash.h"
 
 #include "pimd.h"
 #include "pim_oil.h"
 #include "pim_iface.h"
 #include "pim_time.h"
 
+struct list *pim_channel_oil_list = NULL;
+struct hash *pim_channel_oil_hash = NULL;
+
+static int
+pim_channel_oil_compare (struct channel_oil *c1, struct channel_oil *c2)
+{
+  if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
+     return -1;
+
+   if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
+     return 1;
+
+   if (ntohl(c1->oil.mfcc_origin.s_addr) < ntohl(c2->oil.mfcc_origin.s_addr))
+     return -1;
+
+   if (ntohl(c1->oil.mfcc_origin.s_addr) > ntohl(c2->oil.mfcc_origin.s_addr))
+     return 1;
+
+   return 0;
+}
+
+static int
+pim_oil_equal (const void *arg1, const void *arg2)
+{
+  const struct channel_oil *c1 = (const struct channel_oil *)arg1;
+  const struct channel_oil *c2 = (const struct channel_oil *)arg2;
+
+  if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr) &&
+      (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
+    return 1;
+
+  return 0;
+}
+
+static unsigned int
+pim_oil_hash_key (void *arg)
+{
+  struct channel_oil *oil = (struct channel_oil *)arg;
+
+  return jhash_2words (oil->oil.mfcc_mcastgrp.s_addr, oil->oil.mfcc_origin.s_addr, 0);
+}
+
+void
+pim_oil_init (void)
+{
+  pim_channel_oil_hash = hash_create_size (8192, pim_oil_hash_key,
+                                          pim_oil_equal);
+
+  pim_channel_oil_list = list_new();
+  if (!pim_channel_oil_list) {
+    zlog_err("%s %s: failure: channel_oil_list=list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    return;
+  }
+  pim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
+  pim_channel_oil_list->cmp = (int (*)(void *, void *)) pim_channel_oil_compare;
+}
+
+void
+pim_oil_terminate (void)
+{
+  if (pim_channel_oil_list)
+    list_free(pim_channel_oil_list);
+  pim_channel_oil_list = NULL;
+
+  if (pim_channel_oil_hash)
+    hash_free (pim_channel_oil_hash);
+  pim_channel_oil_hash = NULL;
+}
+
 void pim_channel_oil_free(struct channel_oil *c_oil)
 {
   XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
 }
 
-static void pim_channel_oil_delete(struct channel_oil *c_oil)
+static void
+pim_del_channel_oil (struct channel_oil *c_oil)
 {
   /*
     notice that listnode_delete() can't be moved
     into pim_channel_oil_free() because the later is
     called by list_delete_all_node()
   */
-  listnode_delete(qpim_channel_oil_list, c_oil);
+  listnode_delete(pim_channel_oil_list, c_oil);
+  hash_release (pim_channel_oil_hash, c_oil);
 
   pim_channel_oil_free(c_oil);
 }
 
-static struct channel_oil *channel_oil_new(struct in_addr group_addr,
-                                          struct in_addr source_addr,
-                                          int input_vif_index)
+static struct channel_oil *
+pim_add_channel_oil (struct prefix_sg *sg,
+                   int input_vif_index)
 {
   struct channel_oil *c_oil;
-  struct interface *ifp_in;
+  struct interface *ifp;
 
-  ifp_in = pim_if_find_by_vif_index(input_vif_index);
-  if (!ifp_in) {
+  ifp = pim_if_find_by_vif_index(input_vif_index);
+  if (!ifp) {
     /* warning only */
-    char group_str[100]; 
-    char source_str[100];
-    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-    pim_inet4_dump("<source?>", source_addr, source_str, sizeof(source_str));
-    zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+    zlog_warn("%s: (S,G)=%s could not find input interface for input_vif_index=%d",
              __PRETTY_FUNCTION__,
-             source_str, group_str, input_vif_index);
+             pim_str_sg_dump (sg), input_vif_index);
   }
 
   c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
   if (!c_oil) {
     zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
-    return 0;
+    return NULL;
   }
 
-  c_oil->oil.mfcc_mcastgrp = group_addr;
-  c_oil->oil.mfcc_origin   = source_addr;
+  c_oil->oil.mfcc_mcastgrp = sg->grp;
+  c_oil->oil.mfcc_origin   = sg->src;
+  c_oil = hash_get (pim_channel_oil_hash, c_oil, hash_alloc_intern);
+
   c_oil->oil.mfcc_parent   = input_vif_index;
   c_oil->oil_ref_count     = 1;
   c_oil->installed         = 0;
 
-  zassert(c_oil->oil_size == 0);
+  listnode_add_sort(pim_channel_oil_list, c_oil);
 
   return c_oil;
 }
 
-static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr,
-                                              struct in_addr source_addr,
-                                              int input_vif_index)
+static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg)
 {
-  struct channel_oil *c_oil;
+  struct channel_oil *c_oil = NULL;
+  struct channel_oil lookup;
 
-  c_oil = channel_oil_new(group_addr, source_addr, input_vif_index);
-  if (!c_oil) {
-    zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
-    return 0;
-  }
+  lookup.oil.mfcc_mcastgrp = sg->grp;
+  lookup.oil.mfcc_origin   = sg->src;
 
-  listnode_add(qpim_channel_oil_list, c_oil);
+  c_oil = hash_lookup (pim_channel_oil_hash, &lookup);
 
   return c_oil;
 }
 
-static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr,
-                                               struct in_addr source_addr)
-{
-  struct listnode    *node;
-  struct channel_oil *c_oil;
-
-  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
-    if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
-       (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr))
-      return c_oil;
-  }
-  
-  return 0;
-}
-
-struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
-                                       struct in_addr source_addr,
+struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
                                        int input_vif_index)
 {
   struct channel_oil *c_oil;
 
-  c_oil = pim_find_channel_oil(group_addr, source_addr);
+  c_oil = pim_find_channel_oil(sg);
   if (c_oil) {
+    if (c_oil->oil.mfcc_parent != input_vif_index)
+      {
+       c_oil->oil_inherited_rescan = 1;
+       if (PIM_DEBUG_MROUTE)
+         zlog_debug ("%s: Existing channel oil %s points to %d, modifying to point at %d",
+                     __PRETTY_FUNCTION__, pim_str_sg_dump(sg), c_oil->oil.mfcc_parent, input_vif_index);
+      }
+    c_oil->oil.mfcc_parent = input_vif_index;
     ++c_oil->oil_ref_count;
     return c_oil;
   }
 
-  return pim_add_channel_oil(group_addr, source_addr, input_vif_index);
+  return pim_add_channel_oil(sg, input_vif_index);
 }
 
 void pim_channel_oil_del(struct channel_oil *c_oil)
@@ -136,10 +200,96 @@ void pim_channel_oil_del(struct channel_oil *c_oil)
   --c_oil->oil_ref_count;
 
   if (c_oil->oil_ref_count < 1) {
-    pim_channel_oil_delete(c_oil);
+    pim_del_channel_oil(c_oil);
+  }
+}
+
+int
+pim_channel_del_oif (struct channel_oil *channel_oil,
+                    struct interface *oif,
+                    uint32_t proto_mask)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert (channel_oil);
+  zassert (oif);
+
+  pim_ifp = oif->info;
+
+  /*
+   * Don't do anything if we've been asked to remove a source
+   * that is not actually on it.
+   */
+  if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask))
+    {
+      if (PIM_DEBUG_MROUTE)
+       {
+         char group_str[INET_ADDRSTRLEN];
+         char source_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+         pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+         zlog_debug("%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    proto_mask, channel_oil->oif_flags[pim_ifp->mroute_vif_index],
+                    oif->name, pim_ifp->mroute_vif_index,
+                    channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+                    source_str, group_str);
+       }
+      return 0;
+    }
+
+  channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
+
+  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index])
+    {
+      if (PIM_DEBUG_MROUTE)
+       {
+         char group_str[INET_ADDRSTRLEN];
+         char source_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+         pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+         zlog_debug("%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    oif->name, pim_ifp->mroute_vif_index,
+                    channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+                    source_str, group_str);
+       }
+      return 0;
+    }
+
+  channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+
+  if (pim_mroute_add (channel_oil, __PRETTY_FUNCTION__)) {
+    if (PIM_DEBUG_MROUTE)
+      {
+        char group_str[INET_ADDRSTRLEN];
+        char source_str[INET_ADDRSTRLEN];
+        pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+        pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+        zlog_debug("%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
+                   __FILE__, __PRETTY_FUNCTION__,
+                   oif->name, pim_ifp->mroute_vif_index,
+                   source_str, group_str);
+      }
+    return -1;
   }
+
+  if (PIM_DEBUG_MROUTE)
+    {
+      char group_str[INET_ADDRSTRLEN];
+      char source_str[INET_ADDRSTRLEN];
+      pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
+                __FILE__, __PRETTY_FUNCTION__,
+                source_str, group_str,
+                proto_mask, oif->name, pim_ifp->mroute_vif_index);
+    }
+
+  return 0;
 }
 
+
 int pim_channel_add_oif(struct channel_oil *channel_oil,
                   struct interface *oif,
                   uint32_t proto_mask)
@@ -147,21 +297,18 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
   struct pim_interface *pim_ifp;
   int old_ttl;
 
-  zassert(channel_oil);
+  /*
+   * If we've gotten here we've gone bad, but let's
+   * not take down pim
+   */
+  if (!channel_oil)
+    {
+      zlog_warn ("Attempt to Add OIF for non-existent channel oil");
+      return -1;
+    }
 
   pim_ifp = oif->info;
 
-  if (PIM_DEBUG_MROUTE) {
-    char group_str[100];
-    char source_str[100];
-    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-    zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
-              __FILE__, __PRETTY_FUNCTION__,
-              source_str, group_str,
-              proto_mask, oif->name, pim_ifp->mroute_vif_index);
-  }
-
 #ifdef PIM_ENFORCE_LOOPFREE_MFC
   /*
     Prevent creating MFC entry with OIF=IIF.
@@ -175,10 +322,11 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
     TODO T22.
   */
   if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
+    channel_oil->oil_inherited_rescan = 1;
     if (PIM_DEBUG_MROUTE)
       {
-       char group_str[100];
-       char source_str[100];
+       char group_str[INET_ADDRSTRLEN];
+       char source_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
        pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
        zlog_debug("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
@@ -195,8 +343,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
   if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
     if (PIM_DEBUG_MROUTE)
       {
-       char group_str[100];
-       char source_str[100];
+       char group_str[INET_ADDRSTRLEN];
+       char source_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
        pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
        zlog_debug("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
@@ -218,8 +366,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
     if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
       if (PIM_DEBUG_MROUTE)
        {
-         char group_str[100];
-         char source_str[100];
+         char group_str[INET_ADDRSTRLEN];
+         char source_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
          pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
          zlog_debug("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
@@ -238,8 +386,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
   if (old_ttl > 0) {
     if (PIM_DEBUG_MROUTE)
       {
-       char group_str[100];
-       char source_str[100];
+       char group_str[INET_ADDRSTRLEN];
+       char source_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
        pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
        zlog_debug("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
@@ -252,11 +400,11 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
 
   channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
 
-  if (pim_mroute_add(channel_oil)) {
+  if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
     if (PIM_DEBUG_MROUTE)
       {
-       char group_str[100];
-       char source_str[100];
+       char group_str[INET_ADDRSTRLEN];
+       char source_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
        pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
        zlog_debug("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
@@ -274,8 +422,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
   channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
 
   if (PIM_DEBUG_MROUTE) {
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
     zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
@@ -286,3 +434,24 @@ int pim_channel_add_oif(struct channel_oil *channel_oil,
 
   return 0;
 }
+
+int
+pim_channel_oil_empty (struct channel_oil *c_oil)
+{
+  static uint32_t zero[MAXVIFS];
+  static int inited = 0;
+
+  if (!c_oil)
+    return 1;
+  /*
+   * Not sure that this is necessary, but I would rather ensure
+   * that this works.
+   */
+  if (!inited)
+    {
+      memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
+      inited = 1;
+    }
+
+  return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
+}
index 540acce3a3d7d5ccdc310b2fc14d6e18cf1ec148..143cfb7942d2c1c008876c3eac4d5bd773237a75 100644 (file)
@@ -51,6 +51,7 @@
 
 struct channel_counts
 {
+  unsigned long long lastused;
   unsigned long pktcnt;
   unsigned long oldpktcnt;
   unsigned long bytecnt;
@@ -69,6 +70,7 @@ struct channel_counts
 struct channel_oil {
   struct mfcctl oil;
   int           installed;
+  int           oil_inherited_rescan;
   int           oil_size;
   int           oil_ref_count;
   time_t        oif_creation[MAXVIFS];
@@ -76,14 +78,22 @@ struct channel_oil {
   struct channel_counts cc;
 };
 
+extern struct list *pim_channel_oil_list;
+
+void pim_oil_init (void);
+void pim_oil_terminate (void);
+
 void pim_channel_oil_free(struct channel_oil *c_oil);
-struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
-                                       struct in_addr source_addr,
+struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
                                        int input_vif_index);
 void pim_channel_oil_del(struct channel_oil *c_oil);
 
 int pim_channel_add_oif(struct channel_oil *c_oil,
                        struct interface *oif,
                        uint32_t proto_mask);
+int pim_channel_del_oif (struct channel_oil *c_oil,
+                        struct interface *oif,
+                        uint32_t proto_mask);
 
+int pim_channel_oil_empty (struct channel_oil *c_oil);
 #endif /* PIM_OIL_H */
index 0f41a433153d35629d8ebfddde099b9f2723d770..1dbbd7c65582d25e26a8a6653a4ddcc409f972c7 100644 (file)
@@ -44,6 +44,25 @@ static int on_pim_hello_send(struct thread *t);
 static int pim_hello_send(struct interface *ifp,
                          uint16_t holdtime);
 
+static
+const char *pim_pim_msgtype2str (enum pim_msg_type type)
+{
+  switch (type)
+    {
+    case PIM_MSG_TYPE_HELLO:      return "HELLO";
+    case PIM_MSG_TYPE_REGISTER:   return "REGISTER";
+    case PIM_MSG_TYPE_REG_STOP:   return "REGSTOP";
+    case PIM_MSG_TYPE_JOIN_PRUNE: return "JOINPRUNE";
+    case PIM_MSG_TYPE_BOOTSTRAP:  return "BOOT";
+    case PIM_MSG_TYPE_ASSERT:     return "ASSERT";
+    case PIM_MSG_TYPE_GRAFT:      return "GRAFT";
+    case PIM_MSG_TYPE_GRAFT_ACK:  return "GACK";
+    case PIM_MSG_TYPE_CANDIDATE:  return "CANDIDATE";
+    }
+
+  return "UNKNOWN";
+}
+
 static void sock_close(struct interface *ifp)
 {
   struct pim_interface *pim_ifp = ifp->info;
@@ -70,7 +89,10 @@ static void sock_close(struct interface *ifp)
               pim_ifp->pim_sock_fd, ifp->name);
   }
 
-  if (close(pim_ifp->pim_sock_fd)) {
+  /*
+   * If the fd is already deleted no need to do anything here
+   */
+  if (pim_ifp->pim_sock_fd > 0 && close(pim_ifp->pim_sock_fd)) {
     zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
              pim_ifp->pim_sock_fd, ifp->name,
              errno, safe_strerror(errno));
@@ -78,11 +100,6 @@ static void sock_close(struct interface *ifp)
   
   pim_ifp->pim_sock_fd = -1;
   pim_ifp->pim_sock_creation = 0;
-
-  zassert(pim_ifp->pim_sock_fd < 0);
-  zassert(!pim_ifp->t_pim_sock_read);
-  zassert(!pim_ifp->t_pim_hello_timer);
-  zassert(!pim_ifp->pim_sock_creation);
 }
 
 void pim_sock_delete(struct interface *ifp, const char *delete_message)
@@ -114,67 +131,53 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len)
 {
   struct ip *ip_hdr;
   size_t ip_hlen; /* ip header length in bytes */
-  char src_str[100];
-  char dst_str[100];
+  char src_str[INET_ADDRSTRLEN];
+  char dst_str[INET_ADDRSTRLEN];
   uint8_t *pim_msg;
   int pim_msg_len;
   uint8_t pim_version;
-  uint8_t pim_type;
+  enum pim_msg_type pim_type;
   uint16_t pim_checksum; /* received checksum */
   uint16_t checksum;     /* computed checksum */
   struct pim_neighbor *neigh;
 
-  if (!ifp->info) {
-    zlog_warn("%s: PIM not enabled on interface %s",
-             __PRETTY_FUNCTION__, ifp->name);
-    return -1;
-  }
-    
   if (len < sizeof(*ip_hdr)) {
-    zlog_warn("PIM packet size=%zu shorter than minimum=%zu",
-             len, sizeof(*ip_hdr));
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("PIM packet size=%zu shorter than minimum=%zu",
+                len, sizeof(*ip_hdr));
     return -1;
   }
 
   ip_hdr = (struct ip *) buf;
-
-  pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
-  pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
-
   ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
 
-  if (PIM_DEBUG_PIM_PACKETS) {
-    zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
-              src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p);
-  }
-
   if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) {
-    zlog_warn("IP packet protocol=%d is not PIM=%d",
-             ip_hdr->ip_p, PIM_IP_PROTO_PIM);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("IP packet protocol=%d is not PIM=%d",
+                ip_hdr->ip_p, PIM_IP_PROTO_PIM);
     return -1;
   }
 
   if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
-    zlog_warn("IP packet header size=%zu shorter than minimum=%d",
-             ip_hlen, PIM_IP_HEADER_MIN_LEN);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("IP packet header size=%zu shorter than minimum=%d",
+                ip_hlen, PIM_IP_HEADER_MIN_LEN);
     return -1;
   }
   if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
-    zlog_warn("IP packet header size=%zu greater than maximum=%d",
-             ip_hlen, PIM_IP_HEADER_MAX_LEN);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("IP packet header size=%zu greater than maximum=%d",
+                ip_hlen, PIM_IP_HEADER_MAX_LEN);
     return -1;
   }
 
   pim_msg = buf + ip_hlen;
   pim_msg_len = len - ip_hlen;
 
-  if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
-    pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
-  }
-
   if (pim_msg_len < PIM_PIM_MIN_LEN) {
-    zlog_warn("PIM message size=%d shorter than minimum=%d",
-             pim_msg_len, PIM_PIM_MIN_LEN);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("PIM message size=%d shorter than minimum=%d",
+                pim_msg_len, PIM_PIM_MIN_LEN);
     return -1;
   }
 
@@ -182,8 +185,9 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len)
   pim_type    = PIM_MSG_HDR_GET_TYPE(pim_msg);
 
   if (pim_version != PIM_PROTO_VERSION) {
-    zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d",
-             ifp->name, pim_version);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("Ignoring PIM pkt from %s with unsupported version: %d",
+                ifp->name, pim_version);
     return -1;
   }
 
@@ -195,71 +199,79 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len)
 
   checksum = in_cksum(pim_msg, pim_msg_len);
   if (checksum != pim_checksum) {
-    zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
-             ifp->name, pim_checksum, checksum);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
+                ifp->name, pim_checksum, checksum);
     return -1;
   }
 
   if (PIM_DEBUG_PIM_PACKETS) {
-    zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x",
-              src_str, dst_str, ifp->name, ip_hdr->ip_ttl,
-              pim_version, pim_type, pim_msg_len, checksum);
+    pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
+    pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
+    zlog_debug("Recv PIM %s packet from %s to %s on %s: ttl=%d pim_version=%d pim_msg_size=%d checksum=%x",
+              pim_pim_msgtype2str (pim_type), src_str, dst_str, ifp->name,
+              ip_hdr->ip_ttl, pim_version, pim_msg_len, checksum);
+    if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+      pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
+    }
   }
 
-  if (pim_type == PIM_MSG_TYPE_REG_STOP  ||
-      pim_type == PIM_MSG_TYPE_BOOTSTRAP ||
-      pim_type == PIM_MSG_TYPE_GRAFT     ||
-      pim_type == PIM_MSG_TYPE_GRAFT_ACK ||
-      pim_type == PIM_MSG_TYPE_CANDIDATE)
+  switch (pim_type)
     {
+    case PIM_MSG_TYPE_HELLO:
+      return pim_hello_recv (ifp,
+                            ip_hdr->ip_src,
+                            pim_msg + PIM_MSG_HEADER_LEN,
+                            pim_msg_len - PIM_MSG_HEADER_LEN);
+      break;
+    case PIM_MSG_TYPE_REGISTER:
+      return pim_register_recv (ifp,
+                               ip_hdr->ip_dst,
+                               ip_hdr->ip_src,
+                               pim_msg + PIM_MSG_HEADER_LEN,
+                               pim_msg_len - PIM_MSG_HEADER_LEN);
+      break;
+    case PIM_MSG_TYPE_REG_STOP:
+      return pim_register_stop_recv (pim_msg + PIM_MSG_HEADER_LEN,
+                                    pim_msg_len - PIM_MSG_HEADER_LEN);
+      break;
+    case PIM_MSG_TYPE_JOIN_PRUNE:
+      neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
+      if (!neigh) {
+       if (PIM_DEBUG_PIM_PACKETS)
+         zlog_debug("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    pim_type, src_str, ifp->name);
+       return -1;
+      }
+      pim_neighbor_timer_reset(neigh, neigh->holdtime);
+      return pim_joinprune_recv(ifp, neigh,
+                               ip_hdr->ip_src,
+                               pim_msg + PIM_MSG_HEADER_LEN,
+                               pim_msg_len - PIM_MSG_HEADER_LEN);
+      break;
+    case PIM_MSG_TYPE_ASSERT:
+      neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
+      if (!neigh) {
+       if (PIM_DEBUG_PIM_PACKETS)
+         zlog_debug("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    pim_type, src_str, ifp->name);
+       return -1;
+      }
+      pim_neighbor_timer_reset(neigh, neigh->holdtime);
+      return pim_assert_recv(ifp, neigh,
+                            ip_hdr->ip_src,
+                            pim_msg + PIM_MSG_HEADER_LEN,
+                            pim_msg_len - PIM_MSG_HEADER_LEN);
+      break;
+    default:
       if (PIM_DEBUG_PIM_PACKETS) {
        zlog_debug("Recv PIM packet type %d which is not currently understood",
                   pim_type);
       }
       return -1;
     }
-
-  if (pim_type == PIM_MSG_TYPE_HELLO) {
-    return pim_hello_recv(ifp,
-                         ip_hdr->ip_src,
-                         pim_msg + PIM_MSG_HEADER_LEN,
-                         pim_msg_len - PIM_MSG_HEADER_LEN);
-  } else if (pim_type == PIM_MSG_TYPE_REGISTER) {
-    return pim_register_recv(ifp,
-                            ip_hdr->ip_dst,
-                            ip_hdr->ip_src,
-                            pim_msg + PIM_MSG_HEADER_LEN,
-                            pim_msg_len - PIM_MSG_HEADER_LEN);
-  }
-
-  neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
-  if (!neigh) {
-    zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
-             __FILE__, __PRETTY_FUNCTION__,
-             pim_type, src_str, ifp->name);
-    return -1;
-  }
-
-  switch (pim_type) {
-  case PIM_MSG_TYPE_JOIN_PRUNE:
-    return pim_joinprune_recv(ifp, neigh,
-                             ip_hdr->ip_src,
-                             pim_msg + PIM_MSG_HEADER_LEN,
-                             pim_msg_len - PIM_MSG_HEADER_LEN);
-    break;
-  case PIM_MSG_TYPE_ASSERT:
-    return pim_assert_recv(ifp, neigh,
-                          ip_hdr->ip_src,
-                          pim_msg + PIM_MSG_HEADER_LEN,
-                          pim_msg_len - PIM_MSG_HEADER_LEN);
-    break;
-  default:
-    zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s",
-             __FILE__, __PRETTY_FUNCTION__,
-             pim_type, src_str, ifp->name);
-    break;
-  }
-
   return -1;
 }
 
@@ -278,80 +290,73 @@ static int pim_sock_read(struct thread *t)
   int len;
   ifindex_t ifindex = -1;
   int result = -1; /* defaults to bad */
-
-  zassert(t);
+  static long long count = 0;
+  int cont = 1;
 
   ifp = THREAD_ARG(t);
-  zassert(ifp);
-
   fd = THREAD_FD(t);
 
   pim_ifp = ifp->info;
-  zassert(pim_ifp);
-
-  zassert(fd == pim_ifp->pim_sock_fd);
 
-  len = pim_socket_recvfromto(fd, buf, sizeof(buf),
-                             &from, &fromlen,
-                             &to, &tolen,
-                             &ifindex);
-  if (len < 0) {
-    zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s",
-             fd, errno, safe_strerror(errno));
-    goto done;
-  }
-
-  if (PIM_DEBUG_PIM_PACKETS) {
-    char from_str[100];
-    char to_str[100];
-    
-    if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
-      sprintf(from_str, "<from?>");
-    if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
-      sprintf(to_str, "<to?>");
-    
-    zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
-              len, from_str, to_str, fd, ifindex, ifp->ifindex);
-  }
-
-  if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
-    pim_pkt_dump(__PRETTY_FUNCTION__, buf, len);
-  }
+  while (cont)
+    {
+      len = pim_socket_recvfromto(fd, buf, sizeof(buf),
+                                 &from, &fromlen,
+                                 &to, &tolen,
+                                 &ifindex);
+      if (len < 0)
+       {
+         if (errno == EINTR)
+           continue;
+         if (errno == EWOULDBLOCK || errno == EAGAIN)
+           {
+             cont = 0;
+             break;
+           }
+         if (PIM_DEBUG_PIM_PACKETS)
+           zlog_debug ("Received errno: %d %s", errno, safe_strerror (errno));
+         goto done;
+       }
 
 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
-  /* ifindex sanity check */
-  if (ifindex != (int) ifp->ifindex) {
-    char from_str[100];
-    char to_str[100];
-    struct interface *recv_ifp;
-
-    if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
-      sprintf(from_str, "<from?>");
-    if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
-      sprintf(to_str, "<to?>");
-
-    recv_ifp = if_lookup_by_index(ifindex);
-    if (recv_ifp) {
-      zassert(ifindex == (int) recv_ifp->ifindex);
-    }
+      /* ifindex sanity check */
+      if (ifindex != (int) ifp->ifindex) {
+       char from_str[INET_ADDRSTRLEN];
+       char to_str[INET_ADDRSTRLEN];
+       struct interface *recv_ifp;
+
+       if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
+         sprintf(from_str, "<from?>");
+       if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
+         sprintf(to_str, "<to?>");
+
+       recv_ifp = if_lookup_by_index(ifindex);
+       if (recv_ifp) {
+         zassert(ifindex == (int) recv_ifp->ifindex);
+       }
 
 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
-    zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
-             from_str, to_str, fd,
-             ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
-             ifp->ifindex, ifp->name);
+       zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
+                 from_str, to_str, fd,
+                 ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
+                 ifp->ifindex, ifp->name);
 #endif
-    goto done;
-  }
+       goto done;
+      }
 #endif
 
-  int fail = pim_pim_packet(ifp, buf, len);
-  if (fail) {
-    if (PIM_DEBUG_PIM_PACKETS)
-      zlog_debug("%s: pim_pim_packet() return=%d",
-                __PRETTY_FUNCTION__, fail);
-    goto done;
-  }
+      int fail = pim_pim_packet(ifp, buf, len);
+      if (fail) {
+       if (PIM_DEBUG_PIM_PACKETS)
+         zlog_debug("%s: pim_pim_packet() return=%d",
+                    __PRETTY_FUNCTION__, fail);
+       goto done;
+      }
+
+      count++;
+      if (count % qpim_packet_process == 0)
+        cont = 0;
+    }
 
   result = 0; /* good */
 
@@ -378,7 +383,7 @@ static void pim_sock_read_on(struct interface *ifp)
     zlog_debug("Scheduling READ event on PIM socket fd=%d",
               pim_ifp->pim_sock_fd);
   }
-  pim_ifp->t_pim_sock_read = 0;
+  pim_ifp->t_pim_sock_read = NULL;
   zassert(!pim_ifp->t_pim_sock_read);
   THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp,
                 pim_ifp->pim_sock_fd);
@@ -431,9 +436,9 @@ void pim_sock_reset(struct interface *ifp)
 
   pim_ifp->pim_sock_fd       = -1;
   pim_ifp->pim_sock_creation = 0;
-  pim_ifp->t_pim_sock_read   = 0;
+  pim_ifp->t_pim_sock_read   = NULL;
 
-  pim_ifp->t_pim_hello_timer          = 0;
+  pim_ifp->t_pim_hello_timer          = NULL;
   pim_ifp->pim_hello_period           = PIM_DEFAULT_HELLO_PERIOD;
   pim_ifp->pim_default_holdtime       = -1; /* unset: means 3.5 * pim_hello_period */
   pim_ifp->pim_triggered_hello_delay  = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
@@ -462,18 +467,91 @@ void pim_sock_reset(struct interface *ifp)
   pim_ifstat_reset(ifp);
 }
 
-int pim_msg_send(int fd,
-                struct in_addr dst,
-                uint8_t *pim_msg,
-                int pim_msg_size,
-                const char *ifname)
+static uint16_t ip_id = 0;
+
+
+static int
+pim_msg_send_frame (int fd, char *buf, size_t len,
+                   struct sockaddr *dst, size_t salen)
+{
+  struct ip *ip = (struct ip *)buf;
+
+  while (sendto (fd, buf, len, MSG_DONTWAIT, dst, salen) < 0)
+    {
+      char dst_str[INET_ADDRSTRLEN];
+
+      switch (errno)
+       {
+       case EMSGSIZE:
+         {
+           size_t hdrsize = sizeof (struct ip);
+           size_t newlen1 = ((len - hdrsize) / 2 ) & 0xFFF8;
+           size_t sendlen = newlen1 + hdrsize;
+           size_t offset = ntohs (ip->ip_off);
+
+           ip->ip_len = htons (sendlen);
+           ip->ip_off = htons (offset | IP_MF);
+           if (pim_msg_send_frame (fd, buf, sendlen, dst, salen) == 0)
+             {
+               struct ip *ip2 = (struct ip *)(buf + newlen1);
+               size_t newlen2 = len - sendlen;
+               sendlen = newlen2 + hdrsize;
+
+               memcpy (ip2, ip, hdrsize);
+               ip2->ip_len = htons (sendlen);
+               ip2->ip_off = htons (offset + (newlen1 >> 3));
+               return pim_msg_send_frame (fd, (char *)ip2, sendlen, dst, salen);
+             }
+         }
+
+         return -1;
+         break;
+       default:
+         if (PIM_DEBUG_PIM_PACKETS)
+           {
+             pim_inet4_dump ("<dst?>", ip->ip_dst, dst_str, sizeof (dst_str));
+             zlog_warn ("%s: sendto() failure to %s: fd=%d msg_size=%zd: errno=%d: %s",
+                        __PRETTY_FUNCTION__,
+                        dst_str, fd, len,
+                        errno, safe_strerror(errno));
+           }
+         return -1;
+         break;
+       }
+    }
+
+  return 0;
+}
+
+int
+pim_msg_send(int fd, struct in_addr src,
+            struct in_addr dst, uint8_t *pim_msg,
+            int pim_msg_size, const char *ifname)
 {
-  ssize_t            sent;
   struct sockaddr_in to;
   socklen_t          tolen;
+  unsigned char      buffer[10000];
+  unsigned char      *msg_start;
+  struct ip *ip;
+
+  memset (buffer, 0, 10000);
+  int sendlen = sizeof (struct ip) + pim_msg_size;
+
+  msg_start = buffer + sizeof (struct ip);
+  memcpy (msg_start, pim_msg, pim_msg_size);
+
+  ip = (struct ip *)buffer;
+  ip->ip_id = htons (++ip_id);
+  ip->ip_hl = 5;
+  ip->ip_v = 4;
+  ip->ip_p = PIM_IP_PROTO_PIM;
+  ip->ip_src = src;
+  ip->ip_dst = dst;
+  ip->ip_ttl = MAXTTL;
+  ip->ip_len = htons (sendlen);
 
   if (PIM_DEBUG_PIM_PACKETS) {
-    char dst_str[100];
+    char dst_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
     zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x",
               __PRETTY_FUNCTION__,
@@ -490,27 +568,8 @@ int pim_msg_send(int fd,
     pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size);
   }
 
-  sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT,
-                (struct sockaddr *)&to, tolen);
-  if (sent != (ssize_t) pim_msg_size) {
-    int e = errno;
-    char dst_str[100];
-    pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
-    if (sent < 0) {
-      zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s",
-               __PRETTY_FUNCTION__,
-               dst_str, ifname, fd, pim_msg_size,
-               e, safe_strerror(e));
-    }
-    else {
-      zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd",
-               __PRETTY_FUNCTION__,
-               dst_str, ifname, fd,
-               pim_msg_size, sent);
-    }
-    return -1;
-  }
-
+  pim_msg_send_frame (fd, (char *)buffer, sendlen,
+                     (struct sockaddr *)&to, tolen);
   return 0;
 }
 
@@ -525,7 +584,7 @@ static int hello_send(struct interface *ifp,
   pim_ifp = ifp->info;
 
   if (PIM_DEBUG_PIM_HELLO) {
-    char dst_str[100];
+    char dst_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<dst?>", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str));
     zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
               __PRETTY_FUNCTION__,
@@ -560,6 +619,7 @@ static int hello_send(struct interface *ifp,
                       PIM_MSG_TYPE_HELLO);
 
   if (pim_msg_send(pim_ifp->pim_sock_fd,
+                  pim_ifp->primary_address,
                   qpim_all_pim_routers_addr,
                   pim_msg,
                   pim_msg_size,
@@ -627,16 +687,14 @@ static int on_pim_hello_send(struct thread *t)
   struct pim_interface *pim_ifp;
   struct interface *ifp;
 
-  zassert(t);
   ifp = THREAD_ARG(t);
-  zassert(ifp);
 
   pim_ifp = ifp->info;
 
   /*
    * Schedule next hello
    */
-  pim_ifp->t_pim_hello_timer = 0;
+  pim_ifp->t_pim_hello_timer = NULL;
   hello_resched(ifp);
 
   /*
@@ -692,7 +750,20 @@ void pim_hello_restart_triggered(struct interface *ifp)
   pim_ifp = ifp->info;
   zassert(pim_ifp);
 
-  triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay;
+  /*
+   * There exists situations where we have the a RPF out this
+   * interface, but we haven't formed a neighbor yet.  This
+   * happens especially during interface flaps.  While
+   * we would like to handle this more gracefully in other
+   * parts of the code.  In order to get us up and running
+   * let's just send the hello immediate'ish
+   * This should be revisited when we get nexthop tracking
+   * in and when we have a better handle on safely
+   * handling the rpf information for upstreams that
+   * we cannot legally reach yet.
+   */
+  triggered_hello_delay_msec = 1;
+  //triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay;
 
   if (pim_ifp->t_pim_hello_timer) {
     long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
@@ -703,11 +774,11 @@ void pim_hello_restart_triggered(struct interface *ifp)
     }
 
     THREAD_OFF(pim_ifp->t_pim_hello_timer);
-    pim_ifp->t_pim_hello_timer = 0;
+    pim_ifp->t_pim_hello_timer = NULL;
   }
-  zassert(!pim_ifp->t_pim_hello_timer);
 
-  random_msec = random() % (triggered_hello_delay_msec + 1);
+  random_msec = triggered_hello_delay_msec;
+  //random_msec = random() % (triggered_hello_delay_msec + 1);
 
   if (PIM_DEBUG_PIM_HELLO) {
     zlog_debug("Scheduling %d msec triggered hello on interface %s",
@@ -729,8 +800,9 @@ int pim_sock_add(struct interface *ifp)
   zassert(pim_ifp);
 
   if (pim_ifp->pim_sock_fd >= 0) {
-    zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s",
-             pim_ifp->pim_sock_fd, ifp->name);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("Can't recreate existing PIM socket fd=%d for interface %s",
+                pim_ifp->pim_sock_fd, ifp->name);
     return -1;
   }
 
@@ -738,12 +810,15 @@ int pim_sock_add(struct interface *ifp)
 
   pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex);
   if (pim_ifp->pim_sock_fd < 0) {
-    zlog_warn("Could not open PIM socket on interface %s",
-             ifp->name);
+    if (PIM_DEBUG_PIM_PACKETS)
+      zlog_debug("Could not open PIM socket on interface %s",
+                ifp->name);
     return -2;
   }
 
-  pim_ifp->t_pim_sock_read   = 0;
+  pim_socket_ip_hdr (pim_ifp->pim_sock_fd);
+
+  pim_ifp->t_pim_sock_read   = NULL;
   pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
 
   /*
index 5692a37938de1718a0bdd9b44b849dfaebd8c279..1f7e1e5d4f166a29b57dde6ae8fd9b23938af158 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_PIM_H
@@ -28,8 +32,6 @@
 #define PIM_PIM_BUFSIZE_READ  (20000)
 #define PIM_PIM_BUFSIZE_WRITE (20000)
 
-#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20)
-
 #define PIM_DEFAULT_HELLO_PERIOD                 (30)   /* seconds, RFC 4601: 4.11 */
 #define PIM_DEFAULT_TRIGGERED_HELLO_DELAY        (5)    /* seconds, RFC 4601: 4.11 */
 #define PIM_DEFAULT_DR_PRIORITY                  (1)    /* RFC 4601: 4.3.1 */
 #define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0)    /* boolean */
 #define PIM_DEFAULT_T_PERIODIC                   (60)   /* RFC 4601: 4.11.  Timer Values */
 
-#define PIM_MSG_TYPE_HELLO      (0)
-#define PIM_MSG_TYPE_REGISTER   (1)
-#define PIM_MSG_TYPE_REG_STOP   (2)
-#define PIM_MSG_TYPE_JOIN_PRUNE (3)
-#define PIM_MSG_TYPE_BOOTSTRAP  (4)
-#define PIM_MSG_TYPE_ASSERT     (5)
-#define PIM_MSG_TYPE_GRAFT      (6)
-#define PIM_MSG_TYPE_GRAFT_ACK  (7)
-#define PIM_MSG_TYPE_CANDIDATE  (8)
+enum pim_msg_type {
+  PIM_MSG_TYPE_HELLO = 0,
+  PIM_MSG_TYPE_REGISTER,
+  PIM_MSG_TYPE_REG_STOP,
+  PIM_MSG_TYPE_JOIN_PRUNE,
+  PIM_MSG_TYPE_BOOTSTRAP,
+  PIM_MSG_TYPE_ASSERT,
+  PIM_MSG_TYPE_GRAFT,
+  PIM_MSG_TYPE_GRAFT_ACK,
+  PIM_MSG_TYPE_CANDIDATE
+};
 
 #define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg)
 #define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg)
@@ -67,6 +71,7 @@ void pim_hello_restart_triggered(struct interface *ifp);
 int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len);
 
 int pim_msg_send(int fd,
+                struct in_addr src,
                 struct in_addr dst,
                 uint8_t *pim_msg,
                 int pim_msg_size,
index ce3ac1a4333eb362149f29cf199a179254f6825a..490a05be37644441a0a2d114167ced99d13d0197 100644 (file)
@@ -24,6 +24,9 @@
 #include "log.h"
 #include "if.h"
 #include "thread.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_mroute.h"
 #include "pim_oil.h"
 #include "pim_zebra.h"
 #include "pim_join.h"
+#include "pim_util.h"
 
 struct thread *send_test_packet_timer = NULL;
 
-/*
- * This seems stupidly expensive.  A list lookup.  Why is this
- * not a hash?
- */
-static int
-pim_check_is_my_ip_address (struct in_addr dest_addr)
+void
+pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg,
+                       struct in_addr src, struct in_addr originator)
 {
-  /*
-   * See if we can short-cut some?
-   * This might not make sense if we ever leave a static RP
-   * type of configuration.
-   * Note - Premature optimization might bite our patooeys' here.
-   */
-  if (I_am_RP(dest_addr) && (dest_addr.s_addr == qpim_rp.rpf_addr.s_addr))
-    return 1;
+  struct pim_interface *pinfo;
+  unsigned char buffer[10000];
+  unsigned int b1length = 0;
+  unsigned int length;
+  uint8_t *b1;
+  struct prefix p;
+
+  if (PIM_DEBUG_PIM_REG)
+    {
+      zlog_debug ("Sending Register stop for %s to %s on %s",
+                 pim_str_sg_dump (sg), inet_ntoa(originator), ifp->name);
+    }
 
-  if (if_lookup_exact_address (&dest_addr, AF_INET))
-    return 1;
+  memset (buffer, 0, 10000);
+  b1 = (uint8_t *)buffer + PIM_MSG_REGISTER_STOP_LEN;
 
-  return 0;
+  length = pim_encode_addr_group (b1, AFI_IP, 0, 0, sg->grp);
+  b1length += length;
+  b1 += length;
+
+  p.family = AF_INET;
+  p.u.prefix4 = sg->src;
+  p.prefixlen = 32;
+  length = pim_encode_addr_ucast (b1, &p);
+  b1length += length;
+
+  pim_msg_build_header (buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, PIM_MSG_TYPE_REG_STOP);
+
+  pinfo = (struct pim_interface *)ifp->info;
+  if (!pinfo)
+    {
+      if (PIM_DEBUG_PIM_TRACE)
+        zlog_debug ("%s: No pinfo!\n", __PRETTY_FUNCTION__);
+      return;
+    }
+  if (pim_msg_send (pinfo->pim_sock_fd, src, originator,
+                   buffer, b1length + PIM_MSG_REGISTER_STOP_LEN,
+                   ifp->name))
+    {
+      if (PIM_DEBUG_PIM_TRACE)
+       {
+         zlog_debug ("%s: could not send PIM register stop message on interface %s",
+                     __PRETTY_FUNCTION__, ifp->name);
+       }
+    }
 }
 
-static void
-pim_register_stop_send (struct in_addr src)
+int
+pim_register_stop_recv (uint8_t *buf, int buf_size)
 {
-  return;
+  struct pim_upstream *upstream = NULL;
+  struct prefix source;
+  struct prefix_sg sg;
+  int l;
+
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  l = pim_parse_addr_group (&sg, buf, buf_size);
+  buf += l;
+  buf_size -= l;
+  pim_parse_addr_ucast (&source, buf, buf_size);
+  sg.src = source.u.prefix4;
+
+  upstream = pim_upstream_find (&sg);
+  if (!upstream)
+    {
+      return 0;
+    }
+
+  if (PIM_DEBUG_PIM_REG)
+    zlog_debug ("Received Register stop for %s",
+               upstream->sg_str);
+
+  switch (upstream->join_state)
+    {
+    case PIM_UPSTREAM_NOTJOINED:
+    case PIM_UPSTREAM_PRUNE:
+      return 0;
+      break;
+    case PIM_UPSTREAM_JOINED:
+      upstream->join_state = PIM_UPSTREAM_PRUNE;
+      pim_channel_del_oif (upstream->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+      pim_upstream_start_register_stop_timer (upstream, 0);
+    case PIM_UPSTREAM_JOIN_PENDING:
+      upstream->join_state = PIM_UPSTREAM_PRUNE;
+      pim_upstream_start_register_stop_timer (upstream, 0);
+      return 0;
+      break;
+    }
+
+  return 0;
 }
 
 void
-pim_register_send (const struct ip *ip_hdr, struct pim_rpf *rpg)
+pim_register_send (const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, struct pim_upstream *up)
 {
-  unsigned char buffer[3000];
+  unsigned char buffer[10000];
   unsigned char *b1;
   struct pim_interface *pinfo;
   struct interface *ifp;
-  uint32_t plen;
+
+  if (PIM_DEBUG_PIM_REG)
+    {
+       char rp_str[INET_ADDRSTRLEN];
+       strcpy (rp_str, inet_ntoa (rpg->rpf_addr.u.prefix4));
+       zlog_debug ("Sending %s %sRegister Packet to %s",
+                  up->sg_str, null_register ? "NULL " : "", rp_str);
+    }
 
   ifp = rpg->source_nexthop.interface;
+  if (!ifp)
+    {
+      if (PIM_DEBUG_PIM_REG)
+        zlog_debug ("%s: No interface to transmit register on", __PRETTY_FUNCTION__);
+      return;
+    }
   pinfo = (struct pim_interface *)ifp->info;
   if (!pinfo) {
-    zlog_debug("%s: No pinfo!\n", __PRETTY_FUNCTION__);
+    if (PIM_DEBUG_PIM_REG)
+      zlog_debug("%s: Interface: %s not configured for pim to trasmit on!\n", __PRETTY_FUNCTION__, ifp->name);
     return;
   }
 
-  memset(buffer, 0, 3000);
+  memset(buffer, 0, 10000);
+  b1 = buffer + PIM_MSG_HEADER_LEN;
+  *b1 |= null_register << 6;
   b1 = buffer + PIM_MSG_REGISTER_LEN;
 
-  plen = ntohs(ip_hdr->ip_len);
-  memcpy(b1, (const unsigned char *)ip_hdr, plen);
+  memcpy(b1, (const unsigned char *)buf, buf_size);
 
-  pim_msg_build_header(buffer, plen + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER);
+  pim_msg_build_header(buffer, buf_size + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER);
 
   if (pim_msg_send(pinfo->pim_sock_fd,
-                  rpg->rpf_addr,
+                  src,
+                  rpg->rpf_addr.u.prefix4,
                   buffer,
-                  plen + PIM_MSG_REGISTER_LEN,
+                  buf_size + PIM_MSG_REGISTER_LEN,
                   ifp->name)) {
     if (PIM_DEBUG_PIM_TRACE) {
       zlog_debug("%s: could not send PIM register message on interface %s",
@@ -159,15 +247,16 @@ pim_register_recv (struct interface *ifp,
 {
   int sentRegisterStop = 0;
   struct ip *ip_hdr;
-  //size_t hlen;
-  struct in_addr group = { .s_addr = 0 };
-  struct in_addr source = { .s_addr = 0 };
-  //uint8_t *msg;
+  struct prefix_sg sg;
   uint32_t *bits;
+  int i_am_rp = 0;
 
-  if (!pim_check_is_my_ip_address (dest_addr)) {
-    if (PIM_DEBUG_PIM_PACKETS) {
-      char dest[100];
+#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4
+  ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN);
+
+  if (!pim_rp_check_is_my_ip_address (ip_hdr->ip_dst, dest_addr)) {
+    if (PIM_DEBUG_PIM_REG) {
+      char dest[INET_ADDRSTRLEN];
 
       pim_inet4_dump ("<dst?>", dest_addr, dest, sizeof(dest));
       zlog_debug ("%s: Received Register message for %s that I do not own", __func__,
@@ -176,7 +265,6 @@ pim_register_recv (struct interface *ifp,
     return 0;
   }
 
-#define inherited_olist(S,G) NULL
   /*
    * Please note this is not drawn to get the correct bit/data size
    *
@@ -203,25 +291,33 @@ pim_register_recv (struct interface *ifp,
    * Line above.  So we need to add 4 bytes to get to the
    * start of the actual Encapsulated data.
    */
-#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4
-  ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN);
-  //hlen = (ip_hdr->ip_hl << 2) | PIM_MSG_REGISTER_LEN;
-  //msg = (uint8_t *)tlv_buf + hlen;
-  source = ip_hdr->ip_src;
-  group = ip_hdr->ip_dst;
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = ip_hdr->ip_src;
+  sg.grp = ip_hdr->ip_dst;
 
-  if (I_am_RP (group) && (dest_addr.s_addr == ((RP (group))->rpf_addr.s_addr))) {
+  i_am_rp = I_am_RP (sg.grp);
+
+  if (PIM_DEBUG_PIM_REG)
+    {
+      char src_str[INET_ADDRSTRLEN];
+
+      pim_inet4_dump ("<src?>", src_addr, src_str, sizeof (src_str));
+      zlog_debug ("Received Register message(%s) from %s on %s, rp: %d",
+                  pim_str_sg_dump (&sg), src_str, ifp->name, i_am_rp);
+    }
+
+  if (i_am_rp && (dest_addr.s_addr == ((RP (sg.grp))->rpf_addr.u.prefix4.s_addr))) {
     sentRegisterStop = 0;
 
     if (*bits & PIM_REGISTER_BORDER_BIT) {
-      struct in_addr pimbr = pim_br_get_pmbr (source, group);
+      struct in_addr pimbr = pim_br_get_pmbr (&sg);
       if (PIM_DEBUG_PIM_PACKETS)
        zlog_debug("%s: Received Register message with Border bit set", __func__);
 
       if (pimbr.s_addr == pim_br_unknown.s_addr)
-       pim_br_set_pmbr(source, group, src_addr);
+       pim_br_set_pmbr(&sg, src_addr);
       else if (src_addr.s_addr != pimbr.s_addr) {
-       pim_register_stop_send(src_addr);
+       pim_register_stop_send (ifp, &sg, dest_addr, src_addr);
        if (PIM_DEBUG_PIM_PACKETS)
          zlog_debug("%s: Sending register-Stop to %s and dropping mr. packet",
            __func__, "Sender");
@@ -230,83 +326,81 @@ pim_register_recv (struct interface *ifp,
       }
     }
 
-    struct pim_upstream *upstream = pim_upstream_find (source, group);
+    struct pim_upstream *upstream = pim_upstream_find (&sg);
     /*
      * If we don't have a place to send ignore the packet
      */
     if (!upstream)
-      return 1;
+      {
+       upstream = pim_upstream_add (&sg, ifp,
+                                    PIM_UPSTREAM_FLAG_MASK_SRC_STREAM,
+                                    __PRETTY_FUNCTION__);
+        if (!upstream)
+          {
+            zlog_warn ("Failure to create upstream state");
+            return 1;
+          }
+        PIM_UPSTREAM_FLAG_SET_SRC_STREAM(upstream->flags);
+
+        upstream->upstream_register = src_addr;
+       pim_rp_set_upstream_addr (&upstream->upstream_addr, sg.src, sg.grp);
+       if (pim_nexthop_lookup (&upstream->rpf.source_nexthop,
+                               upstream->upstream_addr, 1) != 0)
+          {
+            if (PIM_DEBUG_PIM_REG)
+              {
+                zlog_debug ("Received Register(%s), for which I have no path back", upstream->sg_str);
+              }
+            PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(upstream->flags);
+            pim_upstream_del (upstream, __PRETTY_FUNCTION__);
+            return 1;
+          }
+       upstream->sg.src = sg.src;
+       upstream->rpf.rpf_addr = upstream->rpf.source_nexthop.mrib_nexthop_addr;
+
+       upstream->join_state = PIM_UPSTREAM_PRUNE;
+
+      }
 
     if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) ||
-       ((SwitchToSptDesired(source, group)) &&
-        (inherited_olist(source, group) == NULL))) {
-      pim_register_stop_send(src_addr);
+       ((SwitchToSptDesired(&sg)) &&
+        pim_upstream_inherited_olist (upstream) == 0)) {
+      //pim_scan_individual_oil (upstream->channel_oil);
+      pim_register_stop_send (ifp, &sg, dest_addr, src_addr);
       sentRegisterStop = 1;
+    } else {
+      if (PIM_DEBUG_PIM_REG)
+         zlog_debug ("(%s) sptbit: %d", upstream->sg_str, upstream->sptbit);
     }
-
     if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) ||
-       (SwitchToSptDesired(source, group))) {
+       (SwitchToSptDesired(&sg))) {
       if (sentRegisterStop) {
-       pim_upstream_keep_alive_timer_start (upstream, PIM_RP_KEEPALIVE_PERIOD);
+       pim_upstream_keep_alive_timer_start (upstream, qpim_rp_keep_alive_time);
       } else {
-       pim_upstream_keep_alive_timer_start (upstream, PIM_KEEPALIVE_PERIOD);
+       pim_upstream_keep_alive_timer_start (upstream, qpim_keep_alive_time);
       }
     }
 
     if (!(upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) &&
        !(*bits & PIM_REGISTER_NR_BIT))
       {
-       pim_rp_set_upstream_addr (&upstream->upstream_addr, source);
-       pim_nexthop_lookup (&upstream->rpf.source_nexthop,
-                           upstream->upstream_addr, NULL);
-       upstream->rpf.source_nexthop.interface = ifp;
-       upstream->source_addr.s_addr = source.s_addr;
-       upstream->rpf.rpf_addr.s_addr = source.s_addr;
-       upstream->channel_oil->oil.mfcc_origin = source;
-       pim_scan_individual_oil (upstream->channel_oil);
-       pim_joinprune_send(upstream->rpf.source_nexthop.interface,
-                          upstream->rpf.source_nexthop.mrib_nexthop_addr,
-                          upstream->source_addr,
-                          upstream->group_addr,
-                          1);
-
        //decapsulate and forward the iner packet to
        //inherited_olist(S,G,rpt)
+       // This is taken care of by the kernel for us
       }
+     pim_upstream_msdp_reg_timer_start(upstream);
   } else {
-    pim_register_stop_send(src_addr);
+    if (PIM_DEBUG_PIM_REG)
+      {
+       if (!i_am_rp)
+         zlog_debug ("Received Register packet for %s, Rejecting packet because I am not the RP configured for group",
+                     pim_str_sg_dump (&sg));
+       else
+         zlog_debug ("Received Register packet for %s, Rejecting packet because the dst ip address is not the actual RP",
+                     pim_str_sg_dump (&sg));
+      }
+    pim_register_stop_send (ifp, &sg, dest_addr, src_addr);
   }
 
   return 1;
 }
-
-
-static int
-pim_register_send_test_packet (struct thread *t)
-{
-  uint8_t *packet;
-
-  packet = THREAD_ARG(t);
-
-  *packet = 4;
-
-  return 1;
-}
-
-/*
- * pim_register_send_test_packet
- *
- * Send a test packet to the RP from source, in group and pps packets per second
- */
-void
-pim_register_send_test_packet_start (struct in_addr source,
-                                    struct in_addr group,
-                                    uint32_t pps)
-{
-  uint8_t *packet = NULL;
-
-  THREAD_TIMER_MSEC_ON(master, send_test_packet_timer,
-                      pim_register_send_test_packet, packet, 1000/pps);
-
-  return;
-}
index 039c0006e0670e694b83fbe50f71f63f33baf8dc..42a908b225af702efff0b0afdbbfb3f31098b691 100644 (file)
 #define PIM_MSG_REGISTER_LEN   (8)
 #define PIM_MSG_REGISTER_STOP_LEN (4)
 
-void pim_register_send_test_packet_start (struct in_addr source,
-                                         struct in_addr group,
-                                         uint32_t pps);
+int pim_register_stop_recv (uint8_t *buf, int buf_size);
 
 int pim_register_recv (struct interface *ifp,
                       struct in_addr dest_addr,
                       struct in_addr src_addr,
                       uint8_t *tlv_buf, int tlv_buf_size);
 
-void pim_register_send (const struct ip *msg, struct pim_rpf *rpg);
+void pim_register_send (const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, struct pim_upstream *up);
+void pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg, struct in_addr src, struct in_addr originator);
 
 #endif
index 26d108bcaac5ad004dec478c41a5829d05f1d270..ba464e98c63b41186a4ff248767c3d436e8b9e3c 100644 (file)
  */
 #include <zebra.h>
 
+#include "lib/json.h"
 #include "log.h"
 #include "network.h"
 #include "if.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "memory.h"
+#include "vty.h"
+#include "vrf.h"
+#include "plist.h"
 
 #include "pimd.h"
+#include "pim_vty.h"
 #include "pim_str.h"
+#include "pim_iface.h"
 #include "pim_rp.h"
 #include "pim_str.h"
 #include "pim_rpf.h"
+#include "pim_sock.h"
+#include "pim_memory.h"
+#include "pim_iface.h"
+#include "pim_msdp.h"
 
-static int i_am_rp = 0;
+struct rp_info
+{
+  struct prefix group;
+  struct pim_rpf rp;
+  int i_am_rp;
+  char *plist;
+};
+
+static struct list *qpim_rp_list = NULL;
+static struct rp_info *tail = NULL;
+
+static void
+pim_rp_info_free (struct rp_info *rp_info)
+{
+  XFREE (MTYPE_PIM_RP, rp_info);
+}
+
+static int
+pim_rp_list_cmp (void *v1, void *v2)
+{
+  struct rp_info *rp1 = (struct rp_info *)v1;
+  struct rp_info *rp2 = (struct rp_info *)v2;
+
+  if (rp1 == rp2)
+    return 0;
+
+  if (!rp1 && rp2)
+    return -1;
+
+  if (rp1 && !rp2)
+    return 1;
+
+  /*
+   * Sort by RP IP address
+   */
+  if (rp1->rp.rpf_addr.u.prefix4.s_addr < rp2->rp.rpf_addr.u.prefix4.s_addr)
+    return -1;
+
+  if (rp1->rp.rpf_addr.u.prefix4.s_addr > rp2->rp.rpf_addr.u.prefix4.s_addr)
+    return 1;
+
+  /*
+   * Sort by group IP address
+   */
+  if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr)
+    return -1;
+
+  if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr)
+    return 1;
+
+  if (rp1 == tail)
+    return 1;
+
+  return -1;
+}
+
+void
+pim_rp_init (void)
+{
+  struct rp_info *rp_info;
+
+  qpim_rp_list = list_new ();
+  qpim_rp_list->del = (void (*)(void *))pim_rp_info_free;
+  qpim_rp_list->cmp = pim_rp_list_cmp;
+
+  rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info));
+
+  if (!rp_info)
+    return;
+
+  str2prefix ("224.0.0.0/4", &rp_info->group);
+  rp_info->group.family = AF_INET;
+  rp_info->rp.rpf_addr.family = AF_INET;
+  rp_info->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE;
+  tail = rp_info;
+
+  listnode_add (qpim_rp_list, rp_info);
+}
+
+void
+pim_rp_free (void)
+{
+  if (qpim_rp_list)
+    list_free (qpim_rp_list);
+}
+
+/*
+ * Given an RP's prefix-list, return the RP's rp_info for that prefix-list
+ */
+static struct rp_info *
+pim_rp_find_prefix_list (struct in_addr rp, const char *plist)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr &&
+          rp_info->plist && strcmp(rp_info->plist, plist) == 0)
+        {
+          return rp_info;
+        }
+    }
+
+  return NULL;
+}
+
+/*
+ * Return true if plist is used by any rp_info
+ */
+static int
+pim_rp_prefix_list_used (const char *plist)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (rp_info->plist && strcmp(rp_info->plist, plist) == 0)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
 
 /*
- * Checks to see if we should elect ourself the actual RP
+ * Given an RP's address, return the RP's rp_info that is an exact match for 'group'
  */
+static struct rp_info *
+pim_rp_find_exact (struct in_addr rp, struct prefix *group)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr &&
+         prefix_same (&rp_info->group, group))
+       return rp_info;
+    }
+
+  return NULL;
+}
+
+/*
+ * Given a group, return the rp_info for that group
+ */
+static struct rp_info *
+pim_rp_find_match_group (struct prefix *group)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+  struct prefix_list *plist;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (rp_info->plist)
+        {
+          plist = prefix_list_lookup (AFI_IP, rp_info->plist);
+
+          if (plist && prefix_list_apply (plist, group) == PREFIX_PERMIT)
+            return rp_info;
+        }
+      else
+        {
+          if (prefix_match (&rp_info->group, group))
+            return rp_info;
+        }
+    }
+
+  return NULL;
+}
+
+/*
+ * When the user makes "ip pim rp" configuration changes or if they change the
+ * prefix-list(s) used by these statements we must tickle the upstream state
+ * for each group to make them re-lookup who their RP should be.
+ *
+ * This is a placeholder function for now.
+ */
+static void
+pim_rp_refresh_group_to_rp_mapping()
+{
+  pim_msdp_i_am_rp_changed();
+}
+
 void
-pim_rp_check_rp (struct in_addr old, struct in_addr new)
-{
-  if (PIM_DEBUG_ZEBRA) {
-    char sold[100];
-    char snew[100];
-    char rp[100];
-    pim_inet4_dump("<rp?>", qpim_rp.rpf_addr, rp, sizeof(rp));
-    pim_inet4_dump("<old?>", old, sold, sizeof(sold));
-    pim_inet4_dump("<new?>", new, snew, sizeof(snew));
-    zlog_debug("%s: %s for old %s new %s", __func__, rp, sold, snew );
+pim_rp_prefix_list_update (struct prefix_list *plist)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+  int refresh_needed = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (rp_info->plist && strcmp(rp_info->plist, prefix_list_name (plist)) == 0)
+        {
+          refresh_needed = 1;
+          break;
+        }
+    }
+
+  if (refresh_needed)
+    pim_rp_refresh_group_to_rp_mapping();
+}
+
+static int
+pim_rp_check_interface_addrs(struct rp_info *rp_info,
+                             struct pim_interface *pim_ifp)
+{
+  struct listnode *node;
+  struct pim_secondary_addr *sec_addr;
+
+  if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
+    return 1;
+
+  if (!pim_ifp->sec_addr_list) {
+    return 0;
   }
 
-  if (qpim_rp.rpf_addr.s_addr == INADDR_NONE)
-    return;
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+    if (sec_addr->addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static void
+pim_rp_check_interfaces (struct rp_info *rp_info)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  rp_info->i_am_rp = 0;
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
+    {
+      struct pim_interface *pim_ifp = ifp->info;
+
+      if (!pim_ifp)
+        continue;
+
+      if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) {
+        rp_info->i_am_rp = 1;
+      }
+    }
+}
+
+int
+pim_rp_new (const char *rp, const char *group_range, const char *plist)
+{
+  int result;
+  struct rp_info *rp_info;
+  struct rp_info *rp_all;
+  struct prefix group_all;
+  struct listnode *node, *nnode;
+  struct rp_info *tmp_rp_info;
+  char buffer[BUFSIZ];
+
+  rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info));
+  if (!rp_info)
+    return PIM_MALLOC_FAIL;
+
+  if (group_range == NULL)
+    result = str2prefix ("224.0.0.0/4", &rp_info->group);
+  else
+    result = str2prefix (group_range, &rp_info->group);
+
+  if (!result)
+    {
+      XFREE (MTYPE_PIM_RP, rp_info);
+      return PIM_GROUP_BAD_ADDRESS;
+    }
+
+  rp_info->rp.rpf_addr.family = AF_INET;
+  result = inet_pton (rp_info->rp.rpf_addr.family, rp, &rp_info->rp.rpf_addr.u.prefix4);
+
+  if (result <= 0)
+    {
+      XFREE (MTYPE_PIM_RP, rp_info);
+      return PIM_RP_BAD_ADDRESS;
+    }
+
+  if (plist)
+    {
+      /*
+       * Return if the prefix-list is already configured for this RP
+       */
+      if (pim_rp_find_prefix_list (rp_info->rp.rpf_addr.u.prefix4, plist))
+        {
+          XFREE (MTYPE_PIM_RP, rp_info);
+          return PIM_SUCCESS;
+        }
+
+      /*
+       * Barf if the prefix-list is already configured for an RP
+       */
+      if (pim_rp_prefix_list_used (plist))
+        {
+          XFREE (MTYPE_PIM_RP, rp_info);
+          return PIM_RP_PFXLIST_IN_USE;
+        }
+
+      /*
+       * Free any existing rp_info entries for this RP
+       */
+      for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info))
+        {
+          if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr)
+            {
+              if (tmp_rp_info->plist)
+                pim_rp_del (rp, NULL, tmp_rp_info->plist);
+              else
+                pim_rp_del (rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), NULL);
+            }
+        }
+
+      rp_info->plist = XSTRDUP(MTYPE_PIM_FILTER_NAME, plist);
+    }
+  else
+    {
+      str2prefix ("224.0.0.0/4", &group_all);
+      rp_all = pim_rp_find_match_group(&group_all);
+
+      /*
+       * Barf if group is a non-multicast subnet
+       */
+      if (! prefix_match (&rp_all->group, &rp_info->group))
+        {
+          XFREE (MTYPE_PIM_RP, rp_info);
+          return PIM_GROUP_BAD_ADDRESS;
+        }
 
-  if (new.s_addr == qpim_rp.rpf_addr.s_addr)
+      /*
+       * Remove any prefix-list rp_info entries for this RP
+       */
+      for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info))
+        {
+          if (tmp_rp_info->plist &&
+              rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr)
+            {
+              pim_rp_del (rp, NULL, tmp_rp_info->plist);
+            }
+        }
+
+      /*
+       * Take over the 224.0.0.0/4 group if the rp is INADDR_NONE
+       */
+      if (prefix_same (&rp_all->group, &rp_info->group) &&
+          pim_rpf_addr_is_inaddr_none (&rp_all->rp))
+        {
+          rp_all->rp.rpf_addr = rp_info->rp.rpf_addr;
+          XFREE (MTYPE_PIM_RP, rp_info);
+
+          if (pim_nexthop_lookup (&rp_all->rp.source_nexthop, rp_all->rp.rpf_addr.u.prefix4, 1) != 0)
+            return PIM_RP_NO_PATH;
+
+          pim_rp_check_interfaces (rp_all);
+         pim_rp_refresh_group_to_rp_mapping();
+          return PIM_SUCCESS;
+        }
+
+      /*
+       * Return if the group is already configured for this RP
+       */
+      if (pim_rp_find_exact (rp_info->rp.rpf_addr.u.prefix4, &rp_info->group))
+        {
+          XFREE (MTYPE_PIM_RP, rp_info);
+          return PIM_SUCCESS;
+        }
+
+      /*
+       * Barf if this group is already covered by some other RP
+       */
+      tmp_rp_info = pim_rp_find_match_group (&rp_info->group);
+
+      if (tmp_rp_info)
+        {
+          if (tmp_rp_info->plist)
+            {
+                XFREE (MTYPE_PIM_RP, rp_info);
+                return PIM_GROUP_PFXLIST_OVERLAP;
+            }
+          else
+            {
+              /*
+               * If the only RP that covers this group is an RP configured for
+               * 224.0.0.0/4 that is fine, ignore that one.  For all others
+               * though we must return PIM_GROUP_OVERLAP
+               */
+              if (! prefix_same (&group_all, &tmp_rp_info->group))
+                {
+                  XFREE (MTYPE_PIM_RP, rp_info);
+                  return PIM_GROUP_OVERLAP;
+                }
+            }
+        }
+    }
+
+  listnode_add_sort (qpim_rp_list, rp_info);
+
+  if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0)
+    return PIM_RP_NO_PATH;
+
+  pim_rp_check_interfaces (rp_info);
+  pim_rp_refresh_group_to_rp_mapping();
+  return PIM_SUCCESS;
+}
+
+int
+pim_rp_del (const char *rp, const char *group_range, const char *plist)
+{
+  struct prefix group;
+  struct in_addr rp_addr;
+  struct prefix g_all;
+  struct rp_info *rp_info;
+  struct rp_info *rp_all;
+  int result;
+
+  if (group_range == NULL)
+    result = str2prefix ("224.0.0.0/4", &group);
+  else
+    result = str2prefix (group_range, &group);
+
+  if (!result)
+    return PIM_GROUP_BAD_ADDRESS;
+
+  result = inet_pton (AF_INET, rp, &rp_addr);
+  if (result <= 0)
+    return PIM_RP_BAD_ADDRESS;
+
+  if (plist)
+    rp_info = pim_rp_find_prefix_list (rp_addr, plist);
+  else
+    rp_info = pim_rp_find_exact (rp_addr, &group);
+
+  if (!rp_info)
+    return PIM_RP_NOT_FOUND;
+
+  if (rp_info->plist)
     {
-      i_am_rp = 1;
-      return;
+      XFREE(MTYPE_PIM_FILTER_NAME, rp_info->plist);
+      rp_info->plist = NULL;
     }
 
-  if (old.s_addr == qpim_rp.rpf_addr.s_addr)
+  str2prefix ("224.0.0.0/4", &g_all);
+  rp_all = pim_rp_find_match_group (&g_all);
+
+  if (rp_all == rp_info)
     {
-      i_am_rp = 0;
-      return;
+      rp_all->rp.rpf_addr.family = AF_INET;
+      rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE;
+      rp_all->i_am_rp = 0;
+      return PIM_SUCCESS;
+    }
+
+  listnode_delete (qpim_rp_list, rp_info);
+  pim_rp_refresh_group_to_rp_mapping();
+  return PIM_SUCCESS;
+}
+
+int
+pim_rp_setup (void)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+  int ret = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
+        continue;
+
+      if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0)
+        {
+         if (PIM_DEBUG_PIM_TRACE)
+           zlog_debug ("Unable to lookup nexthop for rp specified");
+          ret++;
+        }
     }
+
+  if (ret)
+    return 0;
+
+  return 1;
+}
+
+/*
+ * Checks to see if we should elect ourself the actual RP when new if
+ * addresses are added against an interface.
+ */
+void
+pim_rp_check_on_if_add(struct pim_interface *pim_ifp)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+  bool i_am_rp_changed = false;
+
+  if (qpim_rp_list == NULL)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) {
+    if (pim_rpf_addr_is_inaddr_none (&rp_info->rp))
+      continue;
+
+    /* if i_am_rp is already set nothing to be done (adding new addresses
+     * is not going to make a difference). */
+    if (rp_info->i_am_rp) {
+      continue;
+    }
+
+    if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) {
+      i_am_rp_changed = true;
+      rp_info->i_am_rp = 1;
+      if (PIM_DEBUG_ZEBRA) {
+        char rp[PREFIX_STRLEN];
+        pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp));
+        zlog_debug("%s: %s: i am rp", __func__, rp);
+      }
+    }
+  }
+
+  if (i_am_rp_changed) {
+    pim_msdp_i_am_rp_changed();
+  }
+}
+
+/* up-optimized re-evaluation of "i_am_rp". this is used when ifaddresses
+ * are removed. Removing numbers is an uncommon event in an active network
+ * so I have made no attempt to optimize it. */
+void
+pim_i_am_rp_re_evaluate(void)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+  bool i_am_rp_changed = false;
+  int old_i_am_rp;
+
+  if (qpim_rp_list == NULL)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_rp_list, node, rp_info)) {
+    if (pim_rpf_addr_is_inaddr_none(&rp_info->rp))
+      continue;
+
+    old_i_am_rp = rp_info->i_am_rp;
+    pim_rp_check_interfaces(rp_info);
+
+    if (old_i_am_rp != rp_info->i_am_rp) {
+      i_am_rp_changed = true;
+      if (PIM_DEBUG_ZEBRA) {
+        char rp[PREFIX_STRLEN];
+        pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp));
+        if (rp_info->i_am_rp) {
+          zlog_debug("%s: %s: i am rp", __func__, rp);
+        } else {
+          zlog_debug("%s: %s: i am no longer rp", __func__, rp);
+        }
+      }
+    }
+  }
+
+  if (i_am_rp_changed) {
+    pim_msdp_i_am_rp_changed();
+  }
 }
 
 /*
@@ -73,7 +622,20 @@ pim_rp_check_rp (struct in_addr old, struct in_addr new)
 int
 pim_rp_i_am_rp (struct in_addr group)
 {
-  return i_am_rp;
+  struct prefix g;
+  struct rp_info *rp_info;
+
+  memset (&g, 0, sizeof (g));
+  g.family = AF_INET;
+  g.prefixlen = 32;
+  g.u.prefix4 = group;
+
+  rp_info = pim_rp_find_match_group (&g);
+
+  if (rp_info)
+    return rp_info->i_am_rp;
+
+  return 0;
 }
 
 /*
@@ -84,11 +646,24 @@ pim_rp_i_am_rp (struct in_addr group)
 struct pim_rpf *
 pim_rp_g (struct in_addr group)
 {
-  /*
-   * For staticly configured RP, it is always the qpim_rp
-   */
-  pim_nexthop_lookup(&qpim_rp.source_nexthop, qpim_rp.rpf_addr, NULL);
-  return(&qpim_rp);
+  struct prefix g;
+  struct rp_info *rp_info;
+
+  memset (&g, 0, sizeof (g));
+  g.family = AF_INET;
+  g.prefixlen = 32;
+  g.u.prefix4 = group;
+
+  rp_info = pim_rp_find_match_group (&g);
+
+  if (rp_info)
+    {
+      pim_nexthop_lookup(&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1);
+      return (&rp_info->rp);
+    }
+
+  // About to Go Down
+  return NULL;
 }
 
 /*
@@ -100,16 +675,168 @@ pim_rp_g (struct in_addr group)
  *
  */
 int
-pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source)
+pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source, struct in_addr group)
 {
-  if ((qpim_rp.rpf_addr.s_addr == INADDR_NONE) && (source.s_addr == INADDR_ANY))
+  struct rp_info *rp_info;
+  struct prefix g;
+
+  memset (&g, 0, sizeof (g));
+  g.family = AF_INET;
+  g.prefixlen = 32;
+  g.u.prefix4 = group;
+
+  rp_info = pim_rp_find_match_group (&g);
+
+  if ((pim_rpf_addr_is_inaddr_none (&rp_info->rp)) && (source.s_addr == INADDR_ANY))
     {
       if (PIM_DEBUG_PIM_TRACE)
        zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
       return 0;
     }
 
-  *up = (source.s_addr == INADDR_ANY) ? qpim_rp.rpf_addr : source;
+  *up = (source.s_addr == INADDR_ANY) ? rp_info->rp.rpf_addr.u.prefix4 : source;
 
   return 1;
 }
+
+int
+pim_rp_config_write (struct vty *vty)
+{
+  struct listnode *node;
+  struct rp_info *rp_info;
+  char rp_buffer[32];
+  char group_buffer[32];
+  int count = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (pim_rpf_addr_is_inaddr_none (&rp_info->rp))
+        continue;
+
+      if (rp_info->plist)
+        vty_out(vty, "ip pim rp %s prefix-list %s%s",
+                inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32),
+                rp_info->plist, VTY_NEWLINE);
+      else
+        vty_out(vty, "ip pim rp %s %s%s",
+                inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32),
+                prefix2str(&rp_info->group, group_buffer, 32), VTY_NEWLINE);
+      count++;
+    }
+
+  return count;
+}
+
+int
+pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr)
+{
+  struct rp_info *rp_info;
+  struct prefix g;
+
+  memset (&g, 0, sizeof (g));
+  g.family = AF_INET;
+  g.prefixlen = 32;
+  g.u.prefix4 = group;
+
+  rp_info = pim_rp_find_match_group (&g);
+  /*
+   * See if we can short-cut some?
+   * This might not make sense if we ever leave a static RP
+   * type of configuration.
+   * Note - Premature optimization might bite our patooeys' here.
+   */
+  if (I_am_RP(group))
+    {
+     if (dest_addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
+       return 1;
+    }
+
+  if (if_lookup_exact_address (&dest_addr, AF_INET))
+    return 1;
+    
+  return 0;
+}
+
+void
+pim_rp_show_information (struct vty *vty, u_char uj)
+{
+  struct rp_info *rp_info;
+  struct rp_info *prev_rp_info = NULL;
+  struct listnode *node;
+
+  json_object *json = NULL;
+  json_object *json_rp_rows = NULL;
+  json_object *json_row = NULL;
+
+  if (uj)
+    json = json_object_new_object();
+  else
+    vty_out (vty, "RP address       group/prefix-list   OIF         I am RP%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+    {
+      if (!pim_rpf_addr_is_inaddr_none (&rp_info->rp))
+        {
+          char buf[48];
+
+          if (uj)
+            {
+              /*
+               * If we have moved on to a new RP then add the entry for the previous RP
+               */
+              if (prev_rp_info &&
+                  prev_rp_info->rp.rpf_addr.u.prefix4.s_addr != rp_info->rp.rpf_addr.u.prefix4.s_addr)
+                {
+                  json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows);
+                  json_rp_rows = NULL;
+                }
+
+              if (!json_rp_rows)
+                  json_rp_rows = json_object_new_array();
+
+              json_row = json_object_new_object();
+             if (rp_info->rp.source_nexthop.interface)
+               json_object_string_add(json_row, "outboundInterface", rp_info->rp.source_nexthop.interface->name);
+
+              if (rp_info->i_am_rp)
+                json_object_boolean_true_add(json_row, "iAmRP");
+
+              if (rp_info->plist)
+                json_object_string_add(json_row, "prefixList", rp_info->plist);
+              else
+                json_object_string_add(json_row, "group", prefix2str(&rp_info->group, buf, 48));
+
+              json_object_array_add(json_rp_rows, json_row);
+            }
+          else
+            {
+              vty_out (vty, "%-15s  ", inet_ntoa (rp_info->rp.rpf_addr.u.prefix4));
+
+              if (rp_info->plist)
+                vty_out (vty, "%-18s  ", rp_info->plist);
+              else
+                vty_out (vty, "%-18s  ", prefix2str(&rp_info->group, buf, 48));
+
+             if (rp_info->rp.source_nexthop.interface)
+               vty_out (vty, "%-10s  ", rp_info->rp.source_nexthop.interface->name);
+             else
+               vty_out (vty, "%-10s  ", "(Unknown)");
+
+              if (rp_info->i_am_rp)
+                vty_out (vty, "yes%s", VTY_NEWLINE);
+              else
+                vty_out (vty, "no%s", VTY_NEWLINE);
+            }
+
+          prev_rp_info = rp_info;
+        }
+    }
+
+  if (uj) {
+    if (prev_rp_info && json_rp_rows)
+      json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows);
+
+    vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+    json_object_free(json);
+  }
+}
index bb785e7efc3f1f18cd824130dc9a5a9a7c262b45..b32228ed49daa5e7df48b408c100089eacc67881 100644 (file)
 #ifndef PIM_RP_H
 #define PIM_RP_H
 
-void pim_rp_check_rp (struct in_addr old, struct in_addr new);
+void pim_rp_init (void);
+void pim_rp_free (void);
+
+int pim_rp_new (const char *rp, const char *group, const char *plist);
+int pim_rp_del (const char *rp, const char *group, const char *plist);
+void pim_rp_prefix_list_update (struct prefix_list *plist);
+
+int pim_rp_config_write (struct vty *vty);
+
+int pim_rp_setup (void);
+
 int pim_rp_i_am_rp (struct in_addr group);
-int pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source);
+void pim_rp_check_on_if_add(struct pim_interface *pim_ifp);
+void pim_i_am_rp_re_evaluate(void);
+
+int pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr);
+
+int pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source, struct in_addr group);
+
 struct pim_rpf *pim_rp_g (struct in_addr group);
 
 #define I_am_RP(G)  pim_rp_i_am_rp ((G))
 #define RP(G)       pim_rp_g ((G))
+
+void pim_rp_show_information (struct vty *vty, u_char uj);
 #endif
index 8d6e7b70c105af93288d056cd3a7bc5e109d4dc8..e48320222feb9d8255fa96cec65aee0feba42dc7 100644 (file)
 #include "pim_iface.h"
 #include "pim_zlookup.h"
 #include "pim_ifchannel.h"
+#include "pim_time.h"
+
+static long long last_route_change_time = -1;
+long long nexthop_lookups_avoided = 0;
 
 static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up);
 
-int pim_nexthop_lookup(struct pim_nexthop *nexthop,
-                      struct in_addr addr, struct interface *incoming)
+void
+pim_rpf_set_refresh_time (void)
+{
+  last_route_change_time = pim_time_monotonic_usec();
+  if (PIM_DEBUG_TRACE)
+    zlog_debug ("%s: New last route change time: %lld",
+               __PRETTY_FUNCTION__, last_route_change_time);
+}
+
+int pim_nexthop_lookup(struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed)
 {
-  struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+  struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
   int num_ifindex;
   struct interface *ifp;
   int first_ifindex;
+  int found = 0;
+  int i = 0;
 
-  memset (nexthop_tab, 0, sizeof (struct pim_zlookup_nexthop) * PIM_NEXTHOP_IFINDEX_TAB_SIZE);
-
-  if (!incoming)
+  if ((nexthop->last_lookup.s_addr == addr.s_addr) &&
+      (nexthop->last_lookup_time > last_route_change_time))
     {
-      num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
-                                          PIM_NEXTHOP_IFINDEX_TAB_SIZE,
-                                          addr, PIM_NEXTHOP_LOOKUP_MAX);
-      if (num_ifindex < 1) {
-       char addr_str[100];
-       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-       zlog_warn("%s %s: could not find nexthop ifindex for address %s",
-                 __FILE__, __PRETTY_FUNCTION__,
-                 addr_str);
-       return -1;
-      }
-
-      first_ifindex = nexthop_tab[0].ifindex;
-
-      if (num_ifindex > 1) {
-       char addr_str[100];
-       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-       zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
-                 __FILE__, __PRETTY_FUNCTION__,
-                 num_ifindex, addr_str, first_ifindex);
-       /* debug warning only, do not return */
-      }
-
-      ifp = if_lookup_by_index(first_ifindex);
-      if (!ifp) {
-       char addr_str[100];
-       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-       zlog_warn("%s %s: could not find interface for ifindex %d (address %s)",
-                 __FILE__, __PRETTY_FUNCTION__,
-                 first_ifindex, addr_str);
-       return -2;
-      }
+      if (PIM_DEBUG_TRACE)
+       {
+         char addr_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+         zlog_debug ("%s: Using last lookup for %s at %lld, %lld",
+                     __PRETTY_FUNCTION__,
+                     addr_str,
+                     nexthop->last_lookup_time,
+                     last_route_change_time);
+       }
+      nexthop_lookups_avoided++;
+      return 0;
     }
   else
     {
-      ifp = incoming;
-      first_ifindex = ifp->ifindex;
+      if (PIM_DEBUG_TRACE)
+       {
+         char addr_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+         zlog_debug ("%s: Looking up: %s, last lookup time: %lld, %lld",
+                     __PRETTY_FUNCTION__,
+                     addr_str,
+                     nexthop->last_lookup_time,
+                     last_route_change_time);
+       }
     }
 
-  if (!ifp->info) {
-    char addr_str[100];
+  memset (nexthop_tab, 0, sizeof (struct pim_zlookup_nexthop) * MULTIPATH_NUM);
+  num_ifindex = zclient_lookup_nexthop(nexthop_tab,
+                                      MULTIPATH_NUM,
+                                      addr, PIM_NEXTHOP_LOOKUP_MAX);
+  if (num_ifindex < 1) {
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-    zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
-             __PRETTY_FUNCTION__,
-             ifp->name, first_ifindex, addr_str);
-    /* debug warning only, do not return */
+    zlog_warn("%s %s: could not find nexthop ifindex for address %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             addr_str);
+    return -1;
   }
 
-  if (PIM_DEBUG_PIM_TRACE) {
-    char nexthop_str[100];
-    char addr_str[100];
-    pim_inet4_dump("<nexthop?>", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str));
-    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-    zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
-              __FILE__, __PRETTY_FUNCTION__,
-              nexthop_str, addr_str,
-              ifp->name, first_ifindex,
-              nexthop_tab[0].route_metric,
-              nexthop_tab[0].protocol_distance);
-  }
+  while (!found && (i < num_ifindex))
+    {
+      first_ifindex = nexthop_tab[i].ifindex;
 
-  /* update nextop data */
-  nexthop->interface              = ifp;
-  nexthop->mrib_nexthop_addr      = nexthop_tab[0].nexthop_addr;
-  nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance;
-  nexthop->mrib_route_metric      = nexthop_tab[0].route_metric;
+      ifp = if_lookup_by_index(first_ifindex);
+      if (!ifp)
+        {
+          if (PIM_DEBUG_ZEBRA)
+            {
+              char addr_str[INET_ADDRSTRLEN];
+              pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+              zlog_debug("%s %s: could not find interface for ifindex %d (address %s)",
+                         __FILE__, __PRETTY_FUNCTION__,
+                         first_ifindex, addr_str);
+            }
+         i++;
+         continue;
+        }
+
+      if (!ifp->info)
+        {
+         if (PIM_DEBUG_ZEBRA)
+           {
+             char addr_str[INET_ADDRSTRLEN];
+             pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+             zlog_debug("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
+                        __PRETTY_FUNCTION__,
+                        ifp->name, first_ifindex, addr_str);
+           }
+         i++;
+        }
+      else if (neighbor_needed && !pim_if_connected_to_source (ifp, addr))
+        {
+          struct pim_neighbor *nbr;
+
+          nbr = pim_neighbor_find (ifp, nexthop_tab[i].nexthop_addr.u.prefix4);
+          if (PIM_DEBUG_PIM_TRACE_DETAIL)
+            zlog_debug ("ifp name: %s, pim nbr: %p", ifp->name, nbr);
+          if (!nbr && !if_is_loopback (ifp))
+            i++;
+          else
+            found = 1;
+        }
+      else
+        found = 1;
+    }
 
-  return 0;
+  if (found)
+    {
+      if (PIM_DEBUG_ZEBRA) {
+        char nexthop_str[PREFIX_STRLEN];
+        char addr_str[INET_ADDRSTRLEN];
+        pim_addr_dump("<nexthop?>", &nexthop_tab[i].nexthop_addr, nexthop_str, sizeof(nexthop_str));
+        pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+        zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  nexthop_str, addr_str,
+                  ifp->name, first_ifindex,
+                  nexthop_tab[i].route_metric,
+                  nexthop_tab[i].protocol_distance);
+      }
+      /* update nextop data */
+      nexthop->interface                = ifp;
+      nexthop->mrib_nexthop_addr        = nexthop_tab[i].nexthop_addr;
+      nexthop->mrib_metric_preference   = nexthop_tab[i].protocol_distance;
+      nexthop->mrib_route_metric        = nexthop_tab[i].route_metric;
+      nexthop->last_lookup              = addr;
+      nexthop->last_lookup_time         = pim_time_monotonic_usec();
+      return 0;
+    }
+  else
+    return -1;
 }
 
 static int nexthop_mismatch(const struct pim_nexthop *nh1,
                            const struct pim_nexthop *nh2)
 {
-  return (nh1->interface != nh2->interface) 
-    ||
-    (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr)
-    ||
-    (nh1->mrib_metric_preference != nh2->mrib_metric_preference)
-    ||
+  return (nh1->interface != nh2->interface)                          ||
+    (nh1->mrib_nexthop_addr.u.prefix4.s_addr != nh2->mrib_nexthop_addr.u.prefix4.s_addr) ||
+    (nh1->mrib_metric_preference != nh2->mrib_metric_preference)     ||
     (nh1->mrib_route_metric != nh2->mrib_route_metric);
 }
 
-enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
-                                  struct in_addr *old_rpf_addr,
-                                  struct interface *incoming)
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, struct in_addr *old_rpf_addr)
 {
-  struct in_addr      save_rpf_addr;
+  struct prefix       save_rpf_addr;
   struct pim_nexthop  save_nexthop;
   struct pim_rpf     *rpf = &up->rpf;
 
@@ -142,36 +194,31 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
   save_rpf_addr = rpf->rpf_addr;       /* detect change in RPF'(S,G) */
 
   if (pim_nexthop_lookup(&rpf->source_nexthop,
-                        up->upstream_addr, incoming)) {
+                         up->upstream_addr,
+                         !PIM_UPSTREAM_FLAG_TEST_FHR (up->flags) && 
+                         !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))) {
     return PIM_RPF_FAILURE;
   }
 
-  rpf->rpf_addr = pim_rpf_find_rpf_addr(up);
-  if (PIM_INADDR_IS_ANY(rpf->rpf_addr) && PIM_DEBUG_PIM_EVENTS) {
+  rpf->rpf_addr.family = AF_INET;
+  rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up);
+  if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) {
     /* RPF'(S,G) not found */
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s %s: RPF'(%s,%s) not found: won't send join upstream",
+    zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
               __FILE__, __PRETTY_FUNCTION__,
-              src_str, grp_str);
+              up->sg_str);
     /* warning only */
   }
 
   /* detect change in pim_nexthop */
   if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) {
 
-    if (PIM_DEBUG_PIM_EVENTS) {
-      char src_str[100];
-      char grp_str[100];
-      char nhaddr_str[100];
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      pim_inet4_dump("<addr?>", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
-      zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d",
+    if (PIM_DEBUG_ZEBRA) {
+      char nhaddr_str[PREFIX_STRLEN];
+      pim_addr_dump("<addr?>", &rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
+      zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
                 __FILE__, __PRETTY_FUNCTION__,
-                src_str, grp_str,
+                up->sg_str,
                 rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
                 nhaddr_str,
                 rpf->source_nexthop.mrib_metric_preference,
@@ -186,14 +233,10 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
   /* detect change in RPF_interface(S) */
   if (save_nexthop.interface != rpf->source_nexthop.interface) {
 
-    if (PIM_DEBUG_PIM_EVENTS) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s",
+    if (PIM_DEBUG_ZEBRA) {
+      zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
                 __FILE__, __PRETTY_FUNCTION__,
-                src_str, grp_str,
+                up->sg_str,
                 save_nexthop.interface ? save_nexthop.interface->name : "<oldif?>",
                 rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
       /* warning only */
@@ -203,11 +246,11 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
   }
 
   /* detect change in RPF'(S,G) */
-  if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) {
+  if (save_rpf_addr.u.prefix4.s_addr != rpf->rpf_addr.u.prefix4.s_addr) {
 
     /* return old rpf to caller ? */
     if (old_rpf_addr)
-      *old_rpf_addr = save_rpf_addr;
+      *old_rpf_addr = save_rpf_addr.u.prefix4;
 
     return PIM_RPF_CHANGED;
   }
@@ -237,20 +280,16 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up)
   struct in_addr rpf_addr;
 
   if (!up->rpf.source_nexthop.interface) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)",
+    zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
              __PRETTY_FUNCTION__,
-             src_str, grp_str);
+             up->sg_str);
 
     rpf_addr.s_addr = PIM_NET_INADDR_ANY;
     return rpf_addr;
   }
 
   rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface,
-                             up->source_addr, up->group_addr);
+                             &up->sg);
   if (rpf_ch) {
     if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
       return rpf_ch->ifassert_winner;
@@ -260,7 +299,7 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up)
   /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
 
   neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface,
-                              up->rpf.source_nexthop.mrib_nexthop_addr);
+                              up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4);
   if (neigh)
     rpf_addr = neigh->source_addr;
   else
@@ -268,3 +307,52 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up)
 
   return rpf_addr;
 }
+
+int
+pim_rpf_addr_is_inaddr_none (struct pim_rpf *rpf)
+{
+  switch (rpf->rpf_addr.family)
+    {
+    case AF_INET:
+      return rpf->rpf_addr.u.prefix4.s_addr == INADDR_NONE;
+      break;
+    case AF_INET6:
+      zlog_warn ("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__);
+      return 1;
+      break;
+    default:
+      return 0;
+      break;
+    }
+
+  return 0;
+}
+
+int
+pim_rpf_addr_is_inaddr_any (struct pim_rpf *rpf)
+{
+  switch (rpf->rpf_addr.family)
+    {
+    case AF_INET:
+      return rpf->rpf_addr.u.prefix4.s_addr == INADDR_ANY;
+      break;
+    case AF_INET6:
+      zlog_warn ("%s: v6 Unimplmented", __PRETTY_FUNCTION__);
+      return 1;
+      break;
+    default:
+      return 0;
+      break;
+    }
+
+  return 0;
+}
+
+int
+pim_rpf_is_same (struct pim_rpf *rpf1, struct pim_rpf *rpf2)
+{
+  if (rpf1->source_nexthop.interface == rpf2->source_nexthop.interface)
+    return 1;
+
+  return 0;
+}
index 4d55bd688136acb1352a28b6a499e8b4085e59cc..bb7777532486c107d1d8839a4a10389f23a84d8d 100644 (file)
 #include "pim_upstream.h"
 #include "pim_neighbor.h"
 
-int pim_nexthop_lookup(struct pim_nexthop *nexthop,
-                      struct in_addr addr, struct interface *incoming);
-enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
-                                  struct in_addr *old_rpf_addr,
-                                  struct interface *incoming);
+/*
+  RFC 4601:
+
+  Metric Preference
+    Preference value assigned to the unicast routing protocol that
+    provided the route to the multicast source or Rendezvous-Point.
+
+  Metric
+    The unicast routing table metric associated with the route used to
+    reach the multicast source or Rendezvous-Point.  The metric is in
+    units applicable to the unicast routing protocol used.
+*/
+struct pim_nexthop {
+  struct in_addr    last_lookup;
+  long long         last_lookup_time;
+  struct interface *interface;              /* RPF_interface(S) */
+  struct prefix     mrib_nexthop_addr;      /* MRIB.next_hop(S) */
+  uint32_t          mrib_metric_preference; /* MRIB.pref(S) */
+  uint32_t          mrib_route_metric;      /* MRIB.metric(S) */
+};
+
+struct pim_rpf {
+  struct pim_nexthop source_nexthop;
+  struct prefix      rpf_addr;               /* RPF'(S,G) */
+};
+
+enum pim_rpf_result {
+  PIM_RPF_OK = 0,
+  PIM_RPF_CHANGED,
+  PIM_RPF_FAILURE
+};
+
+struct pim_upstream;
+
+extern long long nexthop_lookups_avoided;
+
+int pim_nexthop_lookup(struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed);
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, struct in_addr *old_rpf_addr);
+
+int pim_rpf_addr_is_inaddr_none (struct pim_rpf *rpf);
+int pim_rpf_addr_is_inaddr_any (struct pim_rpf *rpf);
 
+int pim_rpf_is_same (struct pim_rpf *rpf1, struct pim_rpf *rpf2);
+void pim_rpf_set_refresh_time (void);
 #endif /* PIM_RPF_H */
index bd4d9e4857a9be9893be0cd736ca7029eb091f59..0a60bba2526d5ec68df4ff49946d62272a4ce250 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
index 7b25608c105ae5ca99596a0c1a88b09158f8c2d2..8a3f836fa3e3b80f84a1419d9044df9dea1dbfa9 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_SIGNALS_H
index 54816d126be7f1150648993c038c9d8fb88fcf49..de56eb60769b35ab2f417c3137f7f3ed4023591c 100644 (file)
@@ -44,7 +44,8 @@
 /* GLOBAL VARS */
 extern struct zebra_privs_t pimd_privs;
 
-int pim_socket_raw(int protocol)
+int
+pim_socket_raw (int protocol)
 {
   int fd;
 
@@ -67,8 +68,52 @@ int pim_socket_raw(int protocol)
   return fd;
 }
 
+int
+pim_socket_ip_hdr (int fd)
+{
+  const int on = 1;
+  int ret;
+
+  if (pimd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs, %s",
+             __PRETTY_FUNCTION__, safe_strerror (errno));
+
+  ret = setsockopt (fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on));
+
+  if (pimd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs, %s",
+             __PRETTY_FUNCTION__, safe_strerror (errno));
+
+  return ret;
+}
+
+/*
+ * Given a socket and a interface,
+ * Bind that socket to that interface
+ */
+int
+pim_socket_bind (int fd, struct interface *ifp)
+{
+  int ret;
+
+  if (pimd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs, %s",
+             __PRETTY_FUNCTION__, safe_strerror (errno));
+
+  ret = setsockopt (fd, SOL_SOCKET,
+                   SO_BINDTODEVICE, ifp->name, strlen (ifp->name));
+
+  if (pimd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs, %s",
+             __PRETTY_FUNCTION__, safe_strerror (errno));
+
+  return ret;
+}
+
 int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop)
 {
+  int rcvbuf = 1024 * 1024 * 8;
+  struct ip_mreqn mreq;
   int fd;
 
   fd = pim_socket_raw(protocol);
@@ -86,17 +131,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char lo
 
       ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT);
 
-      if (pimd_privs.change (ZPRIVS_RAISE))
-       zlog_err ("%s: could not raise privs, %s",
-                 __PRETTY_FUNCTION__, safe_strerror (errno));
-
-      ret = setsockopt (fd, SOL_SOCKET,
-                       SO_BINDTODEVICE, ifp->name, strlen (ifp->name));
-
-      if (pimd_privs.change (ZPRIVS_LOWER))
-       zlog_err ("%s: could not lower privs, %s",
-                 __PRETTY_FUNCTION__, safe_strerror (errno));
-
+      ret = pim_socket_bind (fd, ifp);
       if (ret)
        {
          zlog_warn("Could not set fd: %d for interface: %s to device",
@@ -180,14 +215,20 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char lo
     return PIM_SOCK_ERR_LOOP;
   }
 
+  memset (&mreq, 0, sizeof (mreq));
+  mreq.imr_ifindex = ifindex;
   if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
-                (void *) &ifaddr, sizeof(ifaddr))) {
+                (void *) &mreq, sizeof(mreq))) {
     zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
              fd, errno, safe_strerror(errno));
     close(fd);
     return PIM_SOCK_ERR_IFACE;
   }
 
+  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)))
+      zlog_warn("%s: Failure to set buffer size to %d",
+               __PRETTY_FUNCTION__, rcvbuf);
+
   {
     long flags;
 
@@ -232,8 +273,8 @@ int pim_socket_join(int fd, struct in_addr group,
 
   ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
   if (ret) {
-    char group_str[100];
-    char ifaddr_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char ifaddr_str[INET_ADDRSTRLEN];
     if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
       sprintf(group_str, "<group?>");
     if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
@@ -245,8 +286,8 @@ int pim_socket_join(int fd, struct in_addr group,
   }
 
   if (PIM_DEBUG_TRACE) {
-    char group_str[100];
-    char ifaddr_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char ifaddr_str[INET_ADDRSTRLEN];
     if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
       sprintf(group_str, "<group?>");
     if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
@@ -265,15 +306,14 @@ int pim_socket_join_source(int fd, ifindex_t ifindex,
                           const char *ifname)
 {
   if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
-    int e = errno;
-    char group_str[100];
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
              __PRETTY_FUNCTION__,
              fd, group_str, source_str, ifindex, ifname,
-             e, safe_strerror(e));
+             errno, safe_strerror(errno));
     return -1;
   }
 
@@ -298,17 +338,14 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
   if (to) {
     struct sockaddr_in si;
     socklen_t si_len = sizeof(si);
-    
-    ((struct sockaddr_in *) to)->sin_family = AF_INET;
 
-    if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) {
-      ((struct sockaddr_in *) to)->sin_port        = ntohs(0);
-      ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0);
-    }
-    else {
-      ((struct sockaddr_in *) to)->sin_port = si.sin_port;
-      ((struct sockaddr_in *) to)->sin_addr = si.sin_addr;
-    }
+    memset (&si, 0, sizeof (si));
+    to->sin_family = AF_INET;
+
+    pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len);
+
+    to->sin_port = si.sin_port;
+    to->sin_addr = si.sin_addr;
 
     if (tolen) 
       *tolen = sizeof(si);
@@ -346,14 +383,6 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
       if (ifindex)
        *ifindex = i->ipi_ifindex;
 
-      if (to && PIM_DEBUG_PACKETS) {
-       char to_str[100];
-       pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
-       zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
-                  __PRETTY_FUNCTION__,
-                  to_str, ntohs(to->sin_port));
-      }
-
       break;
     }
 #endif
@@ -366,14 +395,6 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
       if (tolen)
        *tolen = sizeof(struct sockaddr_in);
 
-      if (to && PIM_DEBUG_PACKETS) {
-       char to_str[100];
-       pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
-       zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
-                  __PRETTY_FUNCTION__,
-                  to_str, ntohs(to->sin_port));
-      }
-
       break;
     }
 #endif
index f905c661dd22e5d785a88e41fe5cc08ddc2b2093..aaf82e862a62abc0bcb924ad4559ab8a98b7543c 100644 (file)
@@ -36,6 +36,8 @@
 #define PIM_SOCK_ERR_NAME    (-10) /* Socket name (getsockname) */
 #define PIM_SOCK_ERR_BIND    (-11) /* Can't bind to interface */
 
+int pim_socket_bind (int fd, struct interface *ifp);
+int pim_socket_ip_hdr (int fd);
 int pim_socket_raw(int protocol);
 int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop);
 int pim_socket_join(int fd, struct in_addr group,
index ece644a8615ba7eb8548b0406b71566262e8531d..e443a92d614800c35a283c19cd153dc69573c8c5 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 #include "memory.h"
 #include "sockopt.h"
 
+#include "pimd.h"
 #include "pim_ssmpingd.h"
 #include "pim_time.h"
 #include "pim_sock.h"
-#include "pim_str.h"
-#include "pimd.h"
 
 static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
 
@@ -96,7 +99,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
   sockaddr.sin_port   = htons(port);
 
   if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
-    char addr_str[100];
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
     zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
              __PRETTY_FUNCTION__,
@@ -195,12 +198,11 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss)
   THREAD_OFF(ss->t_sock_read);
 
   if (close(ss->sock_fd)) {
-    int e = errno;
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
              __PRETTY_FUNCTION__,
-             ss->sock_fd, source_str, e, safe_strerror(e));
+             ss->sock_fd, source_str, errno, safe_strerror(errno));
     /* warning only */
   }
 
@@ -219,14 +221,13 @@ static void ssmpingd_sendto(struct ssmpingd_sock *ss,
   sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
                 (struct sockaddr *)&to, tolen);
   if (sent != len) {
-    int e = errno;
-    char to_str[100];
+    char to_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
     if (sent < 0) {
       zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
                __PRETTY_FUNCTION__,
                to_str, ntohs(to.sin_port), ss->sock_fd, len,
-               e, safe_strerror(e));
+               errno, safe_strerror(errno));
     }
     else {
       zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
@@ -255,7 +256,7 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
                              &to, &tolen,
                              &ifindex);
   if (len < 0) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
              __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
@@ -265,9 +266,9 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
   ifp = if_lookup_by_index(ifindex);
 
   if (buf[0] != PIM_SSMPINGD_REQUEST) {
-    char source_str[100];
-    char from_str[100];
-    char to_str[100];
+    char source_str[INET_ADDRSTRLEN];
+    char from_str[INET_ADDRSTRLEN];
+    char to_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
     pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
     pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
@@ -283,9 +284,9 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
   }
 
   if (PIM_DEBUG_SSMPINGD) {
-    char source_str[100];
-    char from_str[100];
-    char to_str[100];
+    char source_str[INET_ADDRSTRLEN];
+    char from_str[INET_ADDRSTRLEN];
+    char to_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
     pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
     pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
@@ -316,10 +317,7 @@ static int ssmpingd_sock_read(struct thread *t)
   int sock_fd;
   int result;
 
-  zassert(t);
-
   ss = THREAD_ARG(t);
-  zassert(ss);
 
   sock_fd = THREAD_FD(t);
   zassert(sock_fd == ss->sock_fd);
@@ -357,18 +355,18 @@ static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
 
   sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
   if (sock_fd < 0) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: ssmpingd_socket() failure for source %s",
              __PRETTY_FUNCTION__, source_str);
     return 0;
   }
 
-  ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
+  ss = XCALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
   if (!ss) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
-    zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
+    zlog_err("%s: XCALLOC(%zu) failure for ssmpingd source %s",
             __PRETTY_FUNCTION__,
             sizeof(*ss), source_str);
     close(sock_fd);
@@ -399,7 +397,7 @@ int pim_ssmpingd_start(struct in_addr source_addr)
   }
 
   {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_info("%s: starting ssmpingd for source %s",
              __PRETTY_FUNCTION__, source_str);
@@ -407,7 +405,7 @@ int pim_ssmpingd_start(struct in_addr source_addr)
 
   ss = ssmpingd_new(source_addr);
   if (!ss) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: ssmpingd_new() failure for source %s",
              __PRETTY_FUNCTION__, source_str);
@@ -423,7 +421,7 @@ int pim_ssmpingd_stop(struct in_addr source_addr)
 
   ss = ssmpingd_find(source_addr);
   if (!ss) {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_warn("%s: could not find ssmpingd for source %s",
              __PRETTY_FUNCTION__, source_str);
@@ -431,7 +429,7 @@ int pim_ssmpingd_stop(struct in_addr source_addr)
   }
 
   {
-    char source_str[100];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
     zlog_info("%s: stopping ssmpingd for source %s",
              __PRETTY_FUNCTION__, source_str);
index 54f787e2a311445b18311098b4c7f0b31b426da0..29759c9531ffeb3ec10daf92c7c7a59c2b28e32b 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_SSMPINGD_H
index 565d6fe728921bd5c263254809b1f71993c3c009..e1ccec387ace4d03891dfc4e5ebb933660884369 100644 (file)
@@ -78,11 +78,11 @@ static struct static_route *static_route_new(unsigned int   iif,
 
 int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
 {
-   struct listnode *node = 0;
-   struct static_route *s_route = 0;
-   struct static_route *original_s_route = 0;
-   struct pim_interface *pim_iif = iif ? iif->info : 0;
-   struct pim_interface *pim_oif = oif ? oif->info : 0;
+   struct listnode *node = NULL;
+   struct static_route *s_route = NULL;
+   struct static_route *original_s_route = NULL;
+   struct pim_interface *pim_iif = iif ? iif->info : NULL;
+   struct pim_interface *pim_oif = oif ? oif->info : NULL;
    ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
    ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
 
@@ -110,8 +110,8 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr
           s_route->source.s_addr == source.s_addr) {
          if (s_route->iif == iif_index &&
              s_route->oif_ttls[oif_index]) {
-            char gifaddr_str[100];
-            char sifaddr_str[100];
+            char gifaddr_str[INET_ADDRSTRLEN];
+            char sifaddr_str[INET_ADDRSTRLEN];
             pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
             pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
             zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
@@ -174,10 +174,10 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr
       listnode_add(qpim_static_route_list, s_route);
    }
 
-   if (pim_mroute_add(&s_route->c_oil))
+   if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__))
    {
-      char gifaddr_str[100];
-      char sifaddr_str[100];
+      char gifaddr_str[INET_ADDRSTRLEN];
+      char sifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
       pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
       zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
@@ -209,8 +209,8 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr
    }
 
    if (PIM_DEBUG_STATIC) {
-     char gifaddr_str[100];
-     char sifaddr_str[100];
+     char gifaddr_str[INET_ADDRSTRLEN];
+     char sifaddr_str[INET_ADDRSTRLEN];
      pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
      pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
      zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
@@ -226,9 +226,9 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr
 
 int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
 {
-   struct listnode *node = 0;
-   struct listnode *nextnode = 0;
-   struct static_route *s_route = 0;
+   struct listnode *node = NULL;
+   struct listnode *nextnode = NULL;
+   struct static_route *s_route = NULL;
    struct pim_interface *pim_iif = iif ? iif->info : 0;
    struct pim_interface *pim_oif = oif ? oif->info : 0;
    ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
@@ -253,23 +253,23 @@ int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr
 
          /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */
          if (s_route->c_oil.oil_ref_count <= 0 ?
-                 pim_mroute_del(&s_route->c_oil) : pim_mroute_add(&s_route->c_oil)) {
-            char gifaddr_str[100];
-            char sifaddr_str[100];
-            pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
-            pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
-            zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
+            pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__) : pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
+          char gifaddr_str[INET_ADDRSTRLEN];
+          char sifaddr_str[INET_ADDRSTRLEN];
+          pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+          pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+          zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
                      __FILE__, __PRETTY_FUNCTION__,
                      iif_index,
                      oif_index,
                      gifaddr_str,
                      sifaddr_str);
 
-            s_route->oif_ttls[oif_index] = 1;
-            s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
-            ++s_route->c_oil.oil_ref_count;
+          s_route->oif_ttls[oif_index] = 1;
+          s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
+          ++s_route->c_oil.oil_ref_count;
 
-            return -1;
+          return -1;
          }
 
          s_route->c_oil.oif_creation[oif_index] = 0;
@@ -280,8 +280,8 @@ int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr
          }
 
          if (PIM_DEBUG_STATIC) {
-           char gifaddr_str[100];
-           char sifaddr_str[100];
+           char gifaddr_str[INET_ADDRSTRLEN];
+           char sifaddr_str[INET_ADDRSTRLEN];
            pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
            pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
            zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
@@ -297,8 +297,8 @@ int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr
    }
 
    if (!node) {
-      char gifaddr_str[100];
-      char sifaddr_str[100];
+      char gifaddr_str[INET_ADDRSTRLEN];
+      char sifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
       pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
       zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
@@ -319,8 +319,8 @@ pim_static_write_mroute (struct vty *vty, struct interface *ifp)
   struct listnode *node;
   struct static_route *sroute;
   int count = 0;
-  char sbuf[100];
-  char gbuf[100];
+  char sbuf[INET_ADDRSTRLEN];
+  char gbuf[INET_ADDRSTRLEN];
 
   for (ALL_LIST_ELEMENTS_RO (qpim_static_route_list, node, sroute))
     {
index c817045697c418bccd8ec450ee636f106ebd835e..7f814fcda89a44814ee339be55db5194311148e7 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 
 #include "pim_str.h"
 
-void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
+void pim_addr_dump (const char *onfail, struct prefix *p, char *buf, int buf_size)
 {
   int save_errno = errno;
 
-  if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
-    int e = errno;
-    zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
-             buf_size, e, safe_strerror(e));
+  if (!inet_ntop(p->family, &p->u.prefix, buf, buf_size)) {
+    zlog_warn("pim_addr_dump: inet_ntop(buf_size=%d): errno=%d: %s",
+             buf_size, errno, safe_strerror(errno));
     if (onfail)
       snprintf(buf, buf_size, "%s", onfail);
   }
 
   errno = save_errno;
 }
+
+void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
+{
+  int save_errno = errno;
+
+  if (addr.s_addr == INADDR_ANY)
+    strcpy(buf, "*");
+  else
+    {
+      if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
+        zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
+                 buf_size, errno, safe_strerror(errno));
+      if (onfail)
+        snprintf(buf, buf_size, "%s", onfail);
+    }
+  }
+
+  errno = save_errno;
+}
+
+char *
+pim_str_sg_dump (const struct prefix_sg *sg)
+{
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+  static char sg_str[PIM_SG_LEN];
+
+  pim_inet4_dump ("<src?>", sg->src, src_str, sizeof(src_str));
+  pim_inet4_dump ("<grp?>", sg->grp, grp_str, sizeof(grp_str));
+  snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str);
+
+  return sg_str;
+}
+
+char *
+pim_str_sg_set (const struct prefix_sg *sg, char *sg_str)
+{
+  char src_str[INET_ADDRSTRLEN];
+  char grp_str[INET_ADDRSTRLEN];
+
+  pim_inet4_dump ("<src?>", sg->src, src_str, sizeof(src_str));
+  pim_inet4_dump ("<grp?>", sg->grp, grp_str, sizeof(grp_str));
+  snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str);
+
+  return sg_str;
+}
index d2af0110a40a8f0c14cfd1dea35922eee7e7f11c..3536417a952be69d3f38f62eef8fb827a91aed8f 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_STR_H
 #include <sys/socket.h>
 #include <arpa/inet.h>
 
+#include <prefix.h>
+
+/*
+ * Longest possible length of a (S,G) string is 36 bytes
+ * 123.123.123.123 = 16 * 2
+ * (,) = 3
+ * NULL Character at end = 1
+ * (123.123.123.123,123,123,123,123)
+ */
+#define PIM_SG_LEN 36
+
+void pim_addr_dump (const char *onfail, struct prefix *p, char *buf, int buf_size);
 void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size);
+char *pim_str_sg_dump (const struct prefix_sg *sg);
+char *pim_str_sg_set (const struct prefix_sg *sg, char *sg_str);
 
 #endif
index 75f767fef1e867d16dc61f59d1112cd0ab1f10b0..348793cd0b93e9fc7094fda47f1debddc0748473 100644 (file)
@@ -82,6 +82,25 @@ int64_t pim_time_monotonic_dsec()
   return now_dsec;
 }
 
+int64_t
+pim_time_monotonic_usec (void)
+{
+  struct timeval now_tv;
+  int64_t        now_dsec;
+
+  if (gettime_monotonic(&now_tv)) {
+    zlog_err("%s: gettime_monotonic() failure: errno=%d: %s",
+             __PRETTY_FUNCTION__,
+             errno, safe_strerror(errno));
+    return -1;
+  }
+
+  now_dsec = ((int64_t) now_tv.tv_sec) * 1000000 + ((int64_t) now_tv.tv_usec);
+
+  return now_dsec;
+
+}
+
 int pim_time_mmss(char *buf, int buf_size, long sec)
 {
   long mm;
index 8ccc6a9207d0b667046e8940c26705afbce2d855..de304a9f718d5f20f0ac08e39e198fb0f3ffcd16 100644 (file)
@@ -28,6 +28,7 @@
 
 int64_t pim_time_monotonic_sec(void);
 int64_t pim_time_monotonic_dsec(void);
+int64_t pim_time_monotonic_usec(void);
 int pim_time_mmss(char *buf, int buf_size, long sec);
 void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t);
 void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t);
index 546ceb86e189965b25367bb34f1ef95cf0a2a281..2b2a06d3685669e0899ef34f906fba3b0ca38928 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -95,8 +99,35 @@ uint8_t *pim_tlv_append_uint32(uint8_t *buf,
 
 #define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr))
 
-static int
-pim_encode_unicast_address (uint8_t *buf, struct prefix *p)
+/*
+ * An Encoded-Unicast address takes the following format:
+ *
+ *   0                   1                   2                   3
+ *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |  Addr Family  | Encoding Type |     Unicast Address
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+...
+ *
+ *  Addr Family
+ *       The PIM address family of the 'Unicast Address' field of this
+ *       address.
+ *
+ *       Values 0-127 are as assigned by the IANA for Internet Address   *       Families in [7].  Values 128-250 are reserved to be assigned by
+ *       the IANA for PIM-specific Address Families.  Values 251 though
+ *       255 are designated for private use.  As there is no assignment
+ *       authority for this space, collisions should be expected.
+ *
+ *  Encoding Type
+ *       The type of encoding used within a specific Address Family.  The
+ *       value '0' is reserved for this field and represents the native
+ *       encoding of the Address Family.
+ *
+ *  Unicast Address
+ *       The unicast address as represented by the given Address Family
+ *       and Encoding Type.
+ */
+int
+pim_encode_addr_ucast (uint8_t *buf, struct prefix *p)
 {
   switch (p->family)
     {
@@ -114,6 +145,79 @@ pim_encode_unicast_address (uint8_t *buf, struct prefix *p)
     }
 }
 
+#define group_ipv4_encoding_len (4 + sizeof (struct in_addr))
+
+/*
+ * Encoded-Group addresses take the following format:
+ *
+ *   0                   1                   2                   3
+ *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |  Addr Family  | Encoding Type |B| Reserved  |Z|  Mask Len     |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |                Group multicast Address
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+...
+ *
+ *  Addr Family
+ *       Described above.
+ *
+ *  Encoding Type
+ *       Described above.
+ *
+ *  [B]idirectional PIM
+ *       Indicates the group range should use Bidirectional PIM [13].
+ *       For PIM-SM defined in this specification, this bit MUST be zero.
+ *
+ *  Reserved
+ *       Transmitted as zero.  Ignored upon receipt.
+ *
+ *  Admin Scope [Z]one
+ *       indicates the group range is an admin scope zone.  This is used
+ *       in the Bootstrap Router Mechanism [11] only.  For all other
+ *       purposes, this bit is set to zero and ignored on receipt.
+ *
+ *  Mask Len
+ *       The Mask length field is 8 bits.  The value is the number of
+ *       contiguous one bits that are left justified and used as a mask;
+ *       when combined with the group address, it describes a range of
+ *       groups.  It is less than or equal to the address length in bits
+ *       for the given Address Family and Encoding Type.  If the message
+ *       is sent for a single group, then the Mask length must equal the
+ *       address length in bits for the given Address Family and Encoding
+ *       Type (e.g., 32 for IPv4 native encoding, 128 for IPv6 native
+ *       encoding).
+ *
+ *  Group multicast Address
+ *       Contains the group address.
+ */
+int
+pim_encode_addr_group (uint8_t *buf, afi_t afi, int bidir, int scope, struct in_addr group)
+{
+  uint8_t flags = 0;
+
+  flags |= bidir << 8;
+  flags |= scope;
+
+  switch (afi)
+    {
+    case AFI_IP:
+      *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV4;
+      ++buf;
+      *(uint8_t *)buf = 0;
+      ++buf;
+      *(uint8_t *)buf = flags;
+      ++buf;
+      *(uint8_t *)buf = 32;
+      ++buf;
+      memcpy (buf, &group, sizeof (struct in_addr));
+      return group_ipv4_encoding_len;
+      break;
+    default:
+      return 0;
+      break;
+    }
+}
+
 uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
                                       const uint8_t *buf_pastend,
                                       struct list *ifconnected)
@@ -143,7 +247,7 @@ uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
     if ((curr + ucast_ipv4_encoding_len) > buf_pastend)
       return 0;
 
-    l_encode = pim_encode_unicast_address (curr, p);
+    l_encode = pim_encode_addr_ucast (curr, p);
     curr += l_encode;
     option_len += l_encode;
   }
@@ -173,7 +277,7 @@ static int check_tlv_length(const char *label, const char *tlv_name,
                            int correct_len, int option_len)
 {
   if (option_len != correct_len) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s",
              label, tlv_name,
@@ -192,7 +296,7 @@ static void check_tlv_redefinition_uint16(const char *label, const char *tlv_nam
                                          uint16_t new, uint16_t old)
 {
   if (PIM_OPTION_IS_SET(options, opt_mask)) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
              label, tlv_name,
@@ -208,7 +312,7 @@ static void check_tlv_redefinition_uint32(const char *label, const char *tlv_nam
                                          uint32_t new, uint32_t old)
 {
   if (PIM_OPTION_IS_SET(options, opt_mask)) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
              label, tlv_name,
@@ -224,7 +328,7 @@ static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv
                                              uint32_t new, uint32_t old)
 {
   if (PIM_OPTION_IS_SET(options, opt_mask)) {
-    char src_str[100];
+    char src_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
     zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s",
              label, tlv_name,
@@ -408,7 +512,7 @@ pim_parse_addr_ucast (struct prefix *p,
 }
 
 int
-pim_parse_addr_group (struct prefix *p,
+pim_parse_addr_group (struct prefix_sg *sg,
                      const uint8_t *buf,
                      int buf_size)
 {
@@ -450,17 +554,15 @@ pim_parse_addr_group (struct prefix *p,
       return -3;
     }
 
-    p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
-    memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
-    p->prefixlen = mask_len;
+    memcpy(&sg->grp.s_addr, addr, sizeof(struct in_addr));
 
     addr += sizeof(struct in_addr);
 
     break;
   default:
     {
-      zlog_warn("%s: unknown group address encoding family=%d from",
-               __PRETTY_FUNCTION__, family);
+      zlog_warn("%s: unknown group address encoding family=%d mask_len=%d from",
+               __PRETTY_FUNCTION__, family, mask_len);
       return -4;
     }
   }
@@ -469,7 +571,7 @@ pim_parse_addr_group (struct prefix *p,
 }
 
 int
-pim_parse_addr_source(struct prefix *p,
+pim_parse_addr_source(struct prefix_sg *sg,
                      uint8_t *flags,
                      const uint8_t *buf,
                      int buf_size)
@@ -513,9 +615,7 @@ pim_parse_addr_source(struct prefix *p,
       return -3;
     }
 
-    p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
-    memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
-    p->prefixlen = mask_len;
+    memcpy(&sg->src, addr, sizeof(struct in_addr));
 
     /* 
        RFC 4601: 4.9.1  Encoded Source and Group Address Formats
@@ -527,9 +627,9 @@ pim_parse_addr_source(struct prefix *p,
        and 128 for IPv6 native).  A router SHOULD ignore any messages
        received with any other mask length.
     */
-    if (p->prefixlen != 32) {
+    if (mask_len != 32) {
       zlog_warn("%s: IPv4 bad source address mask: %d",
-               __PRETTY_FUNCTION__, p->prefixlen);
+               __PRETTY_FUNCTION__, mask_len);
       return -4;
     }
 
@@ -582,7 +682,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
      */
     addr_offset = pim_parse_addr_ucast(&tmp, addr, pastend - addr);
     if (addr_offset < 1) {
-      char src_str[100];
+      char src_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
       zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
                __PRETTY_FUNCTION__,
@@ -599,8 +699,8 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
       switch (tmp.family) {
       case AF_INET:
        {
-         char addr_str[100];
-         char src_str[100];
+         char addr_str[INET_ADDRSTRLEN];
+         char src_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str));
          pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
          zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s",
@@ -612,7 +712,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
        break;
       default:
        {
-         char src_str[100];
+         char src_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
          zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s",
                     __PRETTY_FUNCTION__,
@@ -629,7 +729,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
      */
     if (tmp.family == AF_INET) {
       if (tmp.u.prefix4.s_addr == src_addr.s_addr) {
-         char src_str[100];
+         char src_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
          zlog_warn("%s: ignoring primary address in secondary list from %s on %s",
                    __PRETTY_FUNCTION__,
index 16c5aa4b97e2fc5b6f96be72024f7e52fd743ccf..d176c264713c111013cef2b7c0117120f22835a1 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_TLV_H
@@ -109,13 +113,16 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
                            uint16_t option_len,
                            const uint8_t *tlv_curr);
 
+int pim_encode_addr_ucast (uint8_t *buf, struct prefix *p);
+int pim_encode_addr_group (uint8_t *buf, afi_t afi, int bidir, int scope, struct in_addr group);
+
 int pim_parse_addr_ucast (struct prefix *p,
                          const uint8_t *buf,
                          int buf_size);
-int pim_parse_addr_group (struct prefix *p,
+int pim_parse_addr_group (struct prefix_sg *sg,
                          const uint8_t *buf,
                          int buf_size);
-int pim_parse_addr_source(struct prefix *p,
+int pim_parse_addr_source(struct prefix_sg *sg,
                          uint8_t *flags,
                          const uint8_t *buf,
                          int buf_size);
index 059de3b62e15ce5411a9d270c3240965f441bc70..fbb7d84a25a1a45a7faebbd65adf1689de410ff5 100644 (file)
 #include "memory.h"
 #include "thread.h"
 #include "linklist.h"
+#include "vty.h"
+#include "plist.h"
+#include "hash.h"
+#include "jhash.h"
+#include "wheel.h"
 
 #include "pimd.h"
 #include "pim_pim.h"
 #include "pim_macro.h"
 #include "pim_rp.h"
 #include "pim_br.h"
+#include "pim_register.h"
+#include "pim_msdp.h"
+
+struct hash *pim_upstream_hash = NULL;
+struct list *pim_upstream_list = NULL;
+struct timer_wheel *pim_upstream_sg_wheel = NULL;
 
 static void join_timer_start(struct pim_upstream *up);
 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
 
+/*
+ * A (*,G) or a (*,*) is going away
+ * remove the parent pointer from
+ * those pointing at us
+ */
+static void
+pim_upstream_remove_children (struct pim_upstream *up)
+{
+  struct pim_upstream *child;
+
+  if (!up->sources)
+    return;
+
+  while (!list_isempty (up->sources))
+    {
+      child = listnode_head (up->sources);
+      child->parent = NULL;
+      listnode_delete (up->sources, child);
+    }
+}
+
+/*
+ * A (*,G) or a (*,*) is being created
+ * Find the children that would point
+ * at us.
+ */
+static void
+pim_upstream_find_new_children (struct pim_upstream *up)
+{
+  struct pim_upstream *child;
+  struct listnode *ch_node;
+
+  if ((up->sg.src.s_addr != INADDR_ANY) &&
+      (up->sg.grp.s_addr != INADDR_ANY))
+    return;
+
+  if ((up->sg.src.s_addr == INADDR_ANY) &&
+      (up->sg.grp.s_addr == INADDR_ANY))
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
+    {
+      if ((up->sg.grp.s_addr != INADDR_ANY) &&
+         (child->sg.grp.s_addr == up->sg.grp.s_addr) &&
+         (child != up))
+       {
+         child->parent = up;
+         listnode_add_sort (up->sources, child);
+       }
+    }
+}
+
+/*
+ * If we have a (*,*) || (S,*) there is no parent
+ * If we have a (S,G), find the (*,G)
+ * If we have a (*,G), find the (*,*)
+ */
+static struct pim_upstream *
+pim_upstream_find_parent (struct pim_upstream *child)
+{
+  struct prefix_sg any = child->sg;
+  struct pim_upstream *up = NULL;
+
+  // (S,G)
+  if ((child->sg.src.s_addr != INADDR_ANY) &&
+      (child->sg.grp.s_addr != INADDR_ANY))
+    {
+      any.src.s_addr = INADDR_ANY;
+      up = pim_upstream_find (&any);
+
+      if (up)
+       listnode_add (up->sources, child);
+
+      return up;
+    }
+
+  return NULL;
+}
+
 void pim_upstream_free(struct pim_upstream *up)
 {
   XFREE(MTYPE_PIM_UPSTREAM, up);
@@ -61,48 +151,89 @@ static void upstream_channel_oil_detach(struct pim_upstream *up)
   }
 }
 
-void pim_upstream_delete(struct pim_upstream *up)
+void
+pim_upstream_del(struct pim_upstream *up, const char *name)
 {
+  bool notify_msdp = false;
+
+  if (PIM_DEBUG_TRACE)
+    zlog_debug ("%s(%s): Delete %s ref count: %d",
+               __PRETTY_FUNCTION__, name, up->sg_str, up->ref_count);
+
+  --up->ref_count;
+
+  if (up->ref_count >= 1)
+    return;
+
   THREAD_OFF(up->t_join_timer);
   THREAD_OFF(up->t_ka_timer);
+  THREAD_OFF(up->t_rs_timer);
+  THREAD_OFF(up->t_msdp_reg_timer);
+
+  if (up->join_state == PIM_UPSTREAM_JOINED) {
+    pim_joinprune_send (up->rpf.source_nexthop.interface,
+                      up->rpf.rpf_addr.u.prefix4,
+                      up, 0);
+    if (up->sg.src.s_addr == INADDR_ANY) {
+        /* if a (*, G) entry in the joined state is being deleted we
+         * need to notify MSDP */
+        notify_msdp = true;
+    }
+  }
+
+  if (up->sg.src.s_addr != INADDR_ANY) {
+    wheel_remove_item (pim_upstream_sg_wheel, up);
+    notify_msdp = true;
+  }
 
+  pim_upstream_remove_children (up);
+  pim_mroute_del (up->channel_oil, __PRETTY_FUNCTION__);
   upstream_channel_oil_detach(up);
 
+  if (up->sources)
+    list_delete (up->sources);
+  up->sources = NULL;
+
   /*
     notice that listnode_delete() can't be moved
     into pim_upstream_free() because the later is
     called by list_delete_all_node()
   */
-  listnode_delete(qpim_upstream_list, up);
+  if (up->parent)
+    {
+      listnode_delete (up->parent->sources, up);
+      up->parent = NULL;
+    }
+  listnode_delete (pim_upstream_list, up);
+  hash_release (pim_upstream_hash, up);
 
+  if (notify_msdp) {
+    pim_msdp_up_del(&up->sg);
+  }
   pim_upstream_free(up);
 }
 
-static void send_join(struct pim_upstream *up)
+void
+pim_upstream_send_join (struct pim_upstream *up)
 {
-  zassert(up->join_state == PIM_UPSTREAM_JOINED);
-
-  
-  if (PIM_DEBUG_PIM_TRACE) {
-    if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
-      char src_str[100];
-      char grp_str[100];
-      char rpf_str[100];
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
-      zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
-               __PRETTY_FUNCTION__,
-               src_str, grp_str, rpf_str);
+  if (PIM_DEBUG_TRACE) {
+    char rpf_str[PREFIX_STRLEN];
+    pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+    zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__,
+               up->sg_str, rpf_str, pim_upstream_state2str (up->join_state),
+               up->rpf.source_nexthop.interface->name);
+    if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
+      zlog_debug("%s: can't send join upstream: RPF'%s=%s",
+                __PRETTY_FUNCTION__,
+                up->sg_str, rpf_str);
       /* warning only */
     }
   }
-  
+
   /* send Join(S,G) to the current upstream neighbor */
   pim_joinprune_send(up->rpf.source_nexthop.interface,
-                    up->rpf.rpf_addr,
-                    up->source_addr,
-                    up->group_addr,
+                    up->rpf.rpf_addr.u.prefix4,
+                    up,
                     1 /* join */);
 }
 
@@ -110,13 +241,23 @@ static int on_join_timer(struct thread *t)
 {
   struct pim_upstream *up;
 
-  zassert(t);
   up = THREAD_ARG(t);
-  zassert(up);
-
-  send_join(up);
 
   up->t_join_timer = NULL;
+
+  /*
+   * In the case of a HFR we will not ahve anyone to send this to.
+   */
+  if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+    return 0;
+
+  /*
+   * Don't send the join if the outgoing interface is a loopback
+   * But since this might change leave the join timer running
+   */
+  if (!if_is_loopback (up->rpf.source_nexthop.interface))
+    pim_upstream_send_join (up);
+
   join_timer_start(up);
 
   return 0;
@@ -125,18 +266,13 @@ static int on_join_timer(struct thread *t)
 static void join_timer_start(struct pim_upstream *up)
 {
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
+    zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
               __PRETTY_FUNCTION__,
               qpim_t_periodic,
-              src_str, grp_str);
+              up->sg_str);
   }
 
-  zassert(!up->t_join_timer);
-
+  THREAD_OFF (up->t_join_timer);
   THREAD_TIMER_ON(master, up->t_join_timer,
                  on_join_timer,
                  up, qpim_t_periodic);
@@ -152,14 +288,10 @@ static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
                                                 int interval_msec)
 {
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
+    zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
               __PRETTY_FUNCTION__,
               interval_msec,
-              src_str, grp_str);
+              up->sg_str);
   }
 
   THREAD_OFF(up->t_join_timer);
@@ -180,29 +312,21 @@ void pim_upstream_join_suppress(struct pim_upstream *up,
 
   join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
 
-  if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
-    char grp_str[100];
-    char rpf_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+  if (PIM_DEBUG_TRACE) {
+    char rpf_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
-    zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
+    zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
               __FILE__, __PRETTY_FUNCTION__, 
-              src_str, grp_str,
+              up->sg_str,
               rpf_str,
               join_timer_remain_msec, t_joinsuppress_msec);
   }
 
   if (join_timer_remain_msec < t_joinsuppress_msec) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
+    if (PIM_DEBUG_TRACE) {
+      zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec",
                 __FILE__, __PRETTY_FUNCTION__, 
-                src_str, grp_str, t_joinsuppress_msec);
+                up->sg_str, t_joinsuppress_msec);
     }
 
     pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
@@ -219,28 +343,20 @@ void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
   join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
   t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
 
-  if (PIM_DEBUG_PIM_TRACE) {
-    char src_str[100];
-    char grp_str[100];
-    char rpf_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+  if (PIM_DEBUG_TRACE) {
+    char rpf_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
-    zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
+    zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
               debug_label,
-              src_str, grp_str, rpf_str,
+              up->sg_str, rpf_str,
               join_timer_remain_msec, t_override_msec);
   }
     
   if (join_timer_remain_msec > t_override_msec) {
-    if (PIM_DEBUG_PIM_TRACE) {
-      char src_str[100];
-      char grp_str[100];
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
+    if (PIM_DEBUG_TRACE) {
+      zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec",
                 debug_label,
-                src_str, grp_str,
+                up->sg_str,
                 t_override_msec);
     }
 
@@ -250,196 +366,327 @@ void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
 
 static void forward_on(struct pim_upstream *up)
 {
-  struct listnode      *ifnode;
-  struct listnode      *ifnextnode;
   struct listnode      *chnode;
   struct listnode      *chnextnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    pim_ifp = ifp->info;
+  /* scan (S,G) state */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-
-      if (ch->upstream != up)
-       continue;
+    if (ch->upstream != up)
+      continue;
 
-      if (pim_macro_chisin_oiflist(ch))
-       pim_forward_start(ch);
+    if (pim_macro_chisin_oiflist(ch))
+      pim_forward_start(ch);
 
-    } /* scan iface channel list */
-  } /* scan iflist */
+  } /* scan iface channel list */
 }
 
 static void forward_off(struct pim_upstream *up)
 {
-  struct listnode      *ifnode;
-  struct listnode      *ifnextnode;
   struct listnode      *chnode;
   struct listnode      *chnextnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    pim_ifp = ifp->info;
+  /* scan per-interface (S,G) state */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+    if (ch->upstream != up)
+      continue;
 
-      if (ch->upstream != up)
-       continue;
+    pim_forward_stop(ch);
 
-      pim_forward_stop(ch);
+  } /* scan iface channel list */
+}
 
-    } /* scan iface channel list */
-  } /* scan iflist */
+static int
+pim_upstream_could_register (struct pim_upstream *up)
+{
+  struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info;
+
+  if (pim_ifp && PIM_I_am_DR (pim_ifp) &&
+      pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
+    return 1;
+
+  return 0;
 }
 
-static void pim_upstream_switch(struct pim_upstream *up,
-                               enum pim_upstream_state new_state)
+void
+pim_upstream_switch(struct pim_upstream *up,
+                   enum pim_upstream_state new_state)
 {
   enum pim_upstream_state old_state = up->join_state;
 
-  zassert(old_state != new_state);
-  
-  up->join_state       = new_state;
-  up->state_transition = pim_time_monotonic_sec();
-
   if (PIM_DEBUG_PIM_EVENTS) {
-    char src_str[100];
-    char grp_str[100];
-    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-    zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
+    zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
               __PRETTY_FUNCTION__,
-              ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
-              src_str, grp_str);
+              up->sg_str,
+              pim_upstream_state2str (up->join_state),
+              pim_upstream_state2str (new_state));
   }
 
+  /*
+   * This code still needs work.
+   */
+  switch (up->join_state)
+    {
+    case PIM_UPSTREAM_PRUNE:
+      if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+        {
+          up->join_state       = new_state;
+          up->state_transition = pim_time_monotonic_sec ();
+        }
+      break;
+    case PIM_UPSTREAM_JOIN_PENDING:
+      break;
+    case PIM_UPSTREAM_NOTJOINED:
+    case PIM_UPSTREAM_JOINED:
+      up->join_state       = new_state;
+      if (old_state != new_state)
+        up->state_transition = pim_time_monotonic_sec();
+
+      break;
+    }
+
   pim_upstream_update_assert_tracking_desired(up);
 
   if (new_state == PIM_UPSTREAM_JOINED) {
-    forward_on(up);
-    send_join(up);
-    join_timer_start(up);
+    if (old_state != PIM_UPSTREAM_JOINED)
+      {
+        int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
+        forward_on(up);
+        pim_msdp_up_join_state_changed(up);
+       if (pim_upstream_could_register (up))
+         {
+            PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+            if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
+              {
+                pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+               pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+              }
+         }
+       else
+          {
+           pim_upstream_send_join (up);
+           join_timer_start (up);
+         }
+      }
+    else
+      {
+        forward_on (up);
+      }
   }
   else {
     forward_off(up);
+    if (old_state == PIM_UPSTREAM_JOINED)
+      pim_msdp_up_join_state_changed(up);
     pim_joinprune_send(up->rpf.source_nexthop.interface,
-                      up->rpf.rpf_addr,
-                      up->source_addr,
-                      up->group_addr,
+                      up->rpf.rpf_addr.u.prefix4,
+                      up,
                       0 /* prune */);
-    zassert(up->t_join_timer);
-    THREAD_OFF(up->t_join_timer);
+    if (up->t_join_timer)
+      THREAD_OFF(up->t_join_timer);
   }
-
 }
 
+static int
+pim_upstream_compare (void *arg1, void *arg2)
+{
+  const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
+  const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
+
+  if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
+    return -1;
+
+  if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
+    return 1;
+
+  if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
+    return -1;
+
+  if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
+    return 1;
 
-static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
-                                            struct in_addr group_addr,
-                                            struct interface *incoming)
+  return 0;
+}
+
+static struct pim_upstream *
+pim_upstream_new (struct prefix_sg *sg,
+                 struct interface *incoming,
+                 int flags)
 {
-  struct pim_upstream *up;
   enum pim_rpf_result rpf_result;
+  struct pim_interface *pim_ifp;
+  struct pim_upstream *up;
 
-  up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+  up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
   if (!up) {
-    zlog_err("%s: PIM XMALLOC(%zu) failure",
+    zlog_err("%s: PIM XCALLOC(%zu) failure",
             __PRETTY_FUNCTION__, sizeof(*up));
     return NULL;
   }
   
-  up->source_addr                = source_addr;
-  if (!pim_rp_set_upstream_addr (&up->upstream_addr, source_addr))
+  up->sg                          = *sg;
+  pim_str_sg_set (sg, up->sg_str);
+  up = hash_get (pim_upstream_hash, up, hash_alloc_intern);
+  if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp))
     {
-      if (PIM_DEBUG_PIM_TRACE)
+      if (PIM_DEBUG_TRACE)
        zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
 
+      hash_release (pim_upstream_hash, up);
       XFREE (MTYPE_PIM_UPSTREAM, up);
       return NULL;
     }
 
-  up->group_addr                 = group_addr;
-  up->flags                      = 0;
+  up->parent                     = pim_upstream_find_parent (up);
+  if (up->sg.src.s_addr == INADDR_ANY)
+    {
+      up->sources = list_new ();
+      up->sources->cmp = pim_upstream_compare;
+    }
+  else
+    up->sources = NULL;
+
+  pim_upstream_find_new_children (up);
+  up->flags                      = flags;
   up->ref_count                  = 1;
   up->t_join_timer               = NULL;
   up->t_ka_timer                 = NULL;
+  up->t_rs_timer                 = NULL;
+  up->t_msdp_reg_timer           = NULL;
   up->join_state                 = 0;
   up->state_transition           = pim_time_monotonic_sec();
   up->channel_oil                = NULL;
   up->sptbit                     = PIM_UPSTREAM_SPTBIT_FALSE;
 
-  up->rpf.source_nexthop.interface                = 0;
-  up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+  up->rpf.source_nexthop.interface                = NULL;
+  up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET;
+  up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
   up->rpf.source_nexthop.mrib_metric_preference   = qpim_infinite_assert_metric.metric_preference;
   up->rpf.source_nexthop.mrib_route_metric        = qpim_infinite_assert_metric.route_metric;
-  up->rpf.rpf_addr.s_addr                         = PIM_NET_INADDR_ANY;
+  up->rpf.rpf_addr.family                         = AF_INET;
+  up->rpf.rpf_addr.u.prefix4.s_addr               = PIM_NET_INADDR_ANY;
+
+  if (up->sg.src.s_addr != INADDR_ANY)
+    wheel_add_item (pim_upstream_sg_wheel, up);
 
-  rpf_result = pim_rpf_update(up, 0, incoming);
+  rpf_result = pim_rpf_update(up, NULL);
   if (rpf_result == PIM_RPF_FAILURE) {
+    if (PIM_DEBUG_TRACE)
+      zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__,
+                  up->sg_str);
+
+    if (up->parent)
+      {
+       listnode_delete (up->parent->sources, up);
+       up->parent = NULL;
+      }
+
+    if (up->sg.src.s_addr != INADDR_ANY)
+      wheel_remove_item (pim_upstream_sg_wheel, up);
+
+    pim_upstream_remove_children (up);
+    if (up->sources)
+      list_delete (up->sources);
+
+    hash_release (pim_upstream_hash, up);
     XFREE(MTYPE_PIM_UPSTREAM, up);
     return NULL;
   }
 
-  listnode_add(qpim_upstream_list, up);
+  pim_ifp = up->rpf.source_nexthop.interface->info;
+  if (pim_ifp)
+    up->channel_oil = pim_channel_oil_add(&up->sg, pim_ifp->mroute_vif_index);
+
+  listnode_add_sort(pim_upstream_list, up);
+
+  if (PIM_DEBUG_TRACE)
+    zlog_debug ("%s: Created Upstream %s", __PRETTY_FUNCTION__, up->sg_str);
 
   return up;
 }
 
-struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
-                                      struct in_addr group_addr)
+struct pim_upstream *pim_upstream_find(struct prefix_sg *sg)
 {
-  struct listnode     *up_node;
-  struct pim_upstream *up;
-
-  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
-    if (group_addr.s_addr == up->group_addr.s_addr) {
-      if ((up->source_addr.s_addr == INADDR_ANY) ||
-         (source_addr.s_addr == up->source_addr.s_addr)) {
-       return up;
-      }
-    }
-  }
+  struct pim_upstream lookup;
+  struct pim_upstream *up = NULL;
 
-  return 0;
+  lookup.sg = *sg;
+  up = hash_lookup (pim_upstream_hash, &lookup);
+  return up;
 }
 
-struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
-                                     struct in_addr group_addr,
-                                     struct interface *incoming)
+static void pim_upstream_ref(struct pim_upstream *up, int flags)
 {
-  struct pim_upstream *up;
+  up->flags |= flags;
+  ++up->ref_count;
+}
 
-  up = pim_upstream_find(source_addr, group_addr);
+struct pim_upstream *pim_upstream_add(struct prefix_sg *sg,
+                                     struct interface *incoming,
+                                     int flags, const char *name)
+{
+  struct pim_upstream *up = NULL;
+  int found = 0;
+  up = pim_upstream_find(sg);
   if (up) {
-    ++up->ref_count;
+    pim_upstream_ref(up, flags);
+    found = 1;
   }
   else {
-    up = pim_upstream_new(source_addr, group_addr, incoming);
+    up = pim_upstream_new(sg, incoming, flags);
   }
 
+  if (PIM_DEBUG_TRACE)
+    {
+      if (up)
+       zlog_debug("%s(%s): %s, found: %d: ref_count: %d",
+                  __PRETTY_FUNCTION__, name,
+                  up->sg_str, found,
+                  up->ref_count);
+      else
+       zlog_debug("%s(%s): (%s) failure to create",
+                  __PRETTY_FUNCTION__, name,
+                  pim_str_sg_dump (sg));
+    }
+
   return up;
 }
 
-void pim_upstream_del(struct pim_upstream *up)
+static int
+pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
+                                             struct pim_ifchannel *ch)
 {
-  --up->ref_count;
+  struct pim_upstream *parent = up->parent;
 
-  if (up->ref_count < 1) {
-    pim_upstream_delete(up);
-  }
+  if (ch->upstream == up)
+    {
+      if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
+       return 1;
+
+      if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+       return 0;
+    }
+
+  /*
+   * joins (*,G)
+   */
+  if (parent && ch->upstream == parent)
+    {
+      if (!pim_macro_ch_lost_assert (ch) && pim_macro_chisin_joins_or_include (ch))
+       return 1;
+    }
+
+  return 0;
 }
 
 /*
@@ -467,34 +714,23 @@ void pim_upstream_del(struct pim_upstream *up)
  */
 int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
 {
-  struct listnode      *ifnode;
-  struct listnode      *ifnextnode;
   struct listnode      *chnode;
   struct listnode      *chnextnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
+  int                  ret = 0;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    pim_ifp = ifp->info;
-    if (!pim_ifp)
-      continue;
-
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-      if (ch->upstream != up)
+  /* scan per-interface (S,G) state */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch))
+    {
+      pim_ifp = ch->interface->info;
+      if (!pim_ifp)
        continue;
 
-      if (pim_macro_ch_lost_assert(ch))
-       continue; /* keep searching */
-
-      if (pim_macro_chisin_joins_or_include(ch))
-       return 1; /* true */
+      ret += pim_upstream_evaluate_join_desired_interface (up, ch);
     } /* scan iface channel list */
-  } /* scan iflist */
 
-  return 0; /* false */
+  return ret; /* false */
 }
 
 /*
@@ -515,14 +751,12 @@ void pim_upstream_update_join_desired(struct pim_upstream *up)
 
   /* switched from false to true */
   if (is_join_desired && !was_join_desired) {
-    zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
     pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
     return;
   }
       
   /* switched from true to false */
   if (!is_join_desired && was_join_desired) {
-    zassert(up->join_state == PIM_UPSTREAM_JOINED);
     pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
     return;
   }
@@ -544,22 +778,18 @@ void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
   struct pim_upstream *up;
 
   /*
-    Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
-  */
-  for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+   * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
+   */
+  for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
 
-    if (PIM_DEBUG_PIM_TRACE) {
-      char neigh_str[100];
-      char src_str[100];
-      char grp_str[100];
-      char rpf_addr_str[100];
+    if (PIM_DEBUG_TRACE) {
+      char neigh_str[INET_ADDRSTRLEN];
+      char rpf_addr_str[PREFIX_STRLEN];
       pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
-      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
-      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
-      pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
-      zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
+      pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+      zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
                 __PRETTY_FUNCTION__,
-                neigh_str, src_str, grp_str,
+                neigh_str, up->sg_str,
                 up->join_state == PIM_UPSTREAM_JOINED,
                 rpf_addr_str);
     }
@@ -569,7 +799,7 @@ void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
       continue;
 
     /* match RPF'(S,G)=neigh_addr */
-    if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
+    if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr)
       continue;
 
     pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
@@ -581,130 +811,141 @@ void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
 void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
                                        struct interface *old_rpf_ifp)
 {
-  struct listnode  *ifnode;
-  struct listnode  *ifnextnode;
-  struct interface *ifp;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct pim_ifchannel *ch;
+  struct pim_interface *pim_ifp;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    struct listnode      *chnode;
-    struct listnode      *chnextnode;
-    struct pim_ifchannel *ch;
-    struct pim_interface *pim_ifp;
+  /* search all ifchannels */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
 
-    pim_ifp = ifp->info;
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* search all ifchannels */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-      if (ch->upstream != up)
-       continue;
+    if (ch->upstream != up)
+      continue;
 
-      if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
-       if (
-           /* RPF_interface(S) was NOT I */
-           (old_rpf_ifp == ch->interface)
-           &&
-           /* RPF_interface(S) stopped being I */
-           (ch->upstream->rpf.source_nexthop.interface != ch->interface)
-           ) {
-         assert_action_a5(ch);
-       }
-      } /* PIM_IFASSERT_I_AM_LOSER */
+    if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+      if (
+         /* RPF_interface(S) was NOT I */
+         (old_rpf_ifp == ch->interface)
+         &&
+         /* RPF_interface(S) stopped being I */
+         (ch->upstream->rpf.source_nexthop.interface != ch->interface)
+         ) {
+       assert_action_a5(ch);
+      }
+    } /* PIM_IFASSERT_I_AM_LOSER */
 
-      pim_ifchannel_update_assert_tracking_desired(ch);
-    }
+    pim_ifchannel_update_assert_tracking_desired(ch);
   }
 }
 
 void pim_upstream_update_could_assert(struct pim_upstream *up)
 {
-  struct listnode      *ifnode;
-  struct listnode      *ifnextnode;
   struct listnode      *chnode;
   struct listnode      *chnextnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    pim_ifp = ifp->info;
+  /* scan per-interface (S,G) state */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-
-      if (ch->upstream != up)
-       continue;
-
-      pim_ifchannel_update_could_assert(ch);
+    if (ch->upstream != up)
+      continue;
 
-    } /* scan iface channel list */
-  } /* scan iflist */
+    pim_ifchannel_update_could_assert(ch);
+  } /* scan iface channel list */
 }
 
 void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
 {
-  struct listnode      *ifnode;
-  struct listnode      *ifnextnode;
   struct listnode      *chnode;
   struct listnode      *chnextnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    pim_ifp = ifp->info;
+  /* scan per-interface (S,G) state */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-
-      if (ch->upstream != up)
-       continue;
+    if (ch->upstream != up)
+      continue;
 
-      pim_ifchannel_update_my_assert_metric(ch);
+    pim_ifchannel_update_my_assert_metric(ch);
 
-    } /* scan iface channel list */
-  } /* scan iflist */
+  } /* scan iface channel list */
 }
 
 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
 {
-  struct listnode      *ifnode;
-  struct listnode      *ifnextnode;
   struct listnode      *chnode;
   struct listnode      *chnextnode;
-  struct interface     *ifp;
   struct pim_interface *pim_ifp;
   struct pim_ifchannel *ch;
 
-  /* scan all interfaces */
-  for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
-    pim_ifp = ifp->info;
+  /* scan per-interface (S,G) state */
+  for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+    pim_ifp = ch->interface->info;
     if (!pim_ifp)
       continue;
 
-    /* scan per-interface (S,G) state */
-    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+    if (ch->upstream != up)
+      continue;
 
-      if (ch->upstream != up)
-       continue;
+    pim_ifchannel_update_assert_tracking_desired(ch);
 
-      pim_ifchannel_update_assert_tracking_desired(ch);
+  } /* scan iface channel list */
+}
 
-    } /* scan iface channel list */
-  } /* scan iflist */
+/* When kat is stopped CouldRegister goes to false so we need to
+ * transition  the (S, G) on FHR to NI state and remove reg tunnel
+ * from the OIL */
+static void pim_upstream_fhr_kat_expiry(struct pim_upstream *up)
+{
+  if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+    return;
+
+  if (PIM_DEBUG_TRACE)
+    zlog_debug ("kat expired on %s; clear fhr reg state", up->sg_str);
+
+  /* stop reg-stop timer */
+  THREAD_OFF(up->t_rs_timer);
+  /* remove regiface from the OIL if it is there*/
+  pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+  /* move to "not-joined" */
+  up->join_state = PIM_UPSTREAM_NOTJOINED;
+  PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
+}
+
+/* When kat is started CouldRegister can go to true. And if it does we
+ * need to transition  the (S, G) on FHR to JOINED state and add reg tunnel
+ * to the OIL */
+static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
+{
+  if (pim_upstream_could_register(up)) {
+    if (PIM_DEBUG_TRACE)
+      zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str);
+
+    PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+    if (up->join_state == PIM_UPSTREAM_NOTJOINED) {
+      pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+      up->join_state = PIM_UPSTREAM_JOINED;
+    }
+  }
 }
 
 /*
  * On an RP, the PMBR value must be cleared when the
  * Keepalive Timer expires
+ * KAT expiry indicates that flow is inactive. If the flow was created or
+ * maintained by activity now is the time to deref it.
  */
 static int
 pim_upstream_keep_alive_timer (struct thread *t)
@@ -712,41 +953,73 @@ pim_upstream_keep_alive_timer (struct thread *t)
   struct pim_upstream *up;
 
   up = THREAD_ARG(t);
+  up->t_ka_timer = NULL;
+
+  if (I_am_RP (up->sg.grp))
+  {
+    pim_br_clear_pmbr (&up->sg);
+    /*
+     * We need to do more here :)
+     * But this is the start.
+     */
+  }
 
-  if (I_am_RP (up->group_addr))
-    {
-      pim_br_clear_pmbr (up->source_addr, up->group_addr);
-      /*
-       * We need to do more here :)
-       * But this is the start.
-       */
-    }
-  else
-    {
-      pim_mroute_update_counters (up->channel_oil);
+  /* source is no longer active - pull the SA from MSDP's cache */
+  pim_msdp_sa_local_del(&up->sg);
+
+  /* if entry was created because of activity we need to deref it */
+  if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
+  {
+    pim_upstream_fhr_kat_expiry(up);
+    if (PIM_DEBUG_TRACE)
+      zlog_debug ("kat expired on %s; remove stream reference", up->sg_str);
+    PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
+    pim_upstream_del(up, __PRETTY_FUNCTION__);
+  }
 
-      if (up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt)
-       {
-         pim_mroute_del (up->channel_oil);
-         pim_upstream_delete (up);
-       }
-      else
-       {
-         up->t_ka_timer = NULL;
-         pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
-       }
-    }
-  return 1;
+  return 0;
 }
 
 void
 pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
                                     uint32_t time)
 {
+  if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
+    if (PIM_DEBUG_TRACE)
+      zlog_debug ("kat start on %s with no stream reference", up->sg_str);
+  }
+  THREAD_OFF (up->t_ka_timer);
   THREAD_TIMER_ON (master,
                   up->t_ka_timer,
                   pim_upstream_keep_alive_timer,
                   up, time);
+
+  /* any time keepalive is started against a SG we will have to
+   * re-evaluate our active source database */
+  pim_msdp_sa_local_update(up);
+}
+
+/* MSDP on RP needs to know if a source is registerable to this RP */
+static int
+pim_upstream_msdp_reg_timer(struct thread *t)
+{
+  struct pim_upstream *up;
+
+  up = THREAD_ARG(t);
+  up->t_msdp_reg_timer = NULL;
+
+  /* source is no longer active - pull the SA from MSDP's cache */
+  pim_msdp_sa_local_del(&up->sg);
+  return 1;
+}
+void
+pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
+{
+  THREAD_OFF(up->t_msdp_reg_timer);
+  THREAD_TIMER_ON(master, up->t_msdp_reg_timer,
+      pim_upstream_msdp_reg_timer, up, PIM_MSDP_REG_RXED_PERIOD);
+
+  pim_msdp_sa_local_update(up);
 }
 
 /*
@@ -778,7 +1051,472 @@ pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
  *  received for the source and group.
  */
 int
-pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group)
+pim_upstream_switch_to_spt_desired (struct prefix_sg *sg)
+{
+  if (I_am_RP (sg->grp))
+    return 1;
+
+  return 0;
+}
+
+int
+pim_upstream_is_sg_rpt (struct pim_upstream *up)
+{
+  struct listnode *chnode;
+  struct pim_ifchannel *ch;
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, chnode, ch))
+    {
+      if ((ch->upstream == up) &&
+         (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)))
+       return 1;
+    }
+
+  return 0;
+}
+/*
+ *  After receiving a packet set SPTbit:
+ *   void
+ *   Update_SPTbit(S,G,iif) {
+ *     if ( iif == RPF_interface(S)
+ *           AND JoinDesired(S,G) == TRUE
+ *           AND ( DirectlyConnected(S) == TRUE
+ *                 OR RPF_interface(S) != RPF_interface(RP(G))
+ *                 OR inherited_olist(S,G,rpt) == NULL
+ *                 OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
+ *                      ( RPF'(S,G) != NULL ) )
+ *                 OR ( I_Am_Assert_Loser(S,G,iif) ) {
+ *        Set SPTbit(S,G) to TRUE
+ *     }
+ *   }
+ */
+void
+pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming)
+{
+  struct pim_rpf *grpf = NULL;
+
+  // iif == RPF_interfvace(S)
+  if (up->rpf.source_nexthop.interface != incoming)
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: Incoming Interface: %s is different than RPF_interface(S) %s",
+                   __PRETTY_FUNCTION__, incoming->name, up->rpf.source_nexthop.interface->name);
+      return;
+    }
+
+  // AND JoinDesired(S,G) == TRUE
+  // FIXME
+
+  // DirectlyConnected(S) == TRUE
+  if (pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: %s is directly connected to the source", __PRETTY_FUNCTION__,
+                   up->sg_str);
+      up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
+      return;
+     }
+
+  // OR RPF_interface(S) != RPF_interface(RP(G))
+  grpf = RP(up->sg.grp);
+  if (!grpf || up->rpf.source_nexthop.interface != grpf->source_nexthop.interface)
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: %s RPF_interface(S) != RPF_interface(RP(G))",
+                   __PRETTY_FUNCTION__, up->sg_str);
+      up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
+      return;
+    }
+
+  // OR inherited_olist(S,G,rpt) == NULL
+  if (pim_upstream_is_sg_rpt(up) && pim_upstream_empty_inherited_olist(up))
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: %s OR inherited_olist(S,G,rpt) == NULL", __PRETTY_FUNCTION__,
+                   up->sg_str);
+      up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
+      return;
+    }
+
+  // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
+  //      ( RPF'(S,G) != NULL ) )
+  if (up->parent && pim_rpf_is_same (&up->rpf, &up->parent->rpf))
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: %s RPF'(S,G) is the same as RPF'(*,G)", __PRETTY_FUNCTION__,
+                   up->sg_str);
+      up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
+      return;
+    }
+
+  return;
+}
+
+const char *
+pim_upstream_state2str (enum pim_upstream_state join_state)
+{
+  switch (join_state)
+    {
+    case PIM_UPSTREAM_NOTJOINED:
+      return "NotJoined";
+      break;
+    case PIM_UPSTREAM_JOINED:
+      return "Joined";
+      break;
+    case PIM_UPSTREAM_JOIN_PENDING:
+      return "JoinPending";
+      break;
+    case PIM_UPSTREAM_PRUNE:
+      return "Prune";
+      break;
+    }
+  return "Unknown";
+}
+
+static int
+pim_upstream_register_stop_timer (struct thread *t)
+{
+  struct pim_interface *pim_ifp;
+  struct pim_upstream *up;
+  struct pim_rpf *rpg;
+  struct ip ip_hdr;
+  up = THREAD_ARG (t);
+
+  up->t_rs_timer = NULL;
+
+  if (PIM_DEBUG_TRACE)
+    {
+      zlog_debug ("%s: (S,G)=%s upstream register stop timer %s",
+                 __PRETTY_FUNCTION__, up->sg_str,
+                  pim_upstream_state2str(up->join_state));
+    }
+
+  switch (up->join_state)
+    {
+    case PIM_UPSTREAM_JOIN_PENDING:
+      up->join_state = PIM_UPSTREAM_JOINED;
+      pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+      break;
+    case PIM_UPSTREAM_JOINED:
+      break;
+    case PIM_UPSTREAM_PRUNE:
+      pim_ifp = up->rpf.source_nexthop.interface->info;
+      if (!pim_ifp)
+        {
+         if (PIM_DEBUG_TRACE)
+           zlog_debug ("%s: Interface: %s is not configured for pim",
+                       __PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name);
+         return 0;
+       }
+      up->join_state = PIM_UPSTREAM_JOIN_PENDING;
+      pim_upstream_start_register_stop_timer (up, 1);
+
+      if (((up->channel_oil->cc.lastused/100) > PIM_KEEPALIVE_PERIOD) &&
+         (I_am_RP (up->sg.grp)))
+       {
+         if (PIM_DEBUG_TRACE)
+           zlog_debug ("%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", __PRETTY_FUNCTION__);
+         return 0;
+       }
+      rpg = RP (up->sg.grp);
+      memset (&ip_hdr, 0, sizeof (struct ip));
+      ip_hdr.ip_p = PIM_IP_PROTO_PIM;
+      ip_hdr.ip_hl = 5;
+      ip_hdr.ip_v = 4;
+      ip_hdr.ip_src = up->sg.src;
+      ip_hdr.ip_dst = up->sg.grp;
+      ip_hdr.ip_len = htons (20);
+      // checksum is broken
+      pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip),
+                        pim_ifp->primary_address, rpg, 1, up);
+      break;
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+void
+pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register)
+{
+  uint32_t time;
+
+  if (up->t_rs_timer)
+    {
+      THREAD_TIMER_OFF (up->t_rs_timer);
+      up->t_rs_timer = NULL;
+    }
+
+  if (!null_register)
+    {
+      uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
+      uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
+      time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD;
+    }
+  else
+    time = PIM_REGISTER_PROBE_PERIOD;
+
+  if (PIM_DEBUG_TRACE)
+    {
+      zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d",
+                 __PRETTY_FUNCTION__, up->sg_str, time);
+    }
+  THREAD_TIMER_ON (master, up->t_rs_timer,
+                  pim_upstream_register_stop_timer,
+                  up, time);
+}
+
+int
+pim_upstream_inherited_olist_decide (struct pim_upstream *up)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *chnextnode;
+  struct pim_ifchannel *ch;
+  struct listnode *chnode;
+  int output_intf = 0;
+
+  pim_ifp = up->rpf.source_nexthop.interface->info;
+  if (pim_ifp && !up->channel_oil)
+    up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index);
+
+  for (ALL_LIST_ELEMENTS (pim_ifchannel_list, chnode, chnextnode, ch))
+    {
+      pim_ifp = ch->interface->info;
+      if (!pim_ifp)
+       continue;
+
+      if (pim_upstream_evaluate_join_desired_interface (up, ch))
+       {
+         pim_channel_add_oif (up->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+         output_intf++;
+       }
+    }
+
+  return output_intf;
+}
+
+/*
+ * For a given upstream, determine the inherited_olist
+ * and apply it.
+ *
+ * inherited_olist(S,G,rpt) =
+ *           ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+ *      (+) ( pim_include(*,G) (-) pim_exclude(S,G))
+ *      (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
+ *
+ *  inherited_olist(S,G) =
+ *      inherited_olist(S,G,rpt) (+)
+ *      joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+ *
+ * return 1 if there are any output interfaces
+ * return 0 if there are not any output interfaces
+ */
+int
+pim_upstream_inherited_olist (struct pim_upstream *up)
+{
+  int output_intf =  pim_upstream_inherited_olist_decide (up);
+
+  /*
+   * If we have output_intf switch state to Join and work like normal
+   * If we don't have an output_intf that means we are probably a
+   * switch on a stick so turn on forwarding to just accept the
+   * incoming packets so we don't bother the other stuff!
+   */
+  if (output_intf)
+    pim_upstream_switch (up, PIM_UPSTREAM_JOINED);
+  else
+    forward_on (up);
+
+  return output_intf;
+}
+
+int
+pim_upstream_empty_inherited_olist (struct pim_upstream *up)
+{
+  return pim_channel_oil_empty (up->channel_oil);
+}
+
+/*
+ * When we have a new neighbor,
+ * find upstreams that don't have their rpf_addr
+ * set and see if the new neighbor allows
+ * the join to be sent
+ */
+void
+pim_upstream_find_new_rpf (void)
+{
+  struct listnode     *up_node;
+  struct listnode     *up_nextnode;
+  struct pim_upstream *up;
+
+  /*
+   * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
+   */
+  for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up))
+    {
+      if (pim_rpf_addr_is_inaddr_any(&up->rpf))
+       {
+         if (PIM_DEBUG_TRACE)
+           zlog_debug ("Upstream %s without a path to send join, checking",
+                       up->sg_str);
+         pim_rpf_update (up, NULL);
+       }
+    }
+}
+
+static unsigned int
+pim_upstream_hash_key (void *arg)
+{
+  struct pim_upstream *up = (struct pim_upstream *)arg;
+
+  return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0);
+}
+
+void pim_upstream_terminate (void)
 {
+  if (pim_upstream_list)
+    list_delete (pim_upstream_list);
+  pim_upstream_list = NULL;
+
+  if (pim_upstream_hash)
+    hash_free (pim_upstream_hash);
+  pim_upstream_hash = NULL;
+}
+
+static int
+pim_upstream_equal (const void *arg1, const void *arg2)
+{
+  const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
+  const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
+
+  if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) &&
+      (up1->sg.src.s_addr == up2->sg.src.s_addr))
+    return 1;
+
   return 0;
 }
+
+/* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
+ * the cases where kat has to be restarted on rxing traffic -
+ *
+ * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
+ * set KeepaliveTimer(S,G) to Keepalive_Period
+ * # Note: a register state transition or UpstreamJPState(S,G)
+ * # transition may happen as a result of restarting
+ * # KeepaliveTimer, and must be dealt with here.
+ * }
+ * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
+ * inherited_olist(S,G) != NULL ) {
+ * set KeepaliveTimer(S,G) to Keepalive_Period
+ * }
+ */
+static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
+{
+  /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
+   * so we will skip that here */
+  if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
+        up->sg.src)) {
+    return true;
+  }
+
+  if ((up->join_state == PIM_UPSTREAM_JOINED) &&
+            !pim_upstream_empty_inherited_olist(up)) {
+    /* XXX: I have added this RP check just for 3.2 and it's a digression from
+     * what rfc-4601 says. Till now we were only running KAT on FHR and RP and
+     * there is some angst around making the change to run it all routers that
+     * maintain the (S, G) state. This is tracked via CM-13601 and MUST be
+     * removed to handle spt turn-arounds correctly in a 3-tier clos */
+    if (I_am_RP (up->sg.grp))
+      return true;
+  }
+
+  return false;
+}
+
+/*
+ * Code to check and see if we've received packets on a S,G mroute
+ * and if so to set the SPT bit appropriately
+ */
+static void
+pim_upstream_sg_running (void *arg)
+{
+  struct pim_upstream *up = (struct pim_upstream *)arg;
+
+  // No packet can have arrived here if this is the case
+  if (!up->channel_oil || !up->channel_oil->installed)
+    {
+      if (PIM_DEBUG_TRACE)
+       zlog_debug ("%s: %s is not installed in mroute",
+                   __PRETTY_FUNCTION__, up->sg_str);
+      return;
+    }
+
+  /*
+   * This is a bit of a hack
+   * We've noted that we should rescan but
+   * we've missed the window for doing so in
+   * pim_zebra.c for some reason.  I am
+   * only doing this at this point in time
+   * to get us up and working for the moment
+   */
+  if (up->channel_oil->oil_inherited_rescan)
+    {
+      if (PIM_DEBUG_TRACE)
+        zlog_debug ("%s: Handling unscanned inherited_olist for %s", __PRETTY_FUNCTION__, up->sg_str);
+      pim_upstream_inherited_olist_decide (up);
+      up->channel_oil->oil_inherited_rescan = 0;
+    }
+  pim_mroute_update_counters (up->channel_oil);
+
+  // Have we seen packets?
+  if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) &&
+      (up->channel_oil->cc.lastused/100 > 30))
+    {
+      if (PIM_DEBUG_TRACE)
+       {
+         zlog_debug ("%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
+                     __PRETTY_FUNCTION__, up->sg_str,
+                     up->channel_oil->cc.oldpktcnt,
+                     up->channel_oil->cc.pktcnt,
+                     up->channel_oil->cc.lastused/100);
+       }
+      return;
+    }
+
+  if (pim_upstream_kat_start_ok(up)) {
+    /* Add a source reference to the stream if
+     * one doesn't already exist */
+    if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
+    {
+      if (PIM_DEBUG_TRACE)
+        zlog_debug ("source reference created on kat restart %s", up->sg_str);
+
+      pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM);
+      PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
+      pim_upstream_fhr_kat_start(up);
+    }
+    pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
+  }
+
+  if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE)
+  {
+    pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface);
+  }
+  return;
+}
+
+void
+pim_upstream_init (void)
+{
+  pim_upstream_sg_wheel = wheel_init (master, 31000, 100,
+                                     pim_upstream_hash_key,
+                                     pim_upstream_sg_running);
+  pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key,
+                                       pim_upstream_equal);
+
+  pim_upstream_list = list_new ();
+  pim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
+  pim_upstream_list->cmp = pim_upstream_compare;
+
+}
index f10c8feb3ba32d609974cdf10f4a7f84ae2a72ea..1a50c0c6e3aacfc89cbd0e4b772a3f2752b4da3f 100644 (file)
 #define PIM_UPSTREAM_H
 
 #include <zebra.h>
+#include <prefix.h>
+
+#include <pimd/pim_rpf.h>
 
 #define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED         (1 << 0)
-#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0)
+#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (1 << 1)
+#define PIM_UPSTREAM_FLAG_MASK_FHR                     (1 << 2)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_IGMP                (1 << 3)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_PIM                 (1 << 4)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_STREAM              (1 << 5)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_MSDP                (1 << 6)
 
 #define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
 #define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+#define PIM_UPSTREAM_FLAG_TEST_FHR(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_FHR)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_PIM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
 
 #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
 #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+#define PIM_UPSTREAM_FLAG_SET_FHR(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_FHR)
+#define PIM_UPSTREAM_FLAG_SET_SRC_IGMP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+#define PIM_UPSTREAM_FLAG_SET_SRC_PIM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+#define PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+#define PIM_UPSTREAM_FLAG_SET_SRC_MSDP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
 
 #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
 #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
-
-/*
-  RFC 4601:
-
-  Metric Preference
-    Preference value assigned to the unicast routing protocol that
-    provided the route to the multicast source or Rendezvous-Point.
-
-  Metric
-    The unicast routing table metric associated with the route used to
-    reach the multicast source or Rendezvous-Point.  The metric is in
-    units applicable to the unicast routing protocol used.
-*/
-struct pim_nexthop {
-  struct interface *interface;              /* RPF_interface(S) */
-  struct in_addr    mrib_nexthop_addr;      /* MRIB.next_hop(S) */
-  uint32_t          mrib_metric_preference; /* MRIB.pref(S) */
-  uint32_t          mrib_route_metric;      /* MRIB.metric(S) */
-};
-
-struct pim_rpf {
-  struct pim_nexthop source_nexthop;
-  struct in_addr     rpf_addr;               /* RPF'(S,G) */
-};
-
-enum pim_rpf_result {
-  PIM_RPF_OK = 0,
-  PIM_RPF_CHANGED,
-  PIM_RPF_FAILURE
-};
+#define PIM_UPSTREAM_FLAG_UNSET_FHR(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_FHR)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
 
 enum pim_upstream_state {
   PIM_UPSTREAM_NOTJOINED,
-  PIM_UPSTREAM_JOINED
+  PIM_UPSTREAM_JOINED,
+  PIM_UPSTREAM_JOIN_PENDING,
+  PIM_UPSTREAM_PRUNE,
 };
 
 enum pim_upstream_sptbit {
@@ -83,11 +78,14 @@ enum pim_upstream_sptbit {
   See RFC 4601: 4.5.7.  Sending (S,G) Join/Prune Message
 */
 struct pim_upstream {
+  struct pim_upstream      *parent;
   struct in_addr           upstream_addr;/* Who we are talking to */
-  struct in_addr           source_addr;  /* (S,G) source key */
-  struct in_addr           group_addr;   /* (S,G) group key */
+  struct in_addr           upstream_register; /*Who we received a register from*/
+  struct prefix_sg         sg;           /* (S,G) group key */
+  char                     sg_str[PIM_SG_LEN];
   uint32_t                 flags;
   struct channel_oil      *channel_oil;
+  struct list             *sources;
 
   enum pim_upstream_state  join_state;
   enum pim_upstream_sptbit sptbit;
@@ -98,6 +96,13 @@ struct pim_upstream {
 
   struct thread           *t_join_timer;
 
+  /*
+   * RST(S,G)
+   */
+  struct thread           *t_rs_timer;
+#define PIM_REGISTER_SUPPRESSION_PERIOD (60)
+#define PIM_REGISTER_PROBE_PERIOD       (15)
+
   /*
    * KAT(S,G)
    */
@@ -105,17 +110,23 @@ struct pim_upstream {
 #define PIM_KEEPALIVE_PERIOD  (210)
 #define PIM_RP_KEEPALIVE_PERIOD ( 3 * qpim_register_suppress_time + qpim_register_probe_time )
 
+  /* on the RP we restart a timer to indicate if registers are being rxed for
+   * SG. This is needed by MSDP to determine its local SA cache */
+  struct thread           *t_msdp_reg_timer;
+#define PIM_MSDP_REG_RXED_PERIOD (3 * (1.5 * qpim_register_suppress_time))
+
   int64_t                  state_transition; /* Record current state uptime */
 };
 
+struct list *pim_upstream_list;
+struct hash *pim_upstream_hash;
+
 void pim_upstream_free(struct pim_upstream *up);
-void pim_upstream_delete(struct pim_upstream *up);
-struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
-                                      struct in_addr group_addr);
-struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
-                                     struct in_addr group_addr,
-                                     struct interface *ifp);
-void pim_upstream_del(struct pim_upstream *up);
+struct pim_upstream *pim_upstream_find (struct prefix_sg *sg);
+struct pim_upstream *pim_upstream_add (struct prefix_sg *sg,
+                                      struct interface *ifp, int flags,
+                                      const char *name);
+void pim_upstream_del(struct pim_upstream *up, const char *name);
 
 int pim_upstream_evaluate_join_desired(struct pim_upstream *up);
 void pim_upstream_update_join_desired(struct pim_upstream *up);
@@ -136,7 +147,27 @@ void pim_upstream_update_my_assert_metric(struct pim_upstream *up);
 
 void pim_upstream_keep_alive_timer_start (struct pim_upstream *up, uint32_t time);
 
-int pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group);
-#define SwitchToSptDesired(S,G) pim_upstream_switch_to_spt_desired ((S), (G))
+int pim_upstream_switch_to_spt_desired (struct prefix_sg *sg);
+#define SwitchToSptDesired(sg) pim_upstream_switch_to_spt_desired (sg)
+int pim_upstream_is_sg_rpt (struct pim_upstream *up);
+
+void pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming);
+
+void pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register);
+
+void pim_upstream_send_join (struct pim_upstream *up);
+
+void pim_upstream_switch (struct pim_upstream *up, enum pim_upstream_state new_state);
+
+const char *pim_upstream_state2str (enum pim_upstream_state join_state);
+
+int pim_upstream_inherited_olist_decide (struct pim_upstream *up);
+int pim_upstream_inherited_olist (struct pim_upstream *up);
+int pim_upstream_empty_inherited_olist (struct pim_upstream *up);
+
+void pim_upstream_find_new_rpf (void);
+void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up);
 
+void pim_upstream_init (void);
+void pim_upstream_terminate (void);
 #endif /* PIM_UPSTREAM_H */
index f5b6a8210abb3077b12020002d3c810e2d5a2648..6d7a8c19089033365eb2d51e430570dc80a0a968 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 
 #include "log.h"
+#include "prefix.h"
 
 #include "pim_util.h"
 
@@ -103,3 +108,43 @@ void pim_pkt_dump(const char *label, const uint8_t *buf, int size)
             size);
   zlog_hexdump(buf, size);
 }
+
+int
+pim_is_group_224_0_0_0_24 (struct in_addr group_addr)
+{
+  static int first = 1;
+  static struct prefix group_224;
+  struct prefix group;
+
+  if (first)
+    {
+      str2prefix ("224.0.0.0/24", &group_224);
+      first = 0;
+    }
+
+  group.family = AF_INET;
+  group.u.prefix4 = group_addr;
+  group.prefixlen = 32;
+
+  return prefix_match (&group_224, &group);
+}
+
+int
+pim_is_group_224_4 (struct in_addr group_addr)
+{
+  static int first = 1;
+  static struct prefix group_all;
+  struct prefix group;
+
+  if (first)
+    {
+      str2prefix ("224.0.0.0/4", &group_all);
+      first = 0;
+    }
+
+  group.family = AF_INET;
+  group.u.prefix4 = group_addr;
+  group.prefixlen = 32;
+
+  return prefix_match (&group_all, &group);
+}
index d780bfbc27554e11ad2074bf4c125bec39544010..fbb7b0196b0460b3a0e1d0f4ca379e8cb5634ce0 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_UTIL_H
@@ -32,4 +36,6 @@ uint16_t igmp_msg_decode8to16(uint8_t code);
 
 void pim_pkt_dump(const char *label, const uint8_t *buf, int size);
 
+int pim_is_group_224_0_0_0_24 (struct in_addr group_addr);
+int pim_is_group_224_4 (struct in_addr group_addr);
 #endif /* PIM_UTIL_H */
index f9bc2319e75fc6485bd0467755e1ef6c5d98ba72..ee4fcd3150a526f936839a96cacb68c09540f03e 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
 
 #include "if.h"
 #include "linklist.h"
+#include "prefix.h"
+#include "vty.h"
 #include "vrf.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_vty.h"
 #include "pim_pim.h"
 #include "pim_oil.h"
 #include "pim_static.h"
+#include "pim_rp.h"
+#include "pim_msdp.h"
 
-int pim_debug_config_write(struct vty *vty)
+int
+pim_debug_config_write (struct vty *vty)
 {
   int writes = 0;
 
+  if (PIM_DEBUG_MSDP_EVENTS) {
+    vty_out(vty, "debug msdp events%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_MSDP_PACKETS) {
+    vty_out(vty, "debug msdp packets%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_MSDP_INTERNAL) {
+    vty_out(vty, "debug msdp internal%s", VTY_NEWLINE);
+    ++writes;
+  }
   if (PIM_DEBUG_IGMP_EVENTS) {
     vty_out(vty, "debug igmp events%s", VTY_NEWLINE);
     ++writes;
@@ -50,12 +72,21 @@ int pim_debug_config_write(struct vty *vty)
     vty_out(vty, "debug igmp trace%s", VTY_NEWLINE);
     ++writes;
   }
+  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
+    vty_out(vty, "debug igmp trace detail%s", VTY_NEWLINE);
+    ++writes;
+  }
 
   if (PIM_DEBUG_MROUTE) {
     vty_out(vty, "debug mroute%s", VTY_NEWLINE);
     ++writes;
   }
 
+  if (PIM_DEBUG_MROUTE_DETAIL) {
+    vty_out (vty, "debug mroute detail%s", VTY_NEWLINE);
+    ++writes;
+  }
+
   if (PIM_DEBUG_PIM_EVENTS) {
     vty_out(vty, "debug pim events%s", VTY_NEWLINE);
     ++writes;
@@ -72,10 +103,15 @@ int pim_debug_config_write(struct vty *vty)
     vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE);
     ++writes;
   }
+
   if (PIM_DEBUG_PIM_TRACE) {
     vty_out(vty, "debug pim trace%s", VTY_NEWLINE);
     ++writes;
   }
+  if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+    vty_out(vty, "debug pim trace detail%s", VTY_NEWLINE);
+    ++writes;
+  }
 
   if (PIM_DEBUG_ZEBRA) {
     vty_out(vty, "debug pim zebra%s", VTY_NEWLINE);
@@ -87,22 +123,66 @@ int pim_debug_config_write(struct vty *vty)
     ++writes;
   }
 
+  if (PIM_DEBUG_PIM_HELLO) {
+    vty_out (vty, "debug pim packets hello%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_PIM_J_P) {
+    vty_out (vty, "debug pim packets joins%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_PIM_REG) {
+    vty_out (vty, "debug pim packets register%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_STATIC) {
+    vty_out (vty, "debug pim static%s", VTY_NEWLINE);
+    ++writes;
+  }
+
   return writes;
 }
 
 int pim_global_config_write(struct vty *vty)
 {
   int writes = 0;
-  char buffer[32];
+
+  writes += pim_msdp_config_write (vty);
 
   if (PIM_MROUTE_IS_ENABLED) {
     vty_out(vty, "ip multicast-routing%s", VTY_NEWLINE);
     ++writes;
   }
-  if (qpim_rp.rpf_addr.s_addr != INADDR_NONE) {
-    vty_out(vty, "ip pim rp %s%s", inet_ntop(AF_INET, &qpim_rp.rpf_addr, buffer, 32), VTY_NEWLINE);
-    ++writes;
-  }
+
+  writes += pim_rp_config_write (vty);
+
+  if (qpim_register_suppress_time != PIM_REGISTER_SUPPRESSION_TIME_DEFAULT)
+    {
+      vty_out (vty, "ip pim register-suppress-time %d%s",
+              qpim_register_suppress_time, VTY_NEWLINE);
+      ++writes;
+    }
+  if (qpim_t_periodic != PIM_DEFAULT_T_PERIODIC)
+    {
+      vty_out (vty, "ip pim join-prune-interval %d%s",
+              qpim_t_periodic, VTY_NEWLINE);
+      ++writes;
+    }
+  if (qpim_keep_alive_time != PIM_KEEPALIVE_PERIOD)
+    {
+      vty_out (vty, "ip pim keep-alive-timer %d%s",
+               qpim_keep_alive_time, VTY_NEWLINE);
+      ++writes;
+    }
+  if (qpim_packet_process != PIM_DEFAULT_PACKET_PROCESS)
+    {
+      vty_out (vty, "ip pim packets %d%s",
+              qpim_packet_process, VTY_NEWLINE);
+      ++writes;
+    }
 
   if (qpim_ssmpingd_list) {
     struct listnode *node;
@@ -110,7 +190,7 @@ int pim_global_config_write(struct vty *vty)
     vty_out(vty, "!%s", VTY_NEWLINE);
     ++writes;
     for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
-      char source_str[100];
+      char source_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
       vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE);
       ++writes;
@@ -159,12 +239,30 @@ int pim_interface_config_write(struct vty *vty)
        vty_out(vty, "%s", VTY_NEWLINE);
       }
 
+      /* update source */
+      if (PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) {
+        char src_str[INET_ADDRSTRLEN];
+        pim_inet4_dump("<src?>", pim_ifp->update_source, src_str,
+            sizeof(src_str));
+        vty_out(vty, " ip pim use-source %s%s", src_str, VTY_NEWLINE);
+        ++writes;
+      }
+
       /* IF ip igmp */
       if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
        vty_out(vty, " ip igmp%s", VTY_NEWLINE);
        ++writes;
       }
 
+      /* ip igmp version */
+      if (pim_ifp->igmp_version != IGMP_DEFAULT_VERSION)
+        {
+          vty_out(vty, " ip igmp version %d%s",
+                  pim_ifp->igmp_version,
+                  VTY_NEWLINE);
+          ++writes;
+        }
+
       /* IF ip igmp query-interval */
       if (pim_ifp->igmp_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL)
        {
@@ -177,7 +275,7 @@ int pim_interface_config_write(struct vty *vty)
       /* IF ip igmp query-max-response-time */
       if (pim_ifp->igmp_query_max_response_time_dsec != IGMP_QUERY_MAX_RESPONSE_TIME_DSEC)
        {
-         vty_out(vty, " ip igmp query-max-response-time-dsec %d%s",
+         vty_out(vty, " ip igpm query-max-response-time %d%s",
                  pim_ifp->igmp_query_max_response_time_dsec,
                  VTY_NEWLINE);
          ++writes;
@@ -188,10 +286,10 @@ int pim_interface_config_write(struct vty *vty)
        struct listnode *node;
        struct igmp_join *ij;
        for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) {
-         char group_str[100];
-         char source_str[100];
+         char group_str[INET_ADDRSTRLEN];
+         char source_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<grp?>", ij->group_addr, group_str, sizeof(group_str));
-         pim_inet4_dump("<src?>", ij->source_addr, source_str, sizeof(source_str));
+          inet_ntop(AF_INET, &ij->source_addr, source_str, sizeof(source_str));
          vty_out(vty, " ip igmp join %s %s%s",
                  group_str, source_str,
                  VTY_NEWLINE);
index 18a632e016278daefdb15ec6dd6bc2c84f0e589c..37ec07b2bff4cad0c075607388ee9e8cb7e7d1c3 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #ifndef PIM_VTY_H
index f2195960c4ee04a070b6d9d53c2892b47aac9479..b21da624d61fe95af8226e8af024168c79b5404b 100644 (file)
@@ -28,6 +28,8 @@
 #include "zclient.h"
 #include "stream.h"
 #include "network.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_pim.h"
@@ -171,6 +173,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient,
   }
 
   if (!if_is_operative(ifp)) {
+    pim_ifchannel_delete_all(ifp);
     /*
       pim_if_addr_del_all() suffices for shutting down IGMP,
       but not for shutting down PIM
@@ -186,6 +189,9 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient,
     }
   }
 
+  if (ifp->info)
+    pim_if_del_vif(ifp);
+
   return 0;
 }
 
@@ -220,7 +226,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
 {
   struct connected *c;
   struct prefix *p;
-  struct in_addr old = { .s_addr = 0 };
+  struct pim_interface *pim_ifp;
 
   /*
     zebra api notifies address adds/dels events by using the same call
@@ -234,10 +240,9 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
   if (!c)
     return 0;
 
+  pim_ifp = c->ifp->info;
   p = c->address;
-  if (p->family != AF_INET)
-    return 0;
-  
+
   if (PIM_DEBUG_ZEBRA) {
     char buf[BUFSIZ];
     prefix2str(p, buf, BUFSIZ);
@@ -251,7 +256,25 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
 #endif
   }
 
-  pim_rp_check_rp (old, p->u.prefix4);
+  if (p->family != AF_INET)
+    {
+      struct listnode *cnode;
+      struct connected *conn;
+      int v4addrs = 0;
+
+      for (ALL_LIST_ELEMENTS_RO (c->ifp->connected, cnode, conn))
+        {
+          if (conn->address->family == AF_INET)
+           v4addrs++;
+        }
+      if (!v4addrs && pim_ifp) 
+       {
+         pim_ifp->primary_address = pim_find_primary_addr (c->ifp);
+         pim_if_addr_add_all (c->ifp);
+          pim_if_add_vif (c->ifp);
+       }
+      return 0;
+    }
 
   if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) {
     /* trying to add primary address */
@@ -262,20 +285,32 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
        /* but we had a primary address already */
 
        char buf[BUFSIZ];
-       char old[100];
 
        prefix2str(p, buf, BUFSIZ);
-       pim_inet4_dump("<old?>", primary_addr, old, sizeof(old));
 
-       zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s",
+       zlog_warn("%s: %s : forcing secondary flag on %s",
                  __PRETTY_FUNCTION__,
-                 c->ifp->name, old, buf);
+                 c->ifp->name, buf);
       }
       SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
     }
   }
 
   pim_if_addr_add(c);
+  if (pim_ifp)
+    pim_rp_check_on_if_add(pim_ifp);
+
+  if (if_is_loopback (c->ifp))
+    {
+      struct listnode *ifnode;
+      struct interface *ifp;
+
+      for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
+        {
+         if (!if_is_loopback (ifp) && if_is_operative (ifp))
+           pim_if_addr_add_all (ifp);
+        }
+    }
 
   return 0;
 }
@@ -285,7 +320,6 @@ static int pim_zebra_if_address_del(int command, struct zclient *client,
 {
   struct connected *c;
   struct prefix *p;
-  struct in_addr new = { .s_addr = 0 };
 
   /*
     zebra api notifies address adds/dels events by using the same call
@@ -316,8 +350,9 @@ static int pim_zebra_if_address_del(int command, struct zclient *client,
 #endif
   }
 
-  pim_rp_check_rp (p->u.prefix4, new);
   pim_if_addr_del(c, 0);
+  pim_rp_setup();
+  pim_i_am_rp_re_evaluate();
   
   return 0;
 }
@@ -328,18 +363,37 @@ static void scan_upstream_rpf_cache()
   struct listnode     *up_nextnode;
   struct pim_upstream *up;
 
-  for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+  for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
     struct in_addr      old_rpf_addr;
+    struct interface    *old_interface;
     enum pim_rpf_result rpf_result;
 
-    rpf_result = pim_rpf_update(up, &old_rpf_addr, NULL);
+    old_interface = up->rpf.source_nexthop.interface;
+    rpf_result = pim_rpf_update(up, &old_rpf_addr);
     if (rpf_result == PIM_RPF_FAILURE)
       continue;
 
     if (rpf_result == PIM_RPF_CHANGED) {
-      
+
+      /*
+       * We have detected a case where we might need to rescan
+       * the inherited o_list so do it.
+       */
+      if (up->channel_oil->oil_inherited_rescan)
+       {
+         pim_upstream_inherited_olist_decide (up);
+         up->channel_oil->oil_inherited_rescan = 0;
+       }
+
       if (up->join_state == PIM_UPSTREAM_JOINED) {
-       
+       /*
+         * If we come up real fast we can be here
+        * where the mroute has not been installed
+        * so install it.
+        */
+       if (!up->channel_oil->installed)
+         pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
+
        /*
          RFC 4601: 4.5.7.  Sending (S,G) Join/Prune Messages
          
@@ -356,17 +410,13 @@ static void scan_upstream_rpf_cache()
 
     
        /* send Prune(S,G) to the old upstream neighbor */
-       pim_joinprune_send(up->rpf.source_nexthop.interface,
-                          old_rpf_addr,
-                          up->source_addr,
-                          up->group_addr,
-                          0 /* prune */);
+       pim_joinprune_send(old_interface, old_rpf_addr,
+                          up, 0 /* prune */);
        
        /* send Join(S,G) to the current upstream neighbor */
        pim_joinprune_send(up->rpf.source_nexthop.interface,
-                          up->rpf.rpf_addr,
-                          up->source_addr,
-                          up->group_addr,
+                          up->rpf.rpf_addr.u.prefix4,
+                          up,
                           1 /* join */);
 
        pim_upstream_join_timer_restart(up);
@@ -389,7 +439,7 @@ pim_scan_individual_oil (struct channel_oil *c_oil)
   int input_iface_vif_index;
   int old_vif_index;
 
-  if (!pim_rp_set_upstream_addr (&vif_source, c_oil->oil.mfcc_origin))
+  if (!pim_rp_set_upstream_addr (&vif_source, c_oil->oil.mfcc_origin, c_oil->oil.mfcc_mcastgrp))
     return;
 
   input_iface_vif_index = fib_lookup_if_vif_index (vif_source);
@@ -397,20 +447,23 @@ pim_scan_individual_oil (struct channel_oil *c_oil)
     {
       if (PIM_DEBUG_ZEBRA)
         {
-          char source_str[100];
-          char group_str[100];
+          char source_str[INET_ADDRSTRLEN];
+          char group_str[INET_ADDRSTRLEN];
           pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
           pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
           zlog_debug("%s %s: could not find input interface(%d) for (S,G)=(%s,%s)",
                     __FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent,
                     source_str, group_str);
         }
-      pim_mroute_del (c_oil);
+      pim_mroute_del (c_oil, __PRETTY_FUNCTION__);
       return;
     }
 
   if (input_iface_vif_index == c_oil->oil.mfcc_parent)
     {
+      if (!c_oil->installed)
+        pim_mroute_add (c_oil, __PRETTY_FUNCTION__);
+
       /* RPF unchanged */
       return;
     }
@@ -419,15 +472,15 @@ pim_scan_individual_oil (struct channel_oil *c_oil)
     {
       struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
       struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
-      char source_str[100];
-      char group_str[100];
+      char source_str[INET_ADDRSTRLEN];
+      char group_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
       zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
                 __FILE__, __PRETTY_FUNCTION__,
                 source_str, group_str,
-                old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
-                new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+                old_iif->name, c_oil->oil.mfcc_parent,
+                new_iif->name, input_iface_vif_index);
     }
 
   /* new iif loops to existing oif ? */
@@ -436,39 +489,41 @@ pim_scan_individual_oil (struct channel_oil *c_oil)
       struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
 
       if (PIM_DEBUG_ZEBRA) {
-       char source_str[100];
-       char group_str[100];
+       char source_str[INET_ADDRSTRLEN];
+       char group_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
        pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
        zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
                   __FILE__, __PRETTY_FUNCTION__,
                   source_str, group_str,
-                  new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+                  new_iif->name, input_iface_vif_index);
       }
 
-      del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
+      //del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
     }
 
     /* update iif vif_index */
     old_vif_index = c_oil->oil.mfcc_parent;
     c_oil->oil.mfcc_parent = input_iface_vif_index;
 
-    zlog_debug ("FF");
     /* update kernel multicast forwarding cache (MFC) */
-    if (pim_mroute_add(c_oil))
+    if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__))
       {
-      /* just log warning */
-      struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
-      struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
-      char source_str[100];
-      char group_str[100]; 
-      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-      zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
-               __FILE__, __PRETTY_FUNCTION__,
-               source_str, group_str,
-               old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
-               new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+       if (PIM_DEBUG_MROUTE)
+         {
+           /* just log warning */
+           struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
+           struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+           char source_str[INET_ADDRSTRLEN];
+           char group_str[INET_ADDRSTRLEN];
+           pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+           pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+           zlog_debug("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
+                      __FILE__, __PRETTY_FUNCTION__,
+                      source_str, group_str,
+                      old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
+                      new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+         }
     }
 }
 
@@ -481,13 +536,12 @@ void pim_scan_oil()
   qpim_scan_oil_last = pim_time_monotonic_sec();
   ++qpim_scan_oil_events;
 
-  for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil))
+  for (ALL_LIST_ELEMENTS(pim_channel_oil_list, node, nextnode, c_oil))
     pim_scan_individual_oil (c_oil);
 }
 
 static int on_rpf_cache_refresh(struct thread *t)
 {
-  zassert(t);
   zassert(qpim_rpf_cache_refresher);
 
   qpim_rpf_cache_refresher = 0;
@@ -501,13 +555,16 @@ static int on_rpf_cache_refresh(struct thread *t)
   qpim_rpf_cache_refresh_last = pim_time_monotonic_sec();
   ++qpim_rpf_cache_refresh_events;
 
+  pim_rp_setup ();
   return 0;
 }
 
-static void sched_rpf_cache_refresh()
+void sched_rpf_cache_refresh(void)
 {
   ++qpim_rpf_cache_refresh_requests;
 
+  pim_rpf_set_refresh_time ();
+
   if (qpim_rpf_cache_refresher) {
     /* Refresh timer is already running */
     return;
@@ -575,17 +632,6 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient,
               CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
   }
 
-  if (length < min_len) {
-    zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s",
-             __FILE__, __PRETTY_FUNCTION__,
-             length, min_len,
-             CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
-             CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
-             CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
-             CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
-    return -1;
-  }
-
   /* IPv4 prefix. */
   stream_get(&p.prefix, s, PSIZE(p.prefixlen));
 
@@ -654,6 +700,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient,
 
   sched_rpf_cache_refresh();
 
+  pim_rp_setup ();
   return 0;
 }
 
@@ -662,6 +709,7 @@ pim_zebra_connected (struct zclient *zclient)
 {
   zclient_send_reg_requests (zclient, VRF_DEFAULT);
 }
+
 void pim_zebra_init(char *zebra_sock_path)
 {
   int i;
@@ -719,9 +767,7 @@ void pim_zebra_init(char *zebra_sock_path)
                __PRETTY_FUNCTION__);
   }
 
-  zassert(!qpim_zclient_lookup);
-  qpim_zclient_lookup = zclient_lookup_new();
-  zassert(qpim_zclient_lookup);
+  zclient_lookup_new();
 }
 
 void igmp_anysource_forward_start(struct igmp_group *group)
@@ -754,36 +800,42 @@ void igmp_anysource_forward_stop(struct igmp_group *group)
 
 static int fib_lookup_if_vif_index(struct in_addr addr)
 {
-  struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+  struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
   int num_ifindex;
   int vif_index;
   ifindex_t first_ifindex;
 
-  num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
-                                      PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr,
+  num_ifindex = zclient_lookup_nexthop(nexthop_tab,
+                                      MULTIPATH_NUM, addr,
                                       PIM_NEXTHOP_LOOKUP_MAX);
   if (num_ifindex < 1) {
-    char addr_str[100];
-    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-    zlog_warn("%s %s: could not find nexthop ifindex for address %s",
-             __FILE__, __PRETTY_FUNCTION__,
-             addr_str);
+    if (PIM_DEBUG_ZEBRA)
+      {
+       char addr_str[INET_ADDRSTRLEN];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_debug("%s %s: could not find nexthop ifindex for address %s",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  addr_str);
+      }
     return -1;
   }
   
   first_ifindex = nexthop_tab[0].ifindex;
   
   if (num_ifindex > 1) {
-    char addr_str[100];
-    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-    zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
-              __FILE__, __PRETTY_FUNCTION__,
-              num_ifindex, addr_str, first_ifindex);
+    if (PIM_DEBUG_ZEBRA)
+      {
+       char addr_str[INET_ADDRSTRLEN];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  num_ifindex, addr_str, first_ifindex);
+      }
     /* debug warning only, do not return */
   }
   
   if (PIM_DEBUG_ZEBRA) {
-    char addr_str[100];
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
     zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s",
               __FILE__, __PRETTY_FUNCTION__,
@@ -793,31 +845,17 @@ static int fib_lookup_if_vif_index(struct in_addr addr)
   vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex);
 
   if (vif_index < 0) {
-    char addr_str[100];
-    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-    zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s",
-             __FILE__, __PRETTY_FUNCTION__,
-             vif_index, addr_str);
+    if (PIM_DEBUG_ZEBRA)
+      {
+       char addr_str[INET_ADDRSTRLEN];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_debug("%s %s: low vif_index=%d < 1 nexthop for address %s",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  vif_index, addr_str);
+      }
     return -2;
   }
 
-  zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
-
-  if (vif_index > qpim_mroute_oif_highest_vif_index) {
-    char addr_str[100];
-    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-    zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s",
-             __FILE__, __PRETTY_FUNCTION__,
-             vif_index, qpim_mroute_oif_highest_vif_index, addr_str);
-
-    zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?",
-             __FILE__, __PRETTY_FUNCTION__,
-             ifindex2ifname(vif_index),
-             vif_index);
-
-    return -3;
-  }
-
   return vif_index;
 }
 
@@ -828,17 +866,11 @@ static int del_oif(struct channel_oil *channel_oil,
   struct pim_interface *pim_ifp;
   int old_ttl;
 
-  zassert(channel_oil);
-
   pim_ifp = oif->info;
 
-  zassert(pim_ifp->mroute_vif_index >= 1);
-  zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
-  zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
-
   if (PIM_DEBUG_MROUTE) {
-    char group_str[100]; 
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN]; 
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
     zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
@@ -850,15 +882,18 @@ static int del_oif(struct channel_oil *channel_oil,
   /* Prevent single protocol from unsubscribing same interface from
      channel (S,G) multiple times */
   if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
-    char group_str[100]; 
-    char source_str[100];
-    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-    zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
-             __FILE__, __PRETTY_FUNCTION__,
-             proto_mask, oif->name, pim_ifp->mroute_vif_index,
-             channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
-             source_str, group_str);
+    if (PIM_DEBUG_MROUTE)
+      {
+       char group_str[INET_ADDRSTRLEN]; 
+       char source_str[INET_ADDRSTRLEN];
+       pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+       pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+       zlog_debug("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  proto_mask, oif->name, pim_ifp->mroute_vif_index,
+                  channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+                  source_str, group_str);
+      }
     return -2;
   }
 
@@ -873,15 +908,18 @@ static int del_oif(struct channel_oil *channel_oil,
     /* Check the OIF keeps existing before returning, and only log
        warning otherwise */
     if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
-      char group_str[100]; 
-      char source_str[100];
-      pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-      pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-      zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
-               __FILE__, __PRETTY_FUNCTION__,
-               proto_mask, oif->name, pim_ifp->mroute_vif_index,
-               channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
-               source_str, group_str);
+      if (PIM_DEBUG_MROUTE)
+       {
+         char group_str[INET_ADDRSTRLEN];
+         char source_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+         pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+         zlog_debug("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    proto_mask, oif->name, pim_ifp->mroute_vif_index,
+                    channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+                    source_str, group_str);
+       }
     }
 
     return 0;
@@ -890,22 +928,25 @@ static int del_oif(struct channel_oil *channel_oil,
   old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
 
   if (old_ttl < 1) {
-    char group_str[100]; 
-    char source_str[100];
-    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-    zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
-             __FILE__, __PRETTY_FUNCTION__,
-             oif->name, pim_ifp->mroute_vif_index,
-             source_str, group_str);
+    if (PIM_DEBUG_MROUTE)
+      {
+       char group_str[INET_ADDRSTRLEN];
+       char source_str[INET_ADDRSTRLEN];
+       pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+       pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+       zlog_debug("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  oif->name, pim_ifp->mroute_vif_index,
+                  source_str, group_str);
+      }
     return -3;
   }
 
   channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
 
-  if (pim_mroute_add(channel_oil)) {
-    char group_str[100]; 
-    char source_str[100];
+  if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+    char group_str[INET_ADDRSTRLEN];
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
     zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)",
@@ -920,21 +961,24 @@ static int del_oif(struct channel_oil *channel_oil,
   --channel_oil->oil_size;
 
   if (channel_oil->oil_size < 1) {
-    if (pim_mroute_del(channel_oil)) {
-      /* just log a warning in case of failure */
-      char group_str[100]; 
-      char source_str[100];
-      pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
-      pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-      zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
-               __FILE__, __PRETTY_FUNCTION__,
-               source_str, group_str);
+    if (pim_mroute_del(channel_oil, __PRETTY_FUNCTION__)) {
+      if (PIM_DEBUG_MROUTE)
+       {
+         /* just log a warning in case of failure */
+         char group_str[INET_ADDRSTRLEN];
+         char source_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+         pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+         zlog_debug("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    source_str, group_str);
+       }
     }
   }
 
   if (PIM_DEBUG_MROUTE) {
-    char group_str[100]; 
-    char source_str[100];
+    char group_str[INET_ADDRSTRLEN]; 
+    char source_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
     pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
     zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
@@ -949,16 +993,17 @@ static int del_oif(struct channel_oil *channel_oil,
 void igmp_source_forward_start(struct igmp_source *source)
 {
   struct igmp_group *group;
+  struct prefix_sg sg;
   int result;
 
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = source->source_addr;
+  sg.grp = source->source_group->group_addr;
+
   if (PIM_DEBUG_IGMP_TRACE) {
-    char source_str[100];
-    char group_str[100]; 
-    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
-    zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+    zlog_debug("%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d",
               __PRETTY_FUNCTION__,
-              source_str, group_str,
+              pim_str_sg_dump (&sg),
               source->source_group->group_igmp_sock->fd,
               source->source_group->group_igmp_sock->interface->name,
               IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
@@ -976,16 +1021,19 @@ void igmp_source_forward_start(struct igmp_source *source)
     struct in_addr vif_source;
     struct pim_interface *pim_oif;
 
-    if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr))
+    if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr, sg.grp))
       return;
 
     int input_iface_vif_index = fib_lookup_if_vif_index(vif_source);
     if (input_iface_vif_index < 1) {
-      char source_str[100];
-      pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
-      zlog_warn("%s %s: could not find input interface for source %s",
-               __FILE__, __PRETTY_FUNCTION__,
-               source_str);
+      if (PIM_DEBUG_IGMP_TRACE)
+       {
+         char source_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+         zlog_debug("%s %s: could not find input interface for source %s",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    source_str);
+       }
       return;
     }
 
@@ -996,28 +1044,21 @@ void igmp_source_forward_start(struct igmp_source *source)
     */
     pim_oif = source->source_group->group_igmp_sock->interface->info;
     if (!pim_oif) {
-      zlog_warn("%s: multicast not enabled on oif=%s ?",
-               __PRETTY_FUNCTION__,
-               source->source_group->group_igmp_sock->interface->name);
-      return;
-    }
-    if (pim_oif->mroute_vif_index < 1) {
-      zlog_warn("%s %s: oif=%s vif_index=%d < 1",
-               __FILE__, __PRETTY_FUNCTION__,
-               source->source_group->group_igmp_sock->interface->name,
-               pim_oif->mroute_vif_index);
+      if (PIM_DEBUG_IGMP_TRACE)
+       {
+         zlog_debug("%s: multicast not enabled on oif=%s ?",
+                    __PRETTY_FUNCTION__,
+                    source->source_group->group_igmp_sock->interface->name);
+       }
       return;
     }
+
     if (input_iface_vif_index == pim_oif->mroute_vif_index) {
       /* ignore request for looped MFC entry */
       if (PIM_DEBUG_IGMP_TRACE) {
-       char source_str[100];
-       char group_str[100]; 
-       pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
-       pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
-       zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d",
+       zlog_debug("%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d",
                   __PRETTY_FUNCTION__,
-                  source_str, group_str,
+                  pim_str_sg_dump (&sg),
                   source->source_group->group_igmp_sock->fd,
                   source->source_group->group_igmp_sock->interface->name,
                   input_iface_vif_index);
@@ -1025,17 +1066,15 @@ void igmp_source_forward_start(struct igmp_source *source)
       return;
     }
 
-    source->source_channel_oil = pim_channel_oil_add(group->group_addr,
-                                                    source->source_addr,
+    source->source_channel_oil = pim_channel_oil_add(&sg,
                                                     input_iface_vif_index);
     if (!source->source_channel_oil) {
-      char group_str[100]; 
-      char source_str[100];
-      pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
-      pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
-      zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
-               __FILE__, __PRETTY_FUNCTION__,
-               source_str, group_str);
+      if (PIM_DEBUG_IGMP_TRACE)
+       {
+         zlog_debug("%s %s: could not create OIL for channel (S,G)=%s",
+                   __FILE__, __PRETTY_FUNCTION__,
+                   pim_str_sg_dump (&sg));
+       }
       return;
     }
   }
@@ -1044,8 +1083,11 @@ void igmp_source_forward_start(struct igmp_source *source)
                               group->group_igmp_sock->interface,
                               PIM_OIF_FLAG_PROTO_IGMP);
   if (result) {
-    zlog_warn("%s: add_oif() failed with return=%d",
-             __func__, result);
+    if (PIM_DEBUG_MROUTE)
+      {
+        zlog_warn("%s: add_oif() failed with return=%d",
+                  __func__, result);
+      }
     return;
   }
 
@@ -1053,8 +1095,7 @@ void igmp_source_forward_start(struct igmp_source *source)
     Feed IGMPv3-gathered local membership information into PIM
     per-interface (S,G) state.
    */
-  pim_ifchannel_local_membership_add(group->group_igmp_sock->interface,
-                                    source->source_addr, group->group_addr);
+  pim_ifchannel_local_membership_add(group->group_igmp_sock->interface, &sg);
 
   IGMP_SOURCE_DO_FORWARDING(source->source_flags);
 }
@@ -1066,16 +1107,17 @@ void igmp_source_forward_start(struct igmp_source *source)
 void igmp_source_forward_stop(struct igmp_source *source)
 {
   struct igmp_group *group;
+  struct prefix_sg sg;
   int result;
 
+  memset (&sg, 0, sizeof (struct prefix_sg));
+  sg.src = source->source_addr;
+  sg.grp = source->source_group->group_addr;
+
   if (PIM_DEBUG_IGMP_TRACE) {
-    char source_str[100];
-    char group_str[100]; 
-    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
-    zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+    zlog_debug("%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d",
               __PRETTY_FUNCTION__,
-              source_str, group_str,
+              pim_str_sg_dump (&sg),
               source->source_group->group_igmp_sock->fd,
               source->source_group->group_igmp_sock->interface->name,
               IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
@@ -1104,8 +1146,9 @@ void igmp_source_forward_stop(struct igmp_source *source)
                   group->group_igmp_sock->interface,
                   PIM_OIF_FLAG_PROTO_IGMP);
   if (result) {
-    zlog_warn("%s: del_oif() failed with return=%d",
-             __func__, result);
+    if (PIM_DEBUG_IGMP_TRACE)
+      zlog_debug("%s: del_oif() failed with return=%d",
+                __func__, result);
     return;
   }
 
@@ -1114,7 +1157,7 @@ void igmp_source_forward_stop(struct igmp_source *source)
     per-interface (S,G) state.
    */
   pim_ifchannel_local_membership_del(group->group_igmp_sock->interface,
-                                    source->source_addr, group->group_addr);
+                                    &sg);
 
   IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
 }
@@ -1124,12 +1167,12 @@ void pim_forward_start(struct pim_ifchannel *ch)
   struct pim_upstream *up = ch->upstream;
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char source_str[100];
-    char group_str[100]; 
-    char upstream_str[100];
+    char source_str[INET_ADDRSTRLEN];
+    char group_str[INET_ADDRSTRLEN]; 
+    char upstream_str[INET_ADDRSTRLEN];
 
-    pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", ch->sg.src, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", ch->sg.grp, group_str, sizeof(group_str));
     pim_inet4_dump("<upstream?>", up->upstream_addr, upstream_str, sizeof(upstream_str));
     zlog_debug("%s: (S,G)=(%s,%s) oif=%s(%s)",
               __PRETTY_FUNCTION__,
@@ -1139,24 +1182,24 @@ void pim_forward_start(struct pim_ifchannel *ch)
   if (!up->channel_oil) {
     int input_iface_vif_index = fib_lookup_if_vif_index(up->upstream_addr);
     if (input_iface_vif_index < 1) {
-      char source_str[100];
-      pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
-      zlog_warn("%s %s: could not find input interface for source %s",
-               __FILE__, __PRETTY_FUNCTION__,
-               source_str);
+      if (PIM_DEBUG_PIM_TRACE)
+       {
+         char source_str[INET_ADDRSTRLEN];
+         pim_inet4_dump("<source?>", up->sg.src, source_str, sizeof(source_str));
+         zlog_debug("%s %s: could not find input interface for source %s",
+                    __FILE__, __PRETTY_FUNCTION__,
+                    source_str);
+       }
       return;
     }
 
-    up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr,
+    up->channel_oil = pim_channel_oil_add(&up->sg,
                                          input_iface_vif_index);
     if (!up->channel_oil) {
-      char group_str[100]; 
-      char source_str[100];
-      pim_inet4_dump("<group?>", up->group_addr, group_str, sizeof(group_str));
-      pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
-      zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
-               __FILE__, __PRETTY_FUNCTION__,
-               source_str, group_str);
+      if (PIM_DEBUG_PIM_TRACE)
+       zlog_debug("%s %s: could not create OIL for channel (S,G)=%s",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  up->sg_str);
       return;
     }
   }
@@ -1171,23 +1214,16 @@ void pim_forward_stop(struct pim_ifchannel *ch)
   struct pim_upstream *up = ch->upstream;
 
   if (PIM_DEBUG_PIM_TRACE) {
-    char source_str[100];
-    char group_str[100]; 
-    pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
-    zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
+    zlog_debug("%s: (S,G)=%s oif=%s",
               __PRETTY_FUNCTION__,
-              source_str, group_str, ch->interface->name);
+              ch->sg_str, ch->interface->name);
   }
 
   if (!up->channel_oil) {
-    char source_str[100];
-    char group_str[100]; 
-    pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
-    pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
-    zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL",
-              __PRETTY_FUNCTION__,
-              source_str, group_str, ch->interface->name);
+    if (PIM_DEBUG_PIM_TRACE)
+      zlog_debug("%s: (S,G)=%s oif=%s missing channel OIL",
+                __PRETTY_FUNCTION__,
+                ch->sg_str, ch->interface->name);
 
     return;
   }
index 257c9b896f584314322ff1589a4de488170d3721..0c302efbd51cd1af36d2d0b7e94ea214d8785fbe 100644 (file)
@@ -38,4 +38,5 @@ void igmp_source_forward_stop(struct igmp_source *source);
 void pim_forward_start(struct pim_ifchannel *ch);
 void pim_forward_stop(struct pim_ifchannel *ch);
 
+void sched_rpf_cache_refresh(void);
 #endif /* PIM_ZEBRA_H */
index af561a0b62b4727a7796510b450675aac0413385..61e3e27261fa438b5543f70dfcea149fb9edd3bd 100644 (file)
 #include "stream.h"
 #include "network.h"
 #include "thread.h"
+#include "prefix.h"
+#include "vty.h"
 
 #include "pimd.h"
+#include "pim_iface.h"
 #include "pim_pim.h"
 #include "pim_str.h"
+#include "pim_oil.h"
 #include "pim_zlookup.h"
 
-extern int zclient_debug;
+static struct zclient *zlookup = NULL;
 
 static void zclient_lookup_sched(struct zclient *zlookup, int delay);
 
@@ -116,15 +120,14 @@ static void zclient_lookup_failed(struct zclient *zlookup)
   zclient_lookup_reconnect(zlookup);
 }
 
-struct zclient *zclient_lookup_new()
+void
+zclient_lookup_new (void)
 {
-  struct zclient *zlookup;
-
   zlookup = zclient_new (master);
   if (!zlookup) {
     zlog_err("%s: zclient_new() failure",
             __PRETTY_FUNCTION__);
-    return 0;
+    return;
   }
 
   zlookup->sock = -1;
@@ -137,7 +140,6 @@ struct zclient *zclient_lookup_new()
   zlog_notice("%s: zclient lookup socket initialized",
              __PRETTY_FUNCTION__);
 
-  return zlookup;
 }
 
 static int zclient_read_nexthop(struct zclient *zlookup,
@@ -159,8 +161,8 @@ static int zclient_read_nexthop(struct zclient *zlookup,
   int nexthop_num;
   int i, err;
 
-  if (PIM_DEBUG_ZEBRA) {
-    char addr_str[100];
+  if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
     zlog_debug("%s: addr=%s", 
               __PRETTY_FUNCTION__,
@@ -192,8 +194,8 @@ static int zclient_read_nexthop(struct zclient *zlookup,
   raddr.s_addr = stream_get_ipv4(s);
 
   if (raddr.s_addr != addr.s_addr) {
-    char addr_str[100];
-    char raddr_str[100];
+    char addr_str[INET_ADDRSTRLEN];
+    char raddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
     pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
     zlog_warn("%s: address mismatch: addr=%s raddr=%s", 
@@ -212,77 +214,52 @@ static int zclient_read_nexthop(struct zclient *zlookup,
     return -6;
   }
 
-  length -= MIN_LEN;
-
   for (i = 0; i < nexthop_num; ++i) {
     enum nexthop_types_t nexthop_type;
+    struct pim_neighbor *nbr;
 
-    if (length < 1) {
-      zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
-              __func__, zlookup->sock, length);
-      return -7;
-    }
-    
     nexthop_type = stream_getc(s);
-    --length;
-
+    if (num_ifindex >= tab_size) {
+      char addr_str[INET_ADDRSTRLEN];
+      pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+      zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
+               __FILE__, __PRETTY_FUNCTION__,
+               (num_ifindex + 1), tab_size, addr_str);
+      return num_ifindex;
+    }
     switch (nexthop_type) {
     case NEXTHOP_TYPE_IFINDEX:
     case NEXTHOP_TYPE_IPV4_IFINDEX:
-      if (num_ifindex >= tab_size) {
-       char addr_str[100];
-       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-       zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
-                __FILE__, __PRETTY_FUNCTION__,
-                (num_ifindex + 1), tab_size, addr_str);
-       return num_ifindex;
-      }
-      if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX) {
-       if (length < 4) {
-         zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
-                  __func__, zlookup->sock, length);
-         return -8;
-       }
-       nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
-       length -= 4;
+    case NEXTHOP_TYPE_IPV4:
+      nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET;
+      if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX ||
+         nexthop_type == NEXTHOP_TYPE_IPV4) {
+       nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = stream_get_ipv4(s);
       }
       else {
-       nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+       nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
       }
       nexthop_tab[num_ifindex].ifindex           = stream_getl(s);
       nexthop_tab[num_ifindex].protocol_distance = distance;
       nexthop_tab[num_ifindex].route_metric      = metric;
       ++num_ifindex;
       break;
-    case NEXTHOP_TYPE_IPV4:
-      if (num_ifindex >= tab_size) {
-       char addr_str[100];
-       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-       zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
-                __FILE__, __PRETTY_FUNCTION__,
-                (num_ifindex + 1), tab_size, addr_str);
-       return num_ifindex;
-      }
-      nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
-      length -= 4;
-      nexthop_tab[num_ifindex].ifindex             = 0;
-      nexthop_tab[num_ifindex].protocol_distance   = distance;
-      nexthop_tab[num_ifindex].route_metric        = metric;
-      if (PIM_DEBUG_ZEBRA) {
-       char addr_str[100];
-       char nexthop_str[100];
-       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-       pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
-       zlog_debug("%s %s: zebra returned recursive nexthop %s for address %s",
-                  __FILE__, __PRETTY_FUNCTION__,
-                  nexthop_str, addr_str);
-      }
-      ++num_ifindex;
+    case NEXTHOP_TYPE_IPV6_IFINDEX:
+      nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET6;
+      stream_get (&nexthop_tab[num_ifindex].nexthop_addr.u.prefix6, s, 16);
+      nexthop_tab[num_ifindex].ifindex = stream_getl (s);
+      nbr = pim_neighbor_find_if (if_lookup_by_index_vrf (nexthop_tab[num_ifindex].ifindex, VRF_DEFAULT));
+      if (nbr)
+        {
+          nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET;
+          nexthop_tab[num_ifindex].nexthop_addr.u.prefix4 = nbr->source_addr;
+        }
+        ++num_ifindex;
       break;
     default:
       /* do nothing */
       {
-       char addr_str[100];
+       char addr_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
        zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
                 __FILE__, __PRETTY_FUNCTION__,
@@ -295,16 +272,16 @@ static int zclient_read_nexthop(struct zclient *zlookup,
   return num_ifindex;
 }
 
-static int zclient_lookup_nexthop_once(struct zclient *zlookup,
-                                      struct pim_zlookup_nexthop nexthop_tab[],
-                                      const int tab_size,
-                                      struct in_addr addr)
+static int
+zclient_lookup_nexthop_once (struct pim_zlookup_nexthop nexthop_tab[],
+                            const int tab_size,
+                            struct in_addr addr)
 {
   struct stream *s;
   int ret;
 
-  if (PIM_DEBUG_ZEBRA) {
-    char addr_str[100];
+  if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
     zlog_debug("%s: addr=%s", 
               __PRETTY_FUNCTION__,
@@ -327,8 +304,8 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup,
   
   ret = writen(zlookup->sock, s->data, stream_get_endp(s));
   if (ret < 0) {
-    zlog_err("%s %s: writen() failure writing to zclient lookup socket",
-            __FILE__, __PRETTY_FUNCTION__);
+    zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket",
+            __FILE__, __PRETTY_FUNCTION__, errno);
     zclient_lookup_failed(zlookup);
     return -2;
   }
@@ -343,26 +320,28 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup,
                              tab_size, addr);
 }
 
-int zclient_lookup_nexthop(struct zclient *zlookup,
-                          struct pim_zlookup_nexthop nexthop_tab[],
-                          const int tab_size,
-                          struct in_addr addr,
-                          int max_lookup)
+int
+zclient_lookup_nexthop (struct pim_zlookup_nexthop nexthop_tab[],
+                       const int tab_size,
+                       struct in_addr addr,
+                       int max_lookup)
 {
   int lookup;
   uint32_t route_metric = 0xFFFFFFFF;
   uint8_t  protocol_distance = 0xFF;
 
+  qpim_nexthop_lookups++;
+
   for (lookup = 0; lookup < max_lookup; ++lookup) {
     int num_ifindex;
     int first_ifindex;
-    struct in_addr nexthop_addr;
+    struct prefix nexthop_addr;
 
-    num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
-                                             PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
+    num_ifindex = zclient_lookup_nexthop_once(nexthop_tab,
+                                             tab_size, addr);
     if (num_ifindex < 1) {
       if (PIM_DEBUG_ZEBRA) {
-       char addr_str[100];
+       char addr_str[INET_ADDRSTRLEN];
        pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
        zlog_debug("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
                   __FILE__, __PRETTY_FUNCTION__,
@@ -378,9 +357,13 @@ int zclient_lookup_nexthop(struct zclient *zlookup,
     }
     
     /*
-      FIXME: Non-recursive nexthop ensured only for first ifindex.
-      However, recursive route lookup should really be fixed in zebra daemon.
-      See also TODO T24.
+     * FIXME: Non-recursive nexthop ensured only for first ifindex.
+     * However, recursive route lookup should really be fixed in zebra daemon.
+     * See also TODO T24.
+     *
+     * So Zebra for NEXTHOP_TYPE_IPV4 returns the ifindex now since
+     * it was being stored.  This Doesn't solve all cases of
+     * recursive lookup but for the most common types it does.
      */
     first_ifindex = nexthop_tab[0].ifindex;
     nexthop_addr = nexthop_tab[0].nexthop_addr;
@@ -390,7 +373,7 @@ int zclient_lookup_nexthop(struct zclient *zlookup,
       if (lookup > 0) {
        /* Report non-recursive success after first lookup */
        if (PIM_DEBUG_ZEBRA) {
-         char addr_str[100];
+         char addr_str[INET_ADDRSTRLEN];
          pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
          zlog_debug("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d",
                     __FILE__, __PRETTY_FUNCTION__,
@@ -400,7 +383,7 @@ int zclient_lookup_nexthop(struct zclient *zlookup,
        }
 
        /* use last address as nexthop address */
-       nexthop_tab[0].nexthop_addr = addr;
+       nexthop_tab[0].nexthop_addr.u.prefix4 = addr;
 
        /* report original route metric/distance */
        nexthop_tab[0].route_metric = route_metric;
@@ -411,10 +394,10 @@ int zclient_lookup_nexthop(struct zclient *zlookup,
     }
 
     if (PIM_DEBUG_ZEBRA) {
-      char addr_str[100];
-      char nexthop_str[100];
+      char addr_str[INET_ADDRSTRLEN];
+      char nexthop_str[PREFIX_STRLEN];
       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
-      pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
+      pim_addr_dump("<nexthop?>", &nexthop_addr, nexthop_str, sizeof(nexthop_str));
       zlog_debug("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d",
                __FILE__, __PRETTY_FUNCTION__,
                lookup, max_lookup, nexthop_str, addr_str,
@@ -422,12 +405,12 @@ int zclient_lookup_nexthop(struct zclient *zlookup,
                nexthop_tab[0].route_metric);
     }
 
-    addr = nexthop_addr; /* use nexthop addr for recursive lookup */
+    addr = nexthop_addr.u.prefix4; /* use nexthop addr for recursive lookup */
 
   } /* for (max_lookup) */
 
   if (PIM_DEBUG_ZEBRA) {
-    char addr_str[100];
+    char addr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
     zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
              __FILE__, __PRETTY_FUNCTION__,
@@ -436,3 +419,100 @@ int zclient_lookup_nexthop(struct zclient *zlookup,
 
   return -2;
 }
+
+void
+pim_zlookup_show_ip_multicast (struct vty *vty)
+{
+  vty_out(vty, "Zclient lookup socket: ");
+  if (zlookup) {
+    vty_out(vty, "%d failures=%d%s", zlookup->sock,
+            zlookup->fail, VTY_NEWLINE);
+  }
+  else {
+    vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
+  }
+}
+
+int
+pim_zlookup_sg_statistics (struct channel_oil *c_oil)
+{
+  struct stream *s = zlookup->obuf;
+  uint16_t command = 0;
+  unsigned long long lastused;
+  struct prefix_sg sg;
+  int count = 0;
+  int ret;
+  struct interface *ifp = pim_if_find_by_vif_index (c_oil->oil.mfcc_parent);
+
+  if (PIM_DEBUG_ZEBRA)
+    {
+      struct prefix_sg more;
+
+      more.src = c_oil->oil.mfcc_origin;
+      more.grp = c_oil->oil.mfcc_mcastgrp;
+      zlog_debug ("Sending Request for New Channel Oil Information(%s)", pim_str_sg_dump (&more));
+    }
+
+  if (!ifp)
+    return -1;
+
+  stream_reset (s);
+  zclient_create_header (s, ZEBRA_IPMR_ROUTE_STATS, VRF_DEFAULT);
+  stream_put_in_addr (s, &c_oil->oil.mfcc_origin);
+  stream_put_in_addr (s, &c_oil->oil.mfcc_mcastgrp);
+  stream_putl (s,  ifp->ifindex);
+  stream_putw_at(s, 0, stream_get_endp(s));
+
+  count = stream_get_endp (s);
+  ret = writen (zlookup->sock, s->data, count);
+  if (ret <= 0)
+    {
+      zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket",
+               __FILE__, __PRETTY_FUNCTION__, errno);
+      return -1;
+    }
+
+  s = zlookup->ibuf;
+
+  while (command != ZEBRA_IPMR_ROUTE_STATS)
+    {
+      int err;
+      uint16_t length = 0;
+      vrf_id_t vrf_id;
+      u_char marker;
+      u_char version;
+
+      stream_reset (s);
+      err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
+                                &vrf_id, &command);
+      if (err < 0)
+        {
+          zlog_err ("%s %s: zclient_read_header() failed",
+                 __FILE__, __PRETTY_FUNCTION__);
+        zclient_lookup_failed(zlookup);
+        return -1;
+        }
+    }
+
+  sg.src.s_addr = stream_get_ipv4 (s);
+  sg.grp.s_addr = stream_get_ipv4 (s);
+  if (sg.src.s_addr != c_oil->oil.mfcc_origin.s_addr ||
+      sg.grp.s_addr != c_oil->oil.mfcc_mcastgrp.s_addr)
+    {
+       zlog_err ("%s: Received wrong %s information",
+                __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
+       zclient_lookup_failed (zlookup);
+       return -3;
+    }
+
+  stream_get (&lastused, s, sizeof (lastused));
+  ret = stream_getl (s);
+
+  if (PIM_DEBUG_ZEBRA)
+    zlog_debug ("Received %lld for %s success: %d", lastused, pim_str_sg_dump (&sg), ret);
+
+  c_oil->cc.lastused = lastused;
+
+  return 0;
+
+}
index dbce92647bb6bb8ee5257af77664292156f37d7a..d8e7ff9e0d252b0955c184ea24e52dc1233c96a0 100644 (file)
 #define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */
 
 struct pim_zlookup_nexthop {
-  struct in_addr nexthop_addr;
+  struct prefix  nexthop_addr;
   ifindex_t      ifindex;
   uint32_t       route_metric;
   uint8_t        protocol_distance;
 };
 
-struct zclient *zclient_lookup_new(void);
+void zclient_lookup_new (void);
 
-int zclient_lookup_nexthop(struct zclient *zlookup,
-                          struct pim_zlookup_nexthop nexthop_tab[],
+int zclient_lookup_nexthop(struct pim_zlookup_nexthop nexthop_tab[],
                           const int tab_size,
                           struct in_addr addr,
                           int max_lookup);
 
+void pim_zlookup_show_ip_multicast (struct vty *vty);
+
+int pim_zlookup_sg_statistics (struct channel_oil *c_oil);
 #endif /* PIM_ZLOOKUP_H */
index 15e52afc1dc29be017b4734fffcc808e99fe9189..1627c4048dd218920c2a87382735c32d8950028c 100644 (file)
@@ -23,6 +23,9 @@
 #include "log.h"
 #include "memory.h"
 #include "if.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
 
 #include "pimd.h"
 #include "pim_cmd.h"
@@ -35,6 +38,7 @@
 #include "pim_rpf.h"
 #include "pim_ssmpingd.h"
 #include "pim_static.h"
+#include "pim_rp.h"
 
 const char *const PIM_ALL_SYSTEMS      = MCAST_ALL_SYSTEMS;
 const char *const PIM_ALL_ROUTERS      = MCAST_ALL_ROUTERS;
@@ -45,21 +49,17 @@ struct thread_master     *master = NULL;
 uint32_t                  qpim_debugs = 0;
 int                       qpim_mroute_socket_fd = -1;
 int64_t                   qpim_mroute_socket_creation = 0; /* timestamp of creation */
-struct thread            *qpim_mroute_socket_reader = 0;
 int                       qpim_mroute_oif_highest_vif_index = -1;
-struct list              *qpim_channel_oil_list = 0;
 int                       qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */
-struct list              *qpim_upstream_list = 0;
-struct zclient           *qpim_zclient_update = 0;
-struct zclient           *qpim_zclient_lookup = 0;
+struct zclient           *qpim_zclient_update = NULL;
 struct pim_assert_metric  qpim_infinite_assert_metric;
-long                      qpim_rpf_cache_refresh_delay_msec = 10000;
-struct thread            *qpim_rpf_cache_refresher = 0;
+long                      qpim_rpf_cache_refresh_delay_msec = 50;
+struct thread            *qpim_rpf_cache_refresher = NULL;
 int64_t                   qpim_rpf_cache_refresh_requests = 0;
 int64_t                   qpim_rpf_cache_refresh_events = 0;
 int64_t                   qpim_rpf_cache_refresh_last =  0;
 struct in_addr            qpim_inaddr_any;
-struct list              *qpim_ssmpingd_list = 0;
+struct list              *qpim_ssmpingd_list = NULL;
 struct in_addr            qpim_ssmpingd_group_addr;
 int64_t                   qpim_scan_oil_events = 0;
 int64_t                   qpim_scan_oil_last = 0;
@@ -67,8 +67,11 @@ int64_t                   qpim_mroute_add_events = 0;
 int64_t                   qpim_mroute_add_last = 0;
 int64_t                   qpim_mroute_del_events = 0;
 int64_t                   qpim_mroute_del_last = 0;
-struct list              *qpim_static_route_list = 0;
-struct pim_rpf            qpim_rp = { .rpf_addr.s_addr = INADDR_NONE };
+struct list              *qpim_static_route_list = NULL;
+unsigned int              qpim_keep_alive_time = PIM_KEEPALIVE_PERIOD;
+signed int                qpim_rp_keep_alive_time = 0;
+int64_t                   qpim_nexthop_lookups = 0;
+int                       qpim_packet_process = PIM_DEFAULT_PACKET_PROCESS;
 
 int32_t qpim_register_suppress_time = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT;
 int32_t qpim_register_probe_time = PIM_REGISTER_PROBE_TIME_DEFAULT;
@@ -77,22 +80,28 @@ static void pim_free()
 {
   pim_ssmpingd_destroy();
 
-  if (qpim_channel_oil_list)
-    list_free(qpim_channel_oil_list);
+  pim_oil_terminate ();
 
-  if (qpim_upstream_list)
-    list_free(qpim_upstream_list);
+  pim_upstream_terminate ();
 
   if (qpim_static_route_list)
      list_free(qpim_static_route_list);
 
   pim_route_map_terminate();
+
+  pim_if_terminate ();
+  pim_rp_free ();
+  pim_route_map_terminate();
 }
 
 void pim_init()
 {
   srandom(time(NULL));
 
+  qpim_rp_keep_alive_time = PIM_RP_KEEPALIVE_PERIOD;
+
+  pim_rp_init ();
+
   if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) {
     zlog_err("%s %s: could not solve %s to group address: errno=%d: %s",
             __FILE__, __PRETTY_FUNCTION__,
@@ -101,22 +110,9 @@ void pim_init()
     return;
   }
 
-  qpim_channel_oil_list = list_new();
-  if (!qpim_channel_oil_list) {
-    zlog_err("%s %s: failure: channel_oil_list=list_new()",
-            __FILE__, __PRETTY_FUNCTION__);
-    return;
-  }
-  qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
+  pim_oil_init ();
 
-  qpim_upstream_list = list_new();
-  if (!qpim_upstream_list) {
-    zlog_err("%s %s: failure: upstream_list=list_new()",
-            __FILE__, __PRETTY_FUNCTION__);
-    pim_free();
-    return;
-  }
-  qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
+  pim_upstream_init ();
 
   qpim_static_route_list = list_new();
   if (!qpim_static_route_list) {
@@ -129,9 +125,6 @@ void pim_init()
   qpim_mroute_socket_fd = -1; /* mark mroute as disabled */
   qpim_mroute_oif_highest_vif_index = -1;
 
-  zassert(!qpim_debugs);
-  zassert(!PIM_MROUTE_IS_ENABLED);
-
   qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY;
 
   /*
index 2230a6ce9185b7e8a0c91155499f8e82f1e31bab..20cf3c2dd2ff21ed6ab272e0bb301555c7d42714 100644 (file)
 
 #include <stdint.h>
 
+#include "pim_str.h"
 #include "pim_memory.h"
 #include "pim_assert.h"
 
 #define PIMD_PROGNAME       "pimd"
 #define PIMD_DEFAULT_CONFIG "pimd.conf"
 #define PIMD_VTY_PORT       2611
-#define PIMD_BUG_ADDRESS    "https://github.com/udhos/qpimd"
 
 #define PIM_IP_HEADER_MIN_LEN         (20)
 #define PIM_IP_HEADER_MAX_LEN         (60)
@@ -51,6 +51,8 @@
 #define PIM_INADDR_IS_ANY(addr) (addr).s_addr == PIM_NET_INADDR_ANY
 #define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */
 
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
 #define PIM_MASK_PIM_EVENTS          (1 << 0)
 #define PIM_MASK_PIM_EVENTS_DETAIL   (1 << 1)
 #define PIM_MASK_PIM_PACKETS         (1 << 2)
 #define PIM_MASK_ZEBRA               (1 << 11)
 #define PIM_MASK_SSMPINGD            (1 << 12)
 #define PIM_MASK_MROUTE              (1 << 13)
-#define PIM_MASK_PIM_HELLO           (1 << 14)
-#define PIM_MASK_PIM_J_P             (1 << 15)
-#define PIM_MASK_STATIC              (1 << 16)
+#define PIM_MASK_MROUTE_DETAIL       (1 << 14)
+#define PIM_MASK_PIM_HELLO           (1 << 15)
+#define PIM_MASK_PIM_J_P             (1 << 16)
+#define PIM_MASK_STATIC              (1 << 17)
+#define PIM_MASK_PIM_REG             (1 << 18)
+#define PIM_MASK_MSDP_EVENTS         (1 << 19)
+#define PIM_MASK_MSDP_PACKETS        (1 << 20)
+#define PIM_MASK_MSDP_INTERNAL       (1 << 21)
+
+
+/* PIM error codes */
+#define PIM_SUCCESS                0
+#define PIM_MALLOC_FAIL           -1
+#define PIM_GROUP_BAD_ADDRESS     -2
+#define PIM_GROUP_OVERLAP         -3
+#define PIM_GROUP_PFXLIST_OVERLAP -4
+#define PIM_RP_BAD_ADDRESS        -5
+#define PIM_RP_NO_PATH            -6
+#define PIM_RP_NOT_FOUND          -7
+#define PIM_RP_PFXLIST_IN_USE     -8
+#define PIM_IFACE_NOT_FOUND       -9
+#define PIM_UPDATE_SOURCE_DUP     -10
 
 const char *const PIM_ALL_SYSTEMS;
 const char *const PIM_ALL_ROUTERS;
@@ -78,14 +99,10 @@ extern struct thread_master     *master;
 uint32_t                  qpim_debugs;
 int                       qpim_mroute_socket_fd;
 int64_t                   qpim_mroute_socket_creation; /* timestamp of creation */
-struct thread            *qpim_mroute_socket_reader;
 int                       qpim_mroute_oif_highest_vif_index;
-struct list              *qpim_channel_oil_list; /* list of struct channel_oil */
 struct in_addr            qpim_all_pim_routers_addr;
 int                       qpim_t_periodic; /* Period between Join/Prune Messages */
-struct list              *qpim_upstream_list; /* list of struct pim_upstream */
 struct zclient           *qpim_zclient_update;
-struct zclient           *qpim_zclient_lookup;
 struct pim_assert_metric  qpim_infinite_assert_metric;
 long                      qpim_rpf_cache_refresh_delay_msec;
 struct thread            *qpim_rpf_cache_refresher;
@@ -101,8 +118,12 @@ int64_t                   qpim_mroute_add_events;
 int64_t                   qpim_mroute_add_last;
 int64_t                   qpim_mroute_del_events;
 int64_t                   qpim_mroute_del_last;
+int64_t                   qpim_nexthop_lookups;
 struct list              *qpim_static_route_list; /* list of routes added statically */
-struct pim_rpf            qpim_rp;
+extern unsigned int       qpim_keep_alive_time;
+extern signed int         qpim_rp_keep_alive_time;
+extern int                qpim_packet_process;
+#define PIM_DEFAULT_PACKET_PROCESS 3
 
 #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2)
 
@@ -132,12 +153,17 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DEBUG_ZEBRA               (qpim_debugs & PIM_MASK_ZEBRA)
 #define PIM_DEBUG_SSMPINGD            (qpim_debugs & PIM_MASK_SSMPINGD)
 #define PIM_DEBUG_MROUTE              (qpim_debugs & PIM_MASK_MROUTE)
+#define PIM_DEBUG_MROUTE_DETAIL       (qpim_debugs & PIM_MASK_MROUTE_DETAIL)
 #define PIM_DEBUG_PIM_HELLO           (qpim_debugs & PIM_MASK_PIM_HELLO)
 #define PIM_DEBUG_PIM_J_P             (qpim_debugs & PIM_MASK_PIM_J_P)
+#define PIM_DEBUG_PIM_REG             (qpim_debugs & PIM_MASK_PIM_REG)
 #define PIM_DEBUG_STATIC              (qpim_debugs & PIM_MASK_STATIC)
+#define PIM_DEBUG_MSDP_EVENTS         (qpim_debugs & PIM_MASK_MSDP_EVENTS)
+#define PIM_DEBUG_MSDP_PACKETS        (qpim_debugs & PIM_MASK_MSDP_PACKETS)
+#define PIM_DEBUG_MSDP_INTERNAL       (qpim_debugs & PIM_MASK_MSDP_INTERNAL)
 
-#define PIM_DEBUG_EVENTS       (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS))
-#define PIM_DEBUG_PACKETS      (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS))
+#define PIM_DEBUG_EVENTS       (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
+#define PIM_DEBUG_PACKETS      (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
 #define PIM_DEBUG_TRACE        (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE))
 
 #define PIM_DO_DEBUG_PIM_EVENTS          (qpim_debugs |= PIM_MASK_PIM_EVENTS)
@@ -152,9 +178,14 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DO_DEBUG_ZEBRA               (qpim_debugs |= PIM_MASK_ZEBRA)
 #define PIM_DO_DEBUG_SSMPINGD            (qpim_debugs |= PIM_MASK_SSMPINGD)
 #define PIM_DO_DEBUG_MROUTE              (qpim_debugs |= PIM_MASK_MROUTE)
+#define PIM_DO_DEBUG_MROUTE_DETAIL       (qpim_debugs |= PIM_MASK_MROUTE_DETAIL)
 #define PIM_DO_DEBUG_PIM_HELLO           (qpim_debugs |= PIM_MASK_PIM_HELLO)
 #define PIM_DO_DEBUG_PIM_J_P             (qpim_debugs |= PIM_MASK_PIM_J_P)
+#define PIM_DO_DEBUG_PIM_REG             (qpim_debugs |= PIM_MASK_PIM_REG)
 #define PIM_DO_DEBUG_STATIC              (qpim_debugs |= PIM_MASK_STATIC)
+#define PIM_DO_DEBUG_MSDP_EVENTS         (qpim_debugs |= PIM_MASK_MSDP_EVENTS)
+#define PIM_DO_DEBUG_MSDP_PACKETS        (qpim_debugs |= PIM_MASK_MSDP_PACKETS)
+#define PIM_DO_DEBUG_MSDP_INTERNAL       (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
 
 #define PIM_DONT_DEBUG_PIM_EVENTS          (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
 #define PIM_DONT_DEBUG_PIM_PACKETS         (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
@@ -168,9 +199,14 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DONT_DEBUG_ZEBRA               (qpim_debugs &= ~PIM_MASK_ZEBRA)
 #define PIM_DONT_DEBUG_SSMPINGD            (qpim_debugs &= ~PIM_MASK_SSMPINGD)
 #define PIM_DONT_DEBUG_MROUTE              (qpim_debugs &= ~PIM_MASK_MROUTE)
+#define PIM_DONT_DEBUG_MROUTE_DETAIL       (qpim_debugs &= ~PIM_MASK_MROUTE_DETAIL)
 #define PIM_DONT_DEBUG_PIM_HELLO           (qpim_debugs &= ~PIM_MASK_PIM_HELLO)
 #define PIM_DONT_DEBUG_PIM_J_P             (qpim_debugs &= ~PIM_MASK_PIM_J_P)
+#define PIM_DONT_DEBUG_PIM_REG             (qpim_debugs &= ~PIM_MASK_PIM_REG)
 #define PIM_DONT_DEBUG_STATIC              (qpim_debugs &= ~PIM_MASK_STATIC)
+#define PIM_DONT_DEBUG_MSDP_EVENTS         (qpim_debugs &= ~PIM_MASK_MSDP_EVENTS)
+#define PIM_DONT_DEBUG_MSDP_PACKETS        (qpim_debugs &= ~PIM_MASK_MSDP_PACKETS)
+#define PIM_DONT_DEBUG_MSDP_INTERNAL       (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
 
 void pim_init(void);
 void pim_terminate(void);
index 29143f362e08a9728b1938cfbe1be4687c2c5fe7..81da867e03f468b1650123720007e5ff9e705e9f 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
index 65927262f22b3119f92ebd7e5b0ea788e8118729..428090d4887c53e47f98e36b50bc65f3273c4569 100644 (file)
@@ -44,7 +44,7 @@ zebra_SOURCES = \
        irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \
        $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \
        zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \
-       $(protobuf_srcs) \
+       $(protobuf_srcs) zebra_mroute.c \
        $(dev_srcs)
 
 testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
@@ -60,7 +60,7 @@ noinst_HEADERS = \
        rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h \
        zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \
        zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \
-       kernel_netlink.h if_netlink.h
+       kernel_netlink.h if_netlink.h zebra_mroute.h
 
 zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS)
 
index 3665ad061d68fafaa98dd2a219eb0a212767111c..660fad65307bfdd27781f5bd7d31b3850174ae12 100644 (file)
@@ -173,6 +173,48 @@ netlink_to_zebra_link_type (unsigned int hwt)
   }
 }
 
+
+//Temporary Assignments to compile on older platforms.
+#ifndef IFLA_BR_MAX
+#define IFLA_BR_MAX   39
+#endif
+
+#ifndef IFLA_VXLAN_ID
+#define IFLA_VXLAN_ID 1
+#endif
+
+#ifndef IFLA_VXLAN_LOCAL
+#define IFLA_VXLAN_LOCAL  4
+#endif
+
+#ifndef IFLA_VXLAN_MAX
+#define IFLA_VXLAN_MAX 26
+#endif
+
+#ifndef IFLA_BRIDGE_MAX
+#define IFLA_BRIDGE_MAX   2
+#endif
+
+#ifndef IFLA_BRIDGE_VLAN_INFO
+#define IFLA_BRIDGE_VLAN_INFO 2
+#endif
+
+#ifndef BRIDGE_VLAN_INFO_PVID
+#define BRIDGE_VLAN_INFO_PVID  (1<<1)
+#endif
+
+#ifndef RTEXT_FILTER_BRVLAN
+#define RTEXT_FILTER_BRVLAN    (1<<1)
+#endif
+
+#ifndef NTF_SELF
+#define NTF_SELF   0x02
+#endif
+
+#ifndef IFLA_BR_VLAN_FILTERING
+#define IFLA_BR_VLAN_FILTERING  7
+#endif
+
 #define parse_rtattr_nested(tb, max, rta) \
           netlink_parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))
 
@@ -433,7 +475,7 @@ netlink_address (int cmd, int family, struct interface *ifp,
     addattr_l (&req.n, sizeof req, IFA_LABEL, ifc->label,
                strlen (ifc->label) + 1);
 
-  return netlink_talk (&req.n, &zns->netlink_cmd, zns);
+  return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns);
 }
 
 int
index 9f9a62f384654bd4bf08fe5ce8405539cd6b2779..02da17af63a56a6724c1f9862046c2c4858ccb58 100644 (file)
@@ -91,15 +91,31 @@ static const struct message rtproto_str[] = {
 #ifdef RTPROT_BIRD
   {RTPROT_BIRD,     "BIRD"},
 #endif /* RTPROT_BIRD */
+  {RTPROT_MROUTED,  "mroute"},
   {0,               NULL}
 };
 
+static const struct message family_str[] = {
+  {AF_INET,           "ipv4"},
+  {AF_INET6,          "ipv6"},
+  {AF_BRIDGE,         "bridge"},
+  {RTNL_FAMILY_IPMR,  "ipv4MR"},
+  {RTNL_FAMILY_IP6MR, "ipv6MR"},
+  {0,                 NULL},
+};
+
+static const struct message rttype_str[] = {
+  {RTN_UNICAST,   "unicast"},
+  {RTN_MULTICAST, "multicast"},
+  {0,             NULL},
+};
+
 extern struct thread_master *master;
 extern u_int32_t nl_rcvbufsize;
 
 extern struct zebra_privs_t zserv_privs;
 
-static int
+int
 netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h,
     ns_id_t ns_id)
 {
@@ -321,7 +337,12 @@ addattr_l (struct nlmsghdr *n, unsigned int maxlen, int type,
   rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
   rta->rta_type = type;
   rta->rta_len = len;
-  memcpy (RTA_DATA (rta), data, alen);
+
+  if (data)
+    memcpy (RTA_DATA (rta), data, alen);
+  else
+    assert (len == 0);
+
   n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len);
 
   return 0;
@@ -342,7 +363,12 @@ rta_addattr_l (struct rtattr *rta, unsigned int maxlen, int type,
   subrta = (struct rtattr *) (((char *) rta) + RTA_ALIGN (rta->rta_len));
   subrta->rta_type = type;
   subrta->rta_len = len;
-  memcpy (RTA_DATA (subrta), data, alen);
+
+  if (data)
+    memcpy (RTA_DATA (subrta), data, alen);
+  else
+    assert (len == 0);
+
   rta->rta_len = NLMSG_ALIGN (rta->rta_len) + RTA_ALIGN (len);
 
   return 0;
@@ -397,6 +423,19 @@ nl_rtproto_to_str (u_char rtproto)
 {
   return lookup (rtproto_str, rtproto);
 }
+
+const char *
+nl_family_to_str (u_char family)
+{
+  return lookup (family_str, family);
+}
+
+const char *
+nl_rttype_to_str (u_char rttype)
+{
+  return lookup (rttype_str, rttype);
+}
+
 /* Receive message from netlink interface and pass those information
    to the given function. */
 int
@@ -592,7 +631,9 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
 
 /* sendmsg() to netlink socket then recvmsg(). */
 int
-netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns)
+netlink_talk (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
+                            ns_id_t),
+             struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns)
 {
   int status;
   struct sockaddr_nl snl;
@@ -648,7 +689,7 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns)
    * Get reply from netlink socket.
    * The reply should either be an acknowlegement or an error.
    */
-  return netlink_parse_info (netlink_talk_filter, nl, zns, 0);
+  return netlink_parse_info (filter, nl, zns, 0);
 }
 
 /* Get type specified information from netlink. */
@@ -718,7 +759,8 @@ kernel_init (struct zebra_ns *zns)
 
   /* Initialize netlink sockets */
   groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
-          RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
+    RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR |
+    RTMGRP_IPV4_MROUTE;
 
   snprintf (zns->netlink.name, sizeof (zns->netlink.name),
            "netlink-listen (NS %u)", zns->ns_id);
index 890236c0471e9d7f2ee5f1bb6f2bc21665c478d0..f17f1380c25c3aaf6ba59f20e771bd00dabdd3fc 100644 (file)
@@ -40,11 +40,17 @@ extern struct rtattr * rta_nest(struct rtattr *rta, int maxlen, int type);
 extern int rta_nest_end(struct rtattr *rta, struct rtattr *nest);
 extern const char * nl_msg_type_to_str (uint16_t msg_type);
 extern const char * nl_rtproto_to_str (u_char rtproto);
+extern const char * nl_family_to_str (u_char family);
+extern const char * nl_rttype_to_str (u_char rttype);
 
 extern int netlink_parse_info (int (*filter) (struct sockaddr_nl *,
                                struct nlmsghdr *, ns_id_t), struct nlsock *nl,
                                struct zebra_ns *zns, int count);
-extern int netlink_talk (struct nlmsghdr *n, struct nlsock *nl,
+extern int netlink_talk_filter (struct sockaddr_nl *, struct nlmsghdr *,
+                               ns_id_t);
+extern int netlink_talk (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
+                                       ns_id_t),
+                        struct nlmsghdr *n, struct nlsock *nl,
                          struct zebra_ns *zns);
 extern int netlink_request (int family, int type, struct nlsock *nl);
 
index 2e7f336d20d23fe91be7e796bfd86467939e6f52..2dfe43f5df7756280bf0e131eeaef10392e16995 100644 (file)
@@ -55,6 +55,8 @@
 #include "zebra/zebra_mpls.h"
 #include "zebra/kernel_netlink.h"
 #include "zebra/rt_netlink.h"
+#include "zebra/zebra_mroute.h"
+
 
 /* TODO - Temporary definitions, need to refine. */
 #ifndef AF_MPLS
 #define RTA_ENCAP      22
 #endif
 
+#ifndef RTA_EXPIRES
+#define RTA_EXPIRES     23
+#endif
+
 #ifndef LWTUNNEL_ENCAP_MPLS
 #define LWTUNNEL_ENCAP_MPLS  1
 #endif
 #ifndef MPLS_IPTUNNEL_DST
 #define MPLS_IPTUNNEL_DST  1
 #endif
+
+#ifndef NDA_MASTER
+#define NDA_MASTER   9
+#endif
 /* End of temporary definitions */
 
 struct gw_family_t
@@ -304,8 +314,8 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
 }
 
 /* Routing information change from the kernel. */
-int
-netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+static int
+netlink_route_change_read_unicast (struct sockaddr_nl *snl, struct nlmsghdr *h,
                       ns_id_t ns_id)
 {
   int len;
@@ -327,35 +337,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
 
   rtm = NLMSG_DATA (h);
 
-  if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE))
-    {
-      /* If this is not route add/delete message print warning. */
-      zlog_warn ("Kernel message: %d", h->nlmsg_type);
-      return 0;
-    }
-
-  /* Connected route. */
-  if (IS_ZEBRA_DEBUG_KERNEL)
-    zlog_debug ("%s %s %s proto %s",
-               h->nlmsg_type ==
-               RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
-               rtm->rtm_family == AF_INET ? "ipv4" : "ipv6",
-               rtm->rtm_type == RTN_UNICAST ? "unicast" : "multicast",
-               nl_rtproto_to_str (rtm->rtm_protocol));
-
-  if (rtm->rtm_type != RTN_UNICAST)
-    {
-      return 0;
-    }
-
-  /* We don't care about change notifications for the MPLS table. */
-  /* TODO: Revisit this. */
-  if (rtm->rtm_family == AF_MPLS)
-    return 0;
-
   len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
-  if (len < 0)
-    return -1;
 
   memset (tb, 0, sizeof tb);
   netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len);
@@ -441,7 +423,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
         {
           char buf[PREFIX_STRLEN];
           zlog_debug ("%s %s vrf %u",
-                      h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
+                      nl_msg_type_to_str (h->nlmsg_type),
                       prefix2str (&p, buf, sizeof(buf)), vrf_id);
         }
 
@@ -528,7 +510,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
         {
          char buf[PREFIX_STRLEN];
           zlog_debug ("%s %s vrf %u",
-                      h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
+                      nl_msg_type_to_str (h->nlmsg_type),
                       prefix2str (&p, buf, sizeof(buf)), vrf_id);
         }
 
@@ -544,6 +526,143 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
   return 0;
 }
 
+static struct mcast_route_data *mroute = NULL;
+
+static int
+netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h,
+                                    ns_id_t ns_id)
+{
+  int len;
+  struct rtmsg *rtm;
+  struct rtattr *tb[RTA_MAX + 1];
+  struct mcast_route_data *m;
+  struct mcast_route_data mr;
+  int iif = 0;
+  int count;
+  int oif[256];
+  int oif_count = 0;
+  char sbuf[40];
+  char gbuf[40];
+  char oif_list[256] = "\0";
+  vrf_id_t vrf = ns_id;
+
+  if (mroute)
+    m = mroute;
+  else
+    {
+      memset (&mr, 0, sizeof (mr));
+      m = &mr;
+    }
+
+  rtm = NLMSG_DATA (h);
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
+
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len);
+
+  if (tb[RTA_IIF])
+    iif = *(int *)RTA_DATA (tb[RTA_IIF]);
+
+  if (tb[RTA_SRC])
+    m->sg.src = *(struct in_addr *)RTA_DATA (tb[RTA_SRC]);
+
+  if (tb[RTA_DST])
+    m->sg.grp = *(struct in_addr *)RTA_DATA (tb[RTA_DST]);
+
+  if ((RTA_EXPIRES <= RTA_MAX) && tb[RTA_EXPIRES])
+    m->lastused = *(unsigned long long *)RTA_DATA (tb[RTA_EXPIRES]);
+
+  if (tb[RTA_MULTIPATH])
+    {
+      struct rtnexthop *rtnh =
+        (struct rtnexthop *)RTA_DATA (tb[RTA_MULTIPATH]);
+
+      len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+      for (;;)
+        {
+          if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+           break;
+
+         oif[oif_count] = rtnh->rtnh_ifindex;
+          oif_count++;
+
+         len -= NLMSG_ALIGN (rtnh->rtnh_len);
+         rtnh = RTNH_NEXT (rtnh);
+        }
+    }
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      struct interface *ifp;
+      strcpy (sbuf, inet_ntoa (m->sg.src));
+      strcpy (gbuf, inet_ntoa (m->sg.grp));
+      for (count = 0; count < oif_count; count++)
+       {
+         ifp = if_lookup_by_index_vrf (oif[count], vrf);
+         char temp[256];
+
+         sprintf (temp, "%s ", ifp->name);
+         strcat (oif_list, temp);
+       }
+      ifp = if_lookup_by_index_vrf (iif, vrf);
+      zlog_debug ("MCAST %s (%s,%s) IIF: %s OIF: %s jiffies: %lld",
+                 nl_msg_type_to_str(h->nlmsg_type), sbuf, gbuf, ifp->name, oif_list, m->lastused);
+    }
+  return 0;
+}
+
+int
+netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+                     ns_id_t ns_id)
+{
+  int len;
+  vrf_id_t vrf_id = ns_id;
+  struct rtmsg *rtm;
+
+  rtm = NLMSG_DATA (h);
+
+  if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE))
+    {
+      /* If this is not route add/delete message print warning. */
+      zlog_warn ("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id);
+      return 0;
+    }
+
+  /* Connected route. */
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("%s %s %s proto %s vrf %u",
+               nl_msg_type_to_str (h->nlmsg_type),
+               nl_family_to_str (rtm->rtm_family),
+               nl_rttype_to_str (rtm->rtm_type),
+               nl_rtproto_to_str (rtm->rtm_protocol),
+               vrf_id);
+
+  /* We don't care about change notifications for the MPLS table. */
+  /* TODO: Revisit this. */
+  if (rtm->rtm_family == AF_MPLS)
+    return 0;
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
+  if (len < 0)
+    return -1;
+
+  switch (rtm->rtm_type)
+    {
+    case RTN_UNICAST:
+      netlink_route_change_read_unicast (snl, h, ns_id);
+      break;
+    case RTN_MULTICAST:
+      netlink_route_change_read_multicast (snl, h, ns_id);
+      break;
+    default:
+      return 0;
+      break;
+    }
+
+  return 0;
+}
+
 /* Routing table read function using netlink interface.  Only called
    bootstrap time. */
 int
@@ -1108,7 +1227,7 @@ netlink_neigh_update (int cmd, int ifindex, uint32_t addr, char *lla, int llalen
   addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4);
   addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen);
 
-  return netlink_talk (&req.n, &zns->netlink_cmd, zns);
+  return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns);
 }
 
 /* Routing table change via netlink interface. */
@@ -1403,7 +1522,40 @@ skip:
   snl.nl_family = AF_NETLINK;
 
   /* Talk to netlink socket. */
-  return netlink_talk (&req.n, &zns->netlink_cmd, zns);
+  return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns);
+}
+
+int
+netlink_get_ipmr_sg_stats (void *in)
+{
+  int suc = 0;
+  struct mcast_route_data *mr = (struct mcast_route_data *)in;
+  struct {
+      struct nlmsghdr         n;
+      struct ndmsg            ndm;
+      char                    buf[256];
+  } req;
+
+  mroute = mr;
+  struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+
+  memset(&req.n, 0, sizeof(req.n));
+  memset(&req.ndm, 0, sizeof(req.ndm));
+
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST;
+  req.ndm.ndm_family = AF_INET;
+  req.n.nlmsg_type = RTM_GETROUTE;
+
+  addattr_l (&req.n, sizeof (req), RTA_IIF, &mroute->ifindex, 4);
+  addattr_l (&req.n, sizeof (req), RTA_OIF, &mroute->ifindex, 4);
+  addattr_l (&req.n, sizeof (req), RTA_SRC, &mroute->sg.src.s_addr, 4);
+  addattr_l (&req.n, sizeof (req), RTA_DST, &mroute->sg.grp.s_addr, 4);
+
+  suc = netlink_talk (netlink_route_change_read_multicast, &req.n, &zns->netlink_cmd, zns);
+
+  mroute = NULL;
+  return suc;
 }
 
 int
@@ -1593,7 +1745,7 @@ netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp)
     }
 
   /* Talk to netlink socket. */
-  return netlink_talk (&req.n, &zns->netlink_cmd, zns);
+  return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns);
 }
 
 /*
index 7183525fba6ec8c48052afb6893f78833233cdbb..6774b7dd3d0669d3e94a1cc0b3f8223b5c04015e 100644 (file)
@@ -37,6 +37,7 @@ extern int netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
                                  ns_id_t ns_id);
 extern int netlink_route_read (struct zebra_ns *zns);
 
+extern int netlink_get_ipmr_sg_stats (void *mroute);
 #endif /* HAVE_NETLINK */
 
 #endif /* _ZEBRA_RT_NETLINK_H */
index 80512c71f62c7bf541837d68b941a4e6daad13a5..8b337152b4936266496d2b429671aba079195b69 100644 (file)
@@ -1622,14 +1622,16 @@ zfpm_init_message_format (const char *format)
 {
   int have_netlink, have_protobuf;
 
-  have_netlink = have_protobuf = 0;
-
 #ifdef HAVE_NETLINK
   have_netlink = 1;
+#else
+  have_netlink = 0;
 #endif
 
 #ifdef HAVE_PROTOBUF
   have_protobuf = 1;
+#else
+  have_protobuf = 0;
 #endif
 
   zfpm_g->message_format = ZFPM_MSG_FORMAT_NONE;
index 0d9f809328a09140228404b1b2ba1f600be58a3f..9fffc9e611474babd23e240ba34d3689c0d138c5 100644 (file)
@@ -28,6 +28,7 @@
 #include "log.h"
 #include "rib.h"
 #include "vty.h"
+#include "prefix.h"
 
 #include "zebra/zserv.h"
 #include "zebra/zebra_ns.h"
@@ -251,10 +252,15 @@ netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
    * particularly in our communication with the FPM.
    */
   if (cmd == RTM_DELROUTE && !rib)
-    goto skip;
+    return 1;
 
-  if (rib)
-    ri->rtm_protocol = netlink_proto_from_route_type (rib->type);
+  if (!rib)
+    {
+      zfpm_debug ("%s: Expected non-NULL rib pointer", __PRETTY_FUNCTION__);
+      return 0;
+    }
+
+  ri->rtm_protocol = netlink_proto_from_route_type (rib->type);
 
   if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
     discard = 1;
@@ -279,9 +285,7 @@ netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
   ri->metric = &rib->metric;
 
   if (discard)
-    {
-      goto skip;
-    }
+    return 1;
 
   for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
@@ -307,7 +311,6 @@ netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
       return 0;
     }
 
- skip:
   return 1;
 }
 
diff --git a/zebra/zebra_mroute.c b/zebra/zebra_mroute.c
new file mode 100644 (file)
index 0000000..d05c906
--- /dev/null
@@ -0,0 +1,68 @@
+/* zebra_mroute code
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "stream.h"
+#include "prefix.h"
+#include "vrf.h"
+#include "rib.h"
+
+#include "zebra/zserv.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_mroute.h"
+#include "zebra/rt_netlink.h"
+
+int
+zebra_ipmr_route_stats (struct zserv *client, int fd, u_short length, struct zebra_vrf *zvrf)
+{
+  struct mcast_route_data mroute;
+  struct stream *s;
+  int suc;
+
+  char sbuf[40];
+  char gbuf[40];
+
+  memset (&mroute, 0, sizeof (mroute));
+  stream_get (&mroute.sg.src, client->ibuf, 4);
+  stream_get (&mroute.sg.grp, client->ibuf, 4);
+  mroute.ifindex = stream_getl (client->ibuf);
+
+  strcpy (sbuf, inet_ntoa (mroute.sg.src));
+  strcpy (gbuf, inet_ntoa (mroute.sg.grp));
+
+  suc = netlink_get_ipmr_sg_stats (&mroute);
+
+  s = client->obuf;
+
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_IPMR_ROUTE_STATS, zvrf_id (zvrf));
+  stream_put_in_addr (s, &mroute.sg.src);
+  stream_put_in_addr (s, &mroute.sg.grp);
+  stream_put (s, &mroute.lastused, sizeof (mroute.lastused));
+  stream_putl (s, suc);
+
+  stream_putw_at (s, 0, stream_get_endp (s));
+  zebra_server_send_message (client);
+  return 0;
+}
diff --git a/zebra/zebra_mroute.h b/zebra/zebra_mroute.h
new file mode 100644 (file)
index 0000000..c0bac43
--- /dev/null
@@ -0,0 +1,35 @@
+/* zebra_mroute.h
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __ZEBRA_MROUTE_H__
+#define __ZEBRA_MROUTE_H__
+
+struct mcast_route_data {
+  struct prefix_sg sg;
+  unsigned int ifindex;
+  unsigned long long lastused;
+};
+
+int zebra_ipmr_route_stats (struct zserv *client, int sock, u_short length, struct zebra_vrf *zvf);
+
+#endif
+
index e48da0479b399b6b5ce61974833fa3b61f41d675..59893b1a0fa2e1fdf025520a0bd5ee90cac142bf 100644 (file)
@@ -1633,7 +1633,7 @@ rib_process (struct route_node *rn)
   if (IS_ZEBRA_DEBUG_RIB_DETAILED)
     zlog_debug ("%u:%s/%d: Processing rn %p", vrf_id, buf, rn->p.prefixlen, rn);
 
-  RNODE_FOREACH_RIB (rn, rib)
+  RNODE_FOREACH_RIB_SAFE (rn, rib, next)
     {
       if (IS_ZEBRA_DEBUG_RIB_DETAILED)
         zlog_debug ("%u:%s/%d: Examine rib %p (type %d) status %x flags %x "
index b323950064f2021d40509973ce60a5d0c7e14852..7d104f30d98a7e0fc494d1df56fed30fb8f254e3 100644 (file)
@@ -50,6 +50,8 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro
   if (! table)
     return;
 
+  memset (&nh_p, 0, sizeof (nh_p));
+
   /* Lookup existing route */
   rn = route_node_get (table, p);
   RNODE_FOREACH_RIB (rn, rib)
index 5e3177e3b82176b6f21ae91d88b1742fccdcfe03..233436f02d59dd52dda5e2323be2286bcfd1d10b 100644 (file)
@@ -31,6 +31,14 @@ struct static_nh_label
   mpls_label_t label[2];
 };
 
+typedef enum {
+  STATIC_IFINDEX,
+  STATIC_IPV4_GATEWAY,
+  STATIC_BLACKHOLE,
+  STATIC_IPV6_GATEWAY,
+  STATIC_IPV6_GATEWAY_IFINDEX,
+} zebra_static_types;
+
 /* Static route information. */
 struct static_route
 {
@@ -48,12 +56,7 @@ struct static_route
   route_tag_t tag;
 
   /* Flag for this static route's type. */
-  u_char type;
-#define STATIC_IFINDEX               1
-#define STATIC_IPV4_GATEWAY          2
-#define STATIC_BLACKHOLE             3
-#define STATIC_IPV6_GATEWAY          4
-#define STATIC_IPV6_GATEWAY_IFINDEX  5
+  zebra_static_types type;
 
   /*
    * Nexthop value.
index 3138e7c867dfd8b1d5827389cdd0e7025b5cd658..113b063913361ca80b9342000e6a00b020557405 100644 (file)
@@ -31,7 +31,6 @@
 #include "nexthop.h"
 #include "vrf.h"
 #include "mpls.h"
-#include "lib/json.h"
 #include "routemap.h"
 
 #include "zebra/zserv.h"
@@ -41,6 +40,7 @@
 #include "zebra/redistribute.h"
 #include "zebra/zebra_routemap.h"
 #include "zebra/zebra_static.h"
+#include "lib/json.h"
 
 extern int allow_delete;
 
@@ -2282,6 +2282,14 @@ static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd)
               case STATIC_BLACKHOLE:
                 vty_out (vty, " Null0");
                 break;
+             case STATIC_IPV6_GATEWAY:
+               vty_out (vty, " %s", inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ));
+               break;
+             case STATIC_IPV6_GATEWAY_IFINDEX:
+               vty_out (vty, " %s %s",
+                        inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ),
+                        ifindex2ifname_vrf (si->ifindex, si->vrf_id));
+               break;
               }
 
             /* flags are incompatible with STATIC_BLACKHOLE */
@@ -3591,6 +3599,9 @@ static_config_ipv6 (struct vty *vty)
 
            switch (si->type)
              {
+             case STATIC_IPV4_GATEWAY:
+                vty_out (vty, " %s", inet_ntoa (si->addr.ipv4));
+                break;
              case STATIC_IPV6_GATEWAY:
                vty_out (vty, " %s", inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ));
                break;
index d3c204d119a53fbe80c08bc06071b143d5f576e0..b069c996c0fe86b00f91cf8c76d48c38cc5106f5 100644 (file)
@@ -54,6 +54,7 @@
 #include "zebra/rtadv.h"
 #include "zebra/zebra_mpls.h"
 #include "zebra/zebra_fpm.h"
+#include "zebra/zebra_mroute.h"
 
 /* Event list of zebra. */
 enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE };
@@ -785,8 +786,6 @@ zsend_write_nexthop (struct stream *s, struct nexthop *nexthop)
   switch (nexthop->type)
     {
     case NEXTHOP_TYPE_IPV4:
-      stream_put_in_addr (s, &nexthop->gate.ipv4);
-      break;
     case NEXTHOP_TYPE_IPV4_IFINDEX:
       stream_put_in_addr (s, &nexthop->gate.ipv4);
       stream_putl (s, nexthop->ifindex);
@@ -1519,7 +1518,7 @@ zread_ipv6_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf)
   struct stream *s;
   struct zapi_ipv6 api;
   struct in6_addr nexthop;
-  union g_addr *pnexthop;
+  union g_addr *pnexthop = NULL;
   unsigned long ifindex;
   struct prefix p;
   
@@ -2051,6 +2050,9 @@ zebra_client_read (struct thread *thread)
     case ZEBRA_MPLS_LABELS_DELETE:
       zread_mpls_labels (command, client, length, vrf_id);
       break;
+    case ZEBRA_IPMR_ROUTE_STATS:
+      zebra_ipmr_route_stats (client, sock, length, zvrf);
+      break;
     default:
       zlog_info ("Zebra received unknown command %d", command);
       break;
@@ -2371,6 +2373,21 @@ zebra_show_client_brief (struct vty *vty, struct zserv *client)
 
 }
 
+struct zserv *
+zebra_find_client (u_char proto)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    {
+      if (client->proto == proto)
+        return client;
+    }
+
+  return NULL;
+}
+
 
 /* Display default rtm_table for all clients. */
 DEFUN (show_table,
index a0434d299bdf108af9de5e80adab25bfe28d7996..d17a28eb6a9f50c6b9c11d9f6944412d2cac49ef 100644 (file)
@@ -176,4 +176,6 @@ extern void zserv_create_header(struct stream *s, uint16_t cmd, vrf_id_t vrf_id)
 extern void zserv_nexthop_num_warn(const char *, const struct prefix *, const unsigned int);
 extern int zebra_server_send_message(struct zserv *client);
 
+extern struct zserv *zebra_find_client (u_char proto);
+
 #endif /* _ZEBRA_ZEBRA_H */