diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 87cff44293..7538a50770 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -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 */ @@ -3282,7 +3284,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 */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 4a06881041..7dbb439be1 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -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; diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index b0a396ec11..9275b3a101 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -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; }; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 38470a3c7e..529a3e46ca 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -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; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 6faf474286..5fdda66258 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2111,10 +2111,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)) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index ec2223d3ed..61d2095659 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -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). diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 2718805130..3dd5523dce 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -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); diff --git a/lib/log.c b/lib/log.c index d48534dc18..13592a0240 100644 --- 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 diff --git a/lib/network.c b/lib/network.c index 506e019136..2b6f2fbab5 100644 --- a/lib/network.c +++ b/lib/network.c @@ -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; diff --git a/lib/zebra.h b/lib/zebra.h index c9be1892e4..bb43d062bc 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -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,12 +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_AFI_L2VPN 25 -#define IANA_AFI_IPMR 128 -#define IANA_AFI_IP6MR 129 +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; #define IANA_SAFI_RESERVED 0 #define IANA_SAFI_UNICAST 1 @@ -531,7 +540,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; @@ -540,7 +549,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; diff --git a/pimd/Makefile.am b/pimd/Makefile.am index bc28aa1e92..314a744339 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -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) diff --git a/pimd/README b/pimd/README index c8997808b6..3d03979a9a 100644 --- a/pimd/README +++ b/pimd/README @@ -1,18 +1,17 @@ INTRODUCTION qpimd aims to implement a PIM (Protocol Independent Multicast) - daemon for the Quagga Routing Suite. + daemon for the FRR Routing Suite. - Initially qpimd targets only PIM SSM (Source-Specific - Multicast) mode as defined in section 4.8.2 (PIM-SSM-Only - Routers) of RFC 4601. + qpimd implements PIM-SM (Sparse Mode) of RFC 4601. + Additionally MSDP has been implemented. In order to deliver end-to-end multicast routing control - plane, qpimd includes the router-side of IGMPv3 (RFC 3376). + plane, qpimd includes the router-side of IGMPv[2|3] (RFC 3376). LICENSE - qpimd - pimd for quagga + qpimd - pimd for FRR Copyright (C) 2008 Everton da Silva Marques qpimd is free software; you can redistribute it and/or modify @@ -34,78 +33,16 @@ HOME SITE qpimd lives at: - https://github.com/udhos/qpimd + https://github.com/freerangerouting/frr PLATFORMS - qpimd has been tested with Debian Lenny under Linux 2.6. + qpimd has been tested with Debian Jessie. REQUIREMENTS - qpimd requires Quagga (0.99.11 or higher from http://www.quagga.net) + qpimd requires FRR (2.0 or higher) - The GNU Build System (Autotools) is required to build from - source code repository. - - gawk is also needed to build with Autotools. Any other awk - usually won't work. - -BUILDING FROM QUAGGA GIT REPOSITORY - - 1) Get the latest quagga source tree - - # git clone git://code.quagga.net/quagga.git quagga - - 2) Apply qpimd patch into quagga source tree - - # patch -p1 -d quagga < pimd-0.153-quagga-git20090623.patch - - 3) Compile and install quagga - - # cd quagga - # ./bootstrap.sh - # ./configure --prefix=/usr/local/quagga --enable-pimd - # make - # make install - -BUILDING FROM QUAGGA TARBALL - - 1) Get the latest quagga tarball - - # wget http://www.quagga.net/download/quagga-0.99.13.tar.gz - - 2) Unpack the quagga tarball - - # tar xzf quagga-0.99.13.tar.gz - - 3) Apply qpimd patch into quagga source tree - - # patch -p1 -d quagga-0.99.13 < pimd-0.153-quagga-0.99.13.patch - - 4) Compile and install quagga - - # cd quagga-0.99.13 - # ./configure --prefix=/usr/local/quagga --enable-pimd - # make - # make install - -USAGE - - 1) Configure and start the zebra daemon - - # cp /usr/local/quagga/etc/zebra.conf.sample /usr/local/quagga/etc/zebra.conf - # vi /usr/local/quagga/etc/zebra.conf - # /usr/local/quagga/sbin/zebra - - 2) Configure and start the pimd daemon - - # cp /usr/local/quagga/etc/pimd.conf.sample /usr/local/quagga/etc/pimd.conf - # vi /usr/local/quagga/etc/pimd.conf - # /usr/local/quagga/sbin/pimd - - 3) Access pimd vty interface at port TCP 2611 - - # telnet localhost 2611 CONFIGURATION COMMANDS @@ -120,7 +57,7 @@ SUPPORT Please post comments, questions, patches, bug reports at the support site: - https://github.com/udhos/qpimd + https://freerangerouting/frr RELATED WORK diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index f09540ea00..d6f372cc44 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -54,31 +54,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char was_str[INET_ADDRSTRLEN]; + char winner_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); pim_inet4_dump("", 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 +90,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_str, sizeof(src_str)); zlog_debug("%s: from %s on %s", label, src_str, ifp->name); @@ -138,13 +130,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +144,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("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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 +181,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 +209,9 @@ static int dispatch_assert(struct interface *ifp, break; default: { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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 +224,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 +239,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_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -273,7 +257,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_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -284,7 +268,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_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 +295,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("", src_addr, neigh_str, sizeof(neigh_str)); pim_inet4_dump("", msg_source_addr.u.prefix4, source_str, sizeof(source_str)); - pim_inet4_dump("", msg_group_addr.u.prefix4, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 +313,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 +383,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("", 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 +396,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("", 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 +432,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 +470,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("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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 +510,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 +520,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +541,10 @@ static int on_assert_timer(struct thread *t) break; default: { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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 +553,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +599,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +613,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +648,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 +666,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +699,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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 +726,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 +758,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); + } } diff --git a/pimd/pim_br.c b/pimd/pim_br.c index 121a45fd16..3f84de79c8 100644 --- a/pimd/pim_br.c +++ b/pimd/pim_br.c @@ -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; } diff --git a/pimd/pim_br.h b/pimd/pim_br.h index 06b10ada30..8e4f719ed0 100644 --- a/pimd/pim_br.h +++ b/pimd/pim_br.h @@ -21,10 +21,10 @@ #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); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 088713012e..85baa4eafe 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -20,10 +20,12 @@ #include +#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 +52,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 +67,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 +138,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 +160,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 +172,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->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", ch->ifassert_winner, + winner_str, sizeof(winner_str)); - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", 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 +228,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->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", 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->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", 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->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", am.ip_address, - addr_str, sizeof(addr_str)); + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", 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]; + 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; + am = &ch->ifassert_winner_metric; - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", am->ip_address, - addr_str, sizeof(addr_str)); + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", 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->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); + 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", - 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 */ + 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 */ } -static void pim_show_membership(struct vty *vty) +static void json_object_pim_ifp_add(struct json_object *json, struct interface *ifp) { - struct listnode *ifnode; - struct interface *ifp; + struct pim_interface *pim_ifp; - vty_out(vty, - "Interface Address Source Group Membership%s", - VTY_NEWLINE); + 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); - 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; + 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, u_char uj) +{ + 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; + + json = json_object_new_object(); + + 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->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", 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 +530,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 +719,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 +733,8 @@ static void igmp_show_interface_join(struct vty *vty) pim_inet4_dump("", 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,159 +755,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); + 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; - 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_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("", 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); - } - } -} - -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; - ifaddr = pim_ifp->primary_address; - - pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), - now, pim_ifp->pim_dr_election_last); - - pim_inet4_dump("", pim_ifp->pim_dr_addr, - dr_str, sizeof(dr_str)); - - 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); - } -} - -static void pim_show_hello(struct vty *vty) -{ - 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); - - 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]; - - pim_ifp = ifp->info; - - if (!pim_ifp) - continue; - - if (pim_ifp->pim_sock_fd < 0) + if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name)) continue; + found_ifname = 1; ifaddr = pim_ifp->primary_address; - - pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer); + pim_inet4_dump("", 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); - 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); + if (uj) { + json_row = json_object_new_object(); + json_object_pim_ifp_add(json_row, ifp); + + 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); + } + + // 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("", 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("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", 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("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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_interfaces(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(); + 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; - vty_out(vty, "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE); + 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 uptime[10]; - int mloop; - pim_ifp = ifp->info; if (!pim_ifp) @@ -671,73 +1054,136 @@ static void pim_show_interfaces(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_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation); + 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++; - 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); + 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)); + + if (pim_ifp->pim_dr_addr.s_addr == pim_ifp->primary_address.s_addr) + json_object_boolean_true_add(json_row, "pimDesignatedRouterLocal"); + + json_object_object_add(json, ifp->name, json_row); } + + 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); + + json_object_object_foreach(json, key, val) { + vty_out(vty, "%-9s ", key); + + json_object_object_get_ex(val, "state", &json_tmp); + vty_out(vty, "%5s ", json_object_get_string(json_tmp)); + + json_object_object_get_ex(val, "address", &json_tmp); + vty_out(vty, "%15s ", json_object_get_string(json_tmp)); + + json_object_object_get_ex(val, "pimNeighbors", &json_tmp); + vty_out(vty, "%8d ", json_object_get_int(json_tmp)); + + 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]; + 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->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", 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); + 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 +1192,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; - + 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; + now = pim_time_monotonic_sec(); - 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); - - 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)) { + pim_inet4_dump("", 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_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_state(struct vty *vty, const char *src_or_group, const char *group, u_char uj) +{ + 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(); + + 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(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; + + if (!c_oil->installed) + continue; + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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, ""); + + if (src_or_group) + { + if (strcmp(src_or_group, src_str) && strcmp(src_or_group, grp_str)) + continue; + + if (group && strcmp(group, grp_str)) + continue; + } + + 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, ""); + + 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_neighbors(struct vty *vty, u_char uj) +{ + 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; + + now = pim_time_monotonic_sec(); + + 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)) { + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + if (uj) + json_ifp_rows = json_object_new_object(); 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("", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str)); 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); + pim_time_timer_to_hhmmss(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'; + 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); - 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); + } else { + vty_out(vty, "%-9s %15s %8s %8s %6d%s", + ifp->name, + neigh_src_str, + uptime, + expire, + neigh->dr_priority, + VTY_NEWLINE); + } } - - } -} - -static void pim_show_lan_prune_delay(struct vty *vty) -{ - 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); - - vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T | Neighbor LPD PrDly OvInt T%s", 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; - - 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 neigh_src_str[100]; - - pim_inet4_dump("", neigh->source_addr, - neigh_src_str, sizeof(neigh_src_str)); - - 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) { + json_object_object_add(json, ifp->name, json_ifp_rows); + json_ifp_rows = NULL; } - } -} -static void pim_show_jp_override_interval(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; - - 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); - - vty_out(vty, "Interface Address LAN_Delay EffPDelay EffOvrInt JPOvrInt%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; - - 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); + 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 +1579,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 +1590,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 +1609,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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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); + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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); - 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) { + 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, "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); + } + } + + 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]; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; - vty_out(vty, - "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s", - VTY_NEWLINE); + 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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"); + + 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 +1782,109 @@ static void pim_show_join_desired(struct vty *vty) VTY_NEWLINE); } } -} -static void pim_show_upstream_rpf(struct vty *vty) -{ - struct listnode *upnode; - struct pim_upstream *up; - - 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]; - struct pim_rpf *rpf; - const char *rpf_ifname; - - rpf = &up->rpf; - - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); - pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); - - rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; - - 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 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; + + 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("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_addr_dump("", &rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); + pim_addr_dump("", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + 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, 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 +1906,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; - show_rpf_refresh_stats(vty, now); + 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); + } - 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); - pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_addr_dump("", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + pim_addr_dump("", &rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; - 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) { + json_object_object_get_ex(json, grp_str, &json_group); -static void igmp_show_querier(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } - vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", 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); - 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]; - - 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); - - 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); + } 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 +2005,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 +2013,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 +2021,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 +2083,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 +2091,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 +2122,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("", 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 +2143,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 +2151,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 +2159,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 +2203,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 +2211,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 +2219,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("", src->source_addr, source_str, sizeof(source_str)); @@ -1555,11 +2301,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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", @@ -1574,11 +2320,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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", @@ -1594,10 +2340,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("", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", 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 +2359,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("", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", 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 +2413,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 +2446,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 +2473,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 +2500,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", @@ -1836,106 +2552,70 @@ DEFUN (show_ip_pim_assert_winner_metric, return CMD_SUCCESS; } -DEFUN (show_ip_pim_dr, - show_ip_pim_dr_cmd, - "show ip pim designated-router", - SHOW_STR - IP_STR - PIM_STR - "PIM interface designated router\n") -{ - pim_show_dr(vty); - - return CMD_SUCCESS; -} - -DEFUN (show_ip_pim_hello, - show_ip_pim_hello_cmd, - "show ip pim hello", - SHOW_STR - IP_STR - PIM_STR - "PIM interface hello information\n") -{ - pim_show_hello(vty); - - 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); - - return CMD_SUCCESS; -} - -DEFUN (show_ip_pim_jp_override_interval, - show_ip_pim_jp_override_interval_cmd, - "show ip pim jp-override-interval", - SHOW_STR - IP_STR - PIM_STR - "PIM interface J/P override interval\n") -{ - pim_show_jp_override_interval(vty); + u_char uj = use_json(argc, argv); + pim_show_membership(vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_neighbor, show_ip_pim_neighbor_cmd, - "show ip pim neighbor", + "show ip pim neighbor [detail|WORD] [json]", SHOW_STR IP_STR PIM_STR - "PIM neighbor information\n") + "PIM neighbor information\n" + "Detailed output\n" + "Name of interface or neighbor\n" + "JavaScript Object Notation\n") { - pim_show_neighbors(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; } @@ -1953,54 +2633,100 @@ DEFUN (show_ip_pim_secondary, return CMD_SUCCESS; } -DEFUN (show_ip_pim_upstream, - show_ip_pim_upstream_cmd, - "show ip pim upstream", +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 upstream information\n") + "PIM state information\n" + "Unicast or Multicast address\n" + "Multicast address\n" + "JavaScript Object Notation\n") { - pim_show_upstream(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 [json]", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream information\n" + "JavaScript Object Notation\n") +{ + 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") + "PIM upstream source rpf\n" + "JavaScript Object Notation\n") { - pim_show_upstream_rpf(vty); + 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 RP information\n" + "JavaScript Object Notation\n") +{ + 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 +2814,8 @@ DEFUN (show_ip_multicast, else { vty_out(vty, "%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, "%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 +2835,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 +2846,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; + 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; - 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); + 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("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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, ""); + + 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, ""); - vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", - source_str, - group_str, - proto, - ifp_in ? ifp_in->name : "", - c_oil->oil.mfcc_parent, - ifp_out ? ifp_out->name : "", - oif_vif_index, - ttl, - oif_uptime, - VTY_NEWLINE); + 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; + } + } + } + + 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("", s_route->group, group_str, sizeof(group_str)); - pim_inet4_dump("", s_route->source, source_str, sizeof(source_str)); + pim_inet4_dump("", s_route->group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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, ""); + + 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"); + if (ifp_out) + strcpy(out_ifname, ifp_out->name); + else + strcpy(out_ifname, ""); - vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", - source_str, - group_str, - proto, - ifp_in ? ifp_in->name : "", - s_route->iif, - ifp_out ? ifp_out->name : "", - oif_vif_index, - ttl, - oif_uptime, - VTY_NEWLINE); + 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 +3149,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 +3165,10 @@ static void show_mroute_count(struct vty *vty) pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +3177,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 +3188,10 @@ static void show_mroute_count(struct vty *vty) pim_inet4_dump("", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +3223,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 +3235,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 +3244,8 @@ DEFUN (show_ip_rib, vty_out(vty, "Address NextHop Interface Metric Preference%s", VTY_NEWLINE); - pim_inet4_dump("", nexthop.mrib_nexthop_addr, - nexthop_addr_str, sizeof(nexthop_addr_str)); + pim_addr_dump("", &nexthop.mrib_nexthop_addr, + nexthop_addr_str, sizeof(nexthop_addr_str)); vty_out(vty, "%-15s %-15s %-9s %6d %10d%s", addr_str, @@ -2379,11 +3273,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("", ss->source_addr, source_str, sizeof(source_str)); @@ -2417,43 +3311,233 @@ DEFUN (show_ip_ssmpingd, return CMD_SUCCESS; } +static int +pim_rp_cmd_worker (struct vty *vty, const char *rp, const char *group, const char *plist) +{ + int result; + + result = pim_rp_new (rp, group, plist); + + 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 (ip_pim_joinprune_time, + ip_pim_joinprune_time_cmd, + "ip pim join-prune-interval <60-600>", + IP_STR + "pim multicast routing\n" + "Join Prune Send Interval\n" + "Seconds\n") +{ + 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_pim_register_suppress, + ip_pim_register_suppress_cmd, + "ip pim register-suppress-time <5-60000>", + IP_STR + "pim multicast routing\n" + "Register Suppress Timer\n" + "Seconds\n") +{ + qpim_keep_alive_time = atoi (argv[3]->arg); + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_register_suppress, + no_ip_pim_register_suppress_cmd, + "no ip pim register-suppress-time <5-60000>", + NO_STR + IP_STR + "pim multicast routing\n" + "Register Suppress Timer\n" + "Seconds\n") +{ + qpim_register_suppress_time = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT; + return CMD_SUCCESS; +} + +DEFUN (ip_pim_keep_alive, + ip_pim_keep_alive_cmd, + "ip pim keep-alive-timer <31-60000>", + IP_STR + "pim multicast routing\n" + "Keep alive Timer\n" + "Seconds\n") +{ + qpim_rp_keep_alive_time = atoi (argv[4]->arg); + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_keep_alive, + no_ip_pim_keep_alive_cmd, + "no ip pim keep-alive-timer <31-60000>", + NO_STR + IP_STR + "pim multicast routing\n" + "Keep alive Timer\n" + "Seconds\n") +{ + 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", + "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; - int result; + return pim_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL); +} - 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; - } +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); +} - 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; - } +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]", + "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") { - qpim_rp.rpf_addr.s_addr = INADDR_NONE; + int idx_ipv4 = 4; + return pim_no_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL); +} - return CMD_SUCCESS; +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, @@ -2718,7 +3802,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("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("%s: Querier %s on %s reconfig query_interval=%d", __PRETTY_FUNCTION__, @@ -2913,16 +3997,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 +4074,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 +4082,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 +4097,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 +4111,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 +4136,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 +4151,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 +4236,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 +4250,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 +4301,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 +4308,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 +4710,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 +4732,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 +4775,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 +4790,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 +4838,29 @@ DEFUN (debug_pim_packets, DEFUN (debug_pim_packets_filter, debug_pim_packets_filter_cmd, - "debug pim packets ", + "debug pim packets ", 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 +4882,7 @@ DEFUN (no_debug_pim_packets, DEFUN (no_debug_pim_packets_filter, no_debug_pim_packets_filter_cmd, - "no debug pim packets ", + "no debug pim packets ", NO_STR DEBUG_STR DEBUG_PIM_STR @@ -3793,17 +4891,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,6 +5034,95 @@ DEFUN (no_debug_pim_zebra, } +DEFUN (debug_msdp, + debug_msdp_cmd, + "debug msdp", + DEBUG_STR + DEBUG_MSDP_STR) +{ + PIM_DO_DEBUG_MSDP_EVENTS; + PIM_DO_DEBUG_MSDP_PACKETS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_msdp, + no_debug_msdp_cmd, + "no debug msdp", + NO_STR + DEBUG_STR + DEBUG_MSDP_STR) +{ + PIM_DONT_DEBUG_MSDP_EVENTS; + PIM_DONT_DEBUG_MSDP_PACKETS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_msdp, + undebug_msdp_cmd, + "undebug msdp", + UNDEBUG_STR + DEBUG_MSDP_STR) + +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; +} + +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; +} + +ALIAS (no_debug_msdp_events, + undebug_msdp_events_cmd, + "undebug msdp events", + UNDEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_EVENTS_STR) + +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; +} + +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; +} + +ALIAS (no_debug_msdp_packets, + undebug_msdp_packets_cmd, + "undebug msdp packets", + UNDEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_PACKETS_STR) + DEFUN (show_debugging_pim, show_debugging_pim_cmd, "show debugging pim", @@ -3942,813 +5134,835 @@ DEFUN (show_debugging_pim, return CMD_SUCCESS; } -static struct igmp_sock *find_igmp_sock_by_fd(int fd) +static int +interface_pim_use_src_cmd_worker(struct vty *vty, const char *source) { - struct listnode *ifnode; - struct interface *ifp; + int result; + struct in_addr source_addr; + VTY_DECLVAR_CONTEXT(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) + 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; + } + + 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; +} + +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; + } + + result = inet_pton(AF_INET, local, &local_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + local, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + 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); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +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); +} + +static int +ip_no_msdp_peer_cmd_worker (struct vty *vty, const char *peer) +{ + enum pim_msdp_err result; + struct in_addr peer_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; + } + + 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); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +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); +} + +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; + } + + 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 (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); +} + +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 member address %s: errno=%d: %s%s", + mbr, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + 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); + } + + 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); +} + +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; + + 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; + } + + 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); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + + +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 result?CMD_WARNING:CMD_SUCCESS; +} + +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); + } + + return result ? CMD_WARNING : CMD_SUCCESS; +} + +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; + } + + pim_inet4_dump("", 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); + } + + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { + pim_inet4_dump("", 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); + } + } + + 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); + } +} + +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 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; + + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Peer Local State Uptime SaCnt%s", VTY_NEWLINE); + } + + 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("", mp->peer, peer_str, sizeof(peer_str)); + pim_inet4_dump("", 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); + } + } + + 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_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(); + } + + for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) { + pim_inet4_dump("", mp->peer, peer_str, sizeof(peer_str)); + if (strcmp(peer, "detail") && + strcmp(peer, peer_str)) continue; - pim_ifp = ifp->info; + 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("", 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); - /* lookup igmp socket under current interface */ - igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd); - if (igmp) - return igmp; - } + 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); - 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; - } - - record_type_str = argv[idx_number_2]->arg; - record_type = atoi(record_type_str); - - /* - 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; - - /* - 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; + 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); } } - num_sources = src_addr - sources; - *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources); - - igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */ - - /* compute checksum */ - *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len); - - /* "receive" message */ - - 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); - 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_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 int hexval(uint8_t ch) +static void +ip_msdp_show_sa(struct vty *vty, u_char uj) { - return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a'); -} + 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; -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); - return CMD_WARNING; + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Source Group RP Local SPT Uptime%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; - } - - /* - 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 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; - } - - for (i = 0; i < str_last; i += 2) { - uint8_t octet; - int left; - uint8_t h1 = str[i]; - uint8_t h2 = str[i + 1]; - - 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; + 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("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", sa->sg.grp, grp_str, sizeof(grp_str)); + if (sa->flags & PIM_MSDP_SAF_PEER) { + pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); + if (sa->up) { + strcpy(spt_str, "yes"); + } else { + strcpy(spt_str, "no"); } - octet = (hexval(h1) << 4) + hexval(h2); + } 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); - 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; + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); } - - pim_msg[pim_msg_size++] = octet; + + 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); } } - ip_msg_len = ip_hlen + pim_msg_size; - 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); - - /* "receive" message */ - - 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; + 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); } - - return 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") +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) { - 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; + 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; - /* 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; + now = pim_time_monotonic_sec(); + pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime); + if (sa->flags & PIM_MSDP_SAF_PEER) { + pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); + pim_inet4_dump("", 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, "-"); } - - /* 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; + 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); - 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); - - /* - 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 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; + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); } - vty_out(vty, - "FIXME WRITEME consider neighbor secondary address %s%s", - sec_str, 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, "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); + } +} + +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; + + if (uj) { + json = json_object_new_object(); } - 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); - return CMD_WARNING; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", sa->sg.grp, grp_str, sizeof(grp_str)); + ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json); } - pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN; - - pim_msg_build_header(pim_msg, pim_msg_size, - PIM_MSG_TYPE_HELLO); - - /* "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; + 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_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); return 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") +static void +ip_msdp_show_sa_addr(struct vty *vty, const char *addr, u_char uj) { - 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; + struct listnode *sanode; + struct pim_msdp_sa *sa; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; - /* 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; + if (uj) { + json = json_object_new_object(); } - /* 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; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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_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; + 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_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; - } - - 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; - } - - /* - 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; - } - - /* "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; - } - - return CMD_SUCCESS; } -static int recv_joinprune(struct vty *vty, - struct cmd_token **argv, - int src_is_join) +static void +ip_msdp_show_sa_sg(struct vty *vty, const char *src, const char *grp, u_char uj) { - 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; + struct listnode *sanode; + struct pim_msdp_sa *sa; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; - /* 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; + if (uj) { + json = json_object_new_object(); } - 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; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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); + } } - /* 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; + 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 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; - } - - /* 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; - } - - /* - 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; - - /* - 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; - } - - 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; - } - - *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; - } - - 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; - } - - if (src_is_join) { - num_joined = 1; - num_pruned = 0; - } - 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; - } - - /* 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); - - /* - "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; - } - - return CMD_SUCCESS; } -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") +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") { - 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 (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; - - upcall_type = argv[idx_type]->arg; - - 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; - } - - msg.im_vif = atoi(argv[idx_number]->arg); - - /* 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; - } - - /* 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; - } - - msg.im_mbz = 0; /* Must be zero */ - - 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; - } + 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 +5973,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 +6023,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 +6049,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 +6064,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 +6087,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 +6106,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 +6125,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); } diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 34f350e36b..e08cefb29b 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -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" @@ -57,6 +58,12 @@ #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); diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 1cd44f2539..3d7ae4ad22 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_addr, src_str, sizeof(src_str)); zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s", __PRETTY_FUNCTION__, diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index bac9692caa..cc4f4f3dce 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -25,6 +25,8 @@ #include "memory.h" #include "prefix.h" #include "vrf.h" +#include "linklist.h" +#include "plist.h" #include "pimd.h" #include "pim_iface.h" @@ -38,14 +40,26 @@ #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 +92,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 +119,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 +153,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 +177,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)); + pim_ifchannel_delete_all (ifp); + igmp_sock_delete_all (ifp); - zassert(pim_ifp->pim_neighbor_list); - zassert(!listcount(pim_ifp->pim_neighbor_list)); - - 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 +193,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 +266,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 +278,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_prim_addr, new_prim_str, sizeof(new_prim_str)); pim_inet4_dump("", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); zlog_debug("%s: old=%s new=%s on interface %s: %s", @@ -286,57 +290,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; - } - - pim_addr_change(ifp); } return changed; } -static void detect_secondary_address_change(struct interface *ifp, +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; + } + } + + 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 int detect_secondary_address_change(struct interface *ifp, + int force_prim_as_any, const char *caller) { - struct pim_interface *pim_ifp; - int changed; + struct pim_interface *pim_ifp = ifp->info; + int changed = 0; - 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 (!changed) { - return; + 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); } - if (!PIM_IF_TEST_PIM(pim_ifp->options)) { - return; - } - - pim_addr_change(ifp); + return changed; } static void detect_address_change(struct interface *ifp, int force_prim_as_any, const char *caller) { - int prim_changed; + int changed = 0; + struct pim_interface *pim_ifp; - 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 */ + pim_ifp = ifp->info; + if (!pim_ifp) return; + + if (detect_primary_address_change(ifp, force_prim_as_any, caller)) { + changed = 1; } - detect_secondary_address_change(ifp, caller); + if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) { + changed = 1; + } + + + if (changed) { + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; + } + + 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 */ +} + +int pim_update_source_set(struct interface *ifp, struct in_addr source) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + return PIM_IFACE_NOT_FOUND; + } + + 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 +583,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 +674,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 +747,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 +792,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 +821,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 +1043,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 +1142,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_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 +1228,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1238,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1288,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1300,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1311,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1349,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: could not find IGMP group %s source %s on interface %s", @@ -1117,14 +1360,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1491,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; +} diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index e56559ca46..244de598db 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -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 -*/ + */ #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 */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 7afb7a5bdf..07318791e7 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -24,6 +24,7 @@ #include "thread.h" #include "memory.h" #include "if.h" +#include "vrf.h" #include "pimd.h" #include "pim_str.h" @@ -36,13 +37,100 @@ #include "pim_join.h" #include "pim_rpf.h" #include "pim_macro.h" +#include "pim_oil.h" +#include "pim_upstream.h" + +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) +{ + 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) { - zassert(!ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - zassert(!ch->t_ifassert_timer); - 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", 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; + ch->t_ifjoin_prune_pending_timer = NULL; - zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING); + 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); - /* 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 */ - /* Save (S,G) */ - ch_source = ch->source_addr; - ch_group = ch->group_addr; - - 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("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("", 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("", &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, up_str, sizeof(up_str)); - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + char neigh_str[INET_ADDRSTRLEN]; pim_inet4_dump("", 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; + THREAD_OFF(ch->t_ifjoin_expiry_timer); - pim_ifp = ifp->info; + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); - zassert(ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - - 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); } - 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_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); + } + 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->sg.grp, grp_str, sizeof(grp_str)); pim_inet4_dump("", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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; + } + } +} diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index ce753222ee..bfe632135c 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -24,6 +24,7 @@ #include #include "if.h" +#include "prefix.h" #include "pim_upstream.h" @@ -35,7 +36,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 +70,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 +112,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 +149,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 */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index ef1b3cbac0..4a23e4f668 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -26,6 +26,7 @@ #include "pimd.h" #include "pim_igmp.h" +#include "pim_igmpv2.h" #include "pim_igmpv3.h" #include "pim_iface.h" #include "pim_sock.h" @@ -35,34 +36,31 @@ #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 +74,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 +85,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 +152,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("", 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 +194,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("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present", ifaddr_str); @@ -210,7 +206,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 +237,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("", 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 +256,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("", 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 +266,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 +295,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_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 +333,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("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)", ifp->name, @@ -333,272 +346,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; + igmp_v3_recv_query(igmp, from_str, igmp_msg); } - /* - 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("", 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_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; - } - - 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, ""); - - 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 +358,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_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 +387,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 +407,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 +485,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 +520,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 +533,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("", 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 +565,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 +577,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("", 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 +623,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("", igmp->ifaddr, querier_str, sizeof(querier_str)); pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); @@ -926,124 +632,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, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) - sprintf(to_str, ""); - - 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, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) - sprintf(to_str, ""); - - 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 : "", - 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 +664,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 +712,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_addr, group_str, sizeof(group_str)); zlog_debug("Deleting IGMP group %s from socket %d interface %s", group_str, @@ -1168,6 +774,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 +804,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 +822,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 +834,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 +916,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 +945,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_addr, group_str, sizeof(group_str)); zlog_debug("%s: Timer for group %s on interface %s", __PRETTY_FUNCTION__, @@ -1315,7 +987,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_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 +1003,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_addr, group_str, sizeof(group_str)); zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s", interval_msec / 1000, @@ -1377,6 +1049,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 +1075,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 +1087,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 +1099,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 +1107,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_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 +1128,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); + } +} diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index eb0377ce8c..802f1ba471 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -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 */ diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c new file mode 100644 index 0000000000..ee4aa7bd9d --- /dev/null +++ b/pimd/pim_igmpv2.c @@ -0,0 +1,190 @@ +/* + * 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 "zebra.h" + +#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_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_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", 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_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", 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("", 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("", 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 index 0000000000..10a2477724 --- /dev/null +++ b/pimd/pim_igmpv2.h @@ -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 */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index bdaf2bb270..f7a6cbd0ce 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -46,8 +46,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_str, sizeof(from_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); @@ -57,50 +57,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 +88,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_addr, group_str, sizeof(group_str)); zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s", group_str, @@ -158,15 +114,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("%s: Source timer expired for group %s source %s on %s", @@ -176,7 +130,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 +184,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("Cancelling TIMER event for group %s source %s on %s", @@ -250,8 +204,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +245,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); @@ -383,7 +337,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 +352,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s", @@ -413,8 +367,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +437,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", src_addr, source_str, sizeof(source_str)); zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s", @@ -493,9 +447,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 +600,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 +658,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 +673,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 +893,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 +1008,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 +1041,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_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 +1056,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 +1103,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 +1140,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_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 +1160,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_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 +1175,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 +1202,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_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 +1217,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 +1242,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_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 +1275,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 +1311,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_addr, group_str, sizeof(group_str)); zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s", lmqi_msec / 1000, @@ -1565,7 +1540,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_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 +1575,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_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +1588,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 +1640,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 +1648,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_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", 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 +1669,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_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", 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 +1698,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_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", 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 +1707,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("", 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_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, ""); + + 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; } diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h index db7895f9be..3a4a81d97e 100644 --- a/pimd/pim_igmpv3.h +++ b/pimd/pim_igmpv3.h @@ -30,6 +30,13 @@ #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 +61,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 +87,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 */ diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 6a5fb851d6..028f77f532 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -23,6 +23,8 @@ #include "log.h" #include "prefix.h" #include "if.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" #include "pim_str.h" @@ -30,15 +32,19 @@ #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_str, sizeof(src_str)); zlog_debug("%s: from %s on %s", label, src_str, ifp->name); @@ -49,58 +55,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, up_str, sizeof(up_str)); - pim_inet4_dump("", source, src_str, sizeof(src_str)); - pim_inet4_dump("", group, grp_str, sizeof(grp_str)); pim_inet4_dump("", 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, up_str, sizeof(up_str)); - pim_inet4_dump("", source, src_str, sizeof(src_str)); - pim_inet4_dump("", group, grp_str, sizeof(grp_str)); pim_inet4_dump("", 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 +146,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 +155,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_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -141,8 +168,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_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 +180,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_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 +195,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_addr, src_str, sizeof(src_str)); pim_inet4_dump("", 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 +226,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_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 +239,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_addr, src_str, sizeof(src_str)); pim_inet4_dump("", msg_upstream_addr.u.prefix4, upstream_str, sizeof(upstream_str)); - pim_inet4_dump("", msg_group_addr.u.prefix4, + pim_inet4_dump("", 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 +268,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 +292,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 +305,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 +325,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("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (PIM_DEBUG_PIM_J_P) { + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (PIM_DEBUG_PIM_J_P) { + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", 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 +360,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("", 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("", 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("", 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, diff --git a/pimd/pim_join.h b/pimd/pim_join.h index dcdca00359..1eeeef756f 100644 --- a/pimd/pim_join.h +++ b/pimd/pim_join.h @@ -34,8 +34,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 */ diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c index 622bef4439..127e0f6252 100644 --- a/pimd/pim_macro.c +++ b/pimd/pim_macro.c @@ -21,22 +21,39 @@ #include #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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 */ } diff --git a/pimd/pim_main.c b/pimd/pim_main.c index ed8f69b517..0749d60b05 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -43,10 +43,9 @@ #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 +69,7 @@ zebra_capabilities_t _caps_p [] = ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, ZCAP_NET_RAW, + ZCAP_BIND, }; /* pimd privileges to run with */ @@ -104,18 +104,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 +168,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 +192,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 +244,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 diff --git a/pimd/pim_memory.c b/pimd/pim_memory.c index 6014725020..ccd0fa81ac 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -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") diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index 81841e58b6..b6b9b23239 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -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 */ diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 3fbed88800..dfd22b7022 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -23,8 +23,11 @@ #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" @@ -34,10 +37,14 @@ #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 +52,75 @@ 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 + 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)); + } - 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; - - 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) + { +#if defined linux + 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; + } +#else + zlog_warn ("PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall"); +#endif + } + return 0; } -static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = { +static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = { "", "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; + + 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__, + pim_str_sg_dump (&sg)); + } + return 0; } - up = pim_upstream_add(msg->im_src, msg->im_dst, ifp); + up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__); if (!up) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: Failure to add upstream information for (%s,%s)", + 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; } - 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)", - __PRETTY_FUNCTION__, - src_str, grp_str); - } - return 0; + /* + * 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_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE); + 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] = ""; - char grp_str[100] = ""; + char ip_src_str[INET_ADDRSTRLEN] = ""; + char ip_dst_str[INET_ADDRSTRLEN] = ""; + char src_str[INET_ADDRSTRLEN] = ""; + char grp_str[INET_ADDRSTRLEN] = ""; + 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) { + 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("", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str)); + pim_inet4_dump("", 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; + } + pim_ifp = ifp->info; + ifaddr = pim_find_primary_addr(ifp); + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr); + + if (PIM_DEBUG_MROUTE) { + pim_inet4_dump("", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str)); + pim_inet4_dump("", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str)); + + 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); + + } else if (ip_hdr->ip_p) { + if (PIM_DEBUG_MROUTE_DETAIL) { pim_inet4_dump("", ip_hdr->ip_src, src_str, sizeof(src_str)); pim_inet4_dump("", 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); + 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); } - return 0; - } - msg = (const struct igmpmsg *) buf; + } else { + msg = (const struct igmpmsg *) buf; - ifp = pim_if_find_by_vif_index(msg->im_vif); + ifp = pim_if_find_by_vif_index(msg->im_vif); - if (PIM_DEBUG_PIM_TRACE) { - pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 : "", - msg->im_vif); - } + if (!ifp) + return 0; + if (PIM_DEBUG_MROUTE) { + pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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); + } - 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; + 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 0; } -static int mroute_read_msg(int fd) -{ - const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg)); - char buf[1000]; - int rd; - - 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; - } - - 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 (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; - } - - return pim_mroute_msg(fd, buf, rd); -} - 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_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]; + if (PIM_DEBUG_MROUTE) + { + struct prefix_sg sg; - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + sg.src = c_oil->oil.mfcc_origin; + sg.grp = c_oil->oil.mfcc_mcastgrp; - 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)); + 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; } diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index f385ce09f4..3c15c800da 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -157,6 +157,10 @@ struct igmpmsg #endif #endif +#ifndef IGMPMSG_WRVIFWHOLE +#define IGMPMSG_WRVIFWHOLE 4 /* For PIM processing */ +#endif + /* Above: from */ @@ -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 index 0000000000..93141f39de --- /dev/null +++ b/pimd/pim_msdp.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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("", mp->peer, peer_str, sizeof(peer_str)); + if (long_format) { + pim_inet4_dump("", 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("", 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_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_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_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("", 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("", 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_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 index 0000000000..33c1d88a45 --- /dev/null +++ b/pimd/pim_msdp.h @@ -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 index 0000000000..fbf34cd91c --- /dev/null +++ b/pimd/pim_msdp_packet.c @@ -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 + +#include +#include +#include +#include +#include + +#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_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_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 index 0000000000..f6fcfee6bb --- /dev/null +++ b/pimd/pim_msdp_packet.h @@ -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 index 0000000000..bc9720f1f3 --- /dev/null +++ b/pimd/pim_msdp_socket.c @@ -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 + +#include +#include +#include +#include +#include + +#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 index 0000000000..bf3d12ad2c --- /dev/null +++ b/pimd/pim_msdp_socket.h @@ -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 diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 9d0fc0ad8f..7ea7b1ad82 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -21,11 +21,20 @@ #include #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 +95,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 +107,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("", 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("", 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("", 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; +} diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index 96a89659e6..5229f85c72 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -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 */ diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 04792eb35e..346b911157 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -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_addr, dr_old_str, sizeof(dr_old_str)); pim_inet4_dump("", 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("", 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("", 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("", 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("", 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. - */ - pim_hello_restart_triggered(neigh->interface); + 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. + */ + 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->u.prefix4, addr_str, sizeof(addr_str)); pim_inet4_dump("", neigh_addr, this_neigh_str, sizeof(this_neigh_str)); diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h index e023a7f1ef..211eda25c7 100644 --- a/pimd/pim_neighbor.h +++ b/pimd/pim_neighbor.h @@ -25,6 +25,7 @@ #include "if.h" #include "linklist.h" +#include "prefix.h" #include "pim_tlv.h" @@ -46,6 +47,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 +61,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); diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index ebbc6e19f9..0cebe47355 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -24,6 +24,8 @@ #include "memory.h" #include "linklist.h" #include "if.h" +#include "hash.h" +#include "jhash.h" #include "pimd.h" #include "pim_oil.h" @@ -31,104 +33,162 @@ #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_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", 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 +196,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 +293,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", 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 +318,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +339,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +362,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +382,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +396,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +418,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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +430,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)); +} diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 540acce3a3..143cfb7942 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -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 */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 0f41a43315..1dbbd7c655 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -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("", ip_hdr->ip_src, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", ip_hdr->ip_src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) - sprintf(to_str, ""); - - 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; + /* 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, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) - sprintf(to_str, ""); + if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); - recv_ifp = if_lookup_by_index(ifindex); - if (recv_ifp) { - zassert(ifindex == (int) recv_ifp->ifindex); - } + 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 : "", - 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 : "", + 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 ("", 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_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_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("", 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(); /* diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h index 5692a37938..00c5f9012e 100644 --- a/pimd/pim_pim.h +++ b/pimd/pim_pim.h @@ -28,8 +28,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 */ @@ -38,15 +36,17 @@ #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 +67,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, diff --git a/pimd/pim_register.c b/pimd/pim_register.c index ce3ac1a433..490a05be37 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -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" @@ -39,65 +42,150 @@ #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 (if_lookup_exact_address (&dest_addr, AF_INET)) - return 1; + 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); + } + + memset (buffer, 0, 10000); + b1 = (uint8_t *)buffer + PIM_MSG_REGISTER_STOP_LEN; + + 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); + } + } +} + +int +pim_register_stop_recv (uint8_t *buf, int buf_size) +{ + 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; } -static void -pim_register_stop_send (struct in_addr src) -{ - return; -} - 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 ("", 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_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; -} diff --git a/pimd/pim_register.h b/pimd/pim_register.h index 039c0006e0..42a908b225 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -31,15 +31,14 @@ #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 diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 26d108bcaa..ba464e98c6 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -20,48 +20,597 @@ */ #include +#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; - -/* - * Checks to see if we should elect ourself the actual RP - */ -void -pim_rp_check_rp (struct in_addr old, struct in_addr new) +struct rp_info { - if (PIM_DEBUG_ZEBRA) { - char sold[100]; - char snew[100]; - char rp[100]; - pim_inet4_dump("", qpim_rp.rpf_addr, rp, sizeof(rp)); - pim_inet4_dump("", old, sold, sizeof(sold)); - pim_inet4_dump("", new, snew, sizeof(snew)); - zlog_debug("%s: %s for old %s new %s", __func__, rp, sold, snew ); - } + struct prefix group; + struct pim_rpf rp; + int i_am_rp; + char *plist; +}; - if (qpim_rp.rpf_addr.s_addr == INADDR_NONE) +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; - if (new.s_addr == qpim_rp.rpf_addr.s_addr) + 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)) { - i_am_rp = 1; - return; + 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; + } } - if (old.s_addr == qpim_rp.rpf_addr.s_addr) + 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)) { - i_am_rp = 0; - return; + if (rp_info->plist && strcmp(rp_info->plist, plist) == 0) + { + return 1; + } } + + return 0; +} + +/* + * 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_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; + } + + 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; + } + + /* + * 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) + { + XFREE(MTYPE_PIM_FILTER_NAME, rp_info->plist); + rp_info->plist = NULL; + } + + str2prefix ("224.0.0.0/4", &g_all); + rp_all = pim_rp_find_match_group (&g_all); + + if (rp_all == rp_info) + { + 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_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_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); + } +} diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index bb785e7efc..b32228ed49 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -21,11 +21,29 @@ #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 diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 8d6e7b70c1..ae00e347b5 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -33,108 +33,160 @@ #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) { - struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + 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[MULTIPATH_NUM]; int num_ifindex; - struct interface *ifp; - int first_ifindex; + struct interface *ifp = NULL; + ifindex_t first_ifindex = 0; + 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_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_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_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_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_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_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_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str)); - pim_inet4_dump("", 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_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; + } - return 0; + if (!ifp->info) + { + if (PIM_DEBUG_ZEBRA) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", 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; + } + + if (found) + { + if (PIM_DEBUG_ZEBRA) { + char nexthop_str[PREFIX_STRLEN]; + char addr_str[INET_ADDRSTRLEN]; + pim_addr_dump("", &nexthop_tab[i].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", 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("", &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 : "", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 : "", rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); /* 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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; +} diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index 4d55bd6881..bb77775324 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -26,10 +26,48 @@ #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 */ diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 54816d126b..eda81c6571 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -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,58 @@ 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 = 0; +#ifdef SO_BINDTODEVICE + + 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)); + +#endif + return ret; +} + int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop) { + int rcvbuf = 1024 * 1024 * 8; +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + struct ip_mreqn mreq; +#else + struct ip_mreq mreq; +#endif int fd; fd = pim_socket_raw(protocol); @@ -86,17 +137,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 +221,28 @@ 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)); +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + mreq.imr_ifindex = ifindex; +#else + /* + * I am not sure what to do here yet for *BSD + */ + //mreq.imr_interface = ifindex; +#endif + 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 +287,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, ""); if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) @@ -245,8 +300,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, ""); if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) @@ -265,15 +320,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("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", 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 +352,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 +397,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->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 +409,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->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 diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index f905c661dd..aaf82e862a 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -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, diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index ece644a861..0354083332 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -25,11 +25,10 @@ #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 +95,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_str, sizeof(addr_str)); zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s", __PRETTY_FUNCTION__, @@ -195,12 +194,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("", 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 +217,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.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 +252,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("", 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 +262,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("", ss->source_addr, source_str, sizeof(source_str)); pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); @@ -283,9 +280,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("", ss->source_addr, source_str, sizeof(source_str)); pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); @@ -316,10 +313,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 +351,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("", 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("", 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 +393,7 @@ int pim_ssmpingd_start(struct in_addr source_addr) } { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_info("%s: starting ssmpingd for source %s", __PRETTY_FUNCTION__, source_str); @@ -407,7 +401,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("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: ssmpingd_new() failure for source %s", __PRETTY_FUNCTION__, source_str); @@ -423,7 +417,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("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: could not find ssmpingd for source %s", __PRETTY_FUNCTION__, source_str); @@ -431,7 +425,7 @@ int pim_ssmpingd_stop(struct in_addr source_addr) } { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_info("%s: stopping ssmpingd for source %s", __PRETTY_FUNCTION__, source_str); diff --git a/pimd/pim_static.c b/pimd/pim_static.c index 565d6fe728..e1ccec387a 100644 --- a/pimd/pim_static.c +++ b/pimd/pim_static.c @@ -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("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); - pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", 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)) { diff --git a/pimd/pim_str.c b/pimd/pim_str.c index c817045697..83f2a635b3 100644 --- a/pimd/pim_str.c +++ b/pimd/pim_str.c @@ -28,17 +28,62 @@ #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 ("", sg->src, src_str, sizeof(src_str)); + pim_inet4_dump ("", 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 ("", sg->src, src_str, sizeof(src_str)); + pim_inet4_dump ("", sg->grp, grp_str, sizeof(grp_str)); + snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str); + + return sg_str; +} diff --git a/pimd/pim_str.h b/pimd/pim_str.h index d2af0110a4..97263e6a37 100644 --- a/pimd/pim_str.h +++ b/pimd/pim_str.h @@ -25,6 +25,20 @@ #include #include +#include + +/* + * 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 diff --git a/pimd/pim_time.c b/pimd/pim_time.c index 75f767fef1..348793cd0b 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -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; diff --git a/pimd/pim_time.h b/pimd/pim_time.h index 8ccc6a9207..de304a9f71 100644 --- a/pimd/pim_time.h +++ b/pimd/pim_time.h @@ -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); diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 546ceb86e1..5223f60e1b 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -95,8 +95,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 +141,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 +243,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 +273,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_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 +292,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_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 +308,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_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 +324,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_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 +508,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 +550,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 +567,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 +611,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 +623,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 +678,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_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -599,8 +695,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("", tmp.u.prefix4, addr_str, sizeof(addr_str)); pim_inet4_dump("", 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 +708,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_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 +725,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_addr, src_str, sizeof(src_str)); zlog_warn("%s: ignoring primary address in secondary list from %s on %s", __PRETTY_FUNCTION__, diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h index 16c5aa4b97..9c4ebc9f0f 100644 --- a/pimd/pim_tlv.h +++ b/pimd/pim_tlv.h @@ -109,13 +109,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); diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 059de3b62e..fbb7d84a25 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -27,6 +27,11 @@ #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" @@ -44,10 +49,95 @@ #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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", 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("", &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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + if (PIM_DEBUG_TRACE) { + char rpf_str[INET_ADDRSTRLEN]; pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + if (PIM_DEBUG_TRACE) { + char rpf_str[INET_ADDRSTRLEN]; pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 iflist */ + } /* scan iface channel list */ } -static void pim_upstream_switch(struct pim_upstream *up, - enum pim_upstream_state new_state) +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; +} + +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("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", 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 struct pim_upstream *pim_upstream_new(struct in_addr source_addr, - struct in_addr group_addr, - struct interface *incoming) +static int +pim_upstream_compare (void *arg1, void *arg2) { - struct pim_upstream *up; - enum pim_rpf_result rpf_result; + const struct pim_upstream *up1 = (const struct pim_upstream *)arg1; + const struct pim_upstream *up2 = (const struct pim_upstream *)arg2; - up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + 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; + + return 0; +} + +static struct pim_upstream * +pim_upstream_new (struct prefix_sg *sg, + struct interface *incoming, + int flags) +{ + enum pim_rpf_result rpf_result; + struct pim_interface *pim_ifp; + struct pim_upstream *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; - rpf_result = pim_rpf_update(up, 0, incoming); + if (up->sg.src.s_addr != INADDR_ANY) + wheel_add_item (pim_upstream_sg_wheel, up); + + 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; + struct pim_upstream lookup; + struct pim_upstream *up = NULL; - 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; - } - } - } - - 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_addr, neigh_str, sizeof(neigh_str)); - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", 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("", &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; - if (ch->upstream != up) - continue; - - pim_ifchannel_update_could_assert(ch); - - } /* 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->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); + 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 (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; + /* 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__); + } + + 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; + +} diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index f10c8feb3b..1a50c0c6e3 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -22,52 +22,47 @@ #define PIM_UPSTREAM_H #include +#include + +#include #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 */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c index f5b6a8210a..1125db00a9 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -21,6 +21,7 @@ #include #include "log.h" +#include "prefix.h" #include "pim_util.h" @@ -103,3 +104,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); +} diff --git a/pimd/pim_util.h b/pimd/pim_util.h index d780bfbc27..94635466d9 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -32,4 +32,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 */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index f9bc2319e7..03ee7e92ec 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -22,7 +22,10 @@ #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" @@ -33,11 +36,26 @@ #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 +68,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 +99,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 +119,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 +186,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("", ss->source_addr, source_str, sizeof(source_str)); vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE); ++writes; @@ -159,12 +235,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("", 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 +271,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 +282,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("", ij->group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", 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); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index f2195960c4..b21da624d6 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -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("", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", 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 : "", c_oil->oil.mfcc_parent, - new_iif ? new_iif->name : "", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", 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 : "", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - pim_inet4_dump("", 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 : "", c_oil->oil.mfcc_parent, - new_iif ? new_iif->name : "", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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 : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", 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_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_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_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_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("", 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_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_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_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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", 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_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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_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_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_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", 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_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", ch->sg.src, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->sg.grp, group_str, sizeof(group_str)); pim_inet4_dump("", 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("", 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("", 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("", up->group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", 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; } diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index 257c9b896f..0c302efbd5 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -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 */ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index af561a0b62..61e3e27261 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -27,13 +27,17 @@ #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_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_str, sizeof(addr_str)); pim_inet4_dump("", 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_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_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_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_str, sizeof(addr_str)); - pim_inet4_dump("", 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_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_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_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_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_str, sizeof(addr_str)); - pim_inet4_dump("", nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_addr_dump("", &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_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, "%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; + +} diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h index dbce92647b..d8e7ff9e0d 100644 --- a/pimd/pim_zlookup.h +++ b/pimd/pim_zlookup.h @@ -28,18 +28,20 @@ #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 */ diff --git a/pimd/pimd.c b/pimd/pimd.c index 15e52afc1d..1627c4048d 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -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; /* diff --git a/pimd/pimd.h b/pimd/pimd.h index 2230a6ce91..20cf3c2dd2 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -23,13 +23,13 @@ #include +#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) @@ -65,9 +67,28 @@ #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); diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 65927262f2..428090d488 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -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) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index d553850546..660fad6530 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -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)) diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index a20597882c..896ca96b48 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -60,3 +60,5 @@ int kernel_neigh_update (int a, int b, uint32_t c, char *d, int e) void kernel_init (struct zebra_ns *zns) { return; } void kernel_terminate (struct zebra_ns *zns) { return; } void route_read (struct zebra_ns *zns) { return; } + +int kernel_get_ipmr_sg_stats (void *m) { return 0; } diff --git a/zebra/rt.h b/zebra/rt.h index e56e3b8fe3..40beb6a45f 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -40,4 +40,5 @@ extern int kernel_upd_lsp (zebra_lsp_t *); extern int kernel_del_lsp (zebra_lsp_t *); extern int mpls_kernel_init (void); +extern int kernel_get_ipmr_sg_stats (void *mroute); #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 913d1d5770..d2781f4c4e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -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 @@ -88,6 +90,10 @@ #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 @@ -520,15 +526,17 @@ netlink_route_change_read_unicast (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; - unsigned long long lastused = 0; struct rtmsg *rtm; struct rtattr *tb[RTA_MAX + 1]; - struct prefix_sg sg; + struct mcast_route_data *m; + struct mcast_route_data mr; int iif = 0; int count; int oif[256]; @@ -536,10 +544,16 @@ netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h char sbuf[40]; char gbuf[40]; char oif_list[256] = "\0"; - memset (&sg, 0, sizeof (sg)); - sg.family = IANA_AFI_IPMR; 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)); @@ -551,13 +565,13 @@ netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h iif = *(int *)RTA_DATA (tb[RTA_IIF]); if (tb[RTA_SRC]) - sg.src = *(struct in_addr *)RTA_DATA (tb[RTA_SRC]); + m->sg.src = *(struct in_addr *)RTA_DATA (tb[RTA_SRC]); if (tb[RTA_DST]) - sg.grp = *(struct in_addr *)RTA_DATA (tb[RTA_DST]); + m->sg.grp = *(struct in_addr *)RTA_DATA (tb[RTA_DST]); if ((RTA_EXPIRES <= RTA_MAX) && tb[RTA_EXPIRES]) - lastused = *(unsigned long long *)RTA_DATA (tb[RTA_EXPIRES]); + m->lastused = *(unsigned long long *)RTA_DATA (tb[RTA_EXPIRES]); if (tb[RTA_MULTIPATH]) { @@ -580,18 +594,20 @@ netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h if (IS_ZEBRA_DEBUG_KERNEL) { - strcpy (sbuf, inet_ntoa (sg.src)); - strcpy (gbuf, inet_ntoa (sg.grp)); + struct interface *ifp; + strcpy (sbuf, inet_ntoa (m->sg.src)); + strcpy (gbuf, inet_ntoa (m->sg.grp)); for (count = 0; count < oif_count; count++) { - struct interface *ifp = if_lookup_by_index_vrf (oif[count], vrf); + ifp = if_lookup_by_index_vrf (oif[count], vrf); char temp[256]; sprintf (temp, "%s ", ifp->name); strcat (oif_list, temp); } - zlog_debug ("MCAST %s (%s,%s) IIF: %d OIF: %s jiffies: %lld", - nl_msg_type_to_str (h->nlmsg_type), sbuf, gbuf, iif, oif_list, lastused); + 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; } @@ -1509,6 +1525,39 @@ skip: return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns); } +int +kernel_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 kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new) { diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index b2c99d9813..f65ec887dd 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -416,3 +416,9 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen) /* TODO */ return 0; } + +extern int +kernel_get_ipmr_sg_stats (void *mroute) +{ + return 0; +} diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 2c781899f4..9fffc9e611 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -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" diff --git a/zebra/zebra_mroute.c b/zebra/zebra_mroute.c new file mode 100644 index 0000000000..86356104bd --- /dev/null +++ b/zebra/zebra_mroute.c @@ -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 + +#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.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 = kernel_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 index 0000000000..c0bac43a81 --- /dev/null +++ b/zebra/zebra_mroute.h @@ -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 + diff --git a/zebra/zserv.c b/zebra/zserv.c index f53b2270f1..5a46de9ce9 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -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); @@ -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;