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;
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;
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 */
stream_putw (s, pkt_afi); /* AFI */
stream_putc (s, pkt_safi); /* SAFI */
+ if (nh_afi == AFI_MAX)
+ nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->extra->mp_nexthop_len);
/* Nexthop */
switch (nh_afi)
{
size_t mpattrlen_pos = 0;
mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi,
- (peer_cap_enhe(peer) ? AFI_IP6 : afi),
+ (peer_cap_enhe(peer) ? AFI_IP6 :
+ AFI_MAX), /* get from NH */
vecarr, attr);
bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag,
addpath_encode, addpath_tx_id);
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 */
if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
{
- vty_out (vty, "Half-life time: %ld min%s",
- damp->half_life / 60, VTY_NEWLINE);
+ vty_out (vty, "Half-life time: %lld min%s",
+ (long long)damp->half_life / 60, VTY_NEWLINE);
vty_out (vty, "Reuse penalty: %d%s",
- damp->reuse_limit, VTY_NEWLINE);
+ damp->reuse_limit, VTY_NEWLINE);
vty_out (vty, "Suppress penalty: %d%s",
- damp->suppress_value, VTY_NEWLINE);
- vty_out (vty, "Max suppress time: %ld min%s",
- damp->max_suppress_time / 60, VTY_NEWLINE);
+ damp->suppress_value, VTY_NEWLINE);
+ vty_out (vty, "Max suppress time: %lld min%s",
+ (long long)damp->max_suppress_time / 60, VTY_NEWLINE);
vty_out (vty, "Max supress penalty: %u%s",
- damp->ceiling, VTY_NEWLINE);
+ damp->ceiling, VTY_NEWLINE);
vty_out (vty, "%s", VTY_NEWLINE);
}
else
#include "bgpd/rfapi/rfapi_backend.h"
#endif
-#define BGP_VPNVX_HELP_STR \
- "Address Family \n" \
- "Address Family \n"
-
-static int
+extern int
argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, int *index, afi_t *afi)
{
int ret = 0;
if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8)
{
- zlog_err ("%s [Error] Update packet error / VPNv4 (prefix length %d less than VPNv4 min length)",
+ zlog_err ("%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)",
peer->host, prefixlen);
return -1;
}
/* sanity check against packet data */
if ((pnt + psize) > lim)
{
- zlog_err ("%s [Error] Update packet error / VPNv4 (prefix length %d exceeds packet size %u)",
+ zlog_err ("%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)",
peer->host,
prefixlen, (uint)(lim-pnt));
return -1;
/* sanity check against storage for the IP address portion */
if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t) sizeof(p.u))
{
- zlog_err ("%s [Error] Update packet error / VPNv4 (psize %d exceeds storage size %zu)",
+ zlog_err ("%s [Error] Update packet error / VPN (psize %d exceeds storage size %zu)",
peer->host,
prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, sizeof(p.u));
return -1;
/* Sanity check against max bitlen of the address family */
if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen (&p))
{
- zlog_err ("%s [Error] Update packet error / VPNv4 (psize %d exceeds family (%u) max byte len %u)",
+ zlog_err ("%s [Error] Update packet error / VPN (psize %d exceeds family (%u) max byte len %u)",
peer->host,
prefixlen - VPN_PREFIXLEN_MIN_BYTES*8,
p.family, prefix_blen (&p));
/* Packet length consistency check. */
if (pnt != lim)
{
- zlog_err ("%s [Error] Update packet error / VPNv4 (%zu data remaining after parsing)",
+ zlog_err ("%s [Error] Update packet error / VPN (%zu data remaining after parsing)",
peer->host, lim - pnt);
return -1;
}
return CMD_SUCCESS;
}
+#ifdef KEEP_OLD_VPN_COMMANDS
DEFUN (show_ip_bgp_vpn_all,
show_ip_bgp_vpn_all_cmd,
"show [ip] bgp <vpnv4|vpnv6>",
}
return CMD_SUCCESS;
}
+#endif /* KEEP_OLD_VPN_COMMANDS */
void
bgp_mplsvpn_init (void)
install_element (BGP_VPNV6_NODE, &no_vpnv6_network_cmd);
install_element (VIEW_NODE, &show_bgp_ip_vpn_rd_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
install_element (VIEW_NODE, &show_ip_bgp_vpn_all_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpn_rd_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpn_all_tags_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpn_rd_neighbor_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpn_all_neighbor_advertised_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpn_rd_neighbor_advertised_routes_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
}
(label) == MPLS_LABEL_IPV6_EXPLICIT_NULL || \
(label) == MPLS_LABEL_IMPLICIT_NULL)
+#define BGP_VPNVX_HELP_STR \
+ "Address Family \n" \
+ "Address Family \n"
+
struct rd_as
{
u_int16_t type;
extern int str2tag (const char *, u_char *);
extern char *prefix_rd2str (struct prefix_rd *, char *, size_t);
+extern int
+argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, int *index, afi_t *afi);
+
#endif /* _QUAGGA_BGP_MPLSVPN_H */
{
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)
{
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;
}
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))
{
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;
return 0;
}
- entry.mpc.afi = afi;
+ entry.mpc.afi = pkt_afi;
entry.mpc.safi = safi;
/* validate number field */
{
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);
{
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);
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",
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. */
{
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;
/* Generic MP capability data */
struct capability_mp_data
{
- afi_t afi;
+ iana_afi_t afi;
u_char reserved;
safi_t safi;
};
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)
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)
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. */
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;
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;
SET_FLAG(attr->rmap_change_flags, BATTR_REFLECTED);
#define NEXTHOP_IS_V6 (\
- (safi != SAFI_ENCAP && \
+ (safi != SAFI_ENCAP && safi != SAFI_MPLS_VPN &&\
(p->family == AF_INET6 || peer_cap_enhe(peer))) || \
- (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 16))
+ ((safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN) &&\
+ attr->extra->mp_nexthop_len >= IPV6_MAX_BYTELEN))
/* IPv6/MP starts with 1 nexthop. The link-local address is passed only if
* the peer (group) is configured to receive link-local nexthop unchanged
}
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))
return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST, uj);
}
-DEFUN (show_ip_bgp_vpnv4_neighbor_prefix_counts,
- show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd,
- "show [ip] bgp vpnv4 all neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]",
+#ifdef KEEP_OLD_VPN_COMMANDS
+DEFUN (show_ip_bgp_vpn_neighbor_prefix_counts,
+ show_ip_bgp_vpn_neighbor_prefix_counts_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]",
SHOW_STR
IP_STR
BGP_STR
- "Address Family\n"
+ BGP_VPNVX_HELP_STR
"Display information about all VPNv4 NLRIs\n"
"Detailed information on TCP and BGP neighbor connections\n"
"Neighbor to display information about\n"
return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MPLS_VPN, uj);
}
-DEFUN (show_ip_bgp_vpnv4_all_route_prefix,
- show_ip_bgp_vpnv4_all_route_prefix_cmd,
- "show [ip] bgp vpnv4 all <A.B.C.D|A.B.C.D/M> [json]",
+DEFUN (show_ip_bgp_vpn_all_route_prefix,
+ show_ip_bgp_vpn_all_route_prefix_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all <A.B.C.D|A.B.C.D/M> [json]",
SHOW_STR
IP_STR
BGP_STR
- "Address Family\n"
+ BGP_VPNVX_HELP_STR
"Display information about all VPNv4 NLRIs\n"
"Network in the BGP routing table to display\n"
"Network in the BGP routing table to display\n"
network = argv_find (argv, argc, "A.B.C.D/M", &idx) ? argv[idx]->arg : NULL;
return bgp_show_route (vty, NULL, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL, use_json(argc, argv));
}
+#endif /* KEEP_OLD_VPN_COMMANDS */
static void
show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd);
install_element (VIEW_NODE, &show_ip_bgp_dampening_params_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_parameters_cmd);
- install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_route_prefix_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element (VIEW_NODE, &show_ip_bgp_vpn_all_route_prefix_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
/* BGP dampening clear commands */
install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_neighbor_prefix_counts_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_prefix_counts_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_prefix_counts_cmd);
- install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element (ENABLE_NODE, &show_ip_bgp_vpn_neighbor_prefix_counts_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_prefix_counts_cmd);
install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_prefix_counts_cmd);
u_char tag[3];
};
+#define BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen) \
+ ((nhlen) < IPV4_MAX_BYTELEN ? 0 : \
+ ((nhlen) < IPV6_MAX_BYTELEN ? AFI_IP : AFI_IP6))
+
#define BGP_ATTR_NEXTHOP_AFI_IP6(attr) \
(! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)) && \
(attr)->extra && ((attr)->extra->mp_nexthop_len == 16 || \
route_set_ipv6_nexthop_peer_free
};
-/* `set vpnv4 nexthop A.B.C.D' */
+/* `set ip vpn nexthop A.B.C.D' */
static route_map_result_t
route_set_vpnv4_nexthop (void *rule, struct prefix *prefix,
return address;
}
+/* `set ipv6 vpn nexthop A.B.C.D' */
+
+static route_map_result_t
+route_set_vpnv6_nexthop (void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
+{
+ struct in6_addr *address;
+ struct bgp_info *bgp_info;
+
+ if (type == RMAP_BGP)
+ {
+ /* Fetch routemap's rule information. */
+ address = rule;
+ bgp_info = object;
+
+ /* Set next hop value. */
+ memcpy (&(bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global, address, sizeof(struct in6_addr));
+ (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL;
+ }
+
+ return RMAP_OKAY;
+}
+
+static void *
+route_set_vpnv6_nexthop_compile (const char *arg)
+{
+ int ret;
+ struct in6_addr *address;
+
+ address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr));
+ ret = inet_pton (AF_INET6, arg, address);
+
+ if (ret == 0)
+ {
+ XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
static void
-route_set_vpnv4_nexthop_free (void *rule)
+route_set_vpn_nexthop_free (void *rule)
{
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
/* Route map commands for ip nexthop set. */
struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd =
{
- "vpnv4 next-hop",
+ "ip vpn next-hop",
route_set_vpnv4_nexthop,
route_set_vpnv4_nexthop_compile,
- route_set_vpnv4_nexthop_free
+ route_set_vpn_nexthop_free
+};
+
+/* Route map commands for ip nexthop set. */
+struct route_map_rule_cmd route_set_vpnv6_nexthop_cmd =
+{
+ "ipv6 vpn next-hop",
+ route_set_vpnv6_nexthop,
+ route_set_vpnv6_nexthop_compile,
+ route_set_vpn_nexthop_free
};
/* `set originator-id' */
"ipv6 next-hop global", argv[idx_ipv6]->arg);
}
-DEFUN (set_vpnv4_nexthop,
- set_vpnv4_nexthop_cmd,
- "set vpnv4 next-hop A.B.C.D",
+#ifdef KEEP_OLD_VPN_COMMANDS
+DEFUN (set_vpn_nexthop,
+ set_vpn_nexthop_cmd,
+ "set <vpnv4|vpnv6> next-hop [A.B.C.D|X:X::X:X]",
SET_STR
"VPNv4 information\n"
- "VPNv4 next-hop address\n"
- "IP address of next hop\n")
+ "VPNv6 information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "IPv6 address of next hop\n")
{
- int idx_ipv4 = 3;
- return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
- "vpnv4 next-hop", argv[idx_ipv4]->arg);
-}
+ int idx_ip = 3;
+ afi_t afi;
+ int idx = 0;
+ if (argv_find_and_parse_vpnvx (argv, argc, &idx, &afi))
+ {
+ if (afi == AFI_IP)
+ return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
+ "ip vpn next-hop", argv[idx_ip]->arg);
+ else
+ return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
+ "ipv6 vpn next-hop", argv[idx_ip]->arg);
+ }
+ return CMD_SUCCESS;
+}
-DEFUN (no_set_vpnv4_nexthop,
- no_set_vpnv4_nexthop_cmd,
- "no set vpnv4 next-hop [A.B.C.D]",
+DEFUN (no_set_vpn_nexthop,
+ no_set_vpn_nexthop_cmd,
+ "no set vpn next-hop [A.B.C.D|X:X::X:X]",
NO_STR
SET_STR
- "VPNv4 information\n"
- "VPNv4 next-hop address\n"
- "IP address of next hop\n")
+ "VPN information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "IPv6 address of next hop\n")
{
- int idx_ipv4 = 4;
- if (argc <= idx_ipv4)
- return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
- "vpnv4 next-hop", NULL);
- return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
- "vpnv4 next-hop", argv[idx_ipv4]->arg);
+ int idx_ip = 4;
+ char *arg;
+ afi_t afi;
+ int idx = 0;
+
+ if (argc <= idx_ip)
+ arg = NULL;
+ else
+ arg = argv[idx_ip]->arg;
+ if (argv_find_and_parse_vpnvx (argv, argc, &idx, &afi))
+ {
+ if (afi == AFI_IP)
+ return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
+ "ip vpn next-hop", arg);
+ else
+ return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
+ "ipv6 vpn next-hop", argv[idx_ip]->arg);
+ }
+ return CMD_SUCCESS;
+}
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+DEFUN (set_ipx_vpn_nexthop,
+ set_ipx_vpn_nexthop_cmd,
+ "set <ipv4|ipv6> vpn next-hop [A.B.C.D|X:X::X:X]",
+ SET_STR
+ "IPv4 information\n"
+ "IPv6 information\n"
+ "VPN information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "IPv6 address of next hop\n")
+{
+ int idx_ip = 4;
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_afi (argv, argc, &idx, &afi))
+ {
+ if (afi == AFI_IP)
+ return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
+ "ip vpn next-hop", argv[idx_ip]->arg);
+ else
+ return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
+ "ipv6 vpn next-hop", argv[idx_ip]->arg);
+ }
+ return CMD_SUCCESS;
}
+DEFUN (no_set_ipx_vpn_nexthop,
+ no_set_ipx_vpn_nexthop_cmd,
+ "no set <ipv4|ipv6> vpn next-hop [A.B.C.D|X:X::X:X]",
+ NO_STR
+ SET_STR
+ "IPv4 information\n"
+ "IPv6 information\n"
+ "VPN information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "IPv6 address of next hop\n")
+{
+ int idx_ip = 5;
+ char *arg;
+ afi_t afi;
+ int idx = 0;
+
+ if (argc <= idx_ip)
+ arg = NULL;
+ else
+ arg = argv[idx_ip]->arg;
+ if (argv_find_and_parse_afi (argv, argc, &idx, &afi))
+ {
+ if (afi == AFI_IP)
+ return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
+ "ip vpn next-hop", arg);
+ else
+ return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
+ "ipv6 vpn next-hop", argv[idx_ip]->arg);
+ }
+ return CMD_SUCCESS;
+}
DEFUN (set_originator_id,
set_originator_id_cmd,
route_map_install_set (&route_set_community_cmd);
route_map_install_set (&route_set_community_delete_cmd);
route_map_install_set (&route_set_vpnv4_nexthop_cmd);
+ route_map_install_set (&route_set_vpnv6_nexthop_cmd);
route_map_install_set (&route_set_originator_id_cmd);
route_map_install_set (&route_set_ecommunity_rt_cmd);
route_map_install_set (&route_set_ecommunity_soo_cmd);
install_element (RMAP_NODE, &no_set_ecommunity_rt_cmd);
install_element (RMAP_NODE, &set_ecommunity_soo_cmd);
install_element (RMAP_NODE, &no_set_ecommunity_soo_cmd);
- install_element (RMAP_NODE, &set_vpnv4_nexthop_cmd);
- install_element (RMAP_NODE, &no_set_vpnv4_nexthop_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element (RMAP_NODE, &set_vpn_nexthop_cmd);
+ install_element (RMAP_NODE, &no_set_vpn_nexthop_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+ install_element (RMAP_NODE, &set_ipx_vpn_nexthop_cmd);
+ install_element (RMAP_NODE, &no_set_ipx_vpn_nexthop_cmd);
install_element (RMAP_NODE, &set_originator_id_cmd);
install_element (RMAP_NODE, &no_set_originator_id_cmd);
if (CHECK_FLAG (vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED))
{
u_int8_t nhlen;
+ afi_t nhafi = AFI_MAX; /* NH AFI is based on nhlen! */
int route_map_sets_nh;
nhlen = stream_getc_from (s, vec->offset);
-
- if (paf->afi == AFI_IP && !peer_cap_enhe(peer))
+ if (paf->afi == AFI_IP || paf->afi == AFI_IP6)
+ {
+ nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen);
+ if (peer_cap_enhe(peer))
+ nhafi = AFI_IP6;
+ if (paf->safi == SAFI_MPLS_VPN && /* if VPN && not global */
+ nhlen != BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+ nhafi = AFI_MAX; /* no change allowed */
+ }
+
+ if (nhafi == AFI_IP)
{
struct in_addr v4nh, *mod_v4nh;
int nh_modified = 0;
(bgp_multiaccess_check_v4 (v4nh, peer) == 0) &&
!CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) &&
- !peer_af_flag_check (peer, paf->afi, paf->safi,
+ !peer_af_flag_check (peer, nhafi, paf->safi,
PEER_FLAG_NEXTHOP_UNCHANGED))
{
+ /* NOTE: not handling case where NH has new AFI */
mod_v4nh = &peer->nexthop.v4;
nh_modified = 1;
}
- if (nh_modified)
- stream_put_in_addr_at (s, vec->offset + 1, mod_v4nh);
+ if (nh_modified) /* allow for VPN RD */
+ stream_put_in_addr_at (s, vec->offset + 1 + nhlen - 4, mod_v4nh);
if (bgp_debug_update(peer, NULL, NULL, 0))
- zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ nexthop %s",
- PAF_SUBGRP(paf)->update_group->id, PAF_SUBGRP(paf)->id,
- peer->host, inet_ntoa (*mod_v4nh));
-
+ zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ nexthop %s%s",
+ PAF_SUBGRP(paf)->update_group->id, PAF_SUBGRP(paf)->id,
+ peer->host, inet_ntoa (*mod_v4nh),
+ (nhlen == 12 ? " and RD" : ""));
}
- else if (paf->afi == AFI_IP6 || peer_cap_enhe(peer))
+ else if (nhafi == AFI_IP6)
{
struct in6_addr v6nhglobal, *mod_v6nhg;
struct in6_addr v6nhlocal, *mod_v6nhl;
else if (peer->sort == BGP_PEER_EBGP &&
!CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) &&
- !peer_af_flag_check (peer, paf->afi, paf->safi,
+ !peer_af_flag_check (peer, nhafi, paf->safi,
PEER_FLAG_NEXTHOP_UNCHANGED))
{
+ /* NOTE: not handling case where NH has new AFI */
mod_v6nhg = &peer->nexthop.v6_global;
gnh_modified = 1;
}
- if (nhlen == 32)
+ if (nhlen == 32 || nhlen == 48) /* 48 == VPN */
{
- stream_get_from (&v6nhlocal, s, vec->offset + 1 + 16, 16);
+ stream_get_from (&v6nhlocal, s, vec->offset + 1 + (nhlen-IPV6_MAX_BYTELEN), IPV6_MAX_BYTELEN);
if (IN6_IS_ADDR_UNSPECIFIED (&v6nhlocal))
{
mod_v6nhl = &peer->nexthop.v6_local;
}
if (gnh_modified)
- stream_put_in6_addr_at (s, vec->offset + 1, mod_v6nhg);
+ stream_put_in6_addr_at (s, vec->offset + 1 + (nhlen-IPV6_MAX_BYTELEN), mod_v6nhg);
if (lnh_modified)
- stream_put_in6_addr_at (s, vec->offset + 1 + 16, mod_v6nhl);
+ stream_put_in6_addr_at (s, vec->offset + 1 + (nhlen-IPV6_MAX_BYTELEN), mod_v6nhl);
if (bgp_debug_update(peer, NULL, NULL, 0))
{
- if (nhlen == 32)
- zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ mp_nexthops %s, %s",
+ if (nhlen == 32 || nhlen == 48)
+ zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ mp_nexthops %s, %s%s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id,
peer->host,
inet_ntop (AF_INET6, mod_v6nhg, buf, BUFSIZ),
- inet_ntop (AF_INET6, mod_v6nhl, buf2, BUFSIZ));
+ inet_ntop (AF_INET6, mod_v6nhl, buf2, BUFSIZ),
+ (nhlen == 48 ? " and RD" : ""));
else
- zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ mp_nexthop %s",
+ zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ mp_nexthop %s%s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id,
peer->host,
- inet_ntop (AF_INET6, mod_v6nhg, buf, BUFSIZ));
+ inet_ntop (AF_INET6, mod_v6nhg, buf, BUFSIZ),
+ (nhlen == 24 ? " and RD" : ""));
}
}
}
if (stream_empty (snlri))
mpattrlen_pos = bgp_packet_mpattr_start (snlri, afi, safi,
- (peer_cap_enhe(peer) ? AFI_IP6 : afi),
+ (peer_cap_enhe(peer) ? AFI_IP6 :
+ AFI_MAX), /* get from NH */
&vecarr, adv->baa->attr);
bgp_packet_mpattr_prefix (snlri, afi, safi, &rn->p, prd, tag,
addpath_encode, addpath_tx_id);
return CMD_SUCCESS;
}
+#ifdef KEEP_OLD_VPN_COMMANDS
DEFUN (address_family_vpnv4,
address_family_vpnv4_cmd,
"address-family vpnv4 [unicast]",
vty->node = BGP_VPNV6_NODE;
return CMD_SUCCESS;
}
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+DEFUN (address_family_ipv4_vpn,
+ address_family_ipv4_vpn_cmd,
+ "address-family ipv4 vpn",
+ "Enter Address Family command mode\n"
+ "Address Family\n"
+ "Subsequent Address Family modifier\n")
+{
+ vty->node = BGP_VPNV4_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (address_family_ipv6_vpn,
+ address_family_ipv6_vpn_cmd,
+ "address-family ipv6 vpn",
+ "Enter Address Family command mode\n"
+ "Address Family\n"
+ "Subsequent Address Family modifier\n")
+{
+ vty->node = BGP_VPNV6_NODE;
+ return CMD_SUCCESS;
+}
DEFUN (address_family_encap,
address_family_encap_cmd,
/* BGP Version. */
json_object_int_add(json_neigh, "bgpVersion", 4);
- json_object_string_add(json_neigh, "remoteRouterId", inet_ntop (AF_INET, &p->remote_id, buf1, BUFSIZ));
+ json_object_string_add(json_neigh, "remoteRouterId",
+ inet_ntop (AF_INET, &p->remote_id, buf1, sizeof(buf1)));
/* Confederation */
if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION) && bgp_confederation_peers_check (bgp, p->as))
/* BGP Version. */
vty_out (vty, " BGP version 4");
- vty_out (vty, ", remote router ID %s%s", inet_ntop (AF_INET, &p->remote_id, buf1, BUFSIZ),
+ vty_out (vty, ", remote router ID %s%s",
+ inet_ntop (AF_INET, &p->remote_id, buf1, sizeof(buf1)),
VTY_NEWLINE);
/* Confederation */
{
if (use_json)
{
- json_object_string_add(json_neigh, "nexthop", inet_ntop (AF_INET, &p->nexthop.v4, buf1, BUFSIZ));
- json_object_string_add(json_neigh, "nexthopGlobal", inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, BUFSIZ));
- json_object_string_add(json_neigh, "nexthopLocal", inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, BUFSIZ));
+ json_object_string_add(json_neigh, "nexthop",
+ inet_ntop (AF_INET, &p->nexthop.v4, buf1, sizeof(buf1)));
+ json_object_string_add(json_neigh, "nexthopGlobal",
+ inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, sizeof(buf1)));
+ json_object_string_add(json_neigh, "nexthopLocal",
+ inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, sizeof(buf1)));
if (p->shared_network)
json_object_string_add(json_neigh, "bgpConnection", "sharedNetwork");
else
else
{
vty_out (vty, "Nexthop: %s%s",
- inet_ntop (AF_INET, &p->nexthop.v4, buf1, BUFSIZ),
- VTY_NEWLINE);
+ inet_ntop (AF_INET, &p->nexthop.v4, buf1, sizeof(buf1)),
+ VTY_NEWLINE);
vty_out (vty, "Nexthop global: %s%s",
- inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, BUFSIZ),
- VTY_NEWLINE);
+ inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, sizeof(buf1)),
+ VTY_NEWLINE);
vty_out (vty, "Nexthop local: %s%s",
- inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, BUFSIZ),
- VTY_NEWLINE);
+ inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, sizeof(buf1)),
+ VTY_NEWLINE);
vty_out (vty, "BGP connection: %s%s",
- p->shared_network ? "shared network" : "non shared network",
- VTY_NEWLINE);
+ p->shared_network ? "shared network" : "non shared network",
+ VTY_NEWLINE);
}
}
install_element (BGP_NODE, &address_family_ipv4_safi_cmd);
install_element (BGP_NODE, &address_family_ipv6_cmd);
install_element (BGP_NODE, &address_family_ipv6_safi_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
install_element (BGP_NODE, &address_family_vpnv4_cmd);
-
install_element (BGP_NODE, &address_family_vpnv6_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+ install_element (BGP_NODE, &address_family_ipv4_vpn_cmd);
+ install_element (BGP_NODE, &address_family_ipv6_vpn_cmd);
install_element (BGP_NODE, &address_family_encap_cmd);
install_element (BGP_NODE, &address_family_encapv6_cmd);
}
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
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).
else if (safi == SAFI_MULTICAST)
vty_out (vty, "ipv4 multicast");
else if (safi == SAFI_MPLS_VPN)
- vty_out (vty, "vpnv4");
+ vty_out (vty, "ipv4 vpn");
else if (safi == SAFI_ENCAP)
- vty_out (vty, "encap");
+ vty_out (vty, "ipv4 encap");
}
else if (afi == AFI_IP6)
{
else if (safi == SAFI_MULTICAST)
vty_out (vty, "ipv6 multicast");
else if (safi == SAFI_MPLS_VPN)
- vty_out (vty, "vpnv6");
+ vty_out (vty, "ipv6 vpn");
else if (safi == SAFI_ENCAP)
- vty_out (vty, "encapv6");
+ vty_out (vty, "ipv6 encap");
}
vty_out (vty, "%s", VTY_NEWLINE);
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);
bgp_attr_extra_free (&attr);
return;
}
- nexthop = un_addr; /* UN used as MPLS NLRI nexthop */
}
if (local_pref)
break;
case BGP_ENCAP_TYPE_MPLS:
- _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls);
- bgp_encap_type_mpls_to_tlv (&tto->bgpinfo.mpls, attr);
+ /* nothing to do for MPLS */
break;
case BGP_ENCAP_TYPE_MPLS_IN_GRE:
for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i)
{
- vnc_zlog_debug_verbose ("backtrace[%2lu]: %s", i, syms[i]);
+ vnc_zlog_debug_verbose ("backtrace[%2zu]: %s", i, syms[i]);
}
free (syms);
rfapiGetTunnelType (attr, &tun_type);
if (p && tun_type == BGP_ENCAP_TYPE_MPLS)
{
- /* MPLS carries UN address in next hop */
- rfapiNexthop2Prefix (attr, p);
- if (p->family != 0)
- return 0;
+ return ENOENT; /* no UN for MPLS */
}
if (attr && attr->extra)
{
if (bi->extra != NULL)
vty_out (vty, " label=%u", decode_label (bi->extra->tag));
- if (rfapiGetVncLifetime (bi->attr, &lifetime))
- {
- if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
- {
- vty_out (vty, " life=none");
- }
- }
- else
+ if (!rfapiGetVncLifetime (bi->attr, &lifetime))
{
vty_out (vty, " life=%d", lifetime);
}
AS_HELP_STRING([--disable-rr-semantics], [disable the v6 Route Replace semantics]))
AC_ARG_ENABLE([protobuf],
AS_HELP_STRING([--enable-protobuf], [Enable experimental protobuf support]))
+AC_ARG_ENABLE([oldvpn_commands],
+ AS_HELP_STRING([--enable-old-vpn-commands], [Keep old vpn commands]))
AC_CHECK_HEADERS(json-c/json.h)
AC_CHECK_LIB(json-c, json_object_get, LIBS="$LIBS -ljson-c")
fi
fi
+#
+# Logic for old vpn commans support.
+#
+if test "$enable_old_vpn_commands" = "yes"; then
+ AC_DEFINE(KEEP_OLD_VPN_COMMANDS,, [Define for compiling with old vpn commands])
+fi
+
# Fail if the user explicity enabled protobuf support and we couldn't
# find the compiler or libraries.
if test "x$have_protobuf" = "xno" && test "x$enable_protobuf" = "xyes"; then
*list = NULL;
}
+#ifdef linux
static const char *
next_dirname(const char *s)
{
return cur;
}
-#ifdef linux
static void
add_namespace(const char *path)
{
int i = 0, fd;
char bpfdev[128];
struct ifreq ifr;
- u_int blen, immediate, seesent;
+ u_int blen, immediate;
+#ifdef BIOCSSEESENT
+ u_int seesent;
+#endif
struct timeval timeout;
struct bpf_program bpf_prog;
for (i = 0; i < vector_active (comps) && !exists; i++)
{
struct cmd_token *curr = vector_slot (comps, i);
+#ifdef VTYSH_DEBUG
exists = !strcmp (curr->text, token->text) &&
!strcmp (curr->desc, token->desc);
+#else
+ exists = !strcmp (curr->text, token->text);
+#endif /* VTYSH_DEBUG */
}
if (!exists)
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
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;
void (*slot_run) (void *);
};
+/*
+ * Creates a timer wheel
+ *
+ * master - Thread master structure for the process
+ * period - The Time in seconds that the timer wheel will
+ * take before it starts issuing commands again
+ * for items in each slot
+ * slots - The number of slots to have in this particular
+ * timer wheel
+ * slot_key - A hashing function of some sort that will allow
+ * the timer wheel to put items into individual slots
+ * slot_run - The function to run over each item in a particular slot
+ *
+ * Creates a timer wheel that will wake up 'slots' times over the entire
+ * wheel. Each time the timer wheel wakes up it will iterate through
+ * and run the slot_run function for each item stored in that particular
+ * slot.
+ *
+ * The timer code is 'intelligent' in that it notices if anything is
+ * in a particular slot and can schedule the next timer to skip
+ * the empty slot.
+ *
+ * The general purpose of a timer wheel is to reduce events in a system.
+ * A perfect example of usage for this is say hello packets that need
+ * to be sent out to all your neighbors. Suppose a large routing protocol
+ * has to send keepalive packets every Y seconds to each of it's peers.
+ * At scale we can have a very large number of peers, X.
+ * This means that we will have X timing events every Y seconds.
+ * If you replace these events with a timer wheel that has Z slots
+ * you will have at most Y/Z timer events if each slot has a work item
+ * in it.
+ *
+ * When X is large the number of events in a system can quickly escalate
+ * and cause significant amount of time handling thread events instead
+ * of running your code.
+ */
struct timer_wheel *wheel_init (struct thread_master *master, int period, size_t slots,
unsigned int (*slot_key) (void *),
void (*slot_run) (void *));
+
+/*
+ * Delete the specified timer wheel created
+ */
void wheel_delete (struct timer_wheel *);
/*
int wheel_stop (struct timer_wheel *wheel);
/*
- * Start the wheel from running again
+ * Start the wheel running again
*/
int wheel_start (struct timer_wheel *wheel);
/*
+ * wheel - The Timer wheel being modified
+ * item - The generic data structure that will be handed
+ * to the slot_run function.
+ *
* Add item to a slot setup by the slot_key,
* possibly change next time pop.
*/
int wheel_add_item (struct timer_wheel *wheel, void *item);
/*
+ * wheel - The Timer wheel being modified.
+ * item - The item to remove from one of the slots in
+ * the timer wheel.
+ *
* Remove a item to a slot setup by the slot_key,
* possibly change next time pop.
*/
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
#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
* 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
#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;
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;
# 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
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
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)
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
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
Please post comments, questions, patches, bug reports at the
support site:
- https://github.com/udhos/qpimd
+ https://freerangerouting/frr
RELATED WORK
if (PIM_DEBUG_PIM_EVENTS) {
if (ch->ifassert_state != new_state) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str,
- pim_ifchannel_ifassert_name(ch->ifassert_state),
- pim_ifchannel_ifassert_name(new_state),
- ch->interface->name);
+ zlog_debug("%s: (S,G)=%s assert state changed from %s to %s on interface %s",
+ __PRETTY_FUNCTION__,
+ ch->sg_str,
+ pim_ifchannel_ifassert_name(ch->ifassert_state),
+ pim_ifchannel_ifassert_name(new_state),
+ ch->interface->name);
}
if (winner_changed) {
- char src_str[100];
- char grp_str[100];
- char was_str[100];
- char winner_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ char was_str[INET_ADDRSTRLEN];
+ char winner_str[INET_ADDRSTRLEN];
pim_inet4_dump("<was?>", ch->ifassert_winner, was_str, sizeof(was_str));
pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
- zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str,
- was_str, winner_str, ch->interface->name);
+ zlog_debug("%s: (S,G)=%s assert winner changed from %s to %s on interface %s",
+ __PRETTY_FUNCTION__,
+ ch->sg_str,
+ was_str, winner_str, ch->interface->name);
}
} /* PIM_DEBUG_PIM_EVENTS */
struct interface *ifp, struct in_addr src)
{
if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
zlog_debug("%s: from %s on %s",
label, src_str, ifp->name);
{
if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
if (assert_action_a1(ch)) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+ zlog_warn("%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
__PRETTY_FUNCTION__, caller,
- src_str, grp_str, ch->interface->name);
+ ch->sg_str, ch->interface->name);
/* log warning only */
}
}
struct pim_assert_metric recv_metric)
{
struct pim_ifchannel *ch;
+ struct prefix_sg sg;
- ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = source_addr;
+ sg.grp = group_addr;
+ ch = pim_ifchannel_add(ifp, &sg, 0);
if (!ch) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
+ zlog_warn("%s: (S,G)=%s failure creating channel on interface %s",
__PRETTY_FUNCTION__,
- source_str, group_str, ifp->name);
+ pim_str_sg_dump (&sg), ifp->name);
return -1;
}
}
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);
}
}
break;
default:
{
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
+ zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
__PRETTY_FUNCTION__,
- source_str, group_str, ch->ifassert_state, ifp->name);
+ ch->sg_str, ch->ifassert_state, ifp->name);
}
return -2;
}
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;
/*
Parse assert group addr
*/
- offset = pim_parse_addr_group (&msg_group_addr, curr, curr_size);
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ offset = pim_parse_addr_group (&sg, curr, curr_size);
if (offset < 1) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
__PRETTY_FUNCTION__,
*/
offset = pim_parse_addr_ucast (&msg_source_addr, curr, curr_size);
if (offset < 1) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
__PRETTY_FUNCTION__,
curr_size -= offset;
if (curr_size != 8) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
__PRETTY_FUNCTION__,
msg_metric.route_metric = pim_read_uint32_host(curr);
if (PIM_DEBUG_PIM_TRACE) {
- char neigh_str[100];
- char source_str[100];
- char group_str[100];
+ char neigh_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
+ pim_inet4_dump("<grp?>", sg.grp, group_str, sizeof(group_str));
zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
__PRETTY_FUNCTION__, neigh_str, ifp->name,
source_str, group_str,
return dispatch_assert(ifp,
msg_source_addr.u.prefix4,
- msg_group_addr.u.prefix4,
+ sg.grp,
msg_metric);
}
remain,
group_addr);
if (!pim_msg_curr) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: failure encoding group address %s: space left=%d",
__PRETTY_FUNCTION__, group_str, remain);
remain,
source_addr);
if (!pim_msg_curr) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure encoding source address %s: space left=%d",
__PRETTY_FUNCTION__, source_str, remain);
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);
pim_hello_require(ifp);
if (PIM_DEBUG_PIM_TRACE) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+ zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
__PRETTY_FUNCTION__,
- ifp->name, source_str, group_str,
+ ifp->name, ch->sg_str,
metric.metric_preference,
metric.route_metric,
PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
}
if (pim_msg_send(pim_ifp->pim_sock_fd,
+ pim_ifp->primary_address,
qpim_all_pim_routers_addr,
pim_msg,
pim_msg_size,
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);
}
struct pim_ifchannel *ch;
struct interface *ifp;
- zassert(t);
ch = THREAD_ARG(t);
- zassert(ch);
ifp = ch->interface;
- zassert(ifp);
if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
+ zlog_debug("%s: (S,G)=%s timer expired on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
+ ch->sg_str, ifp->name);
}
- ch->t_ifassert_timer = 0;
+ ch->t_ifassert_timer = NULL;
switch (ch->ifassert_state) {
case PIM_IFASSERT_I_AM_WINNER:
- zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
assert_action_a3(ch);
break;
case PIM_IFASSERT_I_AM_LOSER:
break;
default:
{
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
- __PRETTY_FUNCTION__,
- source_str, group_str, ch->ifassert_state, ifp->name);
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
+ __PRETTY_FUNCTION__,
+ ch->sg_str, ch->ifassert_state, ifp->name);
}
}
static void assert_timer_off(struct pim_ifchannel *ch)
{
- struct interface *ifp;
-
- zassert(ch);
- ifp = ch->interface;
- zassert(ifp);
-
if (PIM_DEBUG_PIM_TRACE) {
if (ch->t_ifassert_timer) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
+ zlog_debug("%s: (S,G)=%s cancelling timer on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
+ ch->sg_str, ch->interface->name);
}
}
THREAD_OFF(ch->t_ifassert_timer);
- zassert(!ch->t_ifassert_timer);
}
static void pim_assert_timer_set(struct pim_ifchannel *ch,
int interval)
{
- struct interface *ifp;
-
- zassert(ch);
- ifp = ch->interface;
- zassert(ifp);
-
assert_timer_off(ch);
if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
+ zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, interval, ifp->name);
+ ch->sg_str, interval, ch->interface->name);
}
THREAD_TIMER_ON(master, ch->t_ifassert_timer,
struct interface *ifp = ch->interface;
struct pim_interface *pim_ifp;
- zassert(ifp);
-
pim_ifp = ifp->info;
if (!pim_ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s",
+ zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
+ ch->sg_str, ifp->name);
return -1; /* must return since pim_ifp is used below */
}
pim_macro_spt_assert_metric(&ch->upstream->rpf,
pim_ifp->primary_address));
- zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
if (assert_action_a3(ch)) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
+ zlog_warn("%s: (S,G)=%s assert_action_a3 failure on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
+ ch->sg_str, ifp->name);
/* warning only */
}
- zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+ if (ch->ifassert_state != PIM_IFASSERT_I_AM_WINNER)
+ {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
+ __PRETTY_FUNCTION__, ch->sg_str);
+ }
return 0;
}
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);
+ }
}
/*
*/
static int assert_action_a3(struct pim_ifchannel *ch)
{
- zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+ if (ch->ifassert_state != PIM_IFASSERT_I_AM_WINNER)
+ {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_warn("%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
+ __PRETTY_FUNCTION__, ch->sg_str);
+ return -1;
+ }
pim_assert_timer_reset(ch);
if (pim_assert_send(ch)) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
-
- zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
+ zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ch->interface->name);
+ ch->sg_str, ch->interface->name);
return -1;
}
- zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
-
return 0;
}
void assert_action_a4(struct pim_ifchannel *ch)
{
if (pim_assert_cancel(ch)) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
+ zlog_warn("%s: failure sending AssertCancel%s on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ch->interface->name);
+ ch->sg_str, ch->interface->name);
/* log warning only */
}
assert_action_a5(ch);
- zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+ if (ch->ifassert_state != PIM_IFASSERT_NOINFO)
+ {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_warn("%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
+ __PRETTY_FUNCTION__, ch->sg_str);
+ }
}
/*
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);
+ }
}
/*
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);
+ }
}
#include "linklist.h"
struct pim_br {
- struct in_addr source;
- struct in_addr group;
+ struct prefix_sg sg;
struct in_addr pmbr;
};
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;
}
}
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;
}
return;
}
- pim_br->source = source;
- pim_br->group = group;
+ pim_br->sg = *sg;
listnode_add(pim_br_list, pim_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;
}
#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);
#include <zebra.h>
+#include "lib/json.h"
#include "command.h"
#include "if.h"
#include "prefix.h"
#include "zclient.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_mroute.h"
#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,
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;
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 */
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();
"Interface Address Source Group State Winner Uptime Timer%s",
VTY_NEWLINE);
- for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *ch_node;
- struct pim_ifchannel *ch;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[INET_ADDRSTRLEN];
+ char ch_grp_str[INET_ADDRSTRLEN];
+ char winner_str[INET_ADDRSTRLEN];
+ char uptime[10];
+ char timer[10];
- pim_ifp = ifp->info;
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
ifaddr = pim_ifp->primary_address;
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
- char ch_src_str[100];
- char ch_grp_str[100];
- char winner_str[100];
- char uptime[10];
- char timer[10];
-
- pim_inet4_dump("<ch_src?>", ch->source_addr,
- ch_src_str, sizeof(ch_src_str));
- pim_inet4_dump("<ch_grp?>", ch->group_addr,
- ch_grp_str, sizeof(ch_grp_str));
- pim_inet4_dump("<assrt_win?>", ch->ifassert_winner,
- winner_str, sizeof(winner_str));
+ pim_inet4_dump("<ch_src?>", ch->sg.src,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+ ch_grp_str, sizeof(ch_grp_str));
+ pim_inet4_dump("<assrt_win?>", ch->ifassert_winner,
+ winner_str, sizeof(winner_str));
- pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
- pim_time_timer_to_mmss(timer, sizeof(timer),
- ch->t_ifassert_timer);
+ pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
+ pim_time_timer_to_mmss(timer, sizeof(timer),
+ ch->t_ifassert_timer);
- vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- ch_src_str,
- ch_grp_str,
- pim_ifchannel_ifassert_name(ch->ifassert_state),
- winner_str,
- uptime,
- timer,
- VTY_NEWLINE);
- } /* scan interface channels */
- } /* scan interfaces */
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s",
+ ch->interface->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ pim_ifchannel_ifassert_name(ch->ifassert_state),
+ winner_str,
+ uptime,
+ timer,
+ VTY_NEWLINE);
+ } /* scan interface channels */
}
static void pim_show_assert_internal(struct vty *vty)
{
- struct listnode *ifnode;
- struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+ struct in_addr ifaddr;
vty_out(vty,
"CA: CouldAssert%s"
"Interface Address Source Group CA eCA ATD eATD%s",
VTY_NEWLINE);
- for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *ch_node;
- struct pim_ifchannel *ch;
-
- pim_ifp = ifp->info;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
ifaddr = pim_ifp->primary_address;
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
- char ch_src_str[100];
- char ch_grp_str[100];
+ char ch_src_str[INET_ADDRSTRLEN];
+ char ch_grp_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<ch_src?>", ch->source_addr,
- ch_src_str, sizeof(ch_src_str));
- pim_inet4_dump("<ch_grp?>", ch->group_addr,
- ch_grp_str, sizeof(ch_grp_str));
- vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- ch_src_str,
- ch_grp_str,
- PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
- pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
- PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no",
- pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no",
- VTY_NEWLINE);
- } /* scan interface channels */
- } /* scan interfaces */
+ pim_inet4_dump("<ch_src?>", ch->sg.src,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+ ch_grp_str, sizeof(ch_grp_str));
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s",
+ ch->interface->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
+ pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
+ PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no",
+ pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no",
+ VTY_NEWLINE);
+ } /* scan interface channels */
}
static void pim_show_assert_metric(struct vty *vty)
{
- struct listnode *ifnode;
- struct interface *ifp;
-
+ struct pim_interface *pim_ifp;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+ struct in_addr ifaddr;
+
vty_out(vty,
"Interface Address Source Group RPT Pref Metric Address %s",
VTY_NEWLINE);
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *ch_node;
- struct pim_ifchannel *ch;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+ pim_ifp = ch->interface->info;
- pim_ifp = ifp->info;
-
if (!pim_ifp)
continue;
ifaddr = pim_ifp->primary_address;
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
- char ch_src_str[100];
- char ch_grp_str[100];
- char addr_str[100];
- struct pim_assert_metric am;
+ char ch_src_str[INET_ADDRSTRLEN];
+ char ch_grp_str[INET_ADDRSTRLEN];
+ char addr_str[INET_ADDRSTRLEN];
+ struct pim_assert_metric am;
- am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
+ am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
- pim_inet4_dump("<ch_src?>", ch->source_addr,
- ch_src_str, sizeof(ch_src_str));
- pim_inet4_dump("<ch_grp?>", ch->group_addr,
- ch_grp_str, sizeof(ch_grp_str));
- pim_inet4_dump("<addr?>", am.ip_address,
- addr_str, sizeof(addr_str));
+ pim_inet4_dump("<ch_src?>", ch->sg.src,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+ ch_grp_str, sizeof(ch_grp_str));
+ pim_inet4_dump("<addr?>", am.ip_address,
+ addr_str, sizeof(addr_str));
- vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- ch_src_str,
- ch_grp_str,
- am.rpt_bit_flag ? "yes" : "no",
- am.metric_preference,
- am.route_metric,
- addr_str,
- VTY_NEWLINE);
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s",
+ ch->interface->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ am.rpt_bit_flag ? "yes" : "no",
+ am.metric_preference,
+ am.route_metric,
+ addr_str,
+ VTY_NEWLINE);
} /* scan interface channels */
- } /* scan interfaces */
}
static void pim_show_assert_winner_metric(struct vty *vty)
{
- struct listnode *ifnode;
- struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+ struct in_addr ifaddr;
vty_out(vty,
"Interface Address Source Group RPT Pref Metric Address %s",
VTY_NEWLINE);
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *ch_node;
- struct pim_ifchannel *ch;
-
- pim_ifp = ifp->info;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
ifaddr = pim_ifp->primary_address;
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
- char ch_src_str[100];
- char ch_grp_str[100];
- char addr_str[100];
- struct pim_assert_metric *am;
- char pref_str[5];
- char metr_str[7];
-
- am = &ch->ifassert_winner_metric;
-
- pim_inet4_dump("<ch_src?>", ch->source_addr,
- ch_src_str, sizeof(ch_src_str));
- pim_inet4_dump("<ch_grp?>", ch->group_addr,
- ch_grp_str, sizeof(ch_grp_str));
- pim_inet4_dump("<addr?>", am->ip_address,
- addr_str, sizeof(addr_str));
-
- if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
- snprintf(pref_str, sizeof(pref_str), "INFI");
- else
- snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference);
+ char ch_src_str[INET_ADDRSTRLEN];
+ char ch_grp_str[INET_ADDRSTRLEN];
+ char addr_str[INET_ADDRSTRLEN];
+ struct pim_assert_metric *am;
+ char pref_str[5];
+ char metr_str[7];
+
+ am = &ch->ifassert_winner_metric;
+
+ pim_inet4_dump("<ch_src?>", ch->sg.src,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+ ch_grp_str, sizeof(ch_grp_str));
+ pim_inet4_dump("<addr?>", am->ip_address,
+ addr_str, sizeof(addr_str));
+
+ if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
+ snprintf(pref_str, sizeof(pref_str), "INFI");
+ else
+ snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference);
+
+ if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
+ snprintf(metr_str, sizeof(metr_str), "INFI");
+ else
+ snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s",
+ ch->interface->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ am->rpt_bit_flag ? "yes" : "no",
+ pref_str,
+ metr_str,
+ addr_str,
+ VTY_NEWLINE);
+ } /* scan interface channels */
+}
- if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
- snprintf(metr_str, sizeof(metr_str), "INFI");
- else
- snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);
+static void json_object_pim_ifp_add(struct json_object *json, struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
- vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- ch_src_str,
- ch_grp_str,
- am->rpt_bit_flag ? "yes" : "no",
- pref_str,
- metr_str,
- addr_str,
- VTY_NEWLINE);
- } /* scan interface channels */
- } /* scan interfaces */
+ pim_ifp = ifp->info;
+ json_object_string_add(json, "name", ifp->name);
+ json_object_string_add(json, "state", if_is_up(ifp) ? "up" : "down");
+ json_object_string_add(json, "address", inet_ntoa(pim_ifp->primary_address));
+ json_object_int_add(json, "index", ifp->ifindex);
+
+ if (if_is_multicast(ifp))
+ json_object_boolean_true_add(json, "flagMulticast");
+
+ if (if_is_broadcast(ifp))
+ json_object_boolean_true_add(json, "flagBroadcast");
+
+ if (ifp->flags & IFF_ALLMULTI)
+ json_object_boolean_true_add(json, "flagAllMulticast");
+
+ if (ifp->flags & IFF_PROMISC)
+ json_object_boolean_true_add(json, "flagPromiscuous");
+
+ if (PIM_IF_IS_DELETED(ifp))
+ json_object_boolean_true_add(json, "flagDeleted");
+
+ if (pim_if_lan_delay_enabled(ifp))
+ json_object_boolean_true_add(json, "lanDelayEnabled");
}
-static void pim_show_membership(struct vty *vty)
+static void pim_show_membership(struct vty *vty, u_char uj)
{
- struct listnode *ifnode;
- struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+ enum json_type type;
+ json_object *json = NULL;
+ json_object *json_iface = NULL;
+ json_object *json_row = NULL;
+ json_object *json_tmp = NULL;
- vty_out(vty,
- "Interface Address Source Group Membership%s",
- VTY_NEWLINE);
+ json = json_object_new_object();
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *ch_node;
- struct pim_ifchannel *ch;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
+
+ pim_ifp = ch->interface->info;
- pim_ifp = ifp->info;
-
if (!pim_ifp)
continue;
- ifaddr = pim_ifp->primary_address;
+ char ch_src_str[INET_ADDRSTRLEN];
+ char ch_grp_str[INET_ADDRSTRLEN];
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
- char ch_src_str[100];
- char ch_grp_str[100];
+ pim_inet4_dump("<ch_src?>", ch->sg.src,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+ ch_grp_str, sizeof(ch_grp_str));
- pim_inet4_dump("<ch_src?>", ch->source_addr,
- ch_src_str, sizeof(ch_src_str));
- pim_inet4_dump("<ch_grp?>", ch->group_addr,
- ch_grp_str, sizeof(ch_grp_str));
+ json_object_object_get_ex(json, ch->interface->name, &json_iface);
- vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- ch_src_str,
- ch_grp_str,
- ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ?
- "NOINFO" : "INCLUDE",
- VTY_NEWLINE);
- } /* scan interface channels */
- } /* scan interfaces */
+ if (!json_iface) {
+ json_iface = json_object_new_object();
+ json_object_pim_ifp_add(json_iface, ch->interface);
+ json_object_object_add(json, ch->interface->name, json_iface);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "source", ch_src_str);
+ json_object_string_add(json_row, "group", ch_grp_str);
+ json_object_string_add(json_row, "localMembership",
+ ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ? "NOINFO" : "INCLUDE");
+ json_object_object_add(json_iface, ch_grp_str, json_row);
+ } /* scan interface channels */
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ } else {
+ vty_out(vty,
+ "Interface Address Source Group Membership%s",
+ VTY_NEWLINE);
+
+ /*
+ * Example of the json data we are traversing
+ *
+ * {
+ * "swp3":{
+ * "name":"swp3",
+ * "state":"up",
+ * "address":"10.1.20.1",
+ * "index":5,
+ * "flagMulticast":true,
+ * "flagBroadcast":true,
+ * "lanDelayEnabled":true,
+ * "226.10.10.10":{
+ * "source":"*",
+ * "group":"226.10.10.10",
+ * "localMembership":"INCLUDE"
+ * }
+ * }
+ * }
+ */
+
+ /* foreach interface */
+ json_object_object_foreach(json, key, val) {
+
+ /* Find all of the keys where the val is an object. In the example
+ * above the only one is 226.10.10.10
+ */
+ json_object_object_foreach(val, if_field_key, if_field_val) {
+ type = json_object_get_type(if_field_val);
+
+ if (type == json_type_object) {
+ vty_out(vty, "%-9s ", key);
+
+ json_object_object_get_ex(val, "address", &json_tmp);
+ vty_out(vty, "%-15s ", json_object_get_string(json_tmp));
+
+ json_object_object_get_ex(if_field_val, "source", &json_tmp);
+ vty_out(vty, "%-15s ", json_object_get_string(json_tmp));
+
+ /* Group */
+ vty_out(vty, "%-15s ", if_field_key);
+
+ json_object_object_get_ex(if_field_val, "localMembership", &json_tmp);
+ vty_out(vty, "%-10s%s", json_object_get_string(json_tmp), VTY_NEWLINE);
+ }
+ }
+ }
+ }
+
+ json_object_free(json);
+}
+static void 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)
+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;
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)
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;
pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, join_node, ij)) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
char uptime[10];
pim_time_uptime(uptime, sizeof(uptime), now - ij->sock_creation);
}
-static void show_interface_address(struct vty *vty)
+static void pim_show_interfaces_single(struct vty *vty, const char *ifname, u_char uj)
{
- struct listnode *ifpnode;
+ struct in_addr ifaddr;
struct interface *ifp;
-
- vty_out(vty,
- "Interface Primary Secondary %s",
- VTY_NEWLINE);
-
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifpnode, ifp)) {
- struct listnode *ifcnode;
- struct connected *ifc;
- struct in_addr pri_addr;
- char pri_addr_str[100];
-
- pri_addr = pim_find_primary_addr(ifp);
-
- pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
-
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) {
- char sec_addr_str[100];
- struct prefix *p = ifc->address;
-
- if (p->family != AF_INET)
- continue;
-
- if (p->u.prefix4.s_addr == pri_addr.s_addr) {
- sec_addr_str[0] = '\0';
- }
- else {
- pim_inet4_dump("<sec?>", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str));
- }
-
- vty_out(vty, "%-9s %-15s %-15s%s",
- ifp->name,
- pri_addr_str,
- sec_addr_str,
- VTY_NEWLINE);
- }
- }
-}
+ struct listnode *neighnode;
+ struct listnode*node;
+ struct listnode *upnode;
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+ struct pim_upstream *up;
+ time_t now;
+ char dr_str[INET_ADDRSTRLEN];
+ char dr_uptime[10];
+ char expire[10];
+ char grp_str[INET_ADDRSTRLEN];
+ char hello_period[10];
+ char hello_timer[10];
+ char neigh_src_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char stat_uptime[10];
+ char uptime[10];
+ int mloop;
+ int found_ifname = 0;
+ int print_header;
+ json_object *json = NULL;
+ json_object *json_row = NULL;
+ json_object *json_pim_neighbor = NULL;
+ json_object *json_pim_neighbors = NULL;
+ json_object *json_group = NULL;
+ json_object *json_group_source = NULL;
+ json_object *json_fhr_sources = NULL;
+ struct pim_secondary_addr *sec_addr;
+ struct listnode *sec_node;
-static void pim_show_dr(struct vty *vty)
-{
- struct listnode *node;
- struct interface *ifp;
- time_t now;
-
now = pim_time_monotonic_sec();
- vty_out(vty,
- "NonPri: Number of neighbors missing DR Priority hello option%s"
- "DrPri: Designated Router Priority sent%s%s",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
- vty_out(vty, "Interface Address DR Uptime Elections Changes NonPri DrPri%s", VTY_NEWLINE);
+ if (uj)
+ json = json_object_new_object();
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- char dr_str[100];
- char dr_uptime[10];
-
pim_ifp = ifp->info;
-
+
if (!pim_ifp)
continue;
if (pim_ifp->pim_sock_fd < 0)
continue;
+ if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name))
+ continue;
+
+ found_ifname = 1;
ifaddr = pim_ifp->primary_address;
+ pim_inet4_dump("<dr?>", pim_ifp->pim_dr_addr, dr_str, sizeof(dr_str));
+ pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), now, pim_ifp->pim_dr_election_last);
+ pim_time_timer_to_hhmmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer);
+ pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period);
+ pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start);
+ mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
- pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime),
- now, pim_ifp->pim_dr_election_last);
+ if (uj) {
+ json_row = json_object_new_object();
+ json_object_pim_ifp_add(json_row, ifp);
- pim_inet4_dump("<dr?>", pim_ifp->pim_dr_addr,
- dr_str, sizeof(dr_str));
+ if (pim_ifp->update_source.s_addr != INADDR_ANY) {
+ json_object_string_add(json_row, "useSource", inet_ntoa(pim_ifp->update_source));
+ }
+ if (pim_ifp->sec_addr_list) {
+ json_object *sec_list = NULL;
+
+ sec_list = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) {
+ json_object_array_add(sec_list, json_object_new_string(inet_ntoa(sec_addr->addr)));
+ }
+ json_object_object_add(json_row, "secondaryAddressList", sec_list);
+ }
- vty_out(vty, "%-9s %-15s %-15s %8s %9d %7d %6d %10d%s",
- ifp->name,
- inet_ntoa(ifaddr),
- dr_str,
- dr_uptime,
- pim_ifp->pim_dr_election_count,
- pim_ifp->pim_dr_election_changes,
- pim_ifp->pim_dr_num_nondrpri_neighbors,
- pim_ifp->pim_dr_priority,
- VTY_NEWLINE);
+ // PIM neighbors
+ if (pim_ifp->pim_neighbor_list->count) {
+ json_pim_neighbors = json_object_new_object();
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+ json_pim_neighbor = json_object_new_object();
+ pim_inet4_dump("<src?>", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+ json_object_string_add(json_pim_neighbor, "address", neigh_src_str);
+ json_object_string_add(json_pim_neighbor, "upTime", uptime);
+ json_object_string_add(json_pim_neighbor, "holdtime", expire);
+
+ json_object_object_add(json_pim_neighbors, neigh_src_str, json_pim_neighbor);
+ }
+
+ json_object_object_add(json_row, "neighbors", json_pim_neighbors);
+ }
+
+ json_object_string_add(json_row, "drAddress", dr_str);
+ json_object_int_add(json_row, "drPriority", pim_ifp->pim_dr_priority);
+ json_object_string_add(json_row, "drUptime", dr_uptime);
+ json_object_int_add(json_row, "drElections", pim_ifp->pim_dr_election_count);
+ json_object_int_add(json_row, "drChanges", pim_ifp->pim_dr_election_changes);
+
+ // FHR
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+ if (ifp == up->rpf.source_nexthop.interface) {
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) {
+ if (!json_fhr_sources) {
+ json_fhr_sources = json_object_new_object();
+ }
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+
+ /* Does this group live in json_fhr_sources? If not create it. */
+ json_object_object_get_ex(json_fhr_sources, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json_fhr_sources, grp_str, json_group);
+ }
+
+ json_group_source = json_object_new_object();
+ json_object_string_add(json_group_source, "source", src_str);
+ json_object_string_add(json_group_source, "group", grp_str);
+ json_object_string_add(json_group_source, "upTime", uptime);
+ json_object_object_add(json_group, src_str, json_group_source);
+ }
+ }
+ }
+
+ if (json_fhr_sources) {
+ json_object_object_add(json_row, "firstHopRouter", json_fhr_sources);
+ }
+
+ json_object_int_add(json_row, "helloPeriod", pim_ifp->pim_hello_period);
+ json_object_string_add(json_row, "helloTimer", hello_timer);
+ json_object_string_add(json_row, "helloStatStart", stat_uptime);
+ json_object_int_add(json_row, "helloReceived", pim_ifp->pim_ifstat_hello_recv);
+ json_object_int_add(json_row, "helloReceivedFailed", pim_ifp->pim_ifstat_hello_recvfail);
+ json_object_int_add(json_row, "helloSend", pim_ifp->pim_ifstat_hello_sent);
+ json_object_int_add(json_row, "hellosendFailed", pim_ifp->pim_ifstat_hello_sendfail);
+ json_object_int_add(json_row, "helloGenerationId", pim_ifp->pim_generation_id);
+ json_object_int_add(json_row, "flagMulticastLoop", mloop);
+
+ json_object_int_add(json_row, "effectivePropagationDelay", pim_if_effective_propagation_delay_msec(ifp));
+ json_object_int_add(json_row, "effectiveOverrideInterval", pim_if_effective_override_interval_msec(ifp));
+ json_object_int_add(json_row, "joinPruneOverrideInterval", pim_if_jp_override_interval_msec(ifp));
+
+ json_object_int_add(json_row, "propagationDelay", pim_ifp->pim_propagation_delay_msec);
+ json_object_int_add(json_row, "propagationDelayHighest", pim_ifp->pim_neighbors_highest_propagation_delay_msec);
+ json_object_int_add(json_row, "overrideInterval", pim_ifp->pim_override_interval_msec);
+ json_object_int_add(json_row, "overrideIntervalHighest", pim_ifp->pim_neighbors_highest_override_interval_msec);
+ json_object_object_add(json, ifp->name, json_row);
+
+ } else {
+ vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE);
+ vty_out(vty, "State : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE);
+ if (pim_ifp->update_source.s_addr != INADDR_ANY) {
+ vty_out(vty, "Use Source : %s%s", inet_ntoa(pim_ifp->update_source), VTY_NEWLINE);
+ }
+ if (pim_ifp->sec_addr_list) {
+ vty_out(vty, "Address : %s (primary)%s",
+ inet_ntoa(ifaddr), VTY_NEWLINE);
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) {
+ vty_out(vty, " %s%s",
+ inet_ntoa(sec_addr->addr), VTY_NEWLINE);
+ }
+ } else {
+ vty_out(vty, "Address : %s%s", inet_ntoa(ifaddr), VTY_NEWLINE);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ // PIM neighbors
+ print_header = 1;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+
+ if (print_header) {
+ vty_out(vty, "PIM Neighbors%s", VTY_NEWLINE);
+ vty_out(vty, "-------------%s", VTY_NEWLINE);
+ print_header = 0;
+ }
+
+ pim_inet4_dump("<src?>", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+ vty_out(vty, "%-15s : up for %s, holdtime expires in %s%s", neigh_src_str, uptime, expire, VTY_NEWLINE);
+ }
+
+ if (!print_header) {
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ vty_out(vty, "Designated Router%s", VTY_NEWLINE);
+ vty_out(vty, "-----------------%s", VTY_NEWLINE);
+ vty_out(vty, "Address : %s%s", dr_str, VTY_NEWLINE);
+ vty_out(vty, "Priority : %d%s", pim_ifp->pim_dr_priority, VTY_NEWLINE);
+ vty_out(vty, "Uptime : %s%s", dr_uptime, VTY_NEWLINE);
+ vty_out(vty, "Elections : %d%s", pim_ifp->pim_dr_election_count, VTY_NEWLINE);
+ vty_out(vty, "Changes : %d%s", pim_ifp->pim_dr_election_changes, VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ // FHR
+ print_header = 1;
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+ if (strcmp(ifp->name, up->rpf.source_nexthop.interface->name) == 0) {
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) {
+
+ if (print_header) {
+ vty_out(vty, "FHR - First Hop Router%s", VTY_NEWLINE);
+ vty_out(vty, "----------------------%s", VTY_NEWLINE);
+ print_header = 0;
+ }
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+ vty_out(vty, "%s : %s is a source, uptime is %s%s", grp_str, src_str, uptime, VTY_NEWLINE);
+ }
+ }
+ }
+
+ if (!print_header) {
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ vty_out(vty, "Hellos%s", VTY_NEWLINE);
+ vty_out(vty, "------%s", VTY_NEWLINE);
+ vty_out(vty, "Period : %d%s", pim_ifp->pim_hello_period, VTY_NEWLINE);
+ vty_out(vty, "Timer : %s%s", hello_timer, VTY_NEWLINE);
+ vty_out(vty, "StatStart : %s%s", stat_uptime, VTY_NEWLINE);
+ vty_out(vty, "Receive : %d%s", pim_ifp->pim_ifstat_hello_recv, VTY_NEWLINE);
+ vty_out(vty, "Receive Failed : %d%s", pim_ifp->pim_ifstat_hello_recvfail, VTY_NEWLINE);
+ vty_out(vty, "Send : %d%s", pim_ifp->pim_ifstat_hello_sent, VTY_NEWLINE);
+ vty_out(vty, "Send Failed : %d%s", pim_ifp->pim_ifstat_hello_sendfail, VTY_NEWLINE);
+ vty_out(vty, "Generation ID : %08x%s", pim_ifp->pim_generation_id, VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ pim_print_ifp_flags(vty, ifp, mloop);
+
+ vty_out(vty, "Join Prune Interval%s", VTY_NEWLINE);
+ vty_out(vty, "-------------------%s", VTY_NEWLINE);
+ vty_out(vty, "LAN Delay : %s%s", pim_if_lan_delay_enabled(ifp) ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, "Effective Propagation Delay : %d msec%s", pim_if_effective_propagation_delay_msec(ifp), VTY_NEWLINE);
+ vty_out(vty, "Effective Override Interval : %d msec%s", pim_if_effective_override_interval_msec(ifp), VTY_NEWLINE);
+ vty_out(vty, "Join Prune Override Interval : %d msec%s", pim_if_jp_override_interval_msec(ifp), VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "LAN Prune Delay%s", VTY_NEWLINE);
+ vty_out(vty, "---------------%s", VTY_NEWLINE);
+ vty_out(vty, "Propagation Delay : %d msec%s", pim_ifp->pim_propagation_delay_msec, VTY_NEWLINE);
+ vty_out(vty, "Propagation Delay (Highest) : %d msec%s", pim_ifp->pim_neighbors_highest_propagation_delay_msec, VTY_NEWLINE);
+ vty_out(vty, "Override Interval : %d msec%s", pim_ifp->pim_override_interval_msec, VTY_NEWLINE);
+ vty_out(vty, "Override Interval (Highest) : %d msec%s", pim_ifp->pim_neighbors_highest_override_interval_msec, VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ } else {
+ if (!found_ifname)
+ vty_out (vty, "%% No such interface%s", VTY_NEWLINE);
}
}
-static void pim_show_hello(struct vty *vty)
+static void pim_show_interfaces(struct vty *vty, u_char uj)
{
- struct listnode *node;
struct interface *ifp;
- time_t now;
-
- now = pim_time_monotonic_sec();
-
- vty_out(vty, "Interface Address Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE);
+ struct listnode *node;
+ struct listnode *upnode;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up;
+ int fhr = 0;
+ int pim_nbrs = 0;
+ json_object *json = NULL;
+ json_object *json_row = NULL;
+ json_object *json_tmp;
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- char hello_period[10];
- char hello_timer[10];
- char stat_uptime[10];
+ json = json_object_new_object();
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
if (pim_ifp->pim_sock_fd < 0)
continue;
- ifaddr = pim_ifp->primary_address;
+ pim_nbrs = pim_ifp->pim_neighbor_list->count;
+ fhr = 0;
- pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer);
- pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period);
- pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start);
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up))
+ if (ifp == up->rpf.source_nexthop.interface)
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
+ fhr++;
- vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s",
- ifp->name,
- inet_ntoa(ifaddr),
- hello_period,
- hello_timer,
- stat_uptime,
- pim_ifp->pim_ifstat_hello_recv,
- pim_ifp->pim_ifstat_hello_recvfail,
- pim_ifp->pim_ifstat_hello_sent,
- pim_ifp->pim_ifstat_hello_sendfail,
- VTY_NEWLINE);
- }
-}
+ json_row = json_object_new_object();
+ json_object_pim_ifp_add(json_row, ifp);
+ json_object_int_add(json_row, "pimNeighbors", pim_nbrs);
+ json_object_int_add(json_row, "firstHopRouter", fhr);
+ json_object_string_add(json_row, "pimDesignatedRouter", inet_ntoa(pim_ifp->pim_dr_addr));
-static void pim_show_interfaces(struct vty *vty)
-{
- struct listnode *node;
- struct interface *ifp;
- time_t now;
-
- now = pim_time_monotonic_sec();
+ if (pim_ifp->pim_dr_addr.s_addr == pim_ifp->primary_address.s_addr)
+ json_object_boolean_true_add(json_row, "pimDesignatedRouterLocal");
- vty_out(vty, "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE);
+ json_object_object_add(json, ifp->name, json_row);
+ }
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- char uptime[10];
- int mloop;
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ } else {
+ vty_out(vty, "Interface State Address PIM Nbrs PIM DR FHR%s", VTY_NEWLINE);
- pim_ifp = ifp->info;
-
- if (!pim_ifp)
- continue;
+ json_object_object_foreach(json, key, val) {
+ vty_out(vty, "%-9s ", key);
- if (pim_ifp->pim_sock_fd < 0)
- continue;
+ json_object_object_get_ex(val, "state", &json_tmp);
+ vty_out(vty, "%5s ", json_object_get_string(json_tmp));
- ifaddr = pim_ifp->primary_address;
+ json_object_object_get_ex(val, "address", &json_tmp);
+ vty_out(vty, "%15s ", json_object_get_string(json_tmp));
- pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation);
+ json_object_object_get_ex(val, "pimNeighbors", &json_tmp);
+ vty_out(vty, "%8d ", json_object_get_int(json_tmp));
- mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
-
- vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- ifp->ifindex,
- pim_ifp->pim_sock_fd,
- uptime,
- if_is_multicast(ifp) ? "yes" : "no",
- if_is_broadcast(ifp) ? "yes" : "no",
- (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
- (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
- (ifp->flags & IFF_PROMISC) ? "yes" : "no",
- PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
- VTY_NEWLINE);
+ if (json_object_object_get_ex(val, "pimDesignatedRouterLocal", &json_tmp)) {
+ vty_out(vty, "%15s ", "local");
+ } else {
+ json_object_object_get_ex(val, "pimDesignatedRouter", &json_tmp);
+ vty_out(vty, "%15s ", json_object_get_string(json_tmp));
+ }
+
+ json_object_object_get_ex(val, "firstHopRouter", &json_tmp);
+ vty_out(vty, "%3d%s", json_object_get_int(json_tmp), VTY_NEWLINE);
+ }
}
+
+ json_object_free(json);
}
-static void pim_show_join(struct vty *vty)
+static void pim_show_join(struct vty *vty, u_char uj)
{
- struct listnode *ifnode;
- struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
time_t now;
+ json_object *json = NULL;
+ json_object *json_iface = NULL;
+ json_object *json_row = NULL;
+ json_object *json_grp = NULL;
now = pim_time_monotonic_sec();
- vty_out(vty,
- "Interface Address Source Group State Uptime Expire Prune%s",
- VTY_NEWLINE);
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "Interface Address Source Group State Uptime Expire Prune%s",
+ VTY_NEWLINE);
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *ch_node;
- struct pim_ifchannel *ch;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) {
- pim_ifp = ifp->info;
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
ifaddr = pim_ifp->primary_address;
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
- char ch_src_str[100];
- char ch_grp_str[100];
- char uptime[10];
- char expire[10];
- char prune[10];
-
- pim_inet4_dump("<ch_src?>", ch->source_addr,
- ch_src_str, sizeof(ch_src_str));
- pim_inet4_dump("<ch_grp?>", ch->group_addr,
- ch_grp_str, sizeof(ch_grp_str));
-
- pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation);
- pim_time_timer_to_mmss(expire, sizeof(expire),
- ch->t_ifjoin_expiry_timer);
- pim_time_timer_to_mmss(prune, sizeof(prune),
- ch->t_ifjoin_prune_pending_timer);
+ char ch_src_str[INET_ADDRSTRLEN];
+ char ch_grp_str[INET_ADDRSTRLEN];
+ char uptime[10];
+ char expire[10];
+ char prune[10];
+
+ pim_inet4_dump("<ch_src?>", ch->sg.src,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->sg.grp,
+ ch_grp_str, sizeof(ch_grp_str));
+
+ pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation);
+ pim_time_timer_to_mmss(expire, sizeof(expire),
+ ch->t_ifjoin_expiry_timer);
+ pim_time_timer_to_mmss(prune, sizeof(prune),
+ ch->t_ifjoin_prune_pending_timer);
+
+ if (uj) {
+ json_object_object_get_ex(json, ch->interface->name, &json_iface);
+
+ if (!json_iface) {
+ json_iface = json_object_new_object();
+ json_object_pim_ifp_add(json_iface, ch->interface);
+ json_object_object_add(json, ch->interface->name, json_iface);
+ }
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "source", ch_src_str);
+ json_object_string_add(json_row, "group", ch_grp_str);
+ json_object_string_add(json_row, "upTime", uptime);
+ json_object_string_add(json_row, "expire", expire);
+ json_object_string_add(json_row, "prune", prune);
+ json_object_string_add(json_row, "channelJoinName", pim_ifchannel_ifjoin_name(ch->ifjoin_state));
+ if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+ json_object_int_add(json_row, "SGRpt", 1);
+
+ json_object_object_get_ex(json_iface, ch_grp_str, &json_grp);
+ if (!json_grp)
+ {
+ json_grp = json_object_new_object();
+ json_object_object_add(json_grp, ch_src_str, json_row);
+ json_object_object_add(json_iface, ch_grp_str, json_grp);
+ }
+ else
+ json_object_object_add(json_grp, ch_src_str, json_row);
+ } else {
vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s",
- ifp->name,
+ ch->interface->name,
inet_ntoa(ifaddr),
ch_src_str,
ch_grp_str,
expire,
prune,
VTY_NEWLINE);
- } /* scan interface channels */
- } /* scan interfaces */
+ }
+ } /* scan interface channels */
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ }
}
-static void pim_show_neighbors(struct vty *vty)
+static void pim_show_neighbors_single(struct vty *vty, const char *neighbor, u_char uj)
{
struct listnode *node;
+ struct listnode *neighnode;
struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
time_t now;
-
- now = pim_time_monotonic_sec();
+ int found_neighbor = 0;
+ int option_address_list;
+ int option_dr_priority;
+ int option_generation_id;
+ int option_holdtime;
+ int option_lan_prune_delay;
+ int option_t_bit;
+ char uptime[10];
+ char expire[10];
+ char neigh_src_str[INET_ADDRSTRLEN];
+
+ json_object *json = NULL;
+ json_object *json_ifp = NULL;
+ json_object *json_row = NULL;
- vty_out(vty,
- "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s"
- " T=can_disable_join_suppression%s%s",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ now = pim_time_monotonic_sec();
- vty_out(vty, "Interface Address Neighbor Uptime Timer Holdt DrPri GenId Recv %s", VTY_NEWLINE);
+ if (uj)
+ json = json_object_new_object();
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *neighnode;
- struct pim_neighbor *neigh;
-
pim_ifp = ifp->info;
-
+
if (!pim_ifp)
continue;
if (pim_ifp->pim_sock_fd < 0)
continue;
- ifaddr = pim_ifp->primary_address;
-
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
- char uptime[10];
- char holdtime[10];
- char expire[10];
- char neigh_src_str[100];
- char recv[7];
-
pim_inet4_dump("<src?>", neigh->source_addr,
neigh_src_str, sizeof(neigh_src_str));
+
+ /*
+ * The user can specify either the interface name or the PIM neighbor IP.
+ * If this pim_ifp matches neither then skip.
+ */
+ if (strcmp(neighbor, "detail") &&
+ strcmp(neighbor, ifp->name) &&
+ strcmp(neighbor, neigh_src_str))
+ continue;
+
+ found_neighbor = 1;
pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
- pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime);
- pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer);
-
- recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME) ? 'H' : ' ';
- recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? 'L' : ' ';
- recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) ? 'P' : ' ';
- recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ? 'G' : ' ';
- recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST) ? 'A' : ' ';
- recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' ';
- recv[6] = '\0';
-
- vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s",
- ifp->name,
- inet_ntoa(ifaddr),
- neigh_src_str,
- uptime,
- expire,
- holdtime,
- neigh->dr_priority,
- neigh->generation_id,
- recv,
- VTY_NEWLINE);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+ option_address_list = 0;
+ option_dr_priority = 0;
+ option_generation_id = 0;
+ option_holdtime = 0;
+ option_lan_prune_delay = 0;
+ option_t_bit = 0;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST))
+ option_address_list = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY))
+ option_dr_priority = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID))
+ option_generation_id = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME))
+ option_holdtime = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY))
+ option_lan_prune_delay = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION))
+ option_t_bit = 1;
+
+ if (uj) {
+
+ /* Does this ifp live in json? If not create it. */
+ json_object_object_get_ex(json, ifp->name, &json_ifp);
+
+ if (!json_ifp) {
+ json_ifp = json_object_new_object();
+ json_object_pim_ifp_add(json_ifp, ifp);
+ json_object_object_add(json, ifp->name, json_ifp);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "interface", ifp->name);
+ json_object_string_add(json_row, "address", neigh_src_str);
+ json_object_string_add(json_row, "upTime", uptime);
+ json_object_string_add(json_row, "holdtime", expire);
+ json_object_int_add(json_row, "drPriority", neigh->dr_priority);
+ json_object_int_add(json_row, "generationId", neigh->generation_id);
+
+ if (option_address_list)
+ json_object_boolean_true_add(json_row, "helloOptionAddressList");
+
+ if (option_dr_priority)
+ json_object_boolean_true_add(json_row, "helloOptionDrPriority");
+
+ if (option_generation_id)
+ json_object_boolean_true_add(json_row, "helloOptionGenerationId");
+
+ if (option_holdtime)
+ json_object_boolean_true_add(json_row, "helloOptionHoldtime");
+
+ if (option_lan_prune_delay)
+ json_object_boolean_true_add(json_row, "helloOptionLanPruneDelay");
+
+ if (option_t_bit)
+ json_object_boolean_true_add(json_row, "helloOptionTBit");
+
+ json_object_object_add(json_ifp, neigh_src_str, json_row);
+
+ } else {
+ vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE);
+ vty_out(vty, "Neighbor : %s%s", neigh_src_str, VTY_NEWLINE);
+ vty_out(vty, " Uptime : %s%s", uptime, VTY_NEWLINE);
+ vty_out(vty, " Holdtime : %s%s", expire, VTY_NEWLINE);
+ vty_out(vty, " DR Priority : %d%s", neigh->dr_priority, VTY_NEWLINE);
+ vty_out(vty, " Generation ID : %08x%s", neigh->generation_id, VTY_NEWLINE);
+ vty_out(vty, " Override Interval (msec) : %d%s", neigh->override_interval_msec, VTY_NEWLINE);
+ vty_out(vty, " Propagation Delay (msec) : %d%s", neigh->propagation_delay_msec, VTY_NEWLINE);
+ vty_out(vty, " Hello Option - Address List : %s%s", option_address_list ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, " Hello Option - DR Priority : %s%s", option_dr_priority ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, " Hello Option - Generation ID : %s%s", option_generation_id? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, " Hello Option - Holdtime : %s%s", option_holdtime ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, " Hello Option - LAN Prune Delay : %s%s", option_lan_prune_delay ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, " Hello Option - T-bit : %s%s", option_t_bit ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
}
+ }
-
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ } else {
+ {
+ if (!found_neighbor)
+ vty_out (vty, "%% No such interface or neighbor%s", VTY_NEWLINE);
+ }
}
}
-static void pim_show_lan_prune_delay(struct vty *vty)
+static void
+pim_show_state(struct vty *vty, const char *src_or_group, const char *group, u_char uj)
{
- struct listnode *node;
- struct interface *ifp;
-
- vty_out(vty,
- "PrDly=propagation_delay (msec) OvInt=override_interval (msec)%s"
- "HiDly=highest_propagation_delay (msec) HiInt=highest_override_interval (msec)%s"
- "NoDly=number_of_non_lan_delay_neighbors%s"
- "T=t_bit LPD=lan_prune_delay_hello_option%s%s",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ struct channel_oil *c_oil;
+ struct listnode *node;
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_ifp_in = NULL;
+ json_object *json_ifp_out = NULL;
+ json_object *json_source = NULL;
+ time_t now;
+ int first_oif;
+ now = pim_time_monotonic_sec();
- vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T | Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE);
+ if (uj) {
+ json = json_object_new_object();
+ } else {
+ vty_out(vty, "%sSource Group IIF OIL%s", VTY_NEWLINE, VTY_NEWLINE);
+ }
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
- struct listnode *neighnode;
- struct pim_neighbor *neigh;
+ for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+ char grp_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char in_ifname[16];
+ char out_ifname[16];
+ int oif_vif_index;
+ struct interface *ifp_in;
+ first_oif = 1;
- pim_ifp = ifp->info;
-
- if (!pim_ifp)
+ if (!c_oil->installed)
continue;
- if (pim_ifp->pim_sock_fd < 0)
- continue;
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, sizeof(src_str));
+ ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
- ifaddr = pim_ifp->primary_address;
+ if (ifp_in)
+ strcpy(in_ifname, ifp_in->name);
+ else
+ strcpy(in_ifname, "<iif?>");
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
- char neigh_src_str[100];
+ if (src_or_group)
+ {
+ if (strcmp(src_or_group, src_str) && strcmp(src_or_group, grp_str))
+ continue;
- pim_inet4_dump("<src?>", neigh->source_addr,
- neigh_src_str, sizeof(neigh_src_str));
+ if (group && strcmp(group, grp_str))
+ continue;
+ }
- vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u | %-15s %-3s %5u %5u %1u%s",
- ifp->name,
- inet_ntoa(ifaddr),
- pim_ifp->pim_propagation_delay_msec,
- pim_ifp->pim_override_interval_msec,
- pim_ifp->pim_number_of_nonlandelay_neighbors,
- pim_ifp->pim_neighbors_highest_propagation_delay_msec,
- pim_ifp->pim_neighbors_highest_override_interval_msec,
- PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)),
- neigh_src_str,
- PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no",
- neigh->propagation_delay_msec,
- neigh->override_interval_msec,
- PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options,
- PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)),
- VTY_NEWLINE);
+ if (uj) {
+
+ /* Find the group, create it if it doesn't exist */
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ /* Find the source nested under the group, create it if it doesn't exist */
+ json_object_object_get_ex(json_group, src_str, &json_source);
+
+ if (!json_source) {
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, src_str, json_source);
+ }
+
+ /* Find the inbound interface nested under the source, create it if it doesn't exist */
+ json_object_object_get_ex(json_source, in_ifname, &json_ifp_in);
+
+ if (!json_ifp_in) {
+ json_ifp_in = json_object_new_object();
+ json_object_object_add(json_source, in_ifname, json_ifp_in);
+ }
+ } else {
+ vty_out(vty, "%-15s %-15s %-5s ",
+ src_str,
+ grp_str,
+ ifp_in->name);
+ }
+
+ for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
+ struct interface *ifp_out;
+ char oif_uptime[10];
+ int ttl;
+
+ ttl = c_oil->oil.mfcc_ttls[oif_vif_index];
+ if (ttl < 1)
+ continue;
+
+ ifp_out = pim_if_find_by_vif_index(oif_vif_index);
+ pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]);
+
+ if (ifp_out)
+ strcpy(out_ifname, ifp_out->name);
+ else
+ strcpy(out_ifname, "<oif?>");
+
+ if (uj) {
+ json_ifp_out = json_object_new_object();
+ json_object_string_add(json_ifp_out, "source", src_str);
+ json_object_string_add(json_ifp_out, "group", grp_str);
+ json_object_string_add(json_ifp_out, "inboundInterface", in_ifname);
+ json_object_string_add(json_ifp_out, "outboundInterface", out_ifname);
+
+ json_object_object_add(json_ifp_in, out_ifname, json_ifp_out);
+ } else {
+ if (first_oif)
+ {
+ first_oif = 0;
+ vty_out(vty, "%s", out_ifname);
+ }
+ else
+ vty_out(vty, ",%s", out_ifname);
+ }
}
+ if (!uj)
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ } else {
+ vty_out(vty, "%s", VTY_NEWLINE);
}
}
-static void pim_show_jp_override_interval(struct vty *vty)
+static void pim_show_neighbors(struct vty *vty, u_char uj)
{
- struct listnode *node;
+ struct listnode *node;
+ struct listnode *neighnode;
struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+ time_t now;
+ char uptime[10];
+ char expire[10];
+ char neigh_src_str[INET_ADDRSTRLEN];
+ json_object *json = NULL;
+ json_object *json_ifp_rows = NULL;
+ json_object *json_row = NULL;
- vty_out(vty,
- "EffPDelay=effective_propagation_delay (msec)%s"
- "EffOvrInt=override_interval (msec)%s"
- "JPOvrInt=jp_override_interval (msec)%s%s",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ now = pim_time_monotonic_sec();
- vty_out(vty, "Interface Address LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE);
+ if (uj) {
+ json = json_object_new_object();
+ } else {
+ vty_out(vty, "Interface Neighbor Uptime Holdtime DR Pri%s", VTY_NEWLINE);
+ }
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp;
- struct in_addr ifaddr;
-
pim_ifp = ifp->info;
-
+
if (!pim_ifp)
continue;
if (pim_ifp->pim_sock_fd < 0)
continue;
- ifaddr = pim_ifp->primary_address;
+ if (uj)
+ json_ifp_rows = json_object_new_object();
- vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s",
- ifp->name,
- inet_ntoa(ifaddr),
- pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled",
- pim_if_effective_propagation_delay_msec(ifp),
- pim_if_effective_override_interval_msec(ifp),
- pim_if_jp_override_interval_msec(ifp),
- VTY_NEWLINE);
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+ pim_inet4_dump("<src?>", neigh->source_addr,
+ neigh_src_str, sizeof(neigh_src_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+ if (uj) {
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "interface", ifp->name);
+ json_object_string_add(json_row, "neighbor", neigh_src_str);
+ json_object_string_add(json_row, "upTime", uptime);
+ json_object_string_add(json_row, "holdTime", expire);
+ json_object_int_add(json_row, "holdTimeMax", neigh->holdtime);
+ json_object_int_add(json_row, "drPriority", neigh->dr_priority);
+ json_object_object_add(json_ifp_rows, neigh_src_str, json_row);
+
+ } else {
+ vty_out(vty, "%-9s %15s %8s %8s %6d%s",
+ ifp->name,
+ neigh_src_str,
+ uptime,
+ expire,
+ neigh->dr_priority,
+ VTY_NEWLINE);
+ }
+ }
+
+ if (uj) {
+ json_object_object_add(json, ifp->name, json_ifp_rows);
+ json_ifp_rows = NULL;
+ }
+ }
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
}
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;
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;
}
}
-static void pim_show_upstream(struct vty *vty)
+static void
+json_object_pim_upstream_add (json_object *json, struct pim_upstream *up)
+{
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+ json_object_boolean_true_add(json, "drJoinDesired");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+ json_object_boolean_true_add(json, "drJoinDesiredUpdated");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
+ json_object_boolean_true_add(json, "firstHopRouter");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+ json_object_boolean_true_add(json, "sourceIgmp");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+ json_object_boolean_true_add(json, "sourcePim");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+ json_object_boolean_true_add(json, "sourceStream");
+
+ /* XXX: need to print ths flag in the plain text display as well */
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
+ json_object_boolean_true_add(json, "sourceMsdp");
+}
+
+static void pim_show_upstream(struct vty *vty, u_char uj)
{
struct listnode *upnode;
struct pim_upstream *up;
time_t now;
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
now = pim_time_monotonic_sec();
- vty_out(vty, "Iif Source Group State Uptime JoinTimer RefCnt%s", VTY_NEWLINE);
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty, "Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt%s", VTY_NEWLINE);
- for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
- char src_str[100];
- char grp_str[100];
- char uptime[10];
- char join_timer[10];
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char uptime[10];
+ char join_timer[10];
+ char rs_timer[10];
+ char ka_timer[10];
+ char msdp_reg_timer[10];
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+ pim_time_timer_to_hhmmss (join_timer, sizeof(join_timer), up->t_join_timer);
+ pim_time_timer_to_hhmmss (rs_timer, sizeof (rs_timer), up->t_rs_timer);
+ pim_time_timer_to_hhmmss (ka_timer, sizeof (ka_timer), up->t_ka_timer);
+ pim_time_timer_to_hhmmss (msdp_reg_timer, sizeof (msdp_reg_timer), up->t_msdp_reg_timer);
+
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
- pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer);
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "inboundInterface", up->rpf.source_nexthop.interface->name);
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "state", pim_upstream_state2str (up->join_state));
+ json_object_string_add(json_row, "upTime", uptime);
+ json_object_string_add(json_row, "joinTimer", join_timer);
+ json_object_string_add(json_row, "resetTimer", rs_timer);
+ json_object_string_add(json_row, "keepaliveTimer", ka_timer);
+ json_object_string_add(json_row, "msdpRegTimer", msdp_reg_timer);
+ json_object_int_add(json_row, "refCount", up->ref_count);
+ json_object_int_add(json_row, "sptBit", up->sptbit);
+ json_object_object_add(json_group, src_str, json_row);
+ } else {
+ vty_out(vty, "%-10s%-15s %-15s %-11s %-8s %-9s %-9s %-9s %6d%s",
+ up->rpf.source_nexthop.interface->name,
+ src_str,
+ grp_str,
+ pim_upstream_state2str (up->join_state),
+ uptime,
+ join_timer,
+ rs_timer,
+ ka_timer,
+ up->ref_count,
+ VTY_NEWLINE);
+ }
+ }
- vty_out(vty, "%-10s%-15s %-15s %-5s %-8s %-9s %6d%s",
- up->rpf.source_nexthop.interface->name,
- src_str,
- grp_str,
- up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd",
- uptime,
- join_timer,
- up->ref_count,
- VTY_NEWLINE);
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
}
-static void pim_show_join_desired(struct vty *vty)
+static void pim_show_join_desired(struct vty *vty, u_char uj)
{
- struct listnode *ifnode;
struct listnode *chnode;
- struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- char src_str[100];
- char grp_str[100];
-
- vty_out(vty,
- "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s",
- VTY_NEWLINE);
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s",
+ VTY_NEWLINE);
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- pim_ifp = ifp->info;
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, chnode, ch)) {
+ /* scan all interfaces */
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) {
- struct pim_upstream *up = ch->upstream;
+ struct pim_upstream *up = ch->upstream;
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "interface", ch->interface->name);
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+
+ if (pim_macro_ch_lost_assert(ch))
+ json_object_boolean_true_add(json_row, "lostAssert");
+
+ if (pim_macro_chisin_joins(ch))
+ json_object_boolean_true_add(json_row, "joins");
+
+ if (pim_macro_chisin_pim_include(ch))
+ json_object_boolean_true_add(json_row, "pimInclude");
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ if (pim_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",
VTY_NEWLINE);
}
}
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ }
}
-static void pim_show_upstream_rpf(struct vty *vty)
+static void pim_show_upstream_rpf(struct vty *vty, u_char uj)
{
struct listnode *upnode;
struct pim_upstream *up;
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
- vty_out(vty,
- "Source Group RpfIface RibNextHop RpfAddress %s",
- VTY_NEWLINE);
-
- for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
- char src_str[100];
- char grp_str[100];
- char rpf_nexthop_str[100];
- char rpf_addr_str[100];
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "Source Group RpfIface RibNextHop RpfAddress %s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) {
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char rpf_nexthop_str[PREFIX_STRLEN];
+ char rpf_addr_str[PREFIX_STRLEN];
struct pim_rpf *rpf;
const char *rpf_ifname;
-
+
rpf = &up->rpf;
-
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str));
- pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
-
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+ pim_addr_dump("<nexthop?>", &rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str));
+ pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+
rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
-
- vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s",
- src_str,
- grp_str,
- rpf_ifname,
- rpf_nexthop_str,
- rpf_addr_str,
- VTY_NEWLINE);
+
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "rpfInterface", rpf_ifname);
+ json_object_string_add(json_row, "ribNexthop", rpf_nexthop_str);
+ json_object_string_add(json_row, "rpfAddress", rpf_addr_str);
+ json_object_object_add(json_group, src_str, json_row);
+ } else {
+ vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s",
+ src_str,
+ grp_str,
+ rpf_ifname,
+ rpf_nexthop_str,
+ rpf_addr_str,
+ VTY_NEWLINE);
+ }
+ }
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
}
-static void show_rpf_refresh_stats(struct vty *vty, time_t now)
+static void show_rpf_refresh_stats(struct vty *vty, time_t now, json_object *json)
{
char refresh_uptime[10];
pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now, qpim_rpf_cache_refresh_last);
- vty_out(vty,
- "RPF Cache Refresh Delay: %ld msecs%s"
- "RPF Cache Refresh Timer: %ld msecs%s"
- "RPF Cache Refresh Requests: %lld%s"
- "RPF Cache Refresh Events: %lld%s"
- "RPF Cache Refresh Last: %s%s",
- qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE,
- pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE,
- (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE,
- (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE,
- refresh_uptime, VTY_NEWLINE);
+ if (json) {
+ json_object_int_add(json, "rpfCacheRefreshDelayMsecs", qpim_rpf_cache_refresh_delay_msec);
+ json_object_int_add(json, "rpfCacheRefreshTimer", pim_time_timer_remain_msec(qpim_rpf_cache_refresher));
+ json_object_int_add(json, "rpfCacheRefreshRequests", qpim_rpf_cache_refresh_requests);
+ json_object_int_add(json, "rpfCacheRefreshEvents", qpim_rpf_cache_refresh_events);
+ json_object_string_add(json, "rpfCacheRefreshLast", refresh_uptime);
+ json_object_int_add(json, "nexthopLookups", qpim_nexthop_lookups);
+ json_object_int_add(json, "nexthopLookupsAvoided", nexthop_lookups_avoided);
+ } else {
+ vty_out(vty,
+ "RPF Cache Refresh Delay: %ld msecs%s"
+ "RPF Cache Refresh Timer: %ld msecs%s"
+ "RPF Cache Refresh Requests: %lld%s"
+ "RPF Cache Refresh Events: %lld%s"
+ "RPF Cache Refresh Last: %s%s"
+ "Nexthop Lookups: %lld%s"
+ "Nexthop Lookups Avoided: %lld%s",
+ qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE,
+ pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE,
+ (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE,
+ (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE,
+ refresh_uptime, VTY_NEWLINE,
+ (long long) qpim_nexthop_lookups, VTY_NEWLINE,
+ (long long)nexthop_lookups_avoided, VTY_NEWLINE);
+ }
}
static void show_scan_oil_stats(struct vty *vty, time_t now)
uptime_mroute_del, (long long) qpim_mroute_del_events, VTY_NEWLINE);
}
-static void pim_show_rpf(struct vty *vty)
+static void pim_show_rpf(struct vty *vty, u_char uj)
{
struct listnode *up_node;
struct pim_upstream *up;
time_t now = pim_time_monotonic_sec();
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ if (uj) {
+ json = json_object_new_object();
+ show_rpf_refresh_stats(vty, now, json);
+ } else {
+ show_rpf_refresh_stats(vty, now, json);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty,
+ "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s",
+ VTY_NEWLINE);
+ }
- show_rpf_refresh_stats(vty, now);
-
- vty_out(vty, "%s", VTY_NEWLINE);
-
- vty_out(vty,
- "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s",
- VTY_NEWLINE);
-
- for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
- char src_str[100];
- char grp_str[100];
- char rpf_addr_str[100];
- char rib_nexthop_str[100];
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) {
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char rpf_addr_str[PREFIX_STRLEN];
+ char rib_nexthop_str[PREFIX_STRLEN];
const char *rpf_ifname;
struct pim_rpf *rpf = &up->rpf;
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
- pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str));
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+ pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+ pim_addr_dump("<nexthop?>", &rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str));
rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
- vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s",
- src_str,
- grp_str,
- rpf_ifname,
- rpf_addr_str,
- rib_nexthop_str,
- rpf->source_nexthop.mrib_route_metric,
- rpf->source_nexthop.mrib_metric_preference,
- VTY_NEWLINE);
- }
-}
-
-static void igmp_show_querier(struct vty *vty)
-{
- struct listnode *node;
- struct interface *ifp;
-
- vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE);
-
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
- struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
-
- if (!pim_ifp)
- continue;
-
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
- char query_hhmmss[10];
- char other_hhmmss[10];
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
- pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer);
- pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer);
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
- vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s",
- ifp->name,
- inet_ntoa(igmp->ifaddr),
- igmp->t_igmp_query_timer ? "THIS" : "OTHER",
- igmp->startup_query_count,
- query_hhmmss,
- other_hhmmss,
- VTY_NEWLINE);
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "rpfInterface", rpf_ifname);
+ json_object_string_add(json_row, "rpfAddress", rpf_addr_str);
+ json_object_string_add(json_row, "ribNexthop", rib_nexthop_str);
+ json_object_int_add(json_row, "routeMetric", rpf->source_nexthop.mrib_route_metric);
+ json_object_int_add(json_row, "routePreference", rpf->source_nexthop.mrib_metric_preference);
+ json_object_object_add(json_group, src_str, json_row);
+
+ } else {
+ vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s",
+ src_str,
+ grp_str,
+ rpf_ifname,
+ rpf_addr_str,
+ rib_nexthop_str,
+ rpf->source_nexthop.mrib_route_metric,
+ rpf->source_nexthop.mrib_metric_preference,
+ VTY_NEWLINE);
}
}
+
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ }
}
-static void igmp_show_groups(struct vty *vty)
+static void igmp_show_groups(struct vty *vty, u_char uj)
{
struct listnode *ifnode;
struct interface *ifp;
time_t now;
+ json_object *json = NULL;
+ json_object *json_iface = NULL;
+ json_object *json_row = NULL;
now = pim_time_monotonic_sec();
- vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE);
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE);
/* scan interfaces */
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
/* 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;
/* 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];
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)
/* 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;
/* 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;
} /* scan interfaces */
}
-static void igmp_show_parameters(struct vty *vty)
-{
- struct listnode *ifnode;
- struct interface *ifp;
-
- vty_out(vty,
- "QRV: Robustness Variable SQI: Startup Query Interval%s"
- "QQI: Query Interval OQPI: Other Querier Present Interval%s"
- "QRI: Query Response Interval LMQT: Last Member Query Time%s"
- "GMI: Group Membership Interval OHPI: Older Host Present Interval%s%s",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
- vty_out(vty,
- "Interface Address QRV QQI QRI GMI SQI OQPI LMQT OHPI %s",
- VTY_NEWLINE);
-
- /* scan interfaces */
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
-
- if (!pim_ifp)
- continue;
-
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
- char ifaddr_str[100];
- long gmi_dsec; /* Group Membership Interval */
- long oqpi_dsec; /* Other Querier Present Interval */
- int sqi;
- long lmqt_dsec;
- long ohpi_dsec;
- long qri_dsec;
-
- pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
-
- gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec) / 100;
-
- sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
-
- oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec) / 100;
-
- lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec,
- igmp->querier_robustness_variable) / 100;
-
- ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
-
- qri_dsec = pim_ifp->igmp_query_max_response_time_dsec;
-
- vty_out(vty,
- "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s",
- ifp->name,
- ifaddr_str,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- qri_dsec / 10, qri_dsec % 10,
- gmi_dsec / 10, gmi_dsec % 10,
- sqi,
- oqpi_dsec / 10, oqpi_dsec % 10,
- lmqt_dsec / 10, lmqt_dsec % 10,
- ohpi_dsec / 10, ohpi_dsec % 10,
- VTY_NEWLINE);
-
- } /* scan igmp sockets */
- } /* scan interfaces */
-}
-
static void igmp_show_sources(struct vty *vty)
{
struct listnode *ifnode;
/* 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;
/* 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;
/* 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];
/* 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;
/* 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;
/* scan group sources */
for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", src->source_addr, source_str, sizeof(source_str));
struct listnode *node;
struct channel_oil *c_oil;
- for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
- if (pim_mroute_add(c_oil)) {
+ for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+ if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) {
/* just log warning */
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
struct listnode *node;
struct channel_oil *c_oil;
- for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
- if (pim_mroute_del(c_oil)) {
+ for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+ if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) {
/* just log warning */
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
struct static_route *s_route;
for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
- if (pim_mroute_add(&s_route->c_oil)) {
+ if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
/* just log warning */
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
struct static_route *s_route;
for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
- if (pim_mroute_del(&s_route->c_oil)) {
+ if (pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__)) {
/* just log warning */
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
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;
}
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;
}
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",
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",
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);
+ u_char uj = use_json(argc, argv);
+ pim_show_membership(vty, uj);
return CMD_SUCCESS;
}
-DEFUN (show_ip_pim_jp_override_interval,
- show_ip_pim_jp_override_interval_cmd,
- "show ip pim jp-override-interval",
+DEFUN (show_ip_pim_neighbor,
+ show_ip_pim_neighbor_cmd,
+ "show ip pim neighbor [detail|WORD] [json]",
SHOW_STR
IP_STR
PIM_STR
- "PIM interface J/P override interval\n")
+ "PIM neighbor information\n"
+ "Detailed output\n"
+ "Name of interface or neighbor\n"
+ "JavaScript Object Notation\n")
{
- pim_show_jp_override_interval(vty);
+ u_char uj = use_json(argc, argv);
+ if (argv[4]->arg)
+ pim_show_neighbors_single(vty, argv[4]->arg, uj);
+ else
+ pim_show_neighbors(vty, uj);
return CMD_SUCCESS;
}
-DEFUN (show_ip_pim_neighbor,
- show_ip_pim_neighbor_cmd,
- "show ip pim neighbor",
+DEFUN (show_ip_pim_secondary,
+ show_ip_pim_secondary_cmd,
+ "show ip pim secondary",
SHOW_STR
IP_STR
PIM_STR
- "PIM neighbor information\n")
+ "PIM neighbor addresses\n")
{
- pim_show_neighbors(vty);
+ pim_show_neighbors_secondary(vty);
return CMD_SUCCESS;
}
-DEFUN (show_ip_pim_secondary,
- show_ip_pim_secondary_cmd,
- "show ip pim secondary",
+DEFUN (show_ip_pim_state,
+ show_ip_pim_state_cmd,
+ "show ip pim state [A.B.C.D] [A.B.C.D] [json]",
SHOW_STR
IP_STR
PIM_STR
- "PIM neighbor addresses\n")
+ "PIM state information\n"
+ "Unicast or Multicast address\n"
+ "Multicast address\n"
+ "JavaScript Object Notation\n")
{
- pim_show_neighbors_secondary(vty);
+ const char *src_or_group = NULL;
+ const char *group = NULL;
+ u_char uj = use_json(argc, argv);
+
+ src_or_group = argv[4]->arg;
+ group = argv[5]->arg;
+
+ pim_show_state(vty, src_or_group, group, uj);
return CMD_SUCCESS;
}
DEFUN (show_ip_pim_upstream,
show_ip_pim_upstream_cmd,
- "show ip pim upstream",
+ "show ip pim upstream [json]",
SHOW_STR
IP_STR
PIM_STR
- "PIM upstream information\n")
+ "PIM upstream information\n"
+ "JavaScript Object Notation\n")
{
- pim_show_upstream(vty);
+ u_char uj = use_json(argc, argv);
+ pim_show_upstream(vty, uj);
return CMD_SUCCESS;
}
DEFUN (show_ip_pim_upstream_join_desired,
show_ip_pim_upstream_join_desired_cmd,
- "show ip pim upstream-join-desired",
+ "show ip pim upstream-join-desired [json]",
SHOW_STR
IP_STR
PIM_STR
- "PIM upstream join-desired\n")
+ "PIM upstream join-desired\n"
+ "JavaScript Object Notation\n")
{
- pim_show_join_desired(vty);
+ u_char uj = use_json(argc, argv);
+ pim_show_join_desired(vty, uj);
return CMD_SUCCESS;
}
DEFUN (show_ip_pim_upstream_rpf,
show_ip_pim_upstream_rpf_cmd,
- "show ip pim upstream-rpf",
+ "show ip pim upstream-rpf [json]",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM upstream source rpf\n"
+ "JavaScript Object Notation\n")
+{
+ u_char uj = use_json(argc, argv);
+ pim_show_upstream_rpf(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_rp,
+ show_ip_pim_rp_cmd,
+ "show ip pim rp-info [json]",
SHOW_STR
IP_STR
PIM_STR
- "PIM upstream source rpf\n")
+ "PIM RP information\n"
+ "JavaScript Object Notation\n")
{
- pim_show_upstream_rpf(vty);
+ u_char uj = use_json(argc, argv);
+ pim_rp_show_information (vty, uj);
return CMD_SUCCESS;
}
DEFUN (show_ip_pim_rpf,
show_ip_pim_rpf_cmd,
- "show ip pim rpf",
+ "show ip pim rpf [json]",
SHOW_STR
IP_STR
PIM_STR
- "PIM cached source rpf information\n")
+ "PIM cached source rpf information\n"
+ "JavaScript Object Notation\n")
{
- pim_show_rpf(vty);
+ u_char uj = use_json(argc, argv);
+ pim_show_rpf(vty, uj);
return CMD_SUCCESS;
}
else {
vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
}
- vty_out(vty, "Zclient lookup socket: ");
- if (qpim_zclient_lookup) {
- vty_out(vty, "%d failures=%d%s", qpim_zclient_lookup->sock,
- qpim_zclient_lookup->fail, VTY_NEWLINE);
- }
- else {
- vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
- }
+
+ pim_zlookup_show_ip_multicast (vty);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, "Current highest VifIndex: %d%s",
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);
return CMD_SUCCESS;
}
-static void show_mroute(struct vty *vty)
+static void show_mroute(struct vty *vty, u_char uj)
{
struct listnode *node;
struct channel_oil *c_oil;
struct static_route *s_route;
time_t now;
-
- vty_out(vty, "Proto: I=IGMP P=PIM S=STATIC O=SOURCE%s%s", VTY_NEWLINE, VTY_NEWLINE);
-
- vty_out(vty, "Source Group Proto Input iVifI Output oVifI TTL Uptime %s",
- VTY_NEWLINE);
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_source = NULL;
+ json_object *json_oil = NULL;
+ json_object *json_ifp_out = NULL;
+ int found_oif = 0;
+ int first = 1;
+
+ if (uj) {
+ json = json_object_new_object();
+ } else {
+ vty_out(vty, "Source Group Proto Input Output TTL Uptime%s",
+ VTY_NEWLINE);
+ }
now = pim_time_monotonic_sec();
/* print list of PIM and IGMP routes */
- for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
- char group_str[100];
- char source_str[100];
+ for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) {
+ char grp_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char in_ifname[16];
+ char out_ifname[16];
int oif_vif_index;
-
- if (!c_oil->installed)
+ char proto[100];
+ struct interface *ifp_in;
+ found_oif = 0;
+ first = 1;
+ if (!c_oil->installed && !uj)
continue;
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, sizeof(src_str));
+ ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
+
+ if (ifp_in)
+ strcpy(in_ifname, ifp_in->name);
+ else
+ strcpy(in_ifname, "<iif?>");
+
+ if (uj) {
+
+ /* Find the group, create it if it doesn't exist */
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ /* Find the source nested under the group, create it if it doesn't exist */
+ json_object_object_get_ex(json_group, src_str, &json_source);
+
+ if (!json_source) {
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, src_str, json_source);
+ }
+
+ /* Find the inbound interface nested under the source, create it if it doesn't exist */
+ json_object_int_add(json_source, "installed", c_oil->installed);
+ json_object_int_add(json_source, "refCount", c_oil->oil_ref_count);
+ json_object_int_add(json_source, "oilSize", c_oil->oil_size);
+ json_object_int_add(json_source, "OilInheritedRescan", c_oil->oil_inherited_rescan);
+ json_object_string_add(json_source, "iif", in_ifname);
+ json_oil = NULL;
+ }
+
for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
- struct interface *ifp_in;
struct interface *ifp_out;
char oif_uptime[10];
int ttl;
- char proto[5];
ttl = c_oil->oil.mfcc_ttls[oif_vif_index];
if (ttl < 1)
continue;
- ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
ifp_out = pim_if_find_by_vif_index(oif_vif_index);
-
pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]);
+ found_oif = 1;
- proto[0] = '\0';
- if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) {
- strcat(proto, "P");
- }
- if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) {
- strcat(proto, "I");
- }
- if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) {
- strcat(proto, "O");
+ if (ifp_out)
+ strcpy(out_ifname, ifp_out->name);
+ else
+ strcpy(out_ifname, "<oif?>");
+
+ if (uj) {
+ json_ifp_out = json_object_new_object();
+ json_object_string_add(json_ifp_out, "source", src_str);
+ json_object_string_add(json_ifp_out, "group", grp_str);
+
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM)
+ json_object_boolean_true_add(json_ifp_out, "protocolPim");
+
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP)
+ json_object_boolean_true_add(json_ifp_out, "protocolIgmp");
+
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE)
+ json_object_boolean_true_add(json_ifp_out, "protocolSource");
+
+ json_object_string_add(json_ifp_out, "inboundInterface", in_ifname);
+ json_object_int_add(json_ifp_out, "iVifI", c_oil->oil.mfcc_parent);
+ json_object_string_add(json_ifp_out, "outboundInterface", out_ifname);
+ json_object_int_add(json_ifp_out, "oVifI", oif_vif_index);
+ json_object_int_add(json_ifp_out, "ttl", ttl);
+ json_object_string_add(json_ifp_out, "upTime", oif_uptime);
+ if (!json_oil) {
+ json_oil = json_object_new_object();
+ json_object_object_add(json_source, "oil", json_oil);
+ }
+ json_object_object_add(json_oil, out_ifname, json_ifp_out);
+ } else {
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) {
+ strcpy(proto, "PIM");
+ }
+
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) {
+ strcpy(proto, "IGMP");
+ }
+
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) {
+ strcpy(proto, "SRC");
+ }
+
+ vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s",
+ src_str,
+ grp_str,
+ proto,
+ in_ifname,
+ out_ifname,
+ ttl,
+ oif_uptime,
+ VTY_NEWLINE);
+
+ if (first)
+ {
+ src_str[0] = '\0';
+ grp_str[0] = '\0';
+ in_ifname[0] = '\0';
+ first = 0;
+ }
}
+ }
- vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
- source_str,
- group_str,
- proto,
- ifp_in ? ifp_in->name : "<iif?>",
- c_oil->oil.mfcc_parent,
- ifp_out ? ifp_out->name : "<oif?>",
- oif_vif_index,
- ttl,
- oif_uptime,
- VTY_NEWLINE);
+ if (!uj && !found_oif) {
+ vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s",
+ src_str,
+ grp_str,
+ "none",
+ in_ifname,
+ "none",
+ 0,
+ "--:--:--",
+ VTY_NEWLINE);
}
}
/* Print list of static routes */
for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
- char group_str[100];
- char source_str[100];
+ char grp_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char in_ifname[16];
+ char out_ifname[16];
int oif_vif_index;
+ struct interface *ifp_in;
+ char proto[100];
+ first = 1;
if (!s_route->c_oil.installed)
continue;
- pim_inet4_dump("<group?>", s_route->group, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", s_route->source, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", s_route->group, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<source?>", s_route->source, src_str, sizeof(src_str));
+ ifp_in = pim_if_find_by_vif_index(s_route->iif);
+ found_oif = 0;
+
+ if (ifp_in)
+ strcpy(in_ifname, ifp_in->name);
+ else
+ strcpy(in_ifname, "<iif?>");
+
+ if (uj) {
+
+ /* Find the group, create it if it doesn't exist */
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ /* Find the source nested under the group, create it if it doesn't exist */
+ json_object_object_get_ex(json_group, src_str, &json_source);
+
+ if (!json_source) {
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, src_str, json_source);
+ }
+
+ json_object_string_add(json_source, "iif", in_ifname);
+ json_oil = NULL;
+ } else {
+ strcpy(proto, "STATIC");
+ }
for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
- struct interface *ifp_in;
struct interface *ifp_out;
char oif_uptime[10];
int ttl;
- char proto[5];
ttl = s_route->oif_ttls[oif_vif_index];
if (ttl < 1)
continue;
- ifp_in = pim_if_find_by_vif_index(s_route->iif);
ifp_out = pim_if_find_by_vif_index(oif_vif_index);
-
pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - s_route->c_oil.oif_creation[oif_vif_index]);
+ found_oif = 1;
+
+ if (ifp_out)
+ strcpy(out_ifname, ifp_out->name);
+ else
+ strcpy(out_ifname, "<oif?>");
+
+ if (uj) {
+ json_ifp_out = json_object_new_object();
+ json_object_string_add(json_ifp_out, "source", src_str);
+ json_object_string_add(json_ifp_out, "group", grp_str);
+ json_object_boolean_true_add(json_ifp_out, "protocolStatic");
+ json_object_string_add(json_ifp_out, "inboundInterface", in_ifname);
+ json_object_int_add(json_ifp_out, "iVifI", c_oil->oil.mfcc_parent);
+ json_object_string_add(json_ifp_out, "outboundInterface", out_ifname);
+ json_object_int_add(json_ifp_out, "oVifI", oif_vif_index);
+ json_object_int_add(json_ifp_out, "ttl", ttl);
+ json_object_string_add(json_ifp_out, "upTime", oif_uptime);
+ if (!json_oil) {
+ json_oil = json_object_new_object();
+ json_object_object_add(json_source, "oil", json_oil);
+ }
+ json_object_object_add(json_oil, out_ifname, json_ifp_out);
+ } else {
+ vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s",
+ src_str,
+ grp_str,
+ proto,
+ in_ifname,
+ out_ifname,
+ ttl,
+ oif_uptime,
+ VTY_NEWLINE);
+ if (first)
+ {
+ src_str[0] = '\0';
+ grp_str[0] = '\0';
+ in_ifname[0] = '\0';
+ first = 0;
+ }
+ }
+ }
- proto[0] = '\0';
- strcat(proto, "S");
-
- vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
- source_str,
- group_str,
- proto,
- ifp_in ? ifp_in->name : "<iif?>",
- s_route->iif,
- ifp_out ? ifp_out->name : "<oif?>",
- oif_vif_index,
- ttl,
- oif_uptime,
- VTY_NEWLINE);
+ if (!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;
}
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;
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+ vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld%s",
source_str,
group_str,
+ c_oil->cc.lastused/100,
c_oil->cc.pktcnt,
c_oil->cc.bytecnt,
c_oil->cc.wrong_if,
/* 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;
pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str));
- vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+ vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld%s",
source_str,
group_str,
+ s_route->c_oil.cc.lastused,
s_route->c_oil.cc.pktcnt,
s_route->c_oil.cc.bytecnt,
s_route->c_oil.cc.wrong_if,
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) {
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;
vty_out(vty, "Address NextHop Interface Metric Preference%s",
VTY_NEWLINE);
- pim_inet4_dump("<nexthop?>", nexthop.mrib_nexthop_addr,
- nexthop_addr_str, sizeof(nexthop_addr_str));
+ pim_addr_dump("<nexthop?>", &nexthop.mrib_nexthop_addr,
+ nexthop_addr_str, sizeof(nexthop_addr_str));
vty_out(vty, "%-15s %-15s %-9s %6d %10d%s",
addr_str,
now = pim_time_monotonic_sec();
for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
char ss_uptime[10];
struct sockaddr_in bind_addr;
socklen_t len = sizeof(bind_addr);
- char bind_addr_str[100];
+ char bind_addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
return CMD_SUCCESS;
}
-DEFUN (ip_pim_rp,
- ip_pim_rp_cmd,
- "ip pim rp A.B.C.D",
- IP_STR
- "pim multicast routing\n"
- "Rendevous Point\n"
- "ip address of RP\n")
+static int
+pim_rp_cmd_worker (struct vty *vty, const char *rp, const char *group, const char *plist)
{
- int idx_ipv4 = 3;
int result;
- result = inet_pton(AF_INET, argv[idx_ipv4]->arg, &qpim_rp.rpf_addr.s_addr);
- if (result <= 0) {
- vty_out(vty, "%% Bad RP address specified: %s", argv[idx_ipv4]->arg);
- return CMD_WARNING;
- }
+ result = pim_rp_new (rp, group, plist);
- if (pim_nexthop_lookup(&qpim_rp.source_nexthop, qpim_rp.rpf_addr, NULL) != 0) {
- vty_out(vty, "%% No Path to RP address specified: %s", argv[idx_ipv4]->arg);
- return CMD_WARNING;
- }
+ if (result == PIM_MALLOC_FAIL)
+ {
+ vty_out (vty, "%% Out of memory%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_GROUP_BAD_ADDRESS)
+ {
+ vty_out (vty, "%% Bad group address specified: %s%s", group, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_RP_BAD_ADDRESS)
+ {
+ vty_out (vty, "%% Bad RP address specified: %s%s", rp, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_RP_NO_PATH)
+ {
+ vty_out (vty, "%% No Path to RP address specified: %s%s", rp, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_GROUP_OVERLAP)
+ {
+ vty_out (vty, "%% Group range specified cannot overlap%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_GROUP_PFXLIST_OVERLAP)
+ {
+ vty_out (vty, "%% This group is already covered by a RP prefix-list%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_RP_PFXLIST_IN_USE)
+ {
+ vty_out (vty, "%% The same prefix-list cannot be applied to multiple RPs%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
return CMD_SUCCESS;
}
-DEFUN (no_ip_pim_rp,
- no_ip_pim_rp_cmd,
- "no ip pim rp [A.B.C.D]",
- NO_STR
+DEFUN (ip_pim_joinprune_time,
+ ip_pim_joinprune_time_cmd,
+ "ip pim join-prune-interval <60-600>",
IP_STR
"pim multicast routing\n"
- "Rendevous Point\n"
- "ip address of RP\n")
+ "Join Prune Send Interval\n"
+ "Seconds\n")
{
- qpim_rp.rpf_addr.s_addr = INADDR_NONE;
+ qpim_t_periodic = atoi(argv[3]->arg);
+ return CMD_SUCCESS;
+}
+DEFUN (no_ip_pim_joinprune_time,
+ no_ip_pim_joinprune_time_cmd,
+ "no ip pim join-prune-interval <60-600>",
+ NO_STR
+ IP_STR
+ "pim multicast routing\n"
+ "Join Prune Send Interval\n"
+ "Seconds\n")
+{
+ qpim_t_periodic = PIM_DEFAULT_T_PERIODIC;
return CMD_SUCCESS;
}
-DEFUN (ip_multicast_routing,
- ip_multicast_routing_cmd,
- "ip multicast-routing",
+DEFUN (ip_pim_register_suppress,
+ ip_pim_register_suppress_cmd,
+ "ip pim register-suppress-time <5-60000>",
IP_STR
- "Enable IP multicast forwarding\n")
+ "pim multicast routing\n"
+ "Register Suppress Timer\n"
+ "Seconds\n")
{
- pim_mroute_socket_enable();
- pim_if_add_vif_all();
- mroute_add_all();
- static_mroute_add_all();
+ qpim_keep_alive_time = atoi (argv[3]->arg);
return CMD_SUCCESS;
}
-DEFUN (no_ip_multicast_routing,
- no_ip_multicast_routing_cmd,
- "no ip multicast-routing",
+DEFUN (no_ip_pim_register_suppress,
+ no_ip_pim_register_suppress_cmd,
+ "no ip pim register-suppress-time <5-60000>",
NO_STR
IP_STR
- "Global IP configuration subcommands\n"
- "Enable IP multicast forwarding\n")
+ "pim multicast routing\n"
+ "Register Suppress Timer\n"
+ "Seconds\n")
{
- mroute_del_all();
- static_mroute_del_all();
- pim_if_del_vif_all();
- pim_mroute_socket_disable();
+ qpim_register_suppress_time = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT;
return CMD_SUCCESS;
}
-DEFUN (ip_ssmpingd,
- ip_ssmpingd_cmd,
- "ip ssmpingd [A.B.C.D]",
+DEFUN (ip_pim_keep_alive,
+ ip_pim_keep_alive_cmd,
+ "ip pim keep-alive-timer <31-60000>",
IP_STR
- CONF_SSMPINGD_STR
- "Source address\n")
+ "pim multicast routing\n"
+ "Keep alive Timer\n"
+ "Seconds\n")
{
- int idx_ipv4 = 2;
- int result;
- struct in_addr source_addr;
- const char *source_str = (argc > idx_ipv4) ? argv[idx_ipv4]->arg : "0.0.0.0";
-
- result = inet_pton(AF_INET, source_str, &source_addr);
- if (result <= 0) {
- vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
- source_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
- }
+ qpim_rp_keep_alive_time = atoi (argv[4]->arg);
+ return CMD_SUCCESS;
+}
- result = pim_ssmpingd_start(source_addr);
+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 [A.B.C.D/M]",
+ IP_STR
+ "pim multicast routing\n"
+ "Rendevous Point\n"
+ "ip address of RP\n")
+{
+ int idx_ipv4 = 3;
+ return pim_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL);
+}
+
+DEFUN (ip_pim_rp_prefix_list,
+ ip_pim_rp_prefix_list_cmd,
+ "ip pim rp A.B.C.D prefix-list WORD",
+ IP_STR
+ "pim multicast routing\n"
+ "Rendevous Point\n"
+ "ip address of RP\n"
+ "group prefix-list filter\n"
+ "Name of a prefix-list\n")
+{
+ return pim_rp_cmd_worker (vty, argv[3]->arg, NULL, argv[5]->arg);
+}
+
+static int
+pim_no_rp_cmd_worker (struct vty *vty, const char *rp, const char *group,
+ const char *plist)
+{
+ int result = pim_rp_del (rp, group, plist);
+
+ if (result == PIM_GROUP_BAD_ADDRESS)
+ {
+ vty_out (vty, "%% Bad group address specified: %s%s", group, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_RP_BAD_ADDRESS)
+ {
+ vty_out (vty, "%% Bad RP address specified: %s%s", rp, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (result == PIM_RP_NOT_FOUND)
+ {
+ vty_out (vty, "%% Unable to find specified RP%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_pim_rp,
+ no_ip_pim_rp_cmd,
+ "no ip pim rp A.B.C.D [A.B.C.D/M]",
+ NO_STR
+ IP_STR
+ "pim multicast routing\n"
+ "Rendevous Point\n"
+ "ip address of RP\n")
+{
+ int idx_ipv4 = 4;
+ return pim_no_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL);
+}
+
+DEFUN (no_ip_pim_rp_prefix_list,
+ no_ip_pim_rp_prefix_list_cmd,
+ "no ip pim rp A.B.C.D prefix-list WORD",
+ NO_STR
+ IP_STR
+ "pim multicast routing\n"
+ "Rendevous Point\n"
+ "ip address of RP\n"
+ "group prefix-list filter\n"
+ "Name of a prefix-list\n")
+{
+ return pim_no_rp_cmd_worker (vty, argv[4]->arg, NULL, argv[6]->arg);
+}
+
+DEFUN (ip_multicast_routing,
+ ip_multicast_routing_cmd,
+ "ip multicast-routing",
+ IP_STR
+ "Enable IP multicast forwarding\n")
+{
+ pim_mroute_socket_enable();
+ pim_if_add_vif_all();
+ mroute_add_all();
+ static_mroute_add_all();
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_multicast_routing,
+ no_ip_multicast_routing_cmd,
+ "no ip multicast-routing",
+ NO_STR
+ IP_STR
+ "Global IP configuration subcommands\n"
+ "Enable IP multicast forwarding\n")
+{
+ mroute_del_all();
+ static_mroute_del_all();
+ pim_if_del_vif_all();
+ pim_mroute_socket_disable();
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_ssmpingd,
+ ip_ssmpingd_cmd,
+ "ip ssmpingd [A.B.C.D]",
+ IP_STR
+ CONF_SSMPINGD_STR
+ "Source address\n")
+{
+ int idx_ipv4 = 2;
+ int result;
+ struct in_addr source_addr;
+ const char *source_str = (argc > idx_ipv4) ? argv[idx_ipv4]->arg : "0.0.0.0";
+
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ result = pim_ssmpingd_start(source_addr);
if (result) {
vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s",
source_str, result, VTY_NEWLINE);
pim_ifp = ifp->info;
if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("%s: Querier %s on %s reconfig query_interval=%d",
__PRETTY_FUNCTION__,
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;
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,
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
{
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;
#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;
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) {
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;
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 */);
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;
}
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.
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);
}
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",
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,
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;
}
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;
DEFUN (debug_pim_packets_filter,
debug_pim_packets_filter_cmd,
- "debug pim packets <hello|joins>",
+ "debug pim packets <hello|joins|register>",
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_PACKETS_STR
DEBUG_PIM_HELLO_PACKETS_STR
- DEBUG_PIM_J_P_PACKETS_STR)
+ DEBUG_PIM_J_P_PACKETS_STR
+ DEBUG_PIM_PIM_REG_PACKETS_STR)
{
int idx_hello_join = 3;
- if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0)
+ if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0)
{
PIM_DO_DEBUG_PIM_HELLO;
- vty_out (vty, "PIM Hello debugging is on %s", VTY_NEWLINE);
+ vty_out (vty, "PIM Hello debugging is on%s", VTY_NEWLINE);
}
- else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0)
+ else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0)
{
PIM_DO_DEBUG_PIM_J_P;
- vty_out (vty, "PIM Join/Prune debugging is on %s", VTY_NEWLINE);
+ vty_out (vty, "PIM Join/Prune debugging is on%s", VTY_NEWLINE);
+ }
+ else if (strncmp(argv[idx_hello_join]->arg,"r",1) == 0)
+ {
+ PIM_DO_DEBUG_PIM_REG;
+ vty_out (vty, "PIM Register debugging is on%s", VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN (no_debug_pim_packets_filter,
no_debug_pim_packets_filter_cmd,
- "no debug pim packets <hello|joins>",
+ "no debug pim packets <hello|joins|register>",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
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;
}
}
-DEFUN (show_debugging_pim,
- show_debugging_pim_cmd,
- "show debugging pim",
- SHOW_STR
+DEFUN (debug_msdp,
+ debug_msdp_cmd,
+ "debug msdp",
DEBUG_STR
- PIM_STR)
+ DEBUG_MSDP_STR)
{
- pim_debug_config_write(vty);
+ PIM_DO_DEBUG_MSDP_EVENTS;
+ PIM_DO_DEBUG_MSDP_PACKETS;
return CMD_SUCCESS;
}
-static struct igmp_sock *find_igmp_sock_by_fd(int fd)
+DEFUN (no_debug_msdp,
+ no_debug_msdp_cmd,
+ "no debug msdp",
+ NO_STR
+ DEBUG_STR
+ DEBUG_MSDP_STR)
{
- struct listnode *ifnode;
- struct interface *ifp;
-
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) {
- struct pim_interface *pim_ifp;
- struct igmp_sock *igmp;
-
- if (!ifp->info)
- continue;
+ PIM_DONT_DEBUG_MSDP_EVENTS;
+ PIM_DONT_DEBUG_MSDP_PACKETS;
+ return CMD_SUCCESS;
+}
- pim_ifp = ifp->info;
+ALIAS (no_debug_msdp,
+ undebug_msdp_cmd,
+ "undebug msdp",
+ UNDEBUG_STR
+ DEBUG_MSDP_STR)
- /* lookup igmp socket under current interface */
- igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd);
- if (igmp)
- return igmp;
- }
-
- return 0;
-}
-
-DEFUN (test_igmp_receive_report,
- test_igmp_receive_report_cmd,
- "test igmp receive report (0-65535) A.B.C.D (1-6) LINE...",
- "Test\n"
- "Test IGMP protocol\n"
- "Test IGMP message\n"
- "Test IGMP report\n"
- "Socket\n"
- "IGMP group address\n"
- "Record type\n"
- "Sources\n")
-{
- int idx_number = 4;
- int idx_ipv4 = 5;
- int idx_number_2 = 6;
- int idx_line = 7;
- char buf[1000];
- char *igmp_msg;
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
- int ip_msg_len;
- int igmp_msg_len;
- const char *socket;
- int socket_fd;
- const char *grp_str;
- struct in_addr grp_addr;
- const char *record_type_str;
- int record_type;
- const char *src_str;
- int result;
- struct igmp_sock *igmp;
- char *group_record;
- int num_sources;
- struct in_addr *sources;
- struct in_addr *src_addr;
- int argi;
-
- socket = argv[idx_number]->arg;
- socket_fd = atoi(socket);
- igmp = find_igmp_sock_by_fd(socket_fd);
- if (!igmp) {
- vty_out(vty, "Could not find IGMP socket %s: fd=%d%s",
- socket, socket_fd, VTY_NEWLINE);
- return CMD_WARNING;
- }
+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;
+}
- grp_str = argv[idx_ipv4]->arg;
- result = inet_pton(AF_INET, grp_str, &grp_addr);
- if (result <= 0) {
- vty_out(vty, "Bad group address %s: errno=%d: %s%s",
- grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
- }
+DEFUN (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;
+}
- record_type_str = argv[idx_number_2]->arg;
- record_type = atoi(record_type_str);
+ALIAS (no_debug_msdp_events,
+ undebug_msdp_events_cmd,
+ "undebug msdp events",
+ UNDEBUG_STR
+ DEBUG_MSDP_STR
+ DEBUG_MSDP_EVENTS_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;
- }
- }
- num_sources = src_addr - sources;
+DEFUN (debug_msdp_packets,
+ debug_msdp_packets_cmd,
+ "debug msdp packets",
+ DEBUG_STR
+ DEBUG_MSDP_STR
+ DEBUG_MSDP_PACKETS_STR)
+{
+ PIM_DO_DEBUG_MSDP_PACKETS;
+ return CMD_SUCCESS;
+}
- *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources);
+DEFUN (no_debug_msdp_packets,
+ no_debug_msdp_packets_cmd,
+ "no debug msdp packets",
+ NO_STR
+ DEBUG_STR
+ DEBUG_MSDP_STR
+ DEBUG_MSDP_PACKETS_STR)
+{
+ PIM_DONT_DEBUG_MSDP_PACKETS;
+ return CMD_SUCCESS;
+}
- igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */
+ALIAS (no_debug_msdp_packets,
+ undebug_msdp_packets_cmd,
+ "undebug msdp packets",
+ UNDEBUG_STR
+ DEBUG_MSDP_STR
+ DEBUG_MSDP_PACKETS_STR)
- /* compute checksum */
- *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len);
+DEFUN (show_debugging_pim,
+ show_debugging_pim_cmd,
+ "show debugging pim",
+ SHOW_STR
+ DEBUG_STR
+ PIM_STR)
+{
+ pim_debug_config_write(vty);
+ return CMD_SUCCESS;
+}
- /* "receive" message */
+static int
+interface_pim_use_src_cmd_worker(struct vty *vty, const char *source)
+{
+ int result;
+ struct in_addr source_addr;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
- ip_msg_len = ip_hlen + igmp_msg_len;
- result = pim_igmp_packet(igmp, buf, ip_msg_len);
- if (result) {
- vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s",
- ip_msg_len, result, VTY_NEWLINE);
+ result = inet_pton(AF_INET, source, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+ source, errno, safe_strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
- return CMD_SUCCESS;
+ result = pim_update_source_set(ifp, source_addr);
+ switch (result) {
+ case PIM_SUCCESS:
+ break;
+ case PIM_IFACE_NOT_FOUND:
+ vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE);
+ break;
+ case PIM_UPDATE_SOURCE_DUP:
+ vty_out(vty, "%% Source already set to %s%s", source, VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% Source set failed%s", VTY_NEWLINE);
+ }
+
+ return result?CMD_WARNING:CMD_SUCCESS;
}
-static int hexval(uint8_t ch)
-{
- return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a');
-}
-
-DEFUN (test_pim_receive_dump,
- test_pim_receive_dump_cmd,
- "test pim receive dump INTERFACE A.B.C.D LINE...",
- "Test\n"
- "Test PIM protocol\n"
- "Test PIM message reception\n"
- "Test PIM packet dump reception from neighbor\n"
- "Interface\n"
- "Neighbor address\n"
- "Packet dump\n")
-{
- int idx_interface = 4;
- int idx_ipv4 = 5;
- int idx_line = 6;
- uint8_t buf[1000];
- uint8_t *pim_msg;
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
- int ip_msg_len;
- int pim_msg_size;
- const char *neigh_str;
- struct in_addr neigh_addr;
- const char *ifname;
- struct interface *ifp;
- int argi;
- int result;
-
- /* Find interface */
- ifname = argv[idx_interface]->arg;
- ifp = if_lookup_by_name(ifname);
- if (!ifp) {
- vty_out(vty, "No such interface name %s%s",
- ifname, VTY_NEWLINE);
+DEFUN (interface_pim_use_source,
+ interface_pim_use_source_cmd,
+ "ip pim use-source A.B.C.D",
+ IP_STR
+ "pim multicast routing\n"
+ "Configure primary IP address\n"
+ "source ip address\n")
+{
+ return interface_pim_use_src_cmd_worker (vty, argv[3]->arg);
+}
+
+DEFUN (interface_no_pim_use_source,
+ interface_no_pim_use_source_cmd,
+ "no ip pim use-source",
+ NO_STR
+ IP_STR
+ "pim multicast routing\n"
+ "Delete source IP address\n")
+{
+ return interface_pim_use_src_cmd_worker (vty, "0.0.0.0");
+}
+
+static int
+ip_msdp_peer_cmd_worker (struct vty *vty, const char *peer, const char *local)
+{
+ enum pim_msdp_err result;
+ struct in_addr peer_addr;
+ struct in_addr local_addr;
+
+ result = inet_pton(AF_INET, peer, &peer_addr);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad peer address %s: errno=%d: %s%s",
+ peer, errno, safe_strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
- /* Neighbor address */
- neigh_str = argv[idx_ipv4]->arg;
- result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+ result = inet_pton(AF_INET, local, &local_addr);
if (result <= 0) {
- vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
- neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+ local, errno, safe_strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
- /*
- Tweak IP header
- */
- ip_hdr = (struct ip *) buf;
- ip_hdr->ip_p = PIM_IP_PROTO_PIM;
- ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
- ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
- ip_hdr->ip_src = neigh_addr;
- ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+ result = pim_msdp_peer_add(peer_addr, local_addr, "default", NULL/* mp_p */);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_OOM:
+ vty_out(vty, "%% Out of memory%s", VTY_NEWLINE);
+ break;
+ case PIM_MSDP_ERR_PEER_EXISTS:
+ vty_out(vty, "%% Peer exists%s", VTY_NEWLINE);
+ break;
+ case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+ vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% peer add failed%s", VTY_NEWLINE);
+ }
- /*
- Build PIM hello message
- */
- pim_msg = buf + ip_hlen;
- pim_msg_size = 0;
-
- /* Scan LINE dump into buffer */
- for (argi = idx_line; argi < argc; ++argi) {
- const char *str = argv[argi]->arg;
- int str_len = strlen(str);
- int str_last = str_len - 1;
- int i;
-
- if (str_len % 2) {
- vty_out(vty, "%% Uneven hex array arg %d=%s%s",
- argi, str, VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return result?CMD_WARNING:CMD_SUCCESS;
+}
- for (i = 0; i < str_last; i += 2) {
- uint8_t octet;
- int left;
- uint8_t h1 = str[i];
- uint8_t h2 = str[i + 1];
+DEFUN_HIDDEN (ip_msdp_peer,
+ ip_msdp_peer_cmd,
+ "ip msdp peer A.B.C.D source A.B.C.D",
+ IP_STR
+ CFG_MSDP_STR
+ "Configure MSDP peer\n"
+ "peer ip address\n"
+ "Source address for TCP connection\n"
+ "local ip address\n")
+{
+ return ip_msdp_peer_cmd_worker (vty, argv[3]->arg, argv[5]->arg);
+}
- if (!isxdigit(h1) || !isxdigit(h2)) {
- vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s",
- h1, h2, argi, str, VTY_NEWLINE);
- return CMD_WARNING;
- }
- octet = (hexval(h1) << 4) + hexval(h2);
+static int
+ip_no_msdp_peer_cmd_worker (struct vty *vty, const char *peer)
+{
+ enum pim_msdp_err result;
+ struct in_addr peer_addr;
- left = sizeof(buf) - ip_hlen - pim_msg_size;
- if (left < 1) {
- vty_out(vty, "%% Overflow buf_size=%zu buf_left=%d at hex array arg %d=%s octet %02x%s",
- sizeof(buf), left, argi, str, octet, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- pim_msg[pim_msg_size++] = octet;
- }
+ result = inet_pton(AF_INET, peer, &peer_addr);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad peer address %s: errno=%d: %s%s",
+ peer, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
}
- ip_msg_len = ip_hlen + pim_msg_size;
+ result = pim_msdp_peer_del(peer_addr);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_NO_PEER:
+ vty_out(vty, "%% Peer does not exist%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% peer del failed%s", VTY_NEWLINE);
+ }
- vty_out(vty, "Receiving: buf_size=%zu ip_msg_size=%d pim_msg_size=%d%s",
- sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE);
+ return result?CMD_WARNING:CMD_SUCCESS;
+}
- /* "receive" message */
+DEFUN_HIDDEN (no_ip_msdp_peer,
+ no_ip_msdp_peer_cmd,
+ "no ip msdp peer A.B.C.D",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP peer\n"
+ "peer ip address\n")
+{
+ return ip_no_msdp_peer_cmd_worker (vty, argv[4]->arg);
+}
- result = pim_pim_packet(ifp, buf, ip_msg_len);
- if (result) {
- vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s",
- ip_msg_len, result, VTY_NEWLINE);
+static int
+ip_msdp_mesh_group_member_cmd_worker(struct vty *vty, const char *mg, const char *mbr)
+{
+ enum pim_msdp_err result;
+ struct in_addr mbr_ip;
+
+ result = inet_pton(AF_INET, mbr, &mbr_ip);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad member address %s: errno=%d: %s%s",
+ mbr, errno, safe_strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
- return CMD_SUCCESS;
+ result = pim_msdp_mg_mbr_add(mg, mbr_ip);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_OOM:
+ vty_out(vty, "%% Out of memory%s", VTY_NEWLINE);
+ break;
+ case PIM_MSDP_ERR_MG_MBR_EXISTS:
+ vty_out(vty, "%% mesh-group member exists%s", VTY_NEWLINE);
+ break;
+ case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+ vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% member add failed%s", VTY_NEWLINE);
+ }
+
+ return result?CMD_WARNING:CMD_SUCCESS;
}
-DEFUN (test_pim_receive_hello,
- test_pim_receive_hello_cmd,
- "test pim receive hello INTERFACE A.B.C.D (0-65535) (0-65535) (0-65535) (0-32767) (0-65535) (0-1) [LINE]",
- "Test\n"
- "Test PIM protocol\n"
- "Test PIM message reception\n"
- "Test PIM hello reception from neighbor\n"
- "Interface\n"
- "Neighbor address\n"
- "Neighbor holdtime\n"
- "Neighbor DR priority\n"
- "Neighbor generation ID\n"
- "Neighbor propagation delay (msec)\n"
- "Neighbor override interval (msec)\n"
- "Neighbor LAN prune delay T-bit\n"
- "Neighbor secondary addresses\n")
-{
- int idx_interface = 4;
- int idx_ipv4 = 5;
- int idx_number = 6;
- int idx_number_2 = 7;
- int idx_number_3 = 8;
- int idx_number_4 = 9;
- int idx_number_5 = 10;
- int idx_number_6 = 11;
- int idx_line = 12;
- uint8_t buf[1000];
- uint8_t *pim_msg;
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
- int ip_msg_len;
- int pim_tlv_size;
- int pim_msg_size;
- const char *neigh_str;
- struct in_addr neigh_addr;
- const char *ifname;
- struct interface *ifp;
- uint16_t neigh_holdtime;
- uint16_t neigh_propagation_delay;
- uint16_t neigh_override_interval;
- int neigh_can_disable_join_suppression;
- uint32_t neigh_dr_priority;
- uint32_t neigh_generation_id;
- int argi;
- int result;
-
- /* Find interface */
- ifname = argv[idx_interface]->arg;
- ifp = if_lookup_by_name(ifname);
- if (!ifp) {
- vty_out(vty, "No such interface name %s%s",
- ifname, VTY_NEWLINE);
- return CMD_WARNING;
- }
+DEFUN (ip_msdp_mesh_group_member,
+ ip_msdp_mesh_group_member_cmd,
+ "ip msdp mesh-group WORD member A.B.C.D",
+ IP_STR
+ CFG_MSDP_STR
+ "Configure MSDP mesh-group\n"
+ "mesh group name\n"
+ "mesh group member\n"
+ "peer ip address\n")
+{
+ return ip_msdp_mesh_group_member_cmd_worker(vty, argv[3]->arg, argv[5]->arg);
+}
- /* Neighbor address */
- neigh_str = argv[idx_ipv4]->arg;
- result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+static int
+ip_no_msdp_mesh_group_member_cmd_worker(struct vty *vty, const char *mg, const char *mbr)
+{
+ enum pim_msdp_err result;
+ struct in_addr mbr_ip;
+
+ result = inet_pton(AF_INET, mbr, &mbr_ip);
if (result <= 0) {
- vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
- neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ vty_out(vty, "%% Bad member address %s: errno=%d: %s%s",
+ mbr, errno, safe_strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
- neigh_holdtime = atoi(argv[idx_number]->arg);
- neigh_dr_priority = atoi(argv[idx_number_2]->arg);
- neigh_generation_id = atoi(argv[idx_number_3]->arg);
- neigh_propagation_delay = atoi(argv[idx_number_4]->arg);
- neigh_override_interval = atoi(argv[idx_number_5]->arg);
- neigh_can_disable_join_suppression = atoi(argv[idx_number_6]->arg);
+ result = pim_msdp_mg_mbr_del(mg, mbr_ip);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_NO_MG:
+ vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE);
+ break;
+ case PIM_MSDP_ERR_NO_MG_MBR:
+ vty_out(vty, "%% mesh-group member does not exist%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% mesh-group member del failed%s", VTY_NEWLINE);
+ }
- /*
- Tweak IP header
- */
- ip_hdr = (struct ip *) buf;
- ip_hdr->ip_p = PIM_IP_PROTO_PIM;
- ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
- ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
- ip_hdr->ip_src = neigh_addr;
- ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+ return result?CMD_WARNING:CMD_SUCCESS;
+}
+DEFUN (no_ip_msdp_mesh_group_member,
+ no_ip_msdp_mesh_group_member_cmd,
+ "no ip msdp mesh-group WORD member A.B.C.D",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP mesh-group member\n"
+ "mesh group name\n"
+ "mesh group member\n"
+ "peer ip address\n")
+{
+ return ip_no_msdp_mesh_group_member_cmd_worker(vty, argv[4]->arg, argv[6]->arg);
+}
- /*
- Build PIM hello message
- */
- pim_msg = buf + ip_hlen;
-
- /* Scan LINE addresses */
- for (argi = idx_line; argi < argc; ++argi) {
- const char *sec_str = argv[argi]->arg;
- struct in_addr sec_addr;
- result = inet_pton(AF_INET, sec_str, &sec_addr);
- if (result <= 0) {
- vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s",
- sec_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
- }
+static int
+ip_msdp_mesh_group_source_cmd_worker(struct vty *vty, const char *mg, const char *src)
+{
+ enum pim_msdp_err result;
+ struct in_addr src_ip;
- vty_out(vty,
- "FIXME WRITEME consider neighbor secondary address %s%s",
- sec_str, VTY_NEWLINE);
- }
-
- pim_tlv_size = pim_hello_build_tlv(ifp->name,
- pim_msg + PIM_PIM_MIN_LEN,
- sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN,
- neigh_holdtime,
- neigh_dr_priority,
- neigh_generation_id,
- neigh_propagation_delay,
- neigh_override_interval,
- neigh_can_disable_join_suppression,
- 0 /* FIXME secondary address list */);
- if (pim_tlv_size < 0) {
- vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s",
- pim_tlv_size, VTY_NEWLINE);
+ result = inet_pton(AF_INET, src, &src_ip);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+ src, errno, safe_strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
- pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
+ result = pim_msdp_mg_src_add(mg, src_ip);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_OOM:
+ vty_out(vty, "%% Out of memory%s", VTY_NEWLINE);
+ break;
+ case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+ vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% source add failed%s", VTY_NEWLINE);
+ }
- pim_msg_build_header(pim_msg, pim_msg_size,
- PIM_MSG_TYPE_HELLO);
+ return result?CMD_WARNING:CMD_SUCCESS;
+}
- /* "receive" message */
- ip_msg_len = ip_hlen + pim_msg_size;
- result = pim_pim_packet(ifp, buf, ip_msg_len);
- if (result) {
- vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
- ip_msg_len, result, VTY_NEWLINE);
- return CMD_WARNING;
+DEFUN (ip_msdp_mesh_group_source,
+ ip_msdp_mesh_group_source_cmd,
+ "ip msdp mesh-group WORD source A.B.C.D",
+ IP_STR
+ CFG_MSDP_STR
+ "Configure MSDP mesh-group\n"
+ "mesh group name\n"
+ "mesh group local address\n"
+ "source ip address for the TCP connection\n")
+{
+ return ip_msdp_mesh_group_source_cmd_worker(vty, argv[3]->arg, argv[5]->arg);
+}
+
+static int
+ip_no_msdp_mesh_group_source_cmd_worker(struct vty *vty, const char *mg)
+{
+ enum pim_msdp_err result;
+
+ result = pim_msdp_mg_src_del(mg);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_NO_MG:
+ vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% mesh-group source del failed%s", VTY_NEWLINE);
}
- return CMD_SUCCESS;
+ return result?CMD_WARNING:CMD_SUCCESS;
}
-DEFUN (test_pim_receive_assert,
- test_pim_receive_assert_cmd,
- "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D (0-65535) (0-65535) (0-1)",
- "Test\n"
- "Test PIM protocol\n"
- "Test PIM message reception\n"
- "Test reception of PIM assert\n"
- "Interface\n"
- "Neighbor address\n"
- "Assert multicast group address\n"
- "Assert unicast source address\n"
- "Assert metric preference\n"
- "Assert route metric\n"
- "Assert RPT bit flag\n")
-{
- int idx_interface = 4;
- int idx_ipv4 = 5;
- int idx_ipv4_2 = 6;
- int idx_ipv4_3 = 7;
- int idx_number = 8;
- int idx_number_2 = 9;
- int idx_number_3 = 10;
- uint8_t buf[1000];
- uint8_t *buf_pastend = buf + sizeof(buf);
- uint8_t *pim_msg;
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
- int ip_msg_len;
- int pim_msg_size;
- const char *neigh_str;
- struct in_addr neigh_addr;
- const char *group_str;
- struct in_addr group_addr;
- const char *source_str;
- struct in_addr source_addr;
- const char *ifname;
- struct interface *ifp;
- uint32_t assert_metric_preference;
- uint32_t assert_route_metric;
- uint32_t assert_rpt_bit_flag;
- int remain;
- int result;
-
- /* Find interface */
- ifname = argv[idx_interface]->arg;
- ifp = if_lookup_by_name(ifname);
- if (!ifp) {
- vty_out(vty, "No such interface name %s%s",
- ifname, VTY_NEWLINE);
- return CMD_WARNING;
+static int
+ip_no_msdp_mesh_group_cmd_worker(struct vty *vty, const char *mg)
+{
+ enum pim_msdp_err result;
+
+ result = pim_msdp_mg_del(mg);
+ switch (result) {
+ case PIM_MSDP_ERR_NONE:
+ break;
+ case PIM_MSDP_ERR_NO_MG:
+ vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out(vty, "%% mesh-group source del failed%s", VTY_NEWLINE);
}
- /* Neighbor address */
- neigh_str = argv[idx_ipv4]->arg;
- result = inet_pton(AF_INET, neigh_str, &neigh_addr);
- if (result <= 0) {
- vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
- neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return result ? CMD_WARNING : CMD_SUCCESS;
+}
- /* Group address */
- group_str = argv[idx_ipv4_2]->arg;
- result = inet_pton(AF_INET, group_str, &group_addr);
- if (result <= 0) {
- vty_out(vty, "Bad group address %s: errno=%d: %s%s",
- group_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+DEFUN (no_ip_msdp_mesh_group_source,
+ no_ip_msdp_mesh_group_source_cmd,
+ "no ip msdp mesh-group WORD source [A.B.C.D]",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP mesh-group source\n"
+ "mesh group name\n"
+ "mesh group local address\n")
+{
+ if (argv[6]->arg)
+ return ip_no_msdp_mesh_group_cmd_worker(vty, argv[6]->arg);
+ else
+ return ip_no_msdp_mesh_group_source_cmd_worker(vty, argv[4]->arg);
+}
+
+static void
+print_empty_json_obj(struct vty *vty)
+{
+ json_object *json;
+ json = json_object_new_object();
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+}
+
+static void
+ip_msdp_show_mesh_group(struct vty *vty, u_char uj)
+{
+ struct listnode *mbrnode;
+ struct pim_msdp_mg_mbr *mbr;
+ struct pim_msdp_mg *mg = msdp->mg;
+ char mbr_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char state_str[PIM_MSDP_STATE_STRLEN];
+ enum pim_msdp_peer_state state;
+ json_object *json = NULL;
+ json_object *json_mg_row = NULL;
+ json_object *json_members = NULL;
+ json_object *json_row = NULL;
+
+ if (!mg) {
+ if (uj)
+ print_empty_json_obj(vty);
+ return;
}
- /* Source address */
- source_str = argv[idx_ipv4_3]->arg;
- result = inet_pton(AF_INET, source_str, &source_addr);
- if (result <= 0) {
- vty_out(vty, "Bad source address %s: errno=%d: %s%s",
- source_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+ pim_inet4_dump("<source?>", mg->src_ip, src_str, sizeof(src_str));
+ if (uj) {
+ json = json_object_new_object();
+ /* currently there is only one mesh group but we should still make
+ * it a dict with mg-name as key */
+ json_mg_row = json_object_new_object();
+ json_object_string_add(json_mg_row, "name", mg->mesh_group_name);
+ json_object_string_add(json_mg_row, "source", src_str);
+ } else {
+ vty_out(vty, "Mesh group : %s%s", mg->mesh_group_name, VTY_NEWLINE);
+ vty_out(vty, " Source : %s%s", src_str, VTY_NEWLINE);
+ vty_out(vty, " Member State%s", VTY_NEWLINE);
}
- assert_metric_preference = atoi(argv[idx_number]->arg);
- assert_route_metric = atoi(argv[idx_number_2]->arg);
- assert_rpt_bit_flag = atoi(argv[idx_number_3]->arg);
-
- remain = buf_pastend - buf;
- if (remain < (int) sizeof(struct ip)) {
- vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%zu%s",
- remain, sizeof(struct ip), VTY_NEWLINE);
- return CMD_WARNING;
+ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
+ pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
+ if (mbr->mp) {
+ state = mbr->mp->state;
+ } else {
+ state = PIM_MSDP_DISABLED;
+ }
+ pim_msdp_state_dump(state, state_str, sizeof(state_str));
+ if (uj) {
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "member", mbr_str);
+ json_object_string_add(json_row, "state", state_str);
+ if (!json_members) {
+ json_members = json_object_new_object();
+ json_object_object_add(json_mg_row, "members", json_members);
+ }
+ json_object_object_add(json_members, mbr_str, json_row);
+ } else {
+ vty_out(vty, " %-15s %11s%s",
+ mbr_str, state_str, VTY_NEWLINE);
+ }
}
- /*
- Tweak IP header
- */
- ip_hdr = (struct ip *) buf;
- ip_hdr->ip_p = PIM_IP_PROTO_PIM;
- ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
- ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
- ip_hdr->ip_src = neigh_addr;
- ip_hdr->ip_dst = qpim_all_pim_routers_addr;
-
- /*
- Build PIM assert message
- */
- pim_msg = buf + ip_hlen; /* skip ip header */
-
- pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp,
- group_addr, source_addr,
- assert_metric_preference,
- assert_route_metric,
- assert_rpt_bit_flag);
- if (pim_msg_size < 0) {
- vty_out(vty, "Failure building PIM assert message: size=%d%s",
- pim_msg_size, VTY_NEWLINE);
- return CMD_WARNING;
+ if (uj) {
+ json_object_object_add(json, mg->mesh_group_name, json_mg_row);
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
+}
- /* "receive" message */
-
- ip_msg_len = ip_hlen + pim_msg_size;
- result = pim_pim_packet(ifp, buf, ip_msg_len);
- if (result) {
- vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
- ip_msg_len, result, VTY_NEWLINE);
- return CMD_WARNING;
- }
+DEFUN (show_ip_msdp_mesh_group,
+ show_ip_msdp_mesh_group_cmd,
+ "show ip msdp mesh-group [json]",
+ SHOW_STR
+ IP_STR
+ MSDP_STR
+ "MSDP mesh-group information\n"
+ "JavaScript Object Notation\n")
+{
+ u_char uj = use_json(argc, argv);
+ ip_msdp_show_mesh_group(vty, uj);
return CMD_SUCCESS;
}
-static int recv_joinprune(struct vty *vty,
- struct cmd_token **argv,
- int src_is_join)
-{
- uint8_t buf[1000];
- const uint8_t *buf_pastend = buf + sizeof(buf);
- uint8_t *pim_msg;
- uint8_t *pim_msg_curr;
- int pim_msg_size;
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
- int ip_msg_len;
- uint16_t neigh_holdtime;
- const char *neigh_dst_str;
- struct in_addr neigh_dst_addr;
- const char *neigh_src_str;
- struct in_addr neigh_src_addr;
- const char *group_str;
- struct in_addr group_addr;
- const char *source_str;
- struct in_addr source_addr;
- const char *ifname;
- struct interface *ifp;
- int result;
- int remain;
- uint16_t num_joined;
- uint16_t num_pruned;
-
- /* Find interface */
- ifname = argv[0]->arg;
- ifp = if_lookup_by_name(ifname);
- if (!ifp) {
- vty_out(vty, "No such interface name %s%s",
- ifname, VTY_NEWLINE);
- return CMD_WARNING;
- }
+static void
+ip_msdp_show_peers(struct vty *vty, u_char uj)
+{
+ struct listnode *mpnode;
+ struct pim_msdp_peer *mp;
+ char peer_str[INET_ADDRSTRLEN];
+ char local_str[INET_ADDRSTRLEN];
+ char state_str[PIM_MSDP_STATE_STRLEN];
+ char timebuf[PIM_MSDP_UPTIME_STRLEN];
+ int64_t now;
+ json_object *json = NULL;
+ json_object *json_row = NULL;
- neigh_holdtime = atoi(argv[1]->arg);
- /* Neighbor destination address */
- neigh_dst_str = argv[2]->arg;
- result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr);
- if (result <= 0) {
- vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s",
- neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+ if (uj) {
+ json = json_object_new_object();
+ } else {
+ vty_out(vty, "Peer Local State Uptime SaCnt%s", VTY_NEWLINE);
}
- /* Neighbor source address */
- neigh_src_str = argv[3]->arg;
- result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr);
- if (result <= 0) {
- vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s",
- neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+ for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+ if (mp->state == PIM_MSDP_ESTABLISHED) {
+ now = pim_time_monotonic_sec();
+ pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime);
+ } else {
+ strcpy(timebuf, "-");
+ }
+ pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
+ pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
+ pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
+ if (uj) {
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "peer", peer_str);
+ json_object_string_add(json_row, "local", local_str);
+ json_object_string_add(json_row, "state", state_str);
+ json_object_string_add(json_row, "upTime", timebuf);
+ json_object_int_add(json_row, "saCount", mp->sa_cnt);
+ json_object_object_add(json, peer_str, json_row);
+ } else {
+ vty_out(vty, "%-15s %15s %11s %8s %6d%s",
+ peer_str, local_str, state_str,
+ timebuf, mp->sa_cnt, VTY_NEWLINE);
+ }
}
- /* Multicast group address */
- group_str = argv[4]->arg;
- result = inet_pton(AF_INET, group_str, &group_addr);
- if (result <= 0) {
- vty_out(vty, "Bad group address %s: errno=%d: %s%s",
- group_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
+}
- /* Multicast source address */
- source_str = argv[5]->arg;
- result = inet_pton(AF_INET, source_str, &source_addr);
- if (result <= 0) {
- vty_out(vty, "Bad source address %s: errno=%d: %s%s",
- source_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+static void
+ip_msdp_show_peers_detail(struct vty *vty, const char *peer, u_char uj)
+{
+ struct listnode *mpnode;
+ struct pim_msdp_peer *mp;
+ char peer_str[INET_ADDRSTRLEN];
+ char local_str[INET_ADDRSTRLEN];
+ char state_str[PIM_MSDP_STATE_STRLEN];
+ char timebuf[PIM_MSDP_UPTIME_STRLEN];
+ char katimer[PIM_MSDP_TIMER_STRLEN];
+ char crtimer[PIM_MSDP_TIMER_STRLEN];
+ char holdtimer[PIM_MSDP_TIMER_STRLEN];
+ int64_t now;
+ json_object *json = NULL;
+ json_object *json_row = NULL;
+
+ if (uj) {
+ json = json_object_new_object();
}
- /*
- Tweak IP header
- */
- ip_hdr = (struct ip *) buf;
- ip_hdr->ip_p = PIM_IP_PROTO_PIM;
- ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
- ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
- ip_hdr->ip_src = neigh_src_addr;
- ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+ for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+ pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
+ if (strcmp(peer, "detail") &&
+ strcmp(peer, peer_str))
+ continue;
- /*
- Build PIM message
- */
- pim_msg = buf + ip_hlen;
-
- /* skip room for pim header */
- pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN;
-
- remain = buf_pastend - pim_msg_curr;
- pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
- remain,
- neigh_dst_addr);
- if (!pim_msg_curr) {
- vty_out(vty, "Failure encoding destination address %s: space left=%d%s",
- neigh_dst_str, remain, VTY_NEWLINE);
- return CMD_WARNING;
+ if (mp->state == PIM_MSDP_ESTABLISHED) {
+ now = pim_time_monotonic_sec();
+ pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime);
+ } else {
+ strcpy(timebuf, "-");
+ }
+ pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
+ pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
+ pim_time_timer_to_hhmmss(katimer, sizeof(katimer), mp->ka_timer);
+ pim_time_timer_to_hhmmss(crtimer, sizeof(crtimer), mp->cr_timer);
+ pim_time_timer_to_hhmmss(holdtimer, sizeof(holdtimer), mp->hold_timer);
+
+ if (uj) {
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "peer", peer_str);
+ json_object_string_add(json_row, "local", local_str);
+ json_object_string_add(json_row, "meshGroupName", mp->mesh_group_name);
+ json_object_string_add(json_row, "state", state_str);
+ json_object_string_add(json_row, "upTime", timebuf);
+ json_object_string_add(json_row, "keepAliveTimer", katimer);
+ json_object_string_add(json_row, "connRetryTimer", crtimer);
+ json_object_string_add(json_row, "holdTimer", holdtimer);
+ json_object_string_add(json_row, "lastReset", mp->last_reset);
+ json_object_int_add(json_row, "connAttempts", mp->conn_attempts);
+ json_object_int_add(json_row, "establishedChanges", mp->est_flaps);
+ json_object_int_add(json_row, "saCount", mp->sa_cnt);
+ json_object_int_add(json_row, "kaSent", mp->ka_tx_cnt);
+ json_object_int_add(json_row, "kaRcvd", mp->ka_rx_cnt);
+ json_object_int_add(json_row, "saSent", mp->sa_tx_cnt);
+ json_object_int_add(json_row, "saRcvd", mp->sa_rx_cnt);
+ json_object_object_add(json, peer_str, json_row);
+ } else {
+ vty_out(vty, "Peer : %s%s", peer_str, VTY_NEWLINE);
+ vty_out(vty, " Local : %s%s", local_str, VTY_NEWLINE);
+ vty_out(vty, " Mesh Group : %s%s", mp->mesh_group_name, VTY_NEWLINE);
+ vty_out(vty, " State : %s%s", state_str, VTY_NEWLINE);
+ vty_out(vty, " Uptime : %s%s", timebuf, VTY_NEWLINE);
+
+ vty_out(vty, " Keepalive Timer : %s%s", katimer, VTY_NEWLINE);
+ vty_out(vty, " Conn Retry Timer : %s%s", crtimer, VTY_NEWLINE);
+ vty_out(vty, " Hold Timer : %s%s", holdtimer, VTY_NEWLINE);
+ vty_out(vty, " Last Reset : %s%s", mp->last_reset, VTY_NEWLINE);
+ vty_out(vty, " Conn Attempts : %d%s", mp->conn_attempts, VTY_NEWLINE);
+ vty_out(vty, " Established Changes : %d%s", mp->est_flaps, VTY_NEWLINE);
+ vty_out(vty, " SA Count : %d%s", mp->sa_cnt, VTY_NEWLINE);
+ vty_out(vty, " Statistics :%s", VTY_NEWLINE);
+ vty_out(vty, " Sent Rcvd%s", VTY_NEWLINE);
+ vty_out(vty, " Keepalives : %10d %10d%s",
+ mp->ka_tx_cnt, mp->ka_rx_cnt, VTY_NEWLINE);
+ vty_out(vty, " SAs : %10d %10d%s",
+ mp->sa_tx_cnt, mp->sa_rx_cnt, VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
}
- remain = buf_pastend - pim_msg_curr;
- if (remain < 4) {
- vty_out(vty, "Group will not fit: space left=%d%s",
- remain, VTY_NEWLINE);
- return CMD_WARNING;
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
+}
- *pim_msg_curr = 0; /* reserved */
- ++pim_msg_curr;
- *pim_msg_curr = 1; /* number of groups */
- ++pim_msg_curr;
- *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime);
- ++pim_msg_curr;
- ++pim_msg_curr;
-
- remain = buf_pastend - pim_msg_curr;
- pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
- remain,
- group_addr);
- if (!pim_msg_curr) {
- vty_out(vty, "Failure encoding group address %s: space left=%d%s",
- group_str, remain, VTY_NEWLINE);
- return CMD_WARNING;
+DEFUN (show_ip_msdp_peer_detail,
+ show_ip_msdp_peer_detail_cmd,
+ "show ip msdp peer [detail|A.B.C.D] [json]",
+ SHOW_STR
+ IP_STR
+ MSDP_STR
+ "MSDP peer information\n"
+ "Detailed output\n"
+ "peer ip address\n"
+ "JavaScript Object Notation\n")
+{
+ u_char uj = use_json(argc, argv);
+ if (argv[4]->arg)
+ ip_msdp_show_peers_detail(vty, argv[4]->arg, uj);
+ else
+ ip_msdp_show_peers(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
+static void
+ip_msdp_show_sa(struct vty *vty, u_char uj)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char rp_str[INET_ADDRSTRLEN];
+ char timebuf[PIM_MSDP_UPTIME_STRLEN];
+ char spt_str[8];
+ char local_str[8];
+ int64_t now;
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ if (uj) {
+ json = json_object_new_object();
+ } else {
+ vty_out(vty, "Source Group RP Local SPT Uptime%s", VTY_NEWLINE);
}
- remain = buf_pastend - pim_msg_curr;
- if (remain < 4) {
- vty_out(vty, "Sources will not fit: space left=%d%s",
- remain, VTY_NEWLINE);
- return CMD_WARNING;
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ now = pim_time_monotonic_sec();
+ pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime);
+ pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+ if (sa->flags & PIM_MSDP_SAF_PEER) {
+ pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
+ if (sa->up) {
+ strcpy(spt_str, "yes");
+ } else {
+ strcpy(spt_str, "no");
+ }
+ } else {
+ strcpy(rp_str, "-");
+ strcpy(spt_str, "-");
+ }
+ if (sa->flags & PIM_MSDP_SAF_LOCAL) {
+ strcpy(local_str, "yes");
+ } else {
+ strcpy(local_str, "no");
+ }
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "rp", rp_str);
+ json_object_string_add(json_row, "local", local_str);
+ json_object_string_add(json_row, "sptSetup", spt_str);
+ json_object_string_add(json_row, "upTime", timebuf);
+ json_object_object_add(json_group, src_str, json_row);
+ } else {
+ vty_out(vty, "%-15s %15s %15s %5c %3c %8s%s",
+ src_str, grp_str, rp_str, local_str[0], spt_str[0], timebuf, VTY_NEWLINE);
+ }
}
- if (src_is_join) {
- num_joined = 1;
- num_pruned = 0;
+
+ 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 {
- 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;
+}
+
+static void
+ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa, const char *src_str,
+ const char *grp_str, struct vty *vty,
+ u_char uj, json_object *json)
+{
+ char rp_str[INET_ADDRSTRLEN];
+ char peer_str[INET_ADDRSTRLEN];
+ char timebuf[PIM_MSDP_UPTIME_STRLEN];
+ char spt_str[8];
+ char local_str[8];
+ char statetimer[PIM_MSDP_TIMER_STRLEN];
+ int64_t now;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ now = pim_time_monotonic_sec();
+ pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime);
+ if (sa->flags & PIM_MSDP_SAF_PEER) {
+ pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
+ pim_inet4_dump("<peer?>", sa->peer, peer_str, sizeof(peer_str));
+ if (sa->up) {
+ strcpy(spt_str, "yes");
+ } else {
+ strcpy(spt_str, "no");
+ }
+ } else {
+ strcpy(rp_str, "-");
+ strcpy(peer_str, "-");
+ strcpy(spt_str, "-");
}
+ if (sa->flags & PIM_MSDP_SAF_LOCAL) {
+ strcpy(local_str, "yes");
+ } else {
+ strcpy(local_str, "no");
+ }
+ pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer), sa->sa_state_timer);
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
- /* Add PIM header */
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
- pim_msg_size = pim_msg_curr - pim_msg;
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "rp", rp_str);
+ json_object_string_add(json_row, "local", local_str);
+ json_object_string_add(json_row, "sptSetup", spt_str);
+ json_object_string_add(json_row, "upTime", timebuf);
+ json_object_string_add(json_row, "stateTimer", statetimer);
+ json_object_object_add(json_group, src_str, json_row);
+ } else {
+ vty_out(vty, "SA : %s%s", sa->sg_str, VTY_NEWLINE);
+ vty_out(vty, " RP : %s%s", rp_str, VTY_NEWLINE);
+ vty_out(vty, " Peer : %s%s", peer_str, VTY_NEWLINE);
+ vty_out(vty, " Local : %s%s", local_str, VTY_NEWLINE);
+ vty_out(vty, " SPT Setup : %s%s", spt_str, VTY_NEWLINE);
+ vty_out(vty, " Uptime : %s%s", timebuf, VTY_NEWLINE);
+ vty_out(vty, " State Timer : %s%s", statetimer, VTY_NEWLINE);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+}
- pim_msg_build_header(pim_msg, pim_msg_size,
- PIM_MSG_TYPE_JOIN_PRUNE);
+static void
+ip_msdp_show_sa_detail(struct vty *vty, u_char uj)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ json_object *json = NULL;
- /*
- "Receive" message
- */
+ if (uj) {
+ json = json_object_new_object();
+ }
- ip_msg_len = ip_hlen + pim_msg_size;
- result = pim_pim_packet(ifp, buf, ip_msg_len);
- if (result) {
- vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
- ip_msg_len, result, VTY_NEWLINE);
- return CMD_WARNING;
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+ ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json);
}
- return CMD_SUCCESS;
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
+ }
}
-DEFUN (test_pim_receive_join,
- test_pim_receive_join_cmd,
- "test pim receive join INTERFACE (0-65535) A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
- "Test\n"
- "Test PIM protocol\n"
- "Test PIM message reception\n"
- "Test PIM join reception from neighbor\n"
- "Interface\n"
- "Neighbor holdtime\n"
- "Upstream neighbor unicast destination address\n"
- "Downstream neighbor unicast source address\n"
- "Multicast group address\n"
- "Unicast source address\n")
-{
- return recv_joinprune(vty, argv, 1 /* src_is_join=true */);
-}
-
-DEFUN (test_pim_receive_prune,
- test_pim_receive_prune_cmd,
- "test pim receive prune INTERFACE (0-65535) A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
- "Test\n"
- "Test PIM protocol\n"
- "Test PIM message reception\n"
- "Test PIM prune reception from neighbor\n"
- "Interface\n"
- "Neighbor holdtime\n"
- "Upstream neighbor unicast destination address\n"
- "Downstream neighbor unicast source address\n"
- "Multicast group address\n"
- "Unicast source address\n")
-{
- return recv_joinprune(vty, argv, 0 /* src_is_join=false */);
-}
-
-DEFUN (test_pim_receive_upcall,
- test_pim_receive_upcall_cmd,
- "test pim receive upcall <nocache|wrongvif|wholepkt> (0-65535) A.B.C.D A.B.C.D",
- "Test\n"
- "Test PIM protocol\n"
- "Test PIM message reception\n"
- "Test reception of kernel upcall\n"
- "NOCACHE kernel upcall\n"
- "WRONGVIF kernel upcall\n"
- "WHOLEPKT kernel upcall\n"
- "Input interface vif index\n"
- "Multicast group address\n"
- "Multicast source address\n")
-{
- int idx_type = 4;
- int idx_number = 5;
- int idx_ipv4 = 6;
- int idx_ipv4_2 = 7;
- struct igmpmsg msg;
- const char *upcall_type;
- const char *group_str;
- const char *source_str;
- int result;
+DEFUN (show_ip_msdp_sa_detail,
+ show_ip_msdp_sa_detail_cmd,
+ "show ip msdp sa detail [json]",
+ SHOW_STR
+ IP_STR
+ MSDP_STR
+ "MSDP active-source information\n"
+ "Detailed output\n"
+ "JavaScript Object Notation\n")
+{
+ u_char uj = use_json(argc, argv);
+ ip_msdp_show_sa_detail(vty, uj);
- upcall_type = argv[idx_type]->arg;
+ return CMD_SUCCESS;
+}
- if (upcall_type[0] == 'n')
- msg.im_msgtype = IGMPMSG_NOCACHE;
- else if (upcall_type[1] == 'r')
- msg.im_msgtype = IGMPMSG_WRONGVIF;
- else if (upcall_type[1] == 'h')
- msg.im_msgtype = IGMPMSG_WHOLEPKT;
- else {
- vty_out(vty, "Unknown kernel upcall type: %s%s",
- upcall_type, VTY_NEWLINE);
- return CMD_WARNING;
+static void
+ip_msdp_show_sa_addr(struct vty *vty, const char *addr, u_char uj)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ json_object *json = NULL;
+
+ if (uj) {
+ json = json_object_new_object();
}
- msg.im_vif = atoi(argv[idx_number]->arg);
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+ if (!strcmp(addr, src_str) || !strcmp(addr, grp_str)) {
+ ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json);
+ }
+ }
- /* Group address */
- group_str = argv[idx_ipv4]->arg;
- result = inet_pton(AF_INET, group_str, &msg.im_dst);
- if (result <= 0) {
- vty_out(vty, "Bad group address %s: errno=%d: %s%s",
- group_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
+}
- /* Source address */
- source_str = argv[idx_ipv4_2]->arg;
- result = inet_pton(AF_INET, source_str, &msg.im_src);
- if (result <= 0) {
- vty_out(vty, "Bad source address %s: errno=%d: %s%s",
- source_str, errno, safe_strerror(errno), VTY_NEWLINE);
- return CMD_WARNING;
+static void
+ip_msdp_show_sa_sg(struct vty *vty, const char *src, const char *grp, u_char uj)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ json_object *json = NULL;
+
+ if (uj) {
+ json = json_object_new_object();
}
- msg.im_mbz = 0; /* Must be zero */
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ pim_inet4_dump("<src?>", sa->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", sa->sg.grp, grp_str, sizeof(grp_str));
+ if (!strcmp(src, src_str) && !strcmp(grp, grp_str)) {
+ ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json);
+ }
+ }
- result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg));
- if (result) {
- vty_out(vty, "pim_mroute_msg(len=%zu) returned failure: %d%s",
- sizeof(msg), result, VTY_NEWLINE);
- return CMD_WARNING;
+ if (uj) {
+ vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE);
+ json_object_free(json);
}
+}
+
+DEFUN (show_ip_msdp_sa_sg,
+ show_ip_msdp_sa_sg_cmd,
+ "show ip msdp sa [A.B.C.D] [A.B.C.D] [json]",
+ SHOW_STR
+ IP_STR
+ MSDP_STR
+ "MSDP active-source information\n"
+ "source or group ip\n"
+ "JavaScript Object Notation\n")
+{
+ u_char uj = use_json(argc, argv);
+ if (argv[5]->arg)
+ ip_msdp_show_sa_sg(vty, argv[4]->arg, argv[5]->arg, uj);
+ else if (argv[4]->arg)
+ ip_msdp_show_sa_addr(vty, argv[4]->arg, uj);
+ else
+ ip_msdp_show_sa(vty, uj);
return CMD_SUCCESS;
}
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);
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);
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);
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);
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);
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);
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);
}
#define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n"
#define DEBUG_PIM_HELLO_PACKETS_STR "PIM Hello protocol packets\n"
#define DEBUG_PIM_J_P_PACKETS_STR "PIM Join/Prune protocol packets\n"
+#define DEBUG_PIM_PIM_REG_PACKETS_STR "PIM Register/Reg-Stop protocol packets\n"
#define DEBUG_PIM_PACKETDUMP_STR "PIM packet dump\n"
#define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n"
#define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n"
#define CLEAR_IP_PIM_STR "PIM clear commands\n"
#define MROUTE_STR "IP multicast routing table\n"
#define RIB_STR "IP unicast routing table\n"
+#define CFG_MSDP_STR "Configure multicast source discovery protocol\n"
+#define MSDP_STR "MSDP information\n"
+#define DEBUG_MSDP_STR "MSDP protocol activity\n"
+#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
+#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
+#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
void pim_cmd_init(void);
struct interface *ifp, struct in_addr src)
{
if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
zlog_debug("%s: from %s on %s",
label, src_str, ifp->name);
int isset, int value)
{
if (isset) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
label,
int isset, uint16_t value)
{
if (isset) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
label,
int isset, uint32_t value)
{
if (isset) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
label,
int isset, uint32_t value)
{
if (isset) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
label,
int isset)
{
if (isset) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s",
label,
int isset, struct list *addr_list)
{
if (isset) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
label,
if (remain < PIM_TLV_MIN_SIZE) {
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
__PRETTY_FUNCTION__,
if ((tlv_curr + option_len) > tlv_pastend) {
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
__PRETTY_FUNCTION__,
}
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
__PRETTY_FUNCTION__,
break;
case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
__PRETTY_FUNCTION__,
break;
default:
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
__PRETTY_FUNCTION__,
if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
__PRETTY_FUNCTION__,
hello_option_override_interval,
hello_option_dr_priority,
hello_option_generation_id,
- hello_option_addr_list);
+ hello_option_addr_list,
+ PIM_NEIGHBOR_SEND_DELAY);
if (!neigh) {
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
__PRETTY_FUNCTION__,
/* GenID mismatch ? */
if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
(hello_option_generation_id != neigh->generation_id)) {
-
- /* GenID changed */
-
- pim_upstream_rpf_genid_changed(neigh->source_addr);
-
/* GenID mismatch, then replace neighbor */
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
__PRETTY_FUNCTION__,
hello_option_override_interval,
hello_option_dr_priority,
hello_option_generation_id,
- hello_option_addr_list);
+ hello_option_addr_list,
+ PIM_NEIGHBOR_SEND_NOW);
if (!neigh) {
if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
__PRETTY_FUNCTION__,
#include "memory.h"
#include "prefix.h"
#include "vrf.h"
+#include "linklist.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_time.h"
#include "pim_ssmpingd.h"
+#include "pim_rp.h"
struct interface *pim_regiface = NULL;
+struct list *pim_ifchannel_list = NULL;
static void pim_if_igmp_join_del_all(struct interface *ifp);
-void pim_if_init()
+void
+pim_if_init (void)
{
vrf_iflist_create(VRF_DEFAULT);
+ pim_ifchannel_list = list_new();
+ pim_ifchannel_list->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
+}
+
+void
+pim_if_terminate (void)
+{
+ if (pim_ifchannel_list)
+ list_free (pim_ifchannel_list);
}
static void *if_list_clean(struct pim_interface *pim_ifp)
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;
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 */
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;
if (pim_ifp->igmp_join_list) {
pim_if_igmp_join_del_all(ifp);
}
- zassert(!pim_ifp->igmp_join_list);
-
- zassert(pim_ifp->igmp_socket_list);
- zassert(!listcount(pim_ifp->igmp_socket_list));
- zassert(pim_ifp->pim_neighbor_list);
- zassert(!listcount(pim_ifp->pim_neighbor_list));
+ pim_ifchannel_delete_all (ifp);
+ igmp_sock_delete_all (ifp);
- zassert(pim_ifp->pim_ifchannel_list);
- zassert(!listcount(pim_ifp->pim_ifchannel_list));
+ pim_neighbor_delete_all (ifp, "Interface removed from configuration");
if (PIM_MROUTE_IS_ENABLED) {
pim_if_del_vif(ifp);
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
- ifp->info = 0;
+ ifp->info = NULL;
}
void pim_if_update_could_assert(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
changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr;
if (PIM_DEBUG_ZEBRA) {
- char new_prim_str[100];
- char old_prim_str[100];
+ char new_prim_str[INET_ADDRSTRLEN];
+ char old_prim_str[INET_ADDRSTRLEN];
pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str));
pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str));
zlog_debug("%s: old=%s new=%s on interface %s: %s",
if (changed) {
pim_ifp->primary_address = new_prim_addr;
+ }
- if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
- return changed;
+ return changed;
+}
+
+static int pim_sec_addr_comp(const void *p1, const void *p2)
+{
+ const struct pim_secondary_addr *sec1 = p1;
+ const struct pim_secondary_addr *sec2 = p2;
+
+ if (ntohl(sec1->addr.s_addr) < ntohl(sec2->addr.s_addr))
+ return -1;
+
+ if (ntohl(sec1->addr.s_addr) > ntohl(sec2->addr.s_addr))
+ return 1;
+
+ return 0;
+}
+
+static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr)
+{
+ XFREE(MTYPE_PIM_SEC_ADDR, sec_addr);
+}
+
+static struct pim_secondary_addr *
+pim_sec_addr_find(struct pim_interface *pim_ifp, struct in_addr addr)
+{
+ struct pim_secondary_addr *sec_addr;
+ struct listnode *node;
+
+ if (!pim_ifp->sec_addr_list) {
+ return NULL;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+ if (sec_addr->addr.s_addr == addr.s_addr) {
+ return sec_addr;
}
+ }
- pim_addr_change(ifp);
+ return NULL;
+}
+
+static void pim_sec_addr_del(struct pim_interface *pim_ifp,
+ struct pim_secondary_addr *sec_addr)
+{
+ listnode_delete(pim_ifp->sec_addr_list, sec_addr);
+ pim_sec_addr_free(sec_addr);
+}
+
+static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct in_addr addr)
+{
+ int changed = 0;
+ struct pim_secondary_addr *sec_addr;
+
+ sec_addr = pim_sec_addr_find(pim_ifp, addr);
+ if (sec_addr) {
+ sec_addr->flags &= ~PIM_SEC_ADDRF_STALE;
+ return changed;
+ }
+
+ if (!pim_ifp->sec_addr_list) {
+ pim_ifp->sec_addr_list = list_new();
+ pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free;
+ pim_ifp->sec_addr_list->cmp = (int (*)(void *, void *))pim_sec_addr_comp;
+ }
+
+ sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr));
+ if (!sec_addr) {
+ if (list_isempty(pim_ifp->sec_addr_list)) {
+ list_free(pim_ifp->sec_addr_list);
+ pim_ifp->sec_addr_list = NULL;
+ }
+ return changed;
+ }
+
+ changed = 1;
+ sec_addr->addr = addr;
+ listnode_add_sort(pim_ifp->sec_addr_list, sec_addr);
+
+ return changed;
+}
+
+static int pim_sec_addr_del_all(struct pim_interface *pim_ifp)
+{
+ int changed = 0;
+
+ if (!pim_ifp->sec_addr_list) {
+ return changed;
+ }
+ if (!list_isempty(pim_ifp->sec_addr_list)) {
+ changed = 1;
+ /* remove all nodes and free up the list itself */
+ list_delete_all_node(pim_ifp->sec_addr_list);
+ list_free(pim_ifp->sec_addr_list);
+ pim_ifp->sec_addr_list = NULL;
+ }
+
+ return changed;
+}
+
+static int pim_sec_addr_update(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct connected *ifc;
+ struct listnode *node;
+ struct listnode *nextnode;
+ struct pim_secondary_addr *sec_addr;
+ int changed = 0;
+
+ if (pim_ifp->sec_addr_list) {
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+ sec_addr->flags |= PIM_SEC_ADDRF_STALE;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET) {
+ continue;
+ }
+
+ if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
+ continue;
+ }
+
+ if (pim_ifp->primary_address.s_addr == p->u.prefix4.s_addr) {
+ /* don't add the primary address into the secondary address list */
+ continue;
+ }
+
+ if (pim_sec_addr_add(pim_ifp, p->u.prefix4)) {
+ changed = 1;
+ }
+ }
+
+ if (pim_ifp->sec_addr_list) {
+ /* Drop stale entries */
+ for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, sec_addr)) {
+ if (sec_addr->flags & PIM_SEC_ADDRF_STALE) {
+ pim_sec_addr_del(pim_ifp, sec_addr);
+ changed = 1;
+ }
+ }
+
+ /* If the list went empty free it up */
+ if (list_isempty(pim_ifp->sec_addr_list)) {
+ list_free(pim_ifp->sec_addr_list);
+ pim_ifp->sec_addr_list = NULL;
+ }
}
return changed;
}
-static void detect_secondary_address_change(struct interface *ifp,
+static int detect_secondary_address_change(struct interface *ifp,
+ int force_prim_as_any,
const char *caller)
{
+ struct pim_interface *pim_ifp = ifp->info;
+ int changed = 0;
+
+ if (force_prim_as_any) {
+ /* if primary address is being forced to zero just flush the
+ * secondary address list */
+ changed = pim_sec_addr_del_all(pim_ifp);
+ } else {
+ /* re-evaluate the secondary address list */
+ changed = pim_sec_addr_update(ifp);
+ }
+
+ return changed;
+}
+
+static void detect_address_change(struct interface *ifp,
+ int force_prim_as_any,
+ const char *caller)
+{
+ int changed = 0;
struct pim_interface *pim_ifp;
- int changed;
pim_ifp = ifp->info;
if (!pim_ifp)
return;
- changed = 1; /* true */
- if (PIM_DEBUG_ZEBRA)
- zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change",
- __PRETTY_FUNCTION__, ifp->name);
+ if (detect_primary_address_change(ifp, force_prim_as_any, caller)) {
+ changed = 1;
+ }
- if (!changed) {
- return;
+ if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) {
+ changed = 1;
}
- if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
- return;
+
+ if (changed) {
+ if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
+ return;
+ }
+
+ pim_addr_change(ifp);
}
- pim_addr_change(ifp);
+ /* XXX: if we have unnumbered interfaces we need to run detect address
+ * address change on all of them when the lo address changes */
}
-static void detect_address_change(struct interface *ifp,
- int force_prim_as_any,
- const char *caller)
+int pim_update_source_set(struct interface *ifp, struct in_addr source)
{
- int prim_changed;
+ struct pim_interface *pim_ifp = ifp->info;
- prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller);
- if (prim_changed) {
- /* no need to detect secondary change because
- the reaction would be the same */
- return;
+ if (!pim_ifp) {
+ return PIM_IFACE_NOT_FOUND;
}
- detect_secondary_address_change(ifp, caller);
+ if (pim_ifp->update_source.s_addr == source.s_addr) {
+ return PIM_UPDATE_SOURCE_DUP;
+ }
+
+ pim_ifp->update_source = source;
+ detect_address_change(ifp, 0 /* force_prim_as_any */,
+ __PRETTY_FUNCTION__);
+
+ return PIM_SUCCESS;
}
void pim_if_addr_add(struct connected *ifc)
if (pim_ifp->mroute_vif_index < 0) {
pim_if_add_vif(ifp);
}
+ pim_ifchannel_scan_forward_start (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)
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)
}
}
-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",
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
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;
}
}
if (PIM_DEBUG_PIM_TRACE) {
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s: neighbor not found for address %s on interface %s",
__PRETTY_FUNCTION__,
addr_str, ifp->name);
}
- return 0;
+ return NULL;
}
long pim_if_t_suppressed_msec(struct interface *ifp)
join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr);
if (join_fd < 0) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
return 0;
}
- ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
+ ij = XCALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
if (!ij) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s",
+ zlog_err("%s: XCALLOC(%zu) failure for IGMP group %s source %s on interface %s",
__PRETTY_FUNCTION__,
sizeof(*ij), group_str, source_str, ifp->name);
close(join_fd);
ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
if (ij) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s",
ij = igmp_join_new(ifp, group_addr, source_addr);
if (!ij) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s",
}
if (PIM_DEBUG_IGMP_EVENTS) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s",
ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
if (!ij) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: could not find IGMP group %s source %s on interface %s",
}
if (close(ij->sock_fd)) {
- int e = errno;
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s",
__PRETTY_FUNCTION__,
- ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e));
+ ij->sock_fd, group_str, source_str, ifp->name, errno, safe_strerror(errno));
/* warning only */
}
listnode_delete(pim_ifp->igmp_join_list, ij);
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;
+}
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
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 */
};
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
((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);
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);
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 */
#include "thread.h"
#include "memory.h"
#include "if.h"
+#include "vrf.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_join.h"
#include "pim_rpf.h"
#include "pim_macro.h"
+#include "pim_oil.h"
+#include "pim_upstream.h"
-void pim_ifchannel_free(struct pim_ifchannel *ch)
+int
+pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2)
+{
+ struct pim_interface *pim_ifp1;
+ struct pim_interface *pim_ifp2;
+
+ if (ntohl(ch1->sg.grp.s_addr) < ntohl(ch2->sg.grp.s_addr))
+ return -1;
+
+ if (ntohl(ch1->sg.grp.s_addr) > ntohl(ch2->sg.grp.s_addr))
+ return 1;
+
+ if (ntohl(ch1->sg.src.s_addr) < ntohl(ch2->sg.src.s_addr))
+ return -1;
+
+ if (ntohl(ch1->sg.src.s_addr) > ntohl(ch2->sg.src.s_addr))
+ return 1;
+
+ pim_ifp1 = ch1->interface->info;
+ pim_ifp2 = ch2->interface->info;
+ if (ntohl(pim_ifp1->primary_address.s_addr) < ntohl(pim_ifp2->primary_address.s_addr))
+ return -1;
+
+ if (ntohl(pim_ifp1->primary_address.s_addr) > ntohl(pim_ifp2->primary_address.s_addr))
+ return 1;
+
+ if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
+ return -1;
+
+ if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * A (*,G) or a (*,*) is going away
+ * remove the parent pointer from
+ * those pointing at us
+ */
+static void
+pim_ifchannel_remove_children (struct pim_ifchannel *ch)
{
- zassert(!ch->t_ifjoin_expiry_timer);
- zassert(!ch->t_ifjoin_prune_pending_timer);
- zassert(!ch->t_ifassert_timer);
+ struct pim_ifchannel *child;
+
+ if (!ch->sources)
+ return;
+
+ while (!list_isempty (ch->sources))
+ {
+ child = listnode_head (ch->sources);
+ child->parent = NULL;
+ listnode_delete (ch->sources, child);
+ }
+}
+
+/*
+ * A (*,G) or a (*,*) is being created
+ * find all the children that would point
+ * at us.
+ */
+static void
+pim_ifchannel_find_new_children (struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp = ch->interface->info;
+ struct pim_ifchannel *child;
+ struct listnode *ch_node;
+
+ // Basic Sanity that we are not being silly
+ if ((ch->sg.src.s_addr != INADDR_ANY) &&
+ (ch->sg.grp.s_addr != INADDR_ANY))
+ return;
+
+ if ((ch->sg.src.s_addr == INADDR_ANY) &&
+ (ch->sg.grp.s_addr == INADDR_ANY))
+ return;
+ for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child))
+ {
+ if ((ch->sg.grp.s_addr != INADDR_ANY) &&
+ (child->sg.grp.s_addr == ch->sg.grp.s_addr) &&
+ (child != ch))
+ {
+ child->parent = ch;
+ listnode_add_sort (ch->sources, child);
+ }
+ }
+}
+
+void pim_ifchannel_free(struct pim_ifchannel *ch)
+{
XFREE(MTYPE_PIM_IFCHANNEL, ch);
}
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,
{
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)",
return;
}
- zassert(old_state != new_state);
-
ch->ifjoin_state = new_state;
+ if (ch->sg.src.s_addr == INADDR_ANY)
+ {
+ struct pim_upstream *up = ch->upstream;
+ struct pim_upstream *child;
+ struct listnode *up_node;
+
+ if (up)
+ {
+ if (ch->ifjoin_state == PIM_IFJOIN_NOINFO)
+ {
+ for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+ {
+ struct channel_oil *c_oil = child->channel_oil;
+ struct pim_interface *pim_ifp = ch->interface->info;
+
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("%s %s: Prune(S,G)=%s from %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ child->sg_str, up->sg_str);
+ if (!c_oil)
+ continue;
+
+ if (!pim_upstream_evaluate_join_desired (child))
+ pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+
+ /*
+ * If the S,G has no if channel and the c_oil still
+ * has output here then the *,G was supplying the implied
+ * if channel. So remove it.
+ */
+ if (!ch && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
+ pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+ }
+ }
+ if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
+ {
+ for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+ {
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("%s %s: Join(S,G)=%s from %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ child->sg_str, up->sg_str);
+
+ if (pim_upstream_evaluate_join_desired (child))
+ {
+ pim_channel_add_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
+ pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
+ }
+ }
+ }
+ }
+ }
/* Transition to/from NOINFO ? */
- if (
- (old_state == PIM_IFJOIN_NOINFO)
- ||
- (new_state == PIM_IFJOIN_NOINFO)
- ) {
+ if ((old_state == PIM_IFJOIN_NOINFO) ||
+ (new_state == PIM_IFJOIN_NOINFO)) {
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
+ zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
- src_str, grp_str, ch->interface->name);
+ ch->sg_str, ch->interface->name);
}
/*
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";
qpim_infinite_assert_metric);
}
-static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
- struct in_addr source_addr,
- struct in_addr group_addr)
-{
- struct pim_ifchannel *ch;
- struct pim_interface *pim_ifp;
- struct pim_upstream *up;
-
- pim_ifp = ifp->info;
- zassert(pim_ifp);
-
- up = pim_upstream_add(source_addr, group_addr, NULL);
- if (!up) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
- zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
- return 0;
- }
-
- ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
- if (!ch) {
- zlog_err("%s: PIM XMALLOC(%zu) failure",
- __PRETTY_FUNCTION__, sizeof(*ch));
- return 0;
- }
-
- ch->flags = 0;
- ch->upstream = up;
- ch->interface = ifp;
- ch->source_addr = source_addr;
- ch->group_addr = group_addr;
- ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
-
- ch->ifjoin_state = PIM_IFJOIN_NOINFO;
- ch->t_ifjoin_expiry_timer = 0;
- ch->t_ifjoin_prune_pending_timer = 0;
- ch->ifjoin_creation = 0;
-
- ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
- ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
-
- ch->ifassert_winner.s_addr = 0;
-
- /* Assert state */
- ch->t_ifassert_timer = 0;
- reset_ifassert_state(ch);
- if (pim_macro_ch_could_assert_eval(ch))
- PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
- else
- PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
-
- if (pim_macro_assert_tracking_desired_eval(ch))
- PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
- else
- PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
-
- /* Attach to list */
- listnode_add(pim_ifp->pim_ifchannel_list, ch);
-
- zassert(IFCHANNEL_NOINFO(ch));
-
- return ch;
-}
-
struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
- struct in_addr source_addr,
- struct in_addr group_addr)
+ struct prefix_sg *sg)
{
struct pim_interface *pim_ifp;
struct listnode *ch_node;
pim_ifp = ifp->info;
if (!pim_ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str,
+ pim_str_sg_dump (sg),
ifp->name);
return 0;
}
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
if (
- (source_addr.s_addr == ch->source_addr.s_addr) &&
- (group_addr.s_addr == ch->group_addr.s_addr)
+ (sg->src.s_addr == ch->sg.src.s_addr) &&
+ (sg->grp.s_addr == ch->sg.grp.s_addr)
) {
return ch;
}
return;
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
+ zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str,
+ ch->sg_str,
membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
ch->interface->name);
}
}
}
-struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
- struct in_addr source_addr,
- struct in_addr group_addr)
+/*
+ * For a given Interface, if we are given a S,G
+ * Find the *,G (If we have it).
+ * If we are passed a *,G, find the *,* ifchannel
+ * if we have it.
+ */
+static struct pim_ifchannel *
+pim_ifchannel_find_parent (struct pim_ifchannel *ch)
+{
+ struct prefix_sg parent_sg = ch->sg;
+ struct pim_ifchannel *parent = NULL;
+
+ // (S,G)
+ if ((parent_sg.src.s_addr != INADDR_ANY) &&
+ (parent_sg.grp.s_addr != INADDR_ANY))
+ {
+ parent_sg.src.s_addr = INADDR_ANY;
+ parent = pim_ifchannel_find (ch->interface, &parent_sg);
+
+ if (parent)
+ listnode_add (parent->sources, ch);
+ return parent;
+ }
+
+ return NULL;
+}
+
+struct pim_ifchannel *
+pim_ifchannel_add(struct interface *ifp,
+ struct prefix_sg *sg, int flags)
{
+ struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- char src_str[100];
- char grp_str[100];
+ struct pim_upstream *up;
- ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+ ch = pim_ifchannel_find(ifp, sg);
if (ch)
return ch;
- ch = pim_ifchannel_new(ifp, source_addr, group_addr);
- if (ch)
- return ch;
-
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
+ pim_ifp = ifp->info;
- return 0;
+ up = pim_upstream_add(sg, NULL, flags, __PRETTY_FUNCTION__);
+ if (!up) {
+ zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
+ __PRETTY_FUNCTION__,
+ pim_str_sg_dump (sg), ifp->name);
+ return NULL;
+ }
+
+ ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
+ if (!ch) {
+ zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
+ __PRETTY_FUNCTION__,
+ up->sg_str, ifp->name);
+
+ pim_upstream_del (up, __PRETTY_FUNCTION__);
+ return NULL;
+ }
+
+ ch->flags = 0;
+ ch->upstream = up;
+ ch->interface = ifp;
+ ch->sg = *sg;
+ pim_str_sg_set (sg, ch->sg_str);
+ ch->parent = pim_ifchannel_find_parent (ch);
+ if (ch->sg.src.s_addr == INADDR_ANY)
+ {
+ ch->sources = list_new ();
+ ch->sources->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
+ }
+ else
+ ch->sources = NULL;
+
+ pim_ifchannel_find_new_children (ch);
+ ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
+
+ ch->ifjoin_state = PIM_IFJOIN_NOINFO;
+ ch->t_ifjoin_expiry_timer = NULL;
+ ch->t_ifjoin_prune_pending_timer = NULL;
+ ch->ifjoin_creation = 0;
+
+ ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
+ ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
+
+ ch->ifassert_winner.s_addr = 0;
+
+ /* Assert state */
+ ch->t_ifassert_timer = NULL;
+ reset_ifassert_state(ch);
+ if (pim_macro_ch_could_assert_eval(ch))
+ PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
+ else
+ PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
+
+ if (pim_macro_assert_tracking_desired_eval(ch))
+ PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
+ else
+ PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
+
+ /* Attach to list */
+ listnode_add_sort(pim_ifp->pim_ifchannel_list, ch);
+ listnode_add_sort(pim_ifchannel_list, ch);
+
+ return ch;
}
static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
{
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 */
return 0;
}
-static void prune_echo(struct interface *ifp,
- struct in_addr source_addr,
- struct in_addr group_addr)
-{
- struct pim_interface *pim_ifp;
- struct in_addr neigh_dst_addr;
-
- pim_ifp = ifp->info;
- zassert(pim_ifp);
-
- neigh_dst_addr = pim_ifp->primary_address;
-
- if (PIM_DEBUG_PIM_EVENTS) {
- char source_str[100];
- char group_str[100];
- char neigh_dst_str[100];
- pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
- zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
- __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
- }
-
- pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
- 0 /* boolean: send_join=false (prune) */);
-}
-
static int on_ifjoin_prune_pending_timer(struct thread *t)
{
struct pim_ifchannel *ch;
int send_prune_echo; /* boolean */
struct interface *ifp;
struct pim_interface *pim_ifp;
- struct in_addr ch_source;
- struct in_addr ch_group;
- zassert(t);
ch = THREAD_ARG(t);
- zassert(ch);
-
- ch->t_ifjoin_prune_pending_timer = 0;
-
- zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
- /* Send PruneEcho(S,G) ? */
- ifp = ch->interface;
- pim_ifp = ifp->info;
- send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
+ ch->t_ifjoin_prune_pending_timer = NULL;
- /* Save (S,G) */
- ch_source = ch->source_addr;
- ch_group = ch->group_addr;
+ if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING)
+ {
+ /* Send PruneEcho(S,G) ? */
+ ifp = ch->interface;
+ pim_ifp = ifp->info;
+ send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
- ifjoin_to_noinfo(ch);
- /* from here ch may have been deleted */
+ ifjoin_to_noinfo(ch);
+ /* from here ch may have been deleted */
- if (send_prune_echo)
- prune_echo(ifp, ch_source, ch_group);
+ if (send_prune_echo)
+ pim_joinprune_send (ifp, pim_ifp->primary_address,
+ ch->upstream, 0);
+ }
+ else
+ {
+ zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
+ __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg),
+ pim_ifchannel_ifjoin_name (ch->ifjoin_state));
+ }
return 0;
}
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)
/* Upstream (S,G) in Joined state */
- if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
+ if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
/* RPF'(S,G) not found */
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s %s: RPF'(%s,%s) not found",
+ zlog_warn("%s %s: RPF'%s not found",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str);
+ up->sg_str);
return;
}
/* upstream directed to RPF'(S,G) ? */
- if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
- char src_str[100];
- char grp_str[100];
- char up_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
+ char up_str[INET_ADDRSTRLEN];
+ char rpf_str[PREFIX_STRLEN];
pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
- pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
+ pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+ zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str,
+ up->sg_str,
up_str, rpf_str, recv_ifp->name);
return;
}
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;
}
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)
{
is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
- if (PIM_DEBUG_PIM_TRACE) {
- char up_str[100];
- char src_str[100];
- char grp_str[100];
+ if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+ char up_str[INET_ADDRSTRLEN];
pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
+ zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s",
__PRETTY_FUNCTION__,
is_join ? "join" : "prune",
- src_str, grp_str,
+ pim_str_sg_dump (sg),
is_local ? "local" : "non-local",
up_str, recv_ifp->name);
}
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 */
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)
{
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;
address of the join message is our primary address.
*/
if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
- char src_str[100];
- char grp_str[100];
- char neigh_str[100];
- pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ char neigh_str[INET_ADDRSTRLEN];
pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
- zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
+ zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, neigh_str, ifp->name);
+ ch->sg_str, neigh_str, ifp->name);
assert_action_a5(ch);
}
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;
}
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,
void pim_ifchannel_prune(struct interface *ifp,
struct in_addr upstream,
- struct in_addr source_addr,
- struct in_addr group_addr,
+ struct prefix_sg *sg,
uint8_t source_flags,
uint16_t holdtime)
{
struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
int jp_override_interval_msec;
if (nonlocal_upstream(0 /* prune */, ifp, upstream,
- source_addr, group_addr, source_flags, holdtime)) {
+ sg, source_flags, holdtime)) {
return;
}
- ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+ ch = pim_ifchannel_find (ifp, sg);
+ if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT))
+ {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
+ __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags);
+ return;
+ }
+
+ ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
if (!ch)
return;
+ pim_ifp = ifp->info;
+
switch (ch->ifjoin_state) {
case PIM_IFJOIN_NOINFO:
+ if (source_flags & PIM_ENCODE_RPT_BIT)
+ {
+ PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
+ ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
+ if (listcount(pim_ifp->pim_neighbor_list) > 1)
+ jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+ else
+ jp_override_interval_msec = 0; /* schedule to expire immediately */
+ /* If we called ifjoin_prune() directly instead, care should
+ be taken not to use "ch" afterwards since it would be
+ deleted. */
+
+ THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
+ THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
+ on_ifjoin_prune_pending_timer,
+ ch, jp_override_interval_msec);
+ THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+ on_ifjoin_expiry_timer,
+ ch, holdtime);
+ }
+ break;
case PIM_IFJOIN_PRUNE_PENDING:
/* nothing to do */
break;
case PIM_IFJOIN_JOIN:
- {
- struct pim_interface *pim_ifp;
-
- pim_ifp = ifp->info;
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
- zassert(ch->t_ifjoin_expiry_timer);
- zassert(!ch->t_ifjoin_prune_pending_timer);
+ pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
- THREAD_OFF(ch->t_ifjoin_expiry_timer);
-
- pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
-
- if (listcount(pim_ifp->pim_neighbor_list) > 1) {
- jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+ if (listcount(pim_ifp->pim_neighbor_list) > 1)
+ jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+ else
+ jp_override_interval_msec = 0; /* schedule to expire immediately */
+ /* If we called ifjoin_prune() directly instead, care should
+ be taken not to use "ch" afterwards since it would be
+ deleted. */
+ THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+ THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
+ on_ifjoin_prune_pending_timer,
+ ch, jp_override_interval_msec);
+ break;
+ case PIM_IFJOIN_PRUNE:
+ if (source_flags & PIM_ENCODE_RPT_BIT)
+ {
+ THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+ THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+ on_ifjoin_expiry_timer,
+ ch, holdtime);
+ }
+ break;
+ case PIM_IFJOIN_PRUNE_TMP:
+ if (source_flags & PIM_ENCODE_RPT_BIT)
+ {
+ ch->ifjoin_state = PIM_IFJOIN_PRUNE;
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
+ THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+ on_ifjoin_expiry_timer,
+ ch, holdtime);
}
- else {
- jp_override_interval_msec = 0; /* schedule to expire immediately */
- /* If we called ifjoin_prune() directly instead, care should
- be taken not to use "ch" afterwards since it would be
- deleted. */
+ break;
+ case PIM_IFJOIN_PRUNE_PENDING_TMP:
+ if (source_flags & PIM_ENCODE_RPT_BIT)
+ {
+ ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
+ THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+ on_ifjoin_expiry_timer,
+ ch, holdtime);
}
-
- THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
- on_ifjoin_prune_pending_timer,
- ch, jp_override_interval_msec);
-
- zassert(!ch->t_ifjoin_expiry_timer);
- zassert(ch->t_ifjoin_prune_pending_timer);
- }
break;
}
-
}
void pim_ifchannel_local_membership_add(struct interface *ifp,
- struct in_addr source_addr,
- struct in_addr group_addr)
+ struct prefix_sg *sg)
{
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
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;
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);
}
return;
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name,
return;
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- char old_addr_str[100];
- char new_addr_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char old_addr_str[INET_ADDRSTRLEN];
+ char new_addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
return;
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name,
}
}
}
+
+/*
+ * 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;
+ }
+ }
+}
#include <zebra.h>
#include "if.h"
+#include "prefix.h"
#include "pim_upstream.h"
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 {
#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;
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,
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 */
#include "pimd.h"
#include "pim_igmp.h"
+#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_time.h"
#include "pim_zebra.h"
-#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
-#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
-#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
-#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
-
static void group_timer_off(struct igmp_group *group);
+/* This socket is used for TXing IGMP packets only, IGMP RX happens
+ * in pim_mroute_msg()
+ */
static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim_options)
{
int fd;
int join = 0;
struct in_addr group;
- fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */);
+ fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1);
+
if (fd < 0)
return -1;
if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
if (inet_aton(PIM_ALL_ROUTERS, &group)) {
if (!pim_socket_join(fd, group, ifaddr, ifindex))
- ++join;
+ ++join;
}
else {
zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
- PIM_ALL_ROUTERS, errno, safe_strerror(errno));
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_ROUTERS, errno, safe_strerror(errno));
}
}
}
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)) {
}
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;
}
{
struct igmp_sock *igmp;
- zassert(t);
igmp = THREAD_ARG(t);
- zassert(igmp);
zassert(igmp->t_other_querier_timer);
zassert(!igmp->t_igmp_query_timer);
if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("%s: Querier %s resuming",
__PRETTY_FUNCTION__,
ifaddr_str);
}
- igmp->t_other_querier_timer = 0;
+ igmp->t_other_querier_timer = NULL;
/*
We are the current querier, then
*/
if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
ifaddr_str);
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);
}
pim_ifp->igmp_query_max_response_time_dsec);
if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
ifaddr_str,
if (PIM_DEBUG_IGMP_TRACE) {
if (igmp->t_other_querier_timer) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
ifaddr_str, igmp->fd, igmp->interface->name);
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) {
return -1;
}
+ /* RFC 3376 defines some guidelines on operating in backwards compatibility
+ * with older versions of IGMP but there are some gaps in the logic:
+ *
+ * - once we drop from say version 3 to version 2 we will never go back to
+ * version 3 even if the node that TXed an IGMP v2 query upgrades to v3
+ *
+ * - The node with the lowest IP is the querier so we will only know to drop
+ * from v3 to v2 if the node that is the querier is also the one that is
+ * running igmp v2. If a non-querier only supports igmp v2 we will have
+ * no way of knowing.
+ *
+ * For now we will simplify things and inform the user that they need to
+ * configure all PIM routers to use the same version of IGMP.
+ */
+ if (query_version != pim_ifp->igmp_version) {
+ zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
+ "configure all PIM routers on this subnet to use the same "
+ "IGMP version",
+ query_version, from_str, ifp->name, pim_ifp->igmp_version);
+ return 0;
+ }
+
if (PIM_DEBUG_IGMP_PACKETS) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
- query_version, from_str, ifp->name,
- igmp_msg_len, checksum, group_str);
+ zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
+ query_version, from_str, ifp->name, group_str);
}
/*
elected querier.
*/
if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
-
+
if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
ifp->name,
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("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
- zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
- ifaddr_str,
- qqi ? "recv-non-default" : "default",
- igmp->querier_query_interval,
- qqic,
- from_str);
- }
- }
-
- /*
- RFC 3376: 6.6.1. Timer Updates
-
- When a router sends or receives a query with a clear Suppress
- Router-Side Processing flag, it must update its timers to reflect
- the correct timeout values for the group or sources being queried.
-
- General queries don't trigger timer update.
- */
- if (query_version == 3) {
- s_flag = (1 << 3) & resv_s_qrv;
- }
- else {
- /* Neither V1 nor V2 have this field. Pimd should really go into
- * a compatibility mode here and run as V2 (or V1) but it doesn't
- * so for now, lets just set the flag to suppress these timer updates.
- */
- s_flag = 1;
- }
-
- if (!s_flag) {
- /* s_flag is clear */
-
- if (PIM_INADDR_IS_ANY(group_addr)) {
- /* this is a general query */
-
- /* log that general query should have the s_flag set */
- zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
- query_version, from_str, ifp->name);
- }
- else {
- struct igmp_group *group;
-
- /* this is a non-general query: perform timer updates */
-
- group = find_group_by_addr(igmp, group_addr);
- if (group) {
- int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
-
- /*
- RFC 3376: 6.6.1. Timer Updates
- Query Q(G,A): Source Timer for sources in A are lowered to LMQT
- Query Q(G): Group Timer is lowered to LMQT
- */
- if (recv_num_sources < 1) {
- /* Query Q(G): Group Timer is lowered to LMQT */
-
- igmp_group_timer_lower_to_lmqt(group);
- }
- else {
- /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
-
- /* Scan sources in query and lower their timers to LMQT */
- struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
- for (i = 0; i < recv_num_sources; ++i) {
- //struct in_addr src_addr = sources[i];
- //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
- struct in_addr src_addr;
- struct igmp_source *src;
- memcpy(&src_addr, sources + i, sizeof(struct in_addr));
- src = igmp_find_source_by_addr(group, src_addr);
- if (src) {
- igmp_source_timer_lower_to_lmqt(src);
- }
- }
- }
-
- }
- else {
- char group_str[100];
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
- query_version, from_str, ifp->name, group_str);
- }
- }
- } /* s_flag is clear: timer updates */
-
- return 0;
-}
-
-static int igmp_v3_report(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
-{
- uint16_t recv_checksum;
- uint16_t checksum;
- int num_groups;
- uint8_t *group_record;
- uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
- struct interface *ifp = igmp->interface;
- int i;
- int local_ncb = 0;
-
- if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
- zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
- from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
- return -1;
- }
-
- recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
-
- /* for computing checksum */
- *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
-
- checksum = in_cksum(igmp_msg, igmp_msg_len);
- if (checksum != recv_checksum) {
- zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
- from_str, ifp->name, recv_checksum, checksum);
- return -1;
- }
-
- num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
- if (num_groups < 1) {
- zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
- from_str, ifp->name);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
- from_str, ifp->name, igmp_msg_len, checksum, num_groups);
- }
-
- group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
-
- /* Scan groups */
- for (i = 0; i < num_groups; ++i) {
- struct in_addr rec_group;
- uint8_t *sources;
- uint8_t *src;
- int rec_type;
- int rec_auxdatalen;
- int rec_num_sources;
- int j;
- struct prefix lncb;
- struct prefix g;
-
- if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
- zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
- from_str, ifp->name);
- return -1;
- }
-
- rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
- rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
- rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
-
- //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
- memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
- from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
- }
-
- /* Scan sources */
-
- sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
-
- for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
-
- if ((src + 4) > report_pastend) {
- zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
- from_str, ifp->name);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- char src_str[200];
-
- if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
- sprintf(src_str, "<source?>");
-
- zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
- from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
- }
- } /* for (sources) */
-
-
- lncb.family = AF_INET;
- lncb.u.prefix4.s_addr = 0x000000E0;
- lncb.prefixlen = 24;
-
- g.family = AF_INET;
- g.u.prefix4 = rec_group;
- g.prefixlen = 32;
- /*
- * If we receive a igmp report with the group in 224.0.0.0/24
- * then we should ignore it
- */
- if (prefix_match(&lncb, &g))
- local_ncb = 1;
-
- if (!local_ncb)
- switch (rec_type) {
- case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
- igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
- igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
- igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
- igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
- igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
- igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- default:
- zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
- from_str, ifp->name, rec_type);
- }
-
- group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
- local_ncb = 0;
-
- } /* for (group records) */
-
return 0;
}
struct interface *ifp, struct in_addr from)
{
if (PIM_DEBUG_IGMP_TRACE) {
- char from_str[100];
+ char from_str[INET_ADDRSTRLEN];
pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
zlog_debug("%s: from %s on %s",
label, from_str, ifp->name);
}
}
-static int igmp_v2_report(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
-{
- struct interface *ifp = igmp->interface;
- struct igmp_group *group;
- struct in_addr group_addr;
-
- on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
- if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
- zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
- from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_warn("%s %s: FIXME WRITEME",
- __FILE__, __PRETTY_FUNCTION__);
- }
-
- //group_addr = *(struct in_addr *)(igmp_msg + 4);
- memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
-
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return -1;
- }
-
- group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
-
- return 0;
-}
-
-static int igmp_v2_leave(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
-{
- struct interface *ifp = igmp->interface;
-
- on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
- if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
- zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
- from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_warn("%s %s: FIXME WRITEME",
- __FILE__, __PRETTY_FUNCTION__);
- }
-
- return 0;
-}
-
-static int igmp_v1_report(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
+static int
+igmp_v1_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
{
struct interface *ifp = igmp->interface;
struct igmp_group *group;
__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} */
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",
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);
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.
The Startup Query Interval is the interval between General Queries
sent by a Querier on startup. Default: 1/4 the Query Interval.
+ The first one should be sent out immediately instead of 125/4
+ seconds from now.
*/
startup_mode = igmp->startup_query_count > 0;
if (startup_mode) {
- --igmp->startup_query_count;
+ /*
+ * If this is the first time we are sending a query on a
+ * newly configured igmp interface send it out in 1 second
+ * just to give the entire world a tiny bit of time to settle
+ * else the query interval is:
+ * query_interval = pim_ifp->igmp_default_query_interval >> 2;
+ */
+ if (igmp->startup_query_count == igmp->querier_robustness_variable)
+ query_interval = 1;
+ else
+ query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
- /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
- query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+ --igmp->startup_query_count;
}
else {
query_interval = igmp->querier_query_interval;
}
if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
ifaddr_str,
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);
if (PIM_DEBUG_IGMP_TRACE) {
if (igmp->t_igmp_query_timer) {
- char ifaddr_str[100];
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
ifaddr_str, igmp->fd, igmp->interface->name);
}
}
THREAD_OFF(igmp->t_igmp_query_timer);
- zassert(!igmp->t_igmp_query_timer);
}
/* Issue IGMP general query */
static int pim_igmp_general_query(struct thread *t)
{
- char query_buf[PIM_IGMP_BUFSIZE_WRITE];
struct igmp_sock *igmp;
struct in_addr dst_addr;
struct in_addr group_addr;
struct pim_interface *pim_ifp;
-
- zassert(t);
+ int query_buf_size;
igmp = THREAD_ARG(t);
- zassert(igmp);
zassert(igmp->interface);
zassert(igmp->interface->info);
pim_ifp = igmp->interface->info;
+ if (pim_ifp->igmp_version == 3) {
+ query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+ } else {
+ query_buf_size = IGMP_V12_MSG_SIZE;
+ }
+
+ char query_buf[query_buf_size];
+
/*
RFC3376: 4.1.12. IP Destination Addresses for Queries
group_addr.s_addr = PIM_NET_INADDR_ANY;
if (PIM_DEBUG_IGMP_TRACE) {
- char querier_str[100];
- char dst_str[100];
+ char querier_str[INET_ADDRSTRLEN];
+ char dst_str[INET_ADDRSTRLEN];
pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
sizeof(querier_str));
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
querier_str, dst_str, igmp->interface->name);
}
- pim_igmp_send_membership_query(0 /* igmp_group */,
- igmp->fd,
- igmp->interface->name,
- query_buf,
- sizeof(query_buf),
- 0 /* num_sources */,
- dst_addr,
- group_addr,
- pim_ifp->igmp_query_max_response_time_dsec,
- 1 /* s_flag: always set for general queries */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query (pim_ifp->igmp_version,
+ 0 /* igmp_group */,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf,
+ sizeof(query_buf),
+ 0 /* num_sources */,
+ dst_addr,
+ group_addr,
+ pim_ifp->igmp_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
pim_igmp_general_query_on(igmp);
return 0;
}
-static int pim_igmp_read(struct thread *t);
-
-static void igmp_read_on(struct igmp_sock *igmp)
-{
- zassert(igmp);
-
- if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
- zlog_debug("Scheduling READ event on IGMP socket fd=%d",
- igmp->fd);
- }
- igmp->t_igmp_read = 0;
- zassert(!igmp->t_igmp_read);
- THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
-}
-
-static int pim_igmp_read(struct thread *t)
-{
- struct igmp_sock *igmp;
- int fd;
- struct sockaddr_in from;
- struct sockaddr_in to;
- socklen_t fromlen = sizeof(from);
- socklen_t tolen = sizeof(to);
- uint8_t buf[PIM_IGMP_BUFSIZE_READ];
- int len;
- ifindex_t ifindex = -1;
- int result = -1; /* defaults to bad */
-
- zassert(t);
-
- igmp = THREAD_ARG(t);
-
- zassert(igmp);
-
- fd = THREAD_FD(t);
-
- zassert(fd == igmp->fd);
-
- len = pim_socket_recvfromto(fd, buf, sizeof(buf),
- &from, &fromlen,
- &to, &tolen,
- &ifindex);
- if (len < 0) {
- zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
- fd, errno, safe_strerror(errno));
- goto done;
- }
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- char from_str[100];
- char to_str[100];
-
- if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
- sprintf(from_str, "<from?>");
- if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
- sprintf(to_str, "<to?>");
-
- zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
- len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
- }
-
-#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
- /* ifindex sanity check */
- if (ifindex != igmp->interface->ifindex) {
- char from_str[100];
- char to_str[100];
- struct interface *ifp;
-
- if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
- sprintf(from_str, "<from?>");
- if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
- sprintf(to_str, "<to?>");
-
- ifp = if_lookup_by_index(ifindex);
- if (ifp) {
- zassert(ifindex == ifp->ifindex);
- }
-
-#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
- zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
- from_str, to_str, fd,
- ifindex, ifp ? ifp->name : "<if-notfound>",
- igmp->interface->ifindex, igmp->interface->name);
-#endif
- goto done;
- }
-#endif
-
- if (pim_igmp_packet(igmp, (char *)buf, len)) {
- goto done;
- }
-
- result = 0; /* good */
-
- done:
- igmp_read_on(igmp);
-
- return result;
-}
-
static void sock_close(struct igmp_sock *igmp)
{
pim_igmp_other_querier_timer_off(igmp);
}
}
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",
struct igmp_source *src;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Deleting IGMP group %s from socket %d interface %s",
group_str,
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)
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;
}
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();
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)
return 0;
}
+ igmp_read_on (igmp);
+
listnode_add(igmp_sock_list, igmp);
#ifdef IGMP_SOCK_DUMP
{
struct igmp_group *group;
- zassert(t);
group = THREAD_ARG(t);
- zassert(group);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("%s: Timer for group %s on interface %s",
__PRETTY_FUNCTION__,
return;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Cancelling TIMER event for group %s on %s",
group_str, group->group_igmp_sock->interface->name);
group_timer_off(group);
if (PIM_DEBUG_IGMP_EVENTS) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
interval_msec / 1000,
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}:
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();
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;
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 */
listnode_add(igmp->igmp_group_list, group);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Creating new IGMP group %s on socket %d interface %s",
group_str, igmp->fd, igmp->interface->name);
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);
+ }
+}
#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)
/* 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;
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);
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 */
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 */
--- /dev/null
+/*
+ * 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, from_str, sizeof(from_str));
+ zlog_debug("%s: from %s on %s",
+ label, from_str, ifp->name);
+ }
+}
+
+void
+igmp_v2_send_query (struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec)
+{
+ ssize_t msg_size = 8;
+ uint8_t max_resp_code;
+ ssize_t sent;
+ struct sockaddr_in to;
+ socklen_t tolen;
+ uint16_t checksum;
+
+ /* max_resp_code must be non-zero else this will look like an IGMP v1 query */
+ max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
+ zassert(max_resp_code > 0);
+
+ query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
+ query_buf[1] = max_resp_code;
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
+
+ checksum = in_cksum(query_buf, msg_size);
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
+ dst_str, ifname, group_str);
+ }
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = dst_addr;
+ tolen = sizeof(to);
+
+ sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
+ (struct sockaddr *)&to, tolen);
+ if (sent != (ssize_t) msg_size) {
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ if (sent < 0) {
+ zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+ dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
+ }
+ else {
+ zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifname, group_str, msg_size, sent);
+ }
+ return;
+ }
+}
+
+int
+igmp_v2_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+ struct in_addr group_addr;
+ char group_str[INET_ADDRSTRLEN];
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s",
+ from_str, ifp->name, group_str);
+ }
+
+ /*
+ * RFC 3376
+ * 7.3.2. In the Presence of Older Version Group Members
+ *
+ * When Group Compatibility Mode is IGMPv2, a router internally
+ * translates the following IGMPv2 messages for that group to their
+ * IGMPv3 equivalents:
+ *
+ * IGMPv2 Message IGMPv3 Equivalent
+ * -------------- -----------------
+ * Report IS_EX( {} )
+ * Leave TO_IN( {} )
+ */
+ igmpv3_report_isex (igmp, from, group_addr, 0, NULL, 1);
+
+ return 0;
+}
+
+int
+igmp_v2_recv_leave (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+ struct in_addr group_addr;
+ char group_str[INET_ADDRSTRLEN];
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s",
+ from_str, ifp->name, group_str);
+ }
+
+ /*
+ * RFC 3376
+ * 7.3.2. In the Presence of Older Version Group Members
+ *
+ * When Group Compatibility Mode is IGMPv2, a router internally
+ * translates the following IGMPv2 messages for that group to their
+ * IGMPv3 equivalents:
+ *
+ * IGMPv2 Message IGMPv3 Equivalent
+ * -------------- -----------------
+ * Report IS_EX( {} )
+ * Leave TO_IN( {} )
+ */
+ igmpv3_report_toin (igmp, from, group_addr, 0, NULL);
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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 */
int num_sources, struct in_addr *sources)
{
if (PIM_DEBUG_IGMP_TRACE) {
- char from_str[100];
- char group_str[100];
+ char from_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
}
}
-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;
pim_ifp->igmp_query_max_response_time_dsec);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
group_str,
struct igmp_source *source;
struct igmp_group *group;
- zassert(t);
source = THREAD_ARG(t);
- zassert(source);
group = source->source_group;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("%s: Source timer expired for group %s source %s on %s",
}
zassert(source->t_source_timer);
- source->t_source_timer = 0;
+ source->t_source_timer = NULL;
/*
RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
return;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("Cancelling TIMER event for group %s source %s on %s",
source_timer_off(group, source);
if (PIM_DEBUG_IGMP_EVENTS) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
pim_ifp->igmp_query_max_response_time_dsec);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
{
if (source->source_channel_oil) {
pim_channel_oil_del(source->source_channel_oil);
- source->source_channel_oil = 0;
+ source->source_channel_oil = NULL;
}
}
group = source->source_group;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
/* sanity check that forwarding has been disabled */
if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
struct igmp_source *src;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
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 */
}
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) */
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;
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);
/* 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;
*/
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;
s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
group_str, igmp->interface->name, s_flag,
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);
}
/*
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);
num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
group_str, igmp->interface->name,
query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
if (num_sources_tosend1 > query_buf1_max_sources) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
__PRETTY_FUNCTION__, group_str, igmp->interface->name,
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 */
query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
if (num_sources_tosend2 > query_buf2_max_sources) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
__PRETTY_FUNCTION__, group_str, igmp->interface->name,
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);
}
}
int num_retransmit_sources_left;
int send_with_sflag_set; /* boolean */
- zassert(t);
group = THREAD_ARG(t);
- zassert(group);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("group_retransmit_timer: group %s on %s",
group_str, group->group_igmp_sock->interface->name);
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
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
lmqi_msec / 1000,
lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
__PRETTY_FUNCTION__,
lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
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;
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;
*(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
checksum = in_cksum(query_buf, msg_size);
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
if (PIM_DEBUG_IGMP_PACKETS) {
- char dst_str[100];
- char group_str[100];
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, num_sources,
- msg_size, s_flag, querier_robustness_variable,
- querier_query_interval, qqic, checksum);
+ zlog_debug("Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
+ dst_str, ifname, group_str,
+ num_sources, msg_size, s_flag, querier_robustness_variable,
+ querier_query_interval, qqic);
}
memset(&to, 0, sizeof(to));
sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != (ssize_t) msg_size) {
- int e = errno;
- char dst_str[100];
- char group_str[100];
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
if (sent < 0) {
- zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, msg_size,
- e, safe_strerror(e));
+ zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+ dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
}
else {
- zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str,
- msg_size, sent);
+ zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifname, group_str, msg_size, sent);
}
return;
}
if (!s_flag) {
/* general query? */
if (PIM_INADDR_IS_ANY(group_addr)) {
- char dst_str[100];
- char group_str[100];
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
dst_str, ifname, group_str, num_sources);
}
}
+}
+
+void
+igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct in_addr group_addr;
+ uint8_t resv_s_qrv = 0;
+ uint8_t s_flag = 0;
+ uint8_t qrv = 0;
+ int i;
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ /*
+ * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+ *
+ * Routers adopt the QRV value from the most recently received Query
+ * as their own [Robustness Variable] value, unless that most
+ * recently received QRV was zero, in which case the receivers use
+ * the default [Robustness Variable] value specified in section 8.1
+ * or a statically configured value.
+ */
+ resv_s_qrv = igmp_msg[8];
+ qrv = 7 & resv_s_qrv;
+ igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
+
+ /*
+ * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+ *
+ * Multicast routers that are not the current querier adopt the QQI
+ * value from the most recently received Query as their own [Query
+ * Interval] value, unless that most recently received QQI was zero,
+ * in which case the receiving routers use the default.
+ */
+ if (igmp->t_other_querier_timer) {
+ /* other querier present */
+ uint8_t qqic;
+ uint16_t qqi;
+ qqic = igmp_msg[9];
+ qqi = igmp_msg_decode8to16(qqic);
+ igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
+ ifaddr_str,
+ qqi ? "recv-non-default" : "default",
+ igmp->querier_query_interval,
+ qqic,
+ from_str);
+ }
+ }
+
+ /*
+ * RFC 3376: 6.6.1. Timer Updates
+ *
+ * When a router sends or receives a query with a clear Suppress
+ * Router-Side Processing flag, it must update its timers to reflect
+ * the correct timeout values for the group or sources being queried.
+ *
+ * General queries don't trigger timer update.
+ */
+ s_flag = (1 << 3) & resv_s_qrv;
+
+ if (!s_flag) {
+ /* s_flag is clear */
+
+ if (PIM_INADDR_IS_ANY(group_addr)) {
+ /* this is a general query */
+ /* log that general query should have the s_flag set */
+ zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
+ from_str, ifp->name);
+ } else {
+ struct igmp_group *group;
+
+ /* this is a non-general query: perform timer updates */
+
+ group = find_group_by_addr(igmp, group_addr);
+ if (group) {
+ int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
+
+ /*
+ * RFC 3376: 6.6.1. Timer Updates
+ * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
+ * Query Q(G): Group Timer is lowered to LMQT
+ */
+ if (recv_num_sources < 1) {
+ /* Query Q(G): Group Timer is lowered to LMQT */
+
+ igmp_group_timer_lower_to_lmqt(group);
+ } else {
+ /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
+
+ /* Scan sources in query and lower their timers to LMQT */
+ struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
+ for (i = 0; i < recv_num_sources; ++i) {
+ struct in_addr src_addr;
+ struct igmp_source *src;
+ memcpy(&src_addr, sources + i, sizeof(struct in_addr));
+ src = igmp_find_source_by_addr(group, src_addr);
+ if (src) {
+ igmp_source_timer_lower_to_lmqt(src);
+ }
+ }
+ }
+ } else {
+ char group_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
+ from_str, ifp->name, group_str);
+ }
+ }
+ } /* s_flag is clear: timer updates */
+}
+
+int
+igmp_v3_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ uint16_t recv_checksum;
+ uint16_t checksum;
+ int num_groups;
+ uint8_t *group_record;
+ uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
+ struct interface *ifp = igmp->interface;
+ int i;
+ int local_ncb = 0;
+
+ if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
+ return -1;
+ }
+
+ recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
+
+ /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+ if (checksum != recv_checksum) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+ num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
+ if (num_groups < 1) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
+ from_str, ifp->name, igmp_msg_len, checksum, num_groups);
+ }
+
+ group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+
+ /* Scan groups */
+ for (i = 0; i < num_groups; ++i) {
+ struct in_addr rec_group;
+ uint8_t *sources;
+ uint8_t *src;
+ int rec_type;
+ int rec_auxdatalen;
+ int rec_num_sources;
+ int j;
+ struct prefix lncb;
+ struct prefix g;
+
+ if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
+ rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
+ rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
+
+ memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
+ from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
+ }
+
+ /* Scan sources */
+
+ sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
+
+ for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
+
+ if ((src + 4) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char src_str[200];
+
+ if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
+ sprintf(src_str, "<source?>");
+
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
+ from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
+ }
+ } /* for (sources) */
+
+
+ lncb.family = AF_INET;
+ lncb.u.prefix4.s_addr = 0x000000E0;
+ lncb.prefixlen = 24;
+
+ g.family = AF_INET;
+ g.u.prefix4 = rec_group;
+ g.prefixlen = 32;
+ /*
+ * If we receive a igmp report with the group in 224.0.0.0/24
+ * then we should ignore it
+ */
+ if (prefix_match(&lncb, &g))
+ local_ncb = 1;
+
+ if (!local_ncb)
+ switch (rec_type) {
+ case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
+ igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+ igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources, 0);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
+ igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+ igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
+ igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
+ igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ default:
+ zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
+ from_str, ifp->name, rec_type);
+ }
+
+ group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
+ local_ncb = 0;
+
+ } /* for (group records) */
+
+ return 0;
}
#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))
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);
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 */
#include "log.h"
#include "prefix.h"
#include "if.h"
+#include "vty.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_msg.h"
#include "pim_pim.h"
#include "pim_join.h"
+#include "pim_oil.h"
#include "pim_iface.h"
#include "pim_hello.h"
#include "pim_ifchannel.h"
+#include "pim_rpf.h"
+#include "pim_rp.h"
-static void on_trace(const char *label,
- struct interface *ifp, struct in_addr src)
+static void
+on_trace (const char *label,
+ struct interface *ifp, struct in_addr src)
{
if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
zlog_debug("%s: from %s on %s",
label, src_str, ifp->name);
struct pim_neighbor *neigh,
uint16_t holdtime,
struct in_addr upstream,
- struct in_addr group,
- struct in_addr source,
+ struct prefix_sg *sg,
uint8_t source_flags)
{
if (PIM_DEBUG_PIM_TRACE) {
- char up_str[100];
- char src_str[100];
- char grp_str[100];
- char neigh_str[100];
+ char up_str[INET_ADDRSTRLEN];
+ char neigh_str[INET_ADDRSTRLEN];
pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
- pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
- zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+ zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
__PRETTY_FUNCTION__,
- src_str, grp_str,
+ pim_str_sg_dump (sg),
source_flags & PIM_RPT_BIT_MASK,
source_flags & PIM_WILDCARD_BIT_MASK,
up_str, holdtime, neigh_str, ifp->name);
}
-
+
+ /*
+ * If the RPT and WC are set it's a (*,G)
+ * and the source is the RP
+ */
+ if ((source_flags & PIM_RPT_BIT_MASK) &&
+ (source_flags & PIM_WILDCARD_BIT_MASK))
+ {
+ struct pim_rpf *rp = RP (sg->grp);
+
+ /*
+ * If the RP sent in the message is not
+ * our RP for the group, drop the message
+ */
+ if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr)
+ return;
+
+ sg->src.s_addr = INADDR_ANY;
+ }
+
/* Restart join expiry timer */
pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
- source, group, source_flags, holdtime);
+ sg, source_flags, holdtime);
+
}
static void recv_prune(struct interface *ifp,
struct pim_neighbor *neigh,
uint16_t holdtime,
struct in_addr upstream,
- struct in_addr group,
- struct in_addr source,
+ struct prefix_sg *sg,
uint8_t source_flags)
{
if (PIM_DEBUG_PIM_TRACE) {
- char up_str[100];
- char src_str[100];
- char grp_str[100];
- char neigh_str[100];
+ char up_str[INET_ADDRSTRLEN];
+ char neigh_str[INET_ADDRSTRLEN];
pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
- pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
- zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+ zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
__PRETTY_FUNCTION__,
- src_str, grp_str,
+ pim_str_sg_dump (sg),
source_flags & PIM_RPT_BIT_MASK,
source_flags & PIM_WILDCARD_BIT_MASK,
up_str, holdtime, neigh_str, ifp->name);
}
-
- pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
+
+ if ((source_flags & PIM_RPT_BIT_MASK) &&
+ (source_flags & PIM_WILDCARD_BIT_MASK))
+ {
+ struct pim_rpf *rp = RP (sg->grp);
+
+ // Ignoring Prune *,G's at the moment.
+ if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr)
+ return;
+
+ sg->src.s_addr = INADDR_ANY;
+ }
+
+ pim_ifchannel_prune(ifp, upstream, sg, source_flags, holdtime);
+
}
int pim_joinprune_recv(struct interface *ifp,
int remain;
int group;
- on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
-
buf = tlv_buf;
pastend = tlv_buf + tlv_buf_size;
addr_offset = pim_parse_addr_ucast (&msg_upstream_addr,
buf, pastend - buf);
if (addr_offset < 1) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
__PRETTY_FUNCTION__,
Check upstream address family
*/
if (msg_upstream_addr.family != AF_INET) {
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
+ if (PIM_DEBUG_PIM_J_P) {
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
__PRETTY_FUNCTION__,
remain = pastend - buf;
if (remain < 4) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
__PRETTY_FUNCTION__,
++buf;
++buf;
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char upstream_str[100];
+ if (PIM_DEBUG_PIM_J_P) {
+ char src_str[INET_ADDRSTRLEN];
+ char upstream_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
upstream_str, sizeof(upstream_str));
- zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
- __PRETTY_FUNCTION__,
- upstream_str, msg_num_groups, msg_holdtime,
- src_str, ifp->name);
+ zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ upstream_str, msg_num_groups, msg_holdtime,
+ src_str, ifp->name);
}
/* Scan groups */
for (group = 0; group < msg_num_groups; ++group) {
- struct prefix msg_group_addr;
- struct prefix msg_source_addr;
+ struct prefix_sg sg;
uint8_t msg_source_flags;
uint16_t msg_num_joined_sources;
uint16_t msg_num_pruned_sources;
int source;
+ struct pim_ifchannel *ch = NULL;
- addr_offset = pim_parse_addr_group (&msg_group_addr,
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ addr_offset = pim_parse_addr_group (&sg,
buf, pastend - buf);
if (addr_offset < 1) {
return -5;
remain = pastend - buf;
if (remain < 4) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
__PRETTY_FUNCTION__,
msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
buf += 2;
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char upstream_str[100];
- char group_str[100];
+ if (PIM_DEBUG_PIM_J_P) {
+ char src_str[INET_ADDRSTRLEN];
+ char upstream_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
upstream_str, sizeof(upstream_str));
- pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
+ pim_inet4_dump("<grp?>", sg.grp,
group_str, sizeof(group_str));
- zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
+ zlog_warn("%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
__PRETTY_FUNCTION__,
- upstream_str, group_str, msg_group_addr.prefixlen,
+ upstream_str, group_str,
msg_num_joined_sources, msg_num_pruned_sources,
src_str, ifp->name);
}
/* Scan joined sources */
for (source = 0; source < msg_num_joined_sources; ++source) {
- addr_offset = pim_parse_addr_source (&msg_source_addr,
+ addr_offset = pim_parse_addr_source (&sg,
&msg_source_flags,
buf, pastend - buf);
if (addr_offset < 1) {
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) {
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;
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);
return -1;
}
- if (PIM_DEBUG_PIM_TRACE) {
- char source_str[100];
- char group_str[100];
- char dst_str[100];
- pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ if (PIM_DEBUG_PIM_J_P) {
+ char dst_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
- zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
+ zlog_debug("%s: sending %s(S,G)=%s to upstream=%s on interface %s",
__PRETTY_FUNCTION__,
send_join ? "Join" : "Prune",
- source_str, group_str, dst_str, ifp->name);
+ up->sg_str, dst_str, ifp->name);
}
if (PIM_INADDR_IS_ANY(upstream_addr)) {
- if (PIM_DEBUG_PIM_TRACE) {
- char source_str[100];
- char group_str[100];
- char dst_str[100];
- pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ if (PIM_DEBUG_PIM_J_P) {
+ char dst_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
- zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
+ zlog_debug("%s: %s(S,G)=%s: upstream=%s is myself on interface %s",
__PRETTY_FUNCTION__,
send_join ? "Join" : "Prune",
- source_str, group_str, dst_str, ifp->name);
+ up->sg_str, dst_str, ifp->name);
}
return 0;
}
/*
Build PIM message
*/
+ pim_msg_size = pim_msg_join_prune_encode (pim_msg, 9000, send_join,
+ up, upstream_addr, PIM_JP_HOLDTIME);
- remain = pastend - pim_msg_curr;
- pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
- remain,
- upstream_addr);
- if (!pim_msg_curr) {
- char dst_str[100];
- pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
- zlog_warn("%s: failure encoding destination address %s: space left=%d",
- __PRETTY_FUNCTION__, dst_str, remain);
- return -3;
- }
-
- remain = pastend - pim_msg_curr;
- if (remain < 4) {
- zlog_warn("%s: group will not fit: space left=%d",
- __PRETTY_FUNCTION__, remain);
- return -4;
- }
-
- *pim_msg_curr = 0; /* reserved */
- ++pim_msg_curr;
- *pim_msg_curr = 1; /* number of groups */
- ++pim_msg_curr;
- *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
- ++pim_msg_curr;
- ++pim_msg_curr;
-
- remain = pastend - pim_msg_curr;
- pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
- remain,
- group_addr);
- if (!pim_msg_curr) {
- char group_str[100];
- pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: failure encoding group address %s: space left=%d",
- __PRETTY_FUNCTION__, group_str, remain);
- return -5;
- }
-
- remain = pastend - pim_msg_curr;
- if (remain < 4) {
- zlog_warn("%s: sources will not fit: space left=%d",
- __PRETTY_FUNCTION__, remain);
- return -6;
- }
-
- /* number of joined sources */
- *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
- ++pim_msg_curr;
- ++pim_msg_curr;
-
- /* number of pruned sources */
- *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
- ++pim_msg_curr;
- ++pim_msg_curr;
-
- remain = pastend - pim_msg_curr;
- pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
- remain,
- source_addr);
- if (!pim_msg_curr) {
- char source_str[100];
- pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- zlog_warn("%s: failure encoding source address %s: space left=%d",
- __PRETTY_FUNCTION__, source_str, remain);
- return -7;
- }
-
- /* Add PIM header */
-
- pim_msg_size = pim_msg_curr - pim_msg;
-
- pim_msg_build_header(pim_msg, pim_msg_size,
- PIM_MSG_TYPE_JOIN_PRUNE);
+ if (pim_msg_size < 0)
+ return pim_msg_size;
if (pim_msg_send(pim_ifp->pim_sock_fd,
+ pim_ifp->primary_address,
qpim_all_pim_routers_addr,
pim_msg,
pim_msg_size,
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 */
#include <zebra.h>
#include "log.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
-#include "pim_macro.h"
#include "pimd.h"
-#include "pim_str.h"
+#include "pim_macro.h"
#include "pim_iface.h"
#include "pim_ifchannel.h"
+#include "pim_rp.h"
/*
DownstreamJPState(S,G,I) is the per-interface state machine for
receiving (S,G) Join/Prune messages.
- DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending
+ DownstreamJPState(*,G,I) is either Join or Prune-Pending
*/
static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
{
- return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
+ switch (ch->ifjoin_state)
+ {
+ case PIM_IFJOIN_NOINFO:
+ case PIM_IFJOIN_PRUNE:
+ case PIM_IFJOIN_PRUNE_TMP:
+ case PIM_IFJOIN_PRUNE_PENDING_TMP:
+ return 0;
+ break;
+ case PIM_IFJOIN_JOIN:
+ case PIM_IFJOIN_PRUNE_PENDING:
+ return 1;
+ break;
+ }
+ return 0;
}
/*
ifp = ch->interface;
if (!ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): null interface",
+ zlog_warn("%s: (S,G)=%s: null interface",
__PRETTY_FUNCTION__,
- src_str, grp_str);
+ ch->sg_str);
return 0; /* false */
}
pim_ifp = ifp->info;
if (!pim_ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
+ ch->sg_str, ifp->name);
return 0; /* false */
}
struct pim_interface *pim_ifp = ch->interface->info;
if (!pim_ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
- src_str, grp_str, ch->interface->name);
+ ch->sg_str, ch->interface->name);
return 0; /* false */
}
ifp = ch->interface;
if (!ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): null interface",
- __PRETTY_FUNCTION__,
- src_str, grp_str);
+ zlog_warn("%s: (S,G)=%s: null interface",
+ __PRETTY_FUNCTION__, ch->sg_str);
return 0; /* false */
}
*/
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 */
}
ifp = ch->interface;
if (!ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): null interface",
- __PRETTY_FUNCTION__,
- src_str, grp_str);
+ zlog_warn("%s: (S,G)=%s: null interface",
+ __PRETTY_FUNCTION__, ch->sg_str);
return 0; /* false */
}
pim_ifp = ifp->info;
if (!pim_ifp) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ch->interface->name);
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name);
return 0; /* false */
}
#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;
ZCAP_NET_ADMIN,
ZCAP_SYS_ADMIN,
ZCAP_NET_RAW,
+ ZCAP_BIND,
};
/* pimd privileges to run with */
-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);
print_version(progname);
exit (0);
break;
-#ifdef PIM_ZCLIENT_DEBUG
- case 'Z':
- zclient_debug = 1;
- break;
-#endif
case 'h':
usage (0);
break;
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
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
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")
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 */
#include "privs.h"
#include "if.h"
#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
#include "pimd.h"
+#include "pim_rpf.h"
#include "pim_mroute.h"
#include "pim_oil.h"
#include "pim_str.h"
#include "pim_rp.h"
#include "pim_oil.h"
#include "pim_register.h"
+#include "pim_ifchannel.h"
+#include "pim_zlookup.h"
/* GLOBAL VARS */
extern struct zebra_privs_t pimd_privs;
+static struct thread *qpim_mroute_socket_reader = NULL;
+
static void mroute_read_on(void);
static int pim_mroute_set(int fd, int enable)
int err;
int opt = enable ? MRT_INIT : MRT_DONE;
socklen_t opt_len = sizeof(opt);
+ int rcvbuf = 1024 * 1024 * 8;
+ long flags;
err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
if (err) {
- int e = errno;
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
- fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
- errno = e;
+ fd, enable ? "MRT_INIT" : "MRT_DONE", opt, errno, safe_strerror(errno));
return -1;
}
-#if 0
- zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
- __FILE__, __PRETTY_FUNCTION__,
- fd, opt);
-#endif
-
- return 0;
-}
-
-static int
-pim_mroute_connected_to_source (struct interface *ifp, struct in_addr src)
-{
- struct listnode *cnode;
- struct connected *c;
- struct prefix p;
-
- p.family = AF_INET;
- p.u.prefix4 = src;
- p.prefixlen = IPV4_MAX_BITLEN;
+ err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+ if (err) {
+ zlog_warn("%s: failure: setsockopt(fd=%d, SOL_SOCKET, %d): errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, rcvbuf, errno, safe_strerror(errno));
+ }
- for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0)
+ {
+ 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))
{
- if ((c->address->family == AF_INET) &&
- prefix_match (CONNECTED_PREFIX (c), &p))
- {
- return 1;
- }
+ 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] = {
"<unknown_upcall?>",
"NOCACHE",
"WRONGVIF",
- "WHOLEPKT", };
+ "WHOLEPKT",
+ "WRVIFWHOLE" };
static int
-pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg,
- const char *src_str, const char *grp_str)
+pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg)
{
struct pim_interface *pim_ifp = ifp->info;
struct pim_upstream *up;
struct pim_rpf *rpg;
+ struct prefix_sg sg;
+ struct channel_oil *oil;
rpg = RP(msg->im_dst);
/*
* the Interface type is SSM we don't need to
* do anything here
*/
- if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
+ if ((pim_rpf_addr_is_inaddr_none (rpg)) ||
(!pim_ifp) ||
(!(PIM_I_am_DR(pim_ifp))) ||
(pim_ifp->itype == PIM_INTERFACE_SSM))
- return 0;
+ {
+ if (PIM_DEBUG_MROUTE_DETAIL)
+ zlog_debug ("%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
/*
* If we've received a multicast packet that isn't connected to
* us
*/
- if (!pim_mroute_connected_to_source (ifp, msg->im_src))
+ if (!pim_if_connected_to_source (ifp, msg->im_src))
{
- if (PIM_DEBUG_PIM_TRACE)
- zlog_debug ("%s: Received incoming packet that does originate on our seg",
+ if (PIM_DEBUG_MROUTE_DETAIL)
+ zlog_debug ("%s: Received incoming packet that doesn't originate on our seg",
__PRETTY_FUNCTION__);
return 0;
}
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
- __PRETTY_FUNCTION__, grp_str, src_str);
- }
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = msg->im_src;
+ sg.grp = msg->im_dst;
- up = pim_upstream_add(msg->im_src, msg->im_dst, ifp);
- if (!up) {
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_debug("%s: Failure to add upstream information for (%s,%s)",
+ oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
+ if (!oil) {
+ if (PIM_DEBUG_MROUTE) {
+ zlog_debug("%s: Failure to add channel oil for %s",
__PRETTY_FUNCTION__,
- src_str, grp_str);
+ pim_str_sg_dump (&sg));
}
return 0;
}
- pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
-
- up->channel_oil = pim_channel_oil_add(msg->im_dst,
- msg->im_src,
- pim_ifp->mroute_vif_index);
- if (!up->channel_oil) {
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_debug("%s: Failure to add channel oil for (%s,%s)",
+ up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
+ if (!up) {
+ if (PIM_DEBUG_MROUTE) {
+ zlog_debug("%s: Failure to add upstream information for %s",
__PRETTY_FUNCTION__,
- src_str, grp_str);
+ pim_str_sg_dump (&sg));
}
return 0;
}
- up->channel_oil->cc.pktcnt++;
- pim_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE);
+ /*
+ * I moved this debug till after the actual add because
+ * I want to take advantage of the up->sg_str being filled in.
+ */
+ if (PIM_DEBUG_MROUTE) {
+ zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
+ __PRETTY_FUNCTION__, up->sg_str);
+ }
+
+ PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
+ pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+
+ up->channel_oil = oil;
+ up->channel_oil->cc.pktcnt++;
+ PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+ pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+ up->join_state = PIM_UPSTREAM_JOINED;
return 0;
}
static int
-pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf,
- const char *src_str, const char *grp_str)
+pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf)
{
struct pim_interface *pim_ifp;
- struct in_addr group;
- struct in_addr src;
+ struct prefix_sg sg;
struct pim_rpf *rpg;
const struct ip *ip_hdr;
struct pim_upstream *up;
ip_hdr = (const struct ip *)buf;
- src = ip_hdr->ip_src;
- group = ip_hdr->ip_dst;
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = ip_hdr->ip_src;
+ sg.grp = ip_hdr->ip_dst;
- up = pim_upstream_find(src, group);
+ up = pim_upstream_find(&sg);
if (!up) {
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
- __PRETTY_FUNCTION__, src_str, grp_str);
+ if (PIM_DEBUG_MROUTE_DETAIL) {
+ zlog_debug("%s: Unable to find upstream channel WHOLEPKT%s",
+ __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
}
return 0;
}
pim_ifp = up->rpf.source_nexthop.interface->info;
- rpg = RP(group);
+ rpg = RP(sg.grp);
- if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
+ if ((pim_rpf_addr_is_inaddr_none (rpg)) ||
(!pim_ifp) ||
(!(PIM_I_am_DR(pim_ifp))) ||
(pim_ifp->itype == PIM_INTERFACE_SSM)) {
- if (PIM_DEBUG_PIM_TRACE) {
+ if (PIM_DEBUG_MROUTE) {
zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__);
}
return 0;
}
- pim_register_send((const struct ip *)(buf + sizeof(struct ip)), rpg);
+ /*
+ * If we've received a register suppress
+ */
+ if (!up->t_rs_timer)
+ pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs (ip_hdr->ip_len),
+ pim_ifp->primary_address, rpg, 0, up);
return 0;
}
static int
-pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg,
- const char *src_str, const char *grp_str)
+pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg)
{
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
+ struct prefix_sg sg;
+
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = msg->im_src;
+ sg.grp = msg->im_dst;
/*
Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
*/
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;
}
/*
*/
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;
}
return 0;
}
+static int
+pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf)
+{
+ const struct ip *ip_hdr = (const struct ip *)buf;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+ struct pim_upstream *up;
+ //struct prefix_sg star_g;
+ struct prefix_sg sg;
+ struct channel_oil *oil;
+
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = ip_hdr->ip_src;
+ sg.grp = ip_hdr->ip_dst;
+
+ ch = pim_ifchannel_find(ifp, &sg);
+ if (ch)
+ {
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug ("WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
+ ch->sg_str, ifp->name);
+ return -1;
+ }
+#if 0
+ star_g = sg;
+ star_g.src.s_addr = INADDR_ANY;
+ ch = pim_ifchannel_find(ifp, &star_g);
+ if (ch)
+ {
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
+ pim_str_sg_dump (&star_g), ifp->name);
+ return -1;
+ }
+#endif
+
+ up = pim_upstream_find (&sg);
+ if (up)
+ {
+ struct pim_nexthop source;
+ struct pim_rpf *rpf = RP (sg.grp);
+ if (!rpf || !rpf->source_nexthop.interface)
+ return 0;
+
+ pim_ifp = rpf->source_nexthop.interface->info;
+
+ memset (&source, 0, sizeof (source));
+ /*
+ * If we are the fhr that means we are getting a callback during
+ * the pimreg period, so I believe we can ignore this packet
+ */
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+ {
+ //No if channel, but upstream we are at the RP.
+ if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
+ pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
+ if (!up->channel_oil)
+ up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
+ pim_upstream_inherited_olist (up);
+ if (!up->channel_oil->installed)
+ pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_set_sptbit (up, ifp);
+ }
+ else
+ {
+ if (I_am_RP (up->sg.grp))
+ {
+ if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
+ pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
+ up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
+ }
+ pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+ pim_upstream_inherited_olist (up);
+ pim_mroute_msg_wholepkt (fd, ifp, buf);
+ }
+ return 0;
+ }
+
+ pim_ifp = ifp->info;
+ oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
+ if (!oil->installed)
+ pim_mroute_add (oil, __PRETTY_FUNCTION__);
+ if (pim_if_connected_to_source (ifp, sg.src))
+ {
+ up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
+ if (!up)
+ {
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface",
+ pim_str_sg_dump (&sg), ifp->name);
+ return -2;
+ }
+ PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
+ pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+ up->channel_oil = oil;
+ up->channel_oil->cc.pktcnt++;
+ pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+ up->join_state = PIM_UPSTREAM_JOINED;
+ pim_upstream_inherited_olist (up);
+
+ // Send the packet to the RP
+ pim_mroute_msg_wholepkt (fd, ifp, buf);
+ }
+
+ return 0;
+}
+
int pim_mroute_msg(int fd, const char *buf, int buf_size)
{
struct interface *ifp;
+ struct pim_interface *pim_ifp;
const struct ip *ip_hdr;
const struct igmpmsg *msg;
- char src_str[100] = "<src?>";
- char grp_str[100] = "<grp?>";
+ char ip_src_str[INET_ADDRSTRLEN] = "";
+ char ip_dst_str[INET_ADDRSTRLEN] = "";
+ char src_str[INET_ADDRSTRLEN] = "<src?>";
+ char grp_str[INET_ADDRSTRLEN] = "<grp?>";
+ struct in_addr ifaddr;
+ struct igmp_sock *igmp;
ip_hdr = (const struct ip *) buf;
- /* kernel upcall must have protocol=0 */
- if (ip_hdr->ip_p) {
- /* this is not a kernel upcall */
- if (PIM_DEBUG_PIM_TRACE) {
- pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
- zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
- __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
+ if (ip_hdr->ip_p == IPPROTO_IGMP) {
+
+ /* We have the IP packet but we do not know which interface this packet was
+ * received on. Find the interface that is on the same subnet as the source
+ * of the IP packet.
+ */
+ ifp = pim_if_lookup_address_vrf (ip_hdr->ip_src, VRF_DEFAULT);
+
+ if (!ifp) {
+ if (PIM_DEBUG_MROUTE_DETAIL) {
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
+
+ zlog_warn("%s: igmp kernel upcall could not find usable interface for %s -> %s",
+ __PRETTY_FUNCTION__,
+ ip_src_str,
+ ip_dst_str);
+ }
+ return 0;
}
- return 0;
- }
+ pim_ifp = ifp->info;
+ ifaddr = pim_find_primary_addr(ifp);
+ igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
- msg = (const struct igmpmsg *) buf;
-
- ifp = pim_if_find_by_vif_index(msg->im_vif);
-
- if (PIM_DEBUG_PIM_TRACE) {
- pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
- zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
- __PRETTY_FUNCTION__,
- igmpmsgtype2str[msg->im_msgtype],
- msg->im_msgtype,
- ip_hdr->ip_p,
- fd,
- src_str,
- grp_str,
- ifp ? ifp->name : "<ifname?>",
- msg->im_vif);
- }
+ if (PIM_DEBUG_MROUTE) {
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
- switch (msg->im_msgtype) {
- case IGMPMSG_WRONGVIF:
- return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str);
- break;
- case IGMPMSG_NOCACHE:
- return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str);
- break;
- case IGMPMSG_WHOLEPKT:
- return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg, src_str, grp_str);
- break;
- default:
- break;
- }
+ zlog_warn("%s: igmp kernel upcall on %s(%p) for %s -> %s",
+ __PRETTY_FUNCTION__, ifp->name, igmp, ip_src_str, ip_dst_str);
+ }
+ if (igmp)
+ pim_igmp_packet(igmp, (char *)buf, buf_size);
- return 0;
-}
+ } else if (ip_hdr->ip_p) {
+ if (PIM_DEBUG_MROUTE_DETAIL) {
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
+ zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
+ __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
+ }
-static int mroute_read_msg(int fd)
-{
- const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
- char buf[1000];
- int rd;
+ } else {
+ msg = (const struct igmpmsg *) buf;
- if (((int) sizeof(buf)) < msg_min_size) {
- zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
- __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
- return -1;
- }
+ ifp = pim_if_find_by_vif_index(msg->im_vif);
- rd = read(fd, buf, sizeof(buf));
- if (rd < 0) {
- zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
- __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
- return -2;
- }
+ if (!ifp)
+ return 0;
+ if (PIM_DEBUG_MROUTE) {
+ pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
+ zlog_warn("%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
+ __PRETTY_FUNCTION__,
+ igmpmsgtype2str[msg->im_msgtype],
+ msg->im_msgtype,
+ ip_hdr->ip_p,
+ fd,
+ src_str,
+ grp_str,
+ ifp->name,
+ msg->im_vif, buf_size);
+ }
- if (rd < msg_min_size) {
- zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
- __PRETTY_FUNCTION__, fd, rd, msg_min_size);
- return -3;
+ switch (msg->im_msgtype) {
+ case IGMPMSG_WRONGVIF:
+ return pim_mroute_msg_wrongvif(fd, ifp, msg);
+ break;
+ case IGMPMSG_NOCACHE:
+ return pim_mroute_msg_nocache(fd, ifp, msg);
+ break;
+ case IGMPMSG_WHOLEPKT:
+ return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
+ break;
+ case IGMPMSG_WRVIFWHOLE:
+ return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
+ break;
+ default:
+ break;
+ }
}
- return pim_mroute_msg(fd, buf, rd);
+ return 0;
}
static int mroute_read(struct thread *t)
{
+ static long long count;
+ char buf[10000];
+ int result = 0;
+ int cont = 1;
int fd;
- int result;
-
- zassert(t);
- zassert(!THREAD_ARG(t));
+ int rd;
fd = THREAD_FD(t);
- zassert(fd == qpim_mroute_socket_fd);
-
- result = mroute_read_msg(fd);
+ while (cont)
+ {
+ rd = read(fd, buf, sizeof(buf));
+ if (rd < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ cont = 0;
+ break;
+ }
+ if (PIM_DEBUG_MROUTE)
+ zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ goto done;
+ }
+
+ result = pim_mroute_msg(fd, buf, rd);
+
+ count++;
+ if (count % qpim_packet_process == 0)
+ cont = 0;
+ }
/* Keep reading */
- qpim_mroute_socket_reader = 0;
+ done:
+ qpim_mroute_socket_reader = NULL;
mroute_read_on();
return result;
qpim_mroute_socket_creation = pim_time_monotonic_sec();
mroute_read_on();
- zassert(PIM_MROUTE_IS_ENABLED);
-
return 0;
}
mroute_read_off();
qpim_mroute_socket_fd = -1;
- zassert(PIM_MROUTE_IS_DISABLED);
-
return 0;
}
err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
if (err) {
- char ifaddr_str[100];
- int e = errno;
+ char ifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
- e, safe_strerror(e));
- errno = e;
+ errno, safe_strerror(errno));
return -2;
}
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;
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;
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;
{
struct sioc_sg_req sgreq;
- memset (&sgreq, 0, sizeof(sgreq));
- sgreq.src = c_oil->oil.mfcc_origin;
- sgreq.grp = c_oil->oil.mfcc_mcastgrp;
-
c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
+ if (!c_oil->installed)
+ {
+ c_oil->cc.lastused = 100 * qpim_keep_alive_time;
+ if (PIM_DEBUG_MROUTE)
+ {
+ struct prefix_sg sg;
+
+ sg.src = c_oil->oil.mfcc_origin;
+ sg.grp = c_oil->oil.mfcc_mcastgrp;
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug("Channel(%s) is not installed no need to collect data from kernel",
+ pim_str_sg_dump (&sg));
+ }
+ return;
+ }
+
+ memset (&sgreq, 0, sizeof(sgreq));
+ sgreq.src = c_oil->oil.mfcc_origin;
+ sgreq.grp = c_oil->oil.mfcc_mcastgrp;
+
+ pim_zlookup_sg_statistics (c_oil);
if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
{
- char group_str[100];
- char source_str[100];
-
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
-
- zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
- (unsigned long)SIOCGETSGCNT,
- source_str,
- group_str,
- errno,
- safe_strerror(errno));
+ if (PIM_DEBUG_MROUTE)
+ {
+ struct prefix_sg sg;
+
+ sg.src = c_oil->oil.mfcc_origin;
+ sg.grp = c_oil->oil.mfcc_mcastgrp;
+
+ zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
+ (unsigned long)SIOCGETSGCNT,
+ pim_str_sg_dump (&sg),
+ errno,
+ safe_strerror(errno));
+ }
return;
}
#endif
#endif
+#ifndef IGMPMSG_WRVIFWHOLE
+#define IGMPMSG_WRVIFWHOLE 4 /* For PIM processing */
+#endif
+
/*
Above: from <linux/mroute.h>
*/
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);
--- /dev/null
+/*
+ * IP MSDP for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <lib/hash.h>
+#include <lib/jhash.h>
+#include <lib/log.h>
+#include <lib/prefix.h>
+#include <lib/sockunion.h>
+#include <lib/stream.h>
+#include <lib/thread.h>
+#include <lib/vty.h>
+#include <lib/plist.h>
+
+#include "pimd.h"
+#include "pim_cmd.h"
+#include "pim_memory.h"
+#include "pim_iface.h"
+#include "pim_rp.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_upstream.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_packet.h"
+#include "pim_msdp_socket.h"
+
+struct pim_msdp pim_msdp, *msdp = &pim_msdp;
+
+static void pim_msdp_peer_listen(struct pim_msdp_peer *mp);
+static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start);
+static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start);
+static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start);
+static void pim_msdp_peer_free(struct pim_msdp_peer *mp);
+static void pim_msdp_enable(void);
+static void pim_msdp_sa_adv_timer_setup(bool start);
+static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags);
+static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2);
+static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr);
+static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr);
+
+/************************ SA cache management ******************************/
+static void
+pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, const char *timer_str)
+{
+ zlog_debug("MSDP SA %s %s timer expired", sa->sg_str, timer_str);
+}
+
+/* RFC-3618:Sec-5.1 - global active source advertisement timer */
+static int
+pim_msdp_sa_adv_timer_cb(struct thread *t)
+{
+ msdp->sa_adv_timer = NULL;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA advertisment timer expired");
+ }
+
+ pim_msdp_sa_adv_timer_setup(true /* start */);
+ pim_msdp_pkt_sa_tx();
+ return 0;
+}
+static void
+pim_msdp_sa_adv_timer_setup(bool start)
+{
+ THREAD_OFF(msdp->sa_adv_timer);
+ if (start) {
+ THREAD_TIMER_ON(msdp->master, msdp->sa_adv_timer,
+ pim_msdp_sa_adv_timer_cb, NULL, PIM_MSDP_SA_ADVERTISMENT_TIME);
+ }
+}
+
+/* RFC-3618:Sec-5.3 - SA cache state timer */
+static int
+pim_msdp_sa_state_timer_cb(struct thread *t)
+{
+ struct pim_msdp_sa *sa;
+
+ sa = THREAD_ARG(t);
+ sa->sa_state_timer = NULL;
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_sa_timer_expiry_log(sa, "state");
+ }
+
+ pim_msdp_sa_deref(sa, PIM_MSDP_SAF_PEER);
+ return 0;
+}
+static void
+pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start)
+{
+ THREAD_OFF(sa->sa_state_timer);
+ if (start) {
+ THREAD_TIMER_ON(msdp->master, sa->sa_state_timer,
+ pim_msdp_sa_state_timer_cb, sa, PIM_MSDP_SA_HOLD_TIME);
+ }
+}
+
+static void
+pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa)
+{
+ struct pim_upstream *up = sa->up;
+ if (!up) {
+ return;
+ }
+
+ sa->up = NULL;
+ if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
+ PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
+ sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG;
+ pim_upstream_del(up, __PRETTY_FUNCTION__);
+ sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG;
+ }
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s de-referenced SPT", sa->sg_str);
+ }
+}
+
+static bool
+pim_msdp_sa_upstream_add_ok(struct pim_msdp_sa *sa, struct pim_upstream *xg_up)
+{
+ if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
+ /* SA should have been rxed from a peer */
+ return false;
+ }
+ /* check if we are RP */
+ if (!I_am_RP(sa->sg.grp)) {
+ return false;
+ }
+
+ /* check if we have a (*, G) with a non-empty immediate OIL */
+ if (!xg_up) {
+ struct prefix_sg sg;
+
+ memset(&sg, 0, sizeof(sg));
+ sg.grp = sa->sg.grp;
+
+ xg_up = pim_upstream_find(&sg);
+ }
+ if (!xg_up || (xg_up->join_state != PIM_UPSTREAM_JOINED)) {
+ /* join desired will be true for such (*, G) entries so we will
+ * just look at join_state and let the PIM state machine do the rest of
+ * the magic */
+ return false;
+ }
+
+ return true;
+}
+
+/* Upstream add evaluation needs to happen everytime -
+ * 1. Peer reference is added or removed.
+ * 2. The RP for a group changes.
+ * 3. joinDesired for the associated (*, G) changes
+ * 4. associated (*, G) is removed - this seems like a bit redundant
+ * (considering #4); but just in case an entry gets nuked without
+ * upstream state transition
+ * */
+static void
+pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa,
+ struct pim_upstream *xg_up, const char *ctx)
+{
+ struct pim_upstream *up;
+
+ if (!pim_msdp_sa_upstream_add_ok(sa, xg_up)) {
+ pim_msdp_sa_upstream_del(sa);
+ return;
+ }
+
+ if (sa->up) {
+ /* nothing to do */
+ return;
+ }
+
+ up = pim_upstream_find(&sa->sg);
+ if (up && (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags))) {
+ /* somehow we lost track of the upstream ptr? best log it */
+ sa->up = up;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s SPT reference missing", sa->sg_str);
+ }
+ return;
+ }
+
+ /* RFC3618: "RP triggers a (S, G) join event towards the data source
+ * as if a JP message was rxed addressed to the RP itself." */
+ up = pim_upstream_add(&sa->sg, NULL /* iif */,
+ PIM_UPSTREAM_FLAG_MASK_SRC_MSDP,
+ __PRETTY_FUNCTION__);
+
+ sa->up = up;
+ if (up) {
+ /* update inherited oil */
+ pim_upstream_inherited_olist(up);
+ /* should we also start the kat in parallel? we will need it when the
+ * SA ages out */
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s referenced SPT", sa->sg_str);
+ }
+ } else {
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s SPT reference failed", sa->sg_str);
+ }
+ }
+}
+
+/* release all mem associated with a sa */
+static void
+pim_msdp_sa_free(struct pim_msdp_sa *sa)
+{
+ XFREE(MTYPE_PIM_MSDP_SA, sa);
+}
+
+static struct pim_msdp_sa *
+pim_msdp_sa_new(struct prefix_sg *sg, struct in_addr rp)
+{
+ struct pim_msdp_sa *sa;
+
+ sa = XCALLOC(MTYPE_PIM_MSDP_SA, sizeof(*sa));
+ if (!sa) {
+ zlog_err("%s: PIM XCALLOC(%zu) failure",
+ __PRETTY_FUNCTION__, sizeof(*sa));
+ return NULL;
+ }
+
+ sa->sg = *sg;
+ pim_str_sg_set(sg, sa->sg_str);
+ sa->rp = rp;
+ sa->uptime = pim_time_monotonic_sec();
+
+ /* insert into misc tables for easy access */
+ sa = hash_get(msdp->sa_hash, sa, hash_alloc_intern);
+ if (!sa) {
+ zlog_err("%s: PIM hash get failure", __PRETTY_FUNCTION__);
+ pim_msdp_sa_free(sa);
+ return NULL;
+ }
+ listnode_add_sort(msdp->sa_list, sa);
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s created", sa->sg_str);
+ }
+
+ return sa;
+}
+
+static struct pim_msdp_sa *
+pim_msdp_sa_find(struct prefix_sg *sg)
+{
+ struct pim_msdp_sa lookup;
+
+ lookup.sg = *sg;
+ return hash_lookup(msdp->sa_hash, &lookup);
+}
+
+static struct pim_msdp_sa *
+pim_msdp_sa_add(struct prefix_sg *sg, struct in_addr rp)
+{
+ struct pim_msdp_sa *sa;
+
+ sa = pim_msdp_sa_find(sg);
+ if (sa) {
+ return sa;
+ }
+
+ return pim_msdp_sa_new(sg, rp);
+}
+
+static void
+pim_msdp_sa_del(struct pim_msdp_sa * sa)
+{
+ /* this is somewhat redundant - still want to be careful not to leave
+ * stale upstream references */
+ pim_msdp_sa_upstream_del(sa);
+
+ /* stop timers */
+ pim_msdp_sa_state_timer_setup(sa, false /* start */);
+
+ /* remove the entry from various tables */
+ listnode_delete(msdp->sa_list, sa);
+ hash_release(msdp->sa_hash, sa);
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s deleted", sa->sg_str);
+ }
+
+ /* free up any associated memory */
+ pim_msdp_sa_free(sa);
+}
+
+static void
+pim_msdp_sa_peer_ip_set(struct pim_msdp_sa *sa, struct pim_msdp_peer *mp, struct in_addr rp)
+{
+ struct pim_msdp_peer *old_mp;
+
+ /* optimize the "no change" case as it will happen
+ * frequently/periodically */
+ if (mp && (sa->peer.s_addr == mp->peer.s_addr)) {
+ return;
+ }
+
+ /* any time the peer ip changes also update the rp address */
+ if (PIM_INADDR_ISNOT_ANY(sa->peer)) {
+ old_mp = pim_msdp_peer_find(sa->peer);
+ if (old_mp && old_mp->sa_cnt) {
+ --old_mp->sa_cnt;
+ }
+ }
+
+ if (mp) {
+ ++mp->sa_cnt;
+ sa->peer = mp->peer;
+ } else {
+ sa->peer.s_addr = PIM_NET_INADDR_ANY;
+ }
+ sa->rp = rp;
+}
+
+/* When a local active-source is removed there is no way to withdraw the
+ * source from peers. We will simply remove it from the SA cache so it will
+ * not be sent in supsequent SA updates. Peers will consequently timeout the
+ * SA.
+ * Similarly a "peer-added" SA is never explicitly deleted. It is simply
+ * aged out overtime if not seen in the SA updates from the peers.
+ * XXX: should we provide a knob to drop entries learnt from a peer when the
+ * peer goes down? */
+static void
+pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags)
+{
+ bool update_up = false;
+
+ if ((sa->flags &PIM_MSDP_SAF_LOCAL)) {
+ if (flags & PIM_MSDP_SAF_LOCAL) {
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s local reference removed", sa->sg_str);
+ }
+ if (msdp->local_cnt)
+ --msdp->local_cnt;
+ }
+ }
+
+ if ((sa->flags &PIM_MSDP_SAF_PEER)) {
+ if (flags & PIM_MSDP_SAF_PEER) {
+ struct in_addr rp;
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s peer reference removed", sa->sg_str);
+ }
+ pim_msdp_sa_state_timer_setup(sa, false /* start */);
+ rp.s_addr = INADDR_ANY;
+ pim_msdp_sa_peer_ip_set(sa, NULL /* mp */, rp);
+ /* if peer ref was removed we need to remove the msdp reference on the
+ * msdp entry */
+ update_up = true;
+ }
+ }
+
+ sa->flags &= ~flags;
+ if (update_up) {
+ pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "sa-deref");
+ }
+
+ if (!(sa->flags & PIM_MSDP_SAF_REF)) {
+ pim_msdp_sa_del(sa);
+ }
+}
+
+void
+pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg,
+ struct in_addr rp)
+{
+ struct pim_msdp_sa *sa;
+
+ sa = pim_msdp_sa_add(sg, rp);
+ if (!sa) {
+ return;
+ }
+
+ /* reference it */
+ if (mp) {
+ if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
+ sa->flags |= PIM_MSDP_SAF_PEER;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s added by peer", sa->sg_str);
+ }
+ }
+ pim_msdp_sa_peer_ip_set(sa, mp, rp);
+ /* start/re-start the state timer to prevent cache expiry */
+ pim_msdp_sa_state_timer_setup(sa, true /* start */);
+ /* We re-evaluate SA "SPT-trigger" everytime we hear abt it from a
+ * peer. XXX: If this becomes too much of a periodic overhead we
+ * can make it event based */
+ pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "peer-ref");
+ } else {
+ if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
+ sa->flags |= PIM_MSDP_SAF_LOCAL;
+ ++msdp->local_cnt;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP SA %s added locally", sa->sg_str);
+ }
+ /* send an immediate SA update to peers */
+ pim_msdp_pkt_sa_tx_one(sa);
+ }
+ sa->flags &= ~PIM_MSDP_SAF_STALE;
+ }
+}
+
+/* The following criteria must be met to originate an SA from the MSDP
+ * speaker -
+ * 1. KAT must be running i.e. source is active.
+ * 2. We must be RP for the group.
+ * 3. Source must be registrable to the RP (this is where the RFC is vague
+ * and especially ambiguous in CLOS networks; with anycast RP all sources
+ * are potentially registrable to all RPs in the domain). We assume #3 is
+ * satisfied if -
+ * a. We are also the FHR-DR for the source (OR)
+ * b. We rxed a pim register (null or data encapsulated) within the last
+ * (3 * (1.5 * register_suppression_timer))).
+ */
+static bool
+pim_msdp_sa_local_add_ok(struct pim_upstream *up)
+{
+ if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
+ return false;
+ }
+
+ if (!up->t_ka_timer) {
+ /* stream is not active */
+ return false;
+ }
+
+ if (!I_am_RP(up->sg.grp)) {
+ /* we are not RP for the group */
+ return false;
+ }
+
+ /* we are the FHR-DR for this stream or we are RP and have seen registers
+ * from a FHR for this source */
+ if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || up->t_msdp_reg_timer) {
+ return true;
+ }
+
+ return false;
+}
+
+static void
+pim_msdp_sa_local_add(struct prefix_sg *sg)
+{
+ struct in_addr rp;
+ rp.s_addr = 0;
+ pim_msdp_sa_ref(NULL /* mp */, sg, rp);
+}
+
+void
+pim_msdp_sa_local_del(struct prefix_sg *sg)
+{
+ struct pim_msdp_sa *sa;
+
+ sa = pim_msdp_sa_find(sg);
+ if (sa) {
+ pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+ }
+}
+
+/* we need to be very cautious with this API as SA del too can trigger an
+ * upstream del and we will get stuck in a simple loop */
+static void
+pim_msdp_sa_local_del_on_up_del(struct prefix_sg *sg)
+{
+ struct pim_msdp_sa *sa;
+
+ sa = pim_msdp_sa_find(sg);
+ if (sa) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP local sa %s del on up del", sa->sg_str);
+ }
+
+ /* if there is no local reference escape */
+ if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP local sa %s del; no local ref", sa->sg_str);
+ }
+ return;
+ }
+
+ if (sa->flags & PIM_MSDP_SAF_UP_DEL_IN_PROG) {
+ /* MSDP is the one that triggered the upstream del. if this happens
+ * we most certainly have a bug in the PIM upstream state machine. We
+ * will not have a local reference unless the KAT is running. And if the
+ * KAT is running there MUST be an additional source-stream reference to
+ * the flow. Accounting for such cases requires lot of changes; perhaps
+ * address this in the next release? - XXX */
+ zlog_err("MSDP sa %s SPT teardown is causing the local entry to be removed", sa->sg_str);
+ return;
+ }
+
+ /* we are dropping the sa on upstream del we should not have an
+ * upstream reference */
+ if (sa->up) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP local sa %s del; up non-NULL", sa->sg_str);
+ }
+ sa->up = NULL;
+ }
+ pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+ }
+}
+
+/* Local SA qualification needs to be re-evaluated when -
+ * 1. KAT is started or stopped
+ * 2. on RP changes
+ * 3. Whenever FHR status changes for a (S,G) - XXX - currently there
+ * is no clear path to transition an entry out of "MASK_FHR" need
+ * to discuss this with Donald. May result in some strangeness if the
+ * FHR is also the RP.
+ * 4. When msdp_reg timer is started or stopped
+ */
+void
+pim_msdp_sa_local_update(struct pim_upstream *up)
+{
+ if (pim_msdp_sa_local_add_ok(up)) {
+ pim_msdp_sa_local_add(&up->sg);
+ } else {
+ pim_msdp_sa_local_del(&up->sg);
+ }
+}
+
+static void
+pim_msdp_sa_local_setup(void)
+{
+ struct pim_upstream *up;
+ struct listnode *up_node;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) {
+ pim_msdp_sa_local_update(up);
+ }
+}
+
+/* whenever the RP changes we need to re-evaluate the "local" SA-cache */
+/* XXX: needs to be tested */
+void
+pim_msdp_i_am_rp_changed(void)
+{
+ struct listnode *sanode;
+ struct listnode *nextnode;
+ struct pim_msdp_sa *sa;
+
+ if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
+ /* if the feature is not enabled do nothing */
+ return;
+ }
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP i_am_rp changed");
+ }
+
+ /* mark all local entries as stale */
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ if (sa->flags & PIM_MSDP_SAF_LOCAL) {
+ sa->flags |= PIM_MSDP_SAF_STALE;
+ }
+ }
+
+ /* re-setup local SA entries */
+ pim_msdp_sa_local_setup();
+
+ for (ALL_LIST_ELEMENTS(msdp->sa_list, sanode, nextnode, sa)) {
+ /* purge stale SA entries */
+ if (sa->flags & PIM_MSDP_SAF_STALE) {
+ /* clear the stale flag; the entry may be kept even after
+ * "local-deref" */
+ sa->flags &= ~PIM_MSDP_SAF_STALE;
+ /* sa_deref can end up freeing the sa; so don't access contents after */
+ pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+ } else {
+ /* if the souce is still active check if we can influence SPT */
+ pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change");
+ }
+ }
+}
+
+/* We track the join state of (*, G) entries. If G has sources in the SA-cache
+ * we need to setup or teardown SPT when the JoinDesired status changes for
+ * (*, G) */
+void
+pim_msdp_up_join_state_changed(struct pim_upstream *xg_up)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP join state changed for %s", xg_up->sg_str);
+ }
+
+ /* If this is not really an XG entry just move on */
+ if ((xg_up->sg.src.s_addr != INADDR_ANY) ||
+ (xg_up->sg.grp.s_addr == INADDR_ANY)) {
+ return;
+ }
+
+ /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
+ * walking */
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ if (sa->sg.grp.s_addr != xg_up->sg.grp.s_addr) {
+ continue;
+ }
+ pim_msdp_sa_upstream_update(sa, xg_up, "up-jp-change");
+ }
+}
+
+static void
+pim_msdp_up_xg_del(struct prefix_sg *sg)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP %s del", pim_str_sg_dump(sg));
+ }
+
+ /* If this is not really an XG entry just move on */
+ if ((sg->src.s_addr != INADDR_ANY) ||
+ (sg->grp.s_addr == INADDR_ANY)) {
+ return;
+ }
+
+ /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
+ * walking */
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ if (sa->sg.grp.s_addr != sg->grp.s_addr) {
+ continue;
+ }
+ pim_msdp_sa_upstream_update(sa, NULL /* xg */, "up-jp-change");
+ }
+}
+
+void
+pim_msdp_up_del(struct prefix_sg *sg)
+{
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP up %s del", pim_str_sg_dump(sg));
+ }
+ if (sg->src.s_addr == INADDR_ANY) {
+ pim_msdp_up_xg_del(sg);
+ } else {
+ pim_msdp_sa_local_del_on_up_del(sg);
+ }
+}
+
+/* sa hash and peer list helpers */
+static unsigned int
+pim_msdp_sa_hash_key_make(void *p)
+{
+ struct pim_msdp_sa *sa = p;
+
+ return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0));
+}
+
+static int
+pim_msdp_sa_hash_eq(const void *p1, const void *p2)
+{
+ const struct pim_msdp_sa *sa1 = p1;
+ const struct pim_msdp_sa *sa2 = p2;
+
+ return ((sa1->sg.src.s_addr == sa2->sg.src.s_addr) &&
+ (sa1->sg.grp.s_addr == sa2->sg.grp.s_addr));
+}
+
+static int
+pim_msdp_sa_comp(const void *p1, const void *p2)
+{
+ const struct pim_msdp_sa *sa1 = p1;
+ const struct pim_msdp_sa *sa2 = p2;
+
+ if (ntohl(sa1->sg.grp.s_addr) < ntohl(sa2->sg.grp.s_addr))
+ return -1;
+
+ if (ntohl(sa1->sg.grp.s_addr) > ntohl(sa2->sg.grp.s_addr))
+ return 1;
+
+ if (ntohl(sa1->sg.src.s_addr) < ntohl(sa2->sg.src.s_addr))
+ return -1;
+
+ if (ntohl(sa1->sg.src.s_addr) > ntohl(sa2->sg.src.s_addr))
+ return 1;
+
+ return 0;
+}
+
+/* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */
+/* XXX: this can use a bit of refining and extensions */
+bool
+pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
+{
+ if (mp->peer.s_addr == rp.s_addr) {
+ return true;
+ }
+
+ return false;
+}
+
+/************************ Peer session management **************************/
+char *
+pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size)
+{
+ switch (state) {
+ case PIM_MSDP_DISABLED:
+ snprintf(buf, buf_size, "%s", "disabled");
+ break;
+ case PIM_MSDP_INACTIVE:
+ snprintf(buf, buf_size, "%s", "inactive");
+ break;
+ case PIM_MSDP_LISTEN:
+ snprintf(buf, buf_size, "%s", "listen");
+ break;
+ case PIM_MSDP_CONNECTING:
+ snprintf(buf, buf_size, "%s", "connecting");
+ break;
+ case PIM_MSDP_ESTABLISHED:
+ snprintf(buf, buf_size, "%s", "established");
+ break;
+ default:
+ snprintf(buf, buf_size, "unk-%d", state);
+ }
+ return buf;
+}
+
+char *
+pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format)
+{
+ char peer_str[INET_ADDRSTRLEN];
+ char local_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
+ if (long_format) {
+ pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
+ snprintf(buf, buf_size, "MSDP peer %s local %s mg %s",
+ peer_str, local_str, mp->mesh_group_name);
+ } else {
+ snprintf(buf, buf_size, "MSDP peer %s", peer_str);
+ }
+
+ return buf;
+}
+
+static void
+pim_msdp_peer_state_chg_log(struct pim_msdp_peer *mp)
+{
+ char state_str[PIM_MSDP_STATE_STRLEN];
+
+ pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
+ zlog_debug("MSDP peer %s state chg to %s", mp->key_str, state_str);
+}
+
+/* MSDP Connection State Machine actions (defined in RFC-3618:Sec-11.2) */
+/* 11.2.A2: active peer - start connect retry timer; when the timer fires
+ * a tcp connection will be made */
+static void
+pim_msdp_peer_connect(struct pim_msdp_peer *mp)
+{
+ mp->state = PIM_MSDP_CONNECTING;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_state_chg_log(mp);
+ }
+
+ pim_msdp_peer_cr_timer_setup(mp, true /* start */);
+}
+
+/* 11.2.A3: passive peer - just listen for connections */
+static void
+pim_msdp_peer_listen(struct pim_msdp_peer *mp)
+{
+ mp->state = PIM_MSDP_LISTEN;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_state_chg_log(mp);
+ }
+
+ /* this is interntionally asymmetric i.e. we set up listen-socket when the
+ * first listening peer is configured; but don't bother tearing it down when
+ * all the peers go down */
+ pim_msdp_sock_listen();
+}
+
+/* 11.2.A4 and 11.2.A5: transition active or passive peer to
+ * established state */
+void
+pim_msdp_peer_established(struct pim_msdp_peer *mp)
+{
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ ++mp->est_flaps;
+ }
+
+ mp->state = PIM_MSDP_ESTABLISHED;
+ mp->uptime = pim_time_monotonic_sec();
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_state_chg_log(mp);
+ }
+
+ /* stop retry timer on active peers */
+ pim_msdp_peer_cr_timer_setup(mp, false /* start */);
+
+ /* send KA; start KA and hold timers */
+ pim_msdp_pkt_ka_tx(mp);
+ pim_msdp_peer_ka_timer_setup(mp, true /* start */);
+ pim_msdp_peer_hold_timer_setup(mp, true /* start */);
+
+ pim_msdp_pkt_sa_tx_to_one_peer(mp);
+
+ PIM_MSDP_PEER_WRITE_ON(mp);
+ PIM_MSDP_PEER_READ_ON(mp);
+}
+
+/* 11.2.A6, 11.2.A7 and 11.2.A8: shutdown the peer tcp connection */
+void
+pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state)
+{
+ if (chg_state) {
+ if (mp->state == PIM_MSDP_ESTABLISHED) {
+ ++mp->est_flaps;
+ }
+ mp->state = PIM_MSDP_INACTIVE;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_state_chg_log(mp);
+ }
+ }
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_peer_stop_tcp_conn", mp->key_str);
+ }
+ /* stop read and write threads */
+ PIM_MSDP_PEER_READ_OFF(mp);
+ PIM_MSDP_PEER_WRITE_OFF(mp);
+
+ /* reset buffers */
+ mp->packet_size = 0;
+ if (mp->ibuf)
+ stream_reset(mp->ibuf);
+ if (mp->obuf)
+ stream_fifo_clean(mp->obuf);
+
+ /* stop all peer timers */
+ pim_msdp_peer_ka_timer_setup(mp, false /* start */);
+ pim_msdp_peer_cr_timer_setup(mp, false /* start */);
+ pim_msdp_peer_hold_timer_setup(mp, false /* start */);
+
+ /* close connection */
+ if (mp->fd >= 0) {
+ close(mp->fd);
+ mp->fd = -1;
+ }
+}
+
+/* RFC-3618:Sec-5.6 - stop the peer tcp connection and startover */
+void
+pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str)
+{
+ if (PIM_DEBUG_EVENTS) {
+ zlog_debug("MSDP peer %s tcp reset %s", mp->key_str, rc_str);
+ snprintf(mp->last_reset, sizeof(mp->last_reset), "%s", rc_str);
+ }
+
+ /* close the connection and transition to listening or connecting */
+ pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
+ if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
+ pim_msdp_peer_listen(mp);
+ } else {
+ pim_msdp_peer_connect(mp);
+ }
+}
+
+static void
+pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp, const char *timer_str)
+{
+ zlog_debug("MSDP peer %s %s timer expired", mp->key_str, timer_str);
+}
+
+/* RFC-3618:Sec-5.4 - peer hold timer */
+static int
+pim_msdp_peer_hold_timer_cb(struct thread *t)
+{
+ struct pim_msdp_peer *mp;
+
+ mp = THREAD_ARG(t);
+ mp->hold_timer = NULL;
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_timer_expiry_log(mp, "hold");
+ }
+
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ return 0;
+ }
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_state_chg_log(mp);
+ }
+ pim_msdp_peer_reset_tcp_conn(mp, "ht-expired");
+ return 0;
+}
+static void
+pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start)
+{
+ THREAD_OFF(mp->hold_timer);
+ if (start) {
+ THREAD_TIMER_ON(msdp->master, mp->hold_timer,
+ pim_msdp_peer_hold_timer_cb, mp, PIM_MSDP_PEER_HOLD_TIME);
+ }
+}
+
+
+/* RFC-3618:Sec-5.5 - peer keepalive timer */
+static int
+pim_msdp_peer_ka_timer_cb(struct thread *t)
+{
+ struct pim_msdp_peer *mp;
+
+ mp = THREAD_ARG(t);
+ mp->ka_timer = NULL;
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_timer_expiry_log(mp, "ka");
+ }
+
+ pim_msdp_pkt_ka_tx(mp);
+ pim_msdp_peer_ka_timer_setup(mp, true /* start */);
+ return 0;
+}
+static void
+pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start)
+{
+ THREAD_OFF(mp->ka_timer);
+ if (start) {
+ THREAD_TIMER_ON(msdp->master, mp->ka_timer,
+ pim_msdp_peer_ka_timer_cb, mp, PIM_MSDP_PEER_KA_TIME);
+ }
+}
+
+static void
+pim_msdp_peer_active_connect(struct pim_msdp_peer *mp)
+{
+ int rc;
+ ++mp->conn_attempts;
+ rc = pim_msdp_sock_connect(mp);
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_peer_active_connect: %d", mp->key_str, rc);
+ }
+
+ switch (rc) {
+ case connect_error:
+ case -1:
+ /* connect failed restart the connect-retry timer */
+ pim_msdp_peer_cr_timer_setup(mp, true /* start */);
+ break;
+
+ case connect_success:
+ /* connect was sucessful move to established */
+ pim_msdp_peer_established(mp);
+ break;
+
+ case connect_in_progress:
+ /* for NB content we need to wait till sock is readable or
+ * writeable */
+ PIM_MSDP_PEER_WRITE_ON(mp);
+ PIM_MSDP_PEER_READ_ON(mp);
+ /* also restart connect-retry timer to reset the socket if connect is
+ * not sucessful */
+ pim_msdp_peer_cr_timer_setup(mp, true /* start */);
+ break;
+ }
+}
+
+/* RFC-3618:Sec-5.6 - connection retry on active peer */
+static int
+pim_msdp_peer_cr_timer_cb(struct thread *t)
+{
+ struct pim_msdp_peer *mp;
+
+ mp = THREAD_ARG(t);
+ mp->cr_timer = NULL;
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ pim_msdp_peer_timer_expiry_log(mp, "connect-retry");
+ }
+
+ if (mp->state != PIM_MSDP_CONNECTING || PIM_MSDP_PEER_IS_LISTENER(mp)) {
+ return 0;
+ }
+
+ pim_msdp_peer_active_connect(mp);
+ return 0;
+}
+static void
+pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start)
+{
+ THREAD_OFF(mp->cr_timer);
+ if (start) {
+ THREAD_TIMER_ON(msdp->master, mp->cr_timer,
+ pim_msdp_peer_cr_timer_cb, mp, PIM_MSDP_PEER_CONNECT_RETRY_TIME);
+ }
+}
+
+/* if a valid packet is rxed from the peer we can restart hold timer */
+void
+pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp)
+{
+ if (mp->state == PIM_MSDP_ESTABLISHED) {
+ pim_msdp_peer_hold_timer_setup(mp, true /* start */);
+ }
+}
+
+/* if a valid packet is txed to the peer we can restart ka timer and avoid
+ * unnecessary ka noise in the network */
+void
+pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp)
+{
+ if (mp->state == PIM_MSDP_ESTABLISHED) {
+ pim_msdp_peer_ka_timer_setup(mp, true /* start */);
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP ka timer restart on pkt tx to %s", mp->key_str);
+ }
+ }
+}
+
+static void pim_msdp_addr2su(union sockunion *su, struct in_addr addr)
+{
+ sockunion_init(su);
+ su->sin.sin_addr = addr;
+ su->sin.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ su->sin.sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+}
+
+/* 11.2.A1: create a new peer and transition state to listen or connecting */
+static enum pim_msdp_err
+pim_msdp_peer_new(struct in_addr peer_addr, struct in_addr local_addr,
+ const char *mesh_group_name, struct pim_msdp_peer **mp_p)
+{
+ struct pim_msdp_peer *mp;
+
+ pim_msdp_enable();
+
+ mp = XCALLOC(MTYPE_PIM_MSDP_PEER, sizeof(*mp));
+ if (!mp) {
+ zlog_err("%s: PIM XCALLOC(%zu) failure",
+ __PRETTY_FUNCTION__, sizeof(*mp));
+ return PIM_MSDP_ERR_OOM;
+ }
+
+ mp->peer = peer_addr;
+ pim_inet4_dump("<peer?>", mp->peer, mp->key_str, sizeof(mp->key_str));
+ pim_msdp_addr2su(&mp->su_peer, mp->peer);
+ mp->local = local_addr;
+ /* XXX: originator_id setting needs to move to the mesh group */
+ msdp->originator_id = local_addr;
+ pim_msdp_addr2su(&mp->su_local, mp->local);
+ mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
+ mp->state = PIM_MSDP_INACTIVE;
+ mp->fd = -1;
+ strcpy(mp->last_reset, "-");
+ /* higher IP address is listener */
+ if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) {
+ mp->flags |= PIM_MSDP_PEERF_LISTENER;
+ }
+
+ /* setup packet buffers */
+ mp->ibuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
+ mp->obuf = stream_fifo_new();
+
+ /* insert into misc tables for easy access */
+ mp = hash_get(msdp->peer_hash, mp, hash_alloc_intern);
+ listnode_add_sort(msdp->peer_list, mp);
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP peer %s created", mp->key_str);
+
+ pim_msdp_peer_state_chg_log(mp);
+ }
+
+ /* fireup the connect state machine */
+ if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
+ pim_msdp_peer_listen(mp);
+ } else {
+ pim_msdp_peer_connect(mp);
+ }
+ if (mp_p) {
+ *mp_p = mp;
+ }
+ return PIM_MSDP_ERR_NONE;
+}
+
+struct pim_msdp_peer *
+pim_msdp_peer_find(struct in_addr peer_addr)
+{
+ struct pim_msdp_peer lookup;
+
+ lookup.peer = peer_addr;
+ return hash_lookup(msdp->peer_hash, &lookup);
+}
+
+/* add peer configuration if it doesn't already exist */
+enum pim_msdp_err
+pim_msdp_peer_add(struct in_addr peer_addr, struct in_addr local_addr,
+ const char *mesh_group_name, struct pim_msdp_peer **mp_p)
+{
+ struct pim_msdp_peer *mp;
+
+ if (mp_p) {
+ *mp_p = NULL;
+ }
+
+ if (peer_addr.s_addr == local_addr.s_addr) {
+ /* skip session setup if config is invalid */
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ char peer_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<peer?>", peer_addr, peer_str, sizeof(peer_str));
+ zlog_debug("%s add skipped as DIP=SIP", peer_str);
+ }
+ return PIM_MSDP_ERR_SIP_EQ_DIP;
+ }
+
+ mp = pim_msdp_peer_find(peer_addr);
+ if (mp) {
+ if (mp_p) {
+ *mp_p = mp;
+ }
+ return PIM_MSDP_ERR_PEER_EXISTS;
+ }
+
+ return pim_msdp_peer_new(peer_addr, local_addr, mesh_group_name, mp_p);
+}
+
+/* release all mem associated with a peer */
+static void
+pim_msdp_peer_free(struct pim_msdp_peer *mp)
+{
+ if (mp->ibuf) {
+ stream_free(mp->ibuf);
+ }
+
+ if (mp->obuf) {
+ stream_fifo_free(mp->obuf);
+ }
+
+ if (mp->mesh_group_name) {
+ XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name);
+ }
+ XFREE(MTYPE_PIM_MSDP_PEER, mp);
+}
+
+/* delete the peer config */
+static enum pim_msdp_err
+pim_msdp_peer_do_del(struct pim_msdp_peer *mp)
+{
+ /* stop the tcp connection and shutdown all timers */
+ pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
+
+ /* remove the session from various tables */
+ listnode_delete(msdp->peer_list, mp);
+ hash_release(msdp->peer_hash, mp);
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP peer %s deleted", mp->key_str);
+ }
+
+ /* free up any associated memory */
+ pim_msdp_peer_free(mp);
+
+ return PIM_MSDP_ERR_NONE;
+}
+
+enum pim_msdp_err
+pim_msdp_peer_del(struct in_addr peer_addr)
+{
+ struct pim_msdp_peer *mp;
+
+ mp = pim_msdp_peer_find(peer_addr);
+ if (!mp) {
+ return PIM_MSDP_ERR_NO_PEER;
+ }
+
+ return pim_msdp_peer_do_del(mp);
+}
+
+/* peer hash and peer list helpers */
+static unsigned int
+pim_msdp_peer_hash_key_make(void *p)
+{
+ struct pim_msdp_peer *mp = p;
+ return (jhash_1word(mp->peer.s_addr, 0));
+}
+
+static int
+pim_msdp_peer_hash_eq(const void *p1, const void *p2)
+{
+ const struct pim_msdp_peer *mp1 = p1;
+ const struct pim_msdp_peer *mp2 = p2;
+
+ return (mp1->peer.s_addr == mp2->peer.s_addr);
+}
+
+static int
+pim_msdp_peer_comp(const void *p1, const void *p2)
+{
+ const struct pim_msdp_peer *mp1 = p1;
+ const struct pim_msdp_peer *mp2 = p2;
+
+ if (ntohl(mp1->peer.s_addr) < ntohl(mp2->peer.s_addr))
+ return -1;
+
+ if (ntohl(mp1->peer.s_addr) > ntohl(mp2->peer.s_addr))
+ return 1;
+
+ return 0;
+}
+
+/************************** Mesh group management **************************/
+static void
+pim_msdp_mg_free(struct pim_msdp_mg *mg)
+{
+ /* If the mesh-group has valid member or src_ip don't delete it */
+ if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) {
+ return;
+ }
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name);
+ }
+ if (mg->mesh_group_name)
+ XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name);
+
+ if (mg->mbr_list)
+ list_free(mg->mbr_list);
+
+ XFREE(MTYPE_PIM_MSDP_MG, mg);
+ msdp->mg = NULL;
+}
+
+static struct pim_msdp_mg *
+pim_msdp_mg_new(const char *mesh_group_name)
+{
+ struct pim_msdp_mg *mg;
+
+ mg = XCALLOC(MTYPE_PIM_MSDP_MG, sizeof(*mg));
+ if (!mg) {
+ zlog_err("%s: PIM XCALLOC(%zu) failure",
+ __PRETTY_FUNCTION__, sizeof(*mg));
+ return NULL;
+ }
+
+ mg->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
+ mg->mbr_list = list_new();
+ mg->mbr_list->del = (void (*)(void *))pim_msdp_mg_mbr_free;
+ mg->mbr_list->cmp = (int (*)(void *, void *))pim_msdp_mg_mbr_comp;
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name);
+ }
+ return mg;
+}
+
+enum pim_msdp_err
+pim_msdp_mg_del(const char *mesh_group_name)
+{
+ struct pim_msdp_mg *mg = msdp->mg;
+ struct pim_msdp_mg_mbr *mbr;
+
+ if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+ return PIM_MSDP_ERR_NO_MG;
+ }
+
+ /* delete all the mesh-group members */
+ while (!list_isempty(mg->mbr_list)) {
+ mbr = listnode_head(mg->mbr_list);
+ pim_msdp_mg_mbr_do_del(mg, mbr);
+ }
+
+ /* clear src ip */
+ mg->src_ip.s_addr = INADDR_ANY;
+
+ /* free up the mesh-group */
+ pim_msdp_mg_free(mg);
+ return PIM_MSDP_ERR_NONE;
+}
+
+static enum pim_msdp_err
+pim_msdp_mg_add(const char *mesh_group_name)
+{
+ if (msdp->mg) {
+ if (!strcmp(msdp->mg->mesh_group_name, mesh_group_name)) {
+ return PIM_MSDP_ERR_NONE;
+ }
+ /* currently only one mesh-group can exist at a time */
+ return PIM_MSDP_ERR_MAX_MESH_GROUPS;
+ }
+
+ msdp->mg = pim_msdp_mg_new(mesh_group_name);
+ if (!msdp->mg) {
+ return PIM_MSDP_ERR_OOM;
+ }
+
+ return PIM_MSDP_ERR_NONE;
+}
+
+static int
+pim_msdp_mg_mbr_comp(const void *p1, const void *p2)
+{
+ const struct pim_msdp_mg_mbr *mbr1 = p1;
+ const struct pim_msdp_mg_mbr *mbr2 = p2;
+
+ if (ntohl(mbr1->mbr_ip.s_addr) < ntohl(mbr2->mbr_ip.s_addr))
+ return -1;
+
+ if (ntohl(mbr1->mbr_ip.s_addr) > ntohl(mbr2->mbr_ip.s_addr))
+ return 1;
+
+ return 0;
+}
+
+static void
+pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr)
+{
+ XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr);
+}
+
+static struct pim_msdp_mg_mbr *
+pim_msdp_mg_mbr_find(struct in_addr mbr_ip)
+{
+ struct pim_msdp_mg_mbr *mbr;
+ struct listnode *mbr_node;
+
+ if (!msdp->mg) {
+ return NULL;
+ }
+ /* we can move this to a hash but considering that number of peers in
+ * a mesh-group that seems like bit of an overkill */
+ for (ALL_LIST_ELEMENTS_RO(msdp->mg->mbr_list, mbr_node, mbr)) {
+ if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) {
+ return mbr;
+ }
+ }
+ return mbr;
+}
+
+enum pim_msdp_err
+pim_msdp_mg_mbr_add(const char *mesh_group_name, struct in_addr mbr_ip)
+{
+ int rc;
+ struct pim_msdp_mg_mbr *mbr;
+ struct pim_msdp_mg *mg;
+
+ rc = pim_msdp_mg_add(mesh_group_name);
+ if (rc != PIM_MSDP_ERR_NONE) {
+ return rc;
+ }
+
+ mg = msdp->mg;
+ mbr = pim_msdp_mg_mbr_find(mbr_ip);
+ if (mbr) {
+ return PIM_MSDP_ERR_MG_MBR_EXISTS;
+ }
+
+ mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr));
+ if (!mbr) {
+ zlog_err("%s: PIM XCALLOC(%zu) failure",
+ __PRETTY_FUNCTION__, sizeof(*mbr));
+ /* if there are no references to the mg free it */
+ pim_msdp_mg_free(mg);
+ return PIM_MSDP_ERR_OOM;
+ }
+ mbr->mbr_ip = mbr_ip;
+ listnode_add_sort(mg->mbr_list, mbr);
+
+ /* if valid SIP has been configured add peer session */
+ if (mg->src_ip.s_addr != INADDR_ANY) {
+ pim_msdp_peer_add(mbr_ip, mg->src_ip, mesh_group_name,
+ &mbr->mp);
+ }
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ char ip_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
+ zlog_debug("MSDP mesh-group %s mbr %s created", mg->mesh_group_name, ip_str);
+ }
+ ++mg->mbr_cnt;
+ return PIM_MSDP_ERR_NONE;
+}
+
+static void
+pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr)
+{
+ /* Delete active peer session if any */
+ if (mbr->mp) {
+ pim_msdp_peer_do_del(mbr->mp);
+ }
+
+ listnode_delete(mg->mbr_list, mbr);
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ char ip_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
+ zlog_debug("MSDP mesh-group %s mbr %s deleted", mg->mesh_group_name, ip_str);
+ }
+ pim_msdp_mg_mbr_free(mbr);
+ if (mg->mbr_cnt) {
+ --mg->mbr_cnt;
+ }
+}
+
+enum pim_msdp_err
+pim_msdp_mg_mbr_del(const char *mesh_group_name, struct in_addr mbr_ip)
+{
+ struct pim_msdp_mg_mbr *mbr;
+ struct pim_msdp_mg *mg = msdp->mg;
+
+ if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+ return PIM_MSDP_ERR_NO_MG;
+ }
+
+ mbr = pim_msdp_mg_mbr_find(mbr_ip);
+ if (!mbr) {
+ return PIM_MSDP_ERR_NO_MG_MBR;
+ }
+
+ pim_msdp_mg_mbr_do_del(mg, mbr);
+ /* if there are no references to the mg free it */
+ pim_msdp_mg_free(mg);
+
+ return PIM_MSDP_ERR_NONE;
+}
+
+static void
+pim_msdp_mg_src_do_del(void)
+{
+ struct pim_msdp_mg_mbr *mbr;
+ struct listnode *mbr_node;
+ struct pim_msdp_mg *mg = msdp->mg;
+
+ /* SIP is being removed - tear down all active peer sessions */
+ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
+ if (mbr->mp) {
+ pim_msdp_peer_do_del(mbr->mp);
+ mbr->mp = NULL;
+ }
+ }
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_debug("MSDP mesh-group %s src cleared", mg->mesh_group_name);
+ }
+}
+
+enum pim_msdp_err
+pim_msdp_mg_src_del(const char *mesh_group_name)
+{
+ struct pim_msdp_mg *mg = msdp->mg;
+
+ if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+ return PIM_MSDP_ERR_NO_MG;
+ }
+
+ if (mg->src_ip.s_addr != INADDR_ANY) {
+ mg->src_ip.s_addr = INADDR_ANY;
+ pim_msdp_mg_src_do_del();
+ /* if there are no references to the mg free it */
+ pim_msdp_mg_free(mg);
+ }
+ return PIM_MSDP_ERR_NONE;
+}
+
+enum pim_msdp_err
+pim_msdp_mg_src_add(const char *mesh_group_name, struct in_addr src_ip)
+{
+ int rc;
+ struct pim_msdp_mg_mbr *mbr;
+ struct listnode *mbr_node;
+ struct pim_msdp_mg *mg;
+
+ if (src_ip.s_addr == INADDR_ANY) {
+ pim_msdp_mg_src_del(mesh_group_name);
+ return PIM_MSDP_ERR_NONE;
+ }
+
+ rc = pim_msdp_mg_add(mesh_group_name);
+ if (rc != PIM_MSDP_ERR_NONE) {
+ return rc;
+ }
+
+ mg = msdp->mg;
+ if (mg->src_ip.s_addr != INADDR_ANY) {
+ pim_msdp_mg_src_do_del();
+ }
+ mg->src_ip = src_ip;
+
+ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
+ pim_msdp_peer_add(mbr->mbr_ip, mg->src_ip, mesh_group_name,
+ &mbr->mp);
+ }
+
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ char ip_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", mg->src_ip, ip_str, sizeof(ip_str));
+ zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name, ip_str);
+ }
+ return PIM_MSDP_ERR_NONE;
+}
+
+/*********************** MSDP feature APIs *********************************/
+int
+pim_msdp_config_write(struct vty *vty)
+{
+ struct listnode *mbrnode;
+ struct pim_msdp_mg_mbr *mbr;
+ struct pim_msdp_mg *mg = msdp->mg;
+ char mbr_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ int count = 0;
+
+ if (!mg) {
+ return count;
+ }
+
+ if (mg->src_ip.s_addr != INADDR_ANY) {
+ pim_inet4_dump("<src?>", mg->src_ip, src_str, sizeof(src_str));
+ vty_out(vty, "ip msdp mesh-group %s source %s%s",
+ mg->mesh_group_name, src_str, VTY_NEWLINE);
+ ++count;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
+ pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
+ vty_out(vty, "ip msdp mesh-group %s member %s%s",
+ mg->mesh_group_name, mbr_str, VTY_NEWLINE);
+ ++count;
+ }
+ return count;
+}
+
+/* Enable feature including active/periodic timers etc. on the first peer
+ * config. Till then MSDP should just stay quiet. */
+static void
+pim_msdp_enable(void)
+{
+ if (msdp->flags & PIM_MSDPF_ENABLE) {
+ /* feature is already enabled */
+ return;
+ }
+ msdp->flags |= PIM_MSDPF_ENABLE;
+ msdp->work_obuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
+ pim_msdp_sa_adv_timer_setup(true /* start */);
+ /* setup sa cache based on local sources */
+ pim_msdp_sa_local_setup();
+}
+
+/* MSDP init */
+void
+pim_msdp_init(struct thread_master *master)
+{
+ msdp->master = master;
+
+ msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make,
+ pim_msdp_peer_hash_eq);
+ msdp->peer_list = list_new();
+ msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free;
+ msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp;
+
+ msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make,
+ pim_msdp_sa_hash_eq);
+ msdp->sa_list = list_new();
+ msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free;
+ msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp;
+}
+
+/* counterpart to MSDP init; XXX: unused currently */
+void
+pim_msdp_exit(void)
+{
+ /* XXX: stop listener and delete all peer sessions */
+
+ if (msdp->peer_hash) {
+ hash_free(msdp->peer_hash);
+ msdp->peer_hash = NULL;
+ }
+
+ if (msdp->peer_list) {
+ list_free(msdp->peer_list);
+ msdp->peer_list = NULL;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * IP MSDP packet helper
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <lib/log.h>
+#include <lib/network.h>
+#include <lib/stream.h>
+#include <lib/thread.h>
+#include <lib/vty.h>
+
+#include "pimd.h"
+#include "pim_str.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_packet.h"
+#include "pim_msdp_socket.h"
+
+static char *
+pim_msdp_pkt_type_dump(enum pim_msdp_tlv type, char *buf, int buf_size)
+{
+ switch (type) {
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ snprintf(buf, buf_size, "%s", "SA");
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST:
+ snprintf(buf, buf_size, "%s", "SA_REQ");
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE:
+ snprintf(buf, buf_size, "%s", "SA_RESP");
+ break;
+ case PIM_MSDP_KEEPALIVE:
+ snprintf(buf, buf_size, "%s", "KA");
+ break;
+ case PIM_MSDP_RESERVED:
+ snprintf(buf, buf_size, "%s", "RSVD");
+ break;
+ case PIM_MSDP_TRACEROUTE_PROGRESS:
+ snprintf(buf, buf_size, "%s", "TRACE_PROG");
+ break;
+ case PIM_MSDP_TRACEROUTE_REPLY:
+ snprintf(buf, buf_size, "%s", "TRACE_REPLY");
+ break;
+ default:
+ snprintf(buf, buf_size, "UNK-%d", type);
+ }
+ return buf;
+}
+
+static void
+pim_msdp_pkt_sa_dump_one(struct stream *s)
+{
+ struct prefix_sg sg;
+
+ /* just throw away the three reserved bytes */
+ stream_get3(s);
+ /* throw away the prefix length also */
+ stream_getc(s);
+
+ memset(&sg, 0, sizeof (struct prefix_sg));
+ sg.grp.s_addr = stream_get_ipv4(s);
+ sg.src.s_addr = stream_get_ipv4(s);
+
+ zlog_debug(" sg %s", pim_str_sg_dump(&sg));
+}
+
+static void
+pim_msdp_pkt_sa_dump(struct stream *s)
+{
+ int entry_cnt;
+ int i;
+ struct in_addr rp; /* Last RP address associated with this SA */
+
+ entry_cnt = stream_getc(s);
+ rp.s_addr = stream_get_ipv4(s);
+
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ char rp_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<rp?>", rp, rp_str, sizeof(rp_str));
+ zlog_debug(" entry_cnt %d rp %s", entry_cnt, rp_str);
+ }
+
+ /* dump SAs */
+ for (i = 0; i < entry_cnt; ++i) {
+ pim_msdp_pkt_sa_dump_one(s);
+ }
+}
+
+static void
+pim_msdp_pkt_dump(struct pim_msdp_peer *mp, int type, int len, bool rx,
+ struct stream *s)
+{
+ char type_str[PIM_MSDP_PKT_TYPE_STRLEN];
+
+ pim_msdp_pkt_type_dump(type, type_str, sizeof(type_str));
+
+ zlog_debug("MSDP peer %s pkt %s type %s len %d",
+ mp->key_str, rx?"rx":"tx", type_str, len);
+
+ if (!s) {
+ return;
+ }
+
+ switch(type) {
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ pim_msdp_pkt_sa_dump(s);
+ break;
+ default:;
+ }
+}
+
+/* Check file descriptor whether connect is established. */
+static void
+pim_msdp_connect_check(struct pim_msdp_peer *mp)
+{
+ int status;
+ socklen_t slen;
+ int ret;
+
+ if (mp->state != PIM_MSDP_CONNECTING) {
+ /* if we are here it means we are not in a connecting or established state
+ * for now treat this as a fatal error */
+ pim_msdp_peer_reset_tcp_conn(mp, "invalid-state");
+ return;
+ }
+
+ PIM_MSDP_PEER_READ_OFF(mp);
+ PIM_MSDP_PEER_WRITE_OFF(mp);
+
+ /* Check file descriptor. */
+ slen = sizeof(status);
+ ret = getsockopt(mp->fd, SOL_SOCKET, SO_ERROR, (void *)&status, &slen);
+
+ /* If getsockopt is fail, this is fatal error. */
+ if (ret < 0) {
+ zlog_err("can't get sockopt for nonblocking connect");
+ pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
+ return;
+ }
+
+ /* When status is 0 then TCP connection is established. */
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_connect_check %s", mp->key_str, status?"fail":"success");
+ }
+ if (status == 0) {
+ pim_msdp_peer_established(mp);
+ } else {
+ pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
+ }
+}
+
+static void
+pim_msdp_pkt_delete(struct pim_msdp_peer *mp)
+{
+ stream_free(stream_fifo_pop(mp->obuf));
+}
+
+static void
+pim_msdp_pkt_add(struct pim_msdp_peer *mp, struct stream *s)
+{
+ stream_fifo_push(mp->obuf, s);
+}
+
+static void
+pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp)
+{
+ if (stream_fifo_head(mp->obuf)) {
+ PIM_MSDP_PEER_WRITE_ON(mp);
+ }
+}
+
+int
+pim_msdp_write(struct thread *thread)
+{
+ struct pim_msdp_peer *mp;
+ struct stream *s;
+ int num;
+ enum pim_msdp_tlv type;
+ int len;
+ int work_cnt = 0;
+ int work_max_cnt = 100;
+
+ mp = THREAD_ARG(thread);
+ mp->t_write = NULL;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_write", mp->key_str);
+ }
+ if (mp->fd < 0) {
+ return -1;
+ }
+
+ /* check if TCP connection is established */
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ pim_msdp_connect_check(mp);
+ return 0;
+ }
+
+ s = stream_fifo_head(mp->obuf);
+ if (!s) {
+ pim_msdp_write_proceed_actions(mp);
+ return 0;
+ }
+
+ sockopt_cork(mp->fd, 1);
+
+ /* Nonblocking write until TCP output buffer is full */
+ do
+ {
+ int writenum;
+
+ /* Number of bytes to be sent */
+ writenum = stream_get_endp(s) - stream_get_getp(s);
+
+ /* Call write() system call */
+ num = write(mp->fd, STREAM_PNT(s), writenum);
+ if (num < 0) {
+ /* write failed either retry needed or error */
+ if (ERRNO_IO_RETRY(errno)) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_write io retry", mp->key_str);
+ }
+ break;
+ }
+
+ pim_msdp_peer_reset_tcp_conn(mp, "pkt-tx-failed");
+ return 0;
+ }
+
+ if (num != writenum) {
+ /* Partial write */
+ stream_forward_getp(s, num);
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_partial_write", mp->key_str);
+ }
+ break;
+ }
+
+ /* Retrieve msdp packet type. */
+ stream_set_getp(s,0);
+ type = stream_getc(s);
+ len = stream_getw(s);
+ switch (type)
+ {
+ case PIM_MSDP_KEEPALIVE:
+ mp->ka_tx_cnt++;
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ mp->sa_tx_cnt++;
+ break;
+ default:;
+ }
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ pim_msdp_pkt_dump(mp, type, len, false /*rx*/, s);
+ }
+
+ /* packet sent delete it. */
+ pim_msdp_pkt_delete(mp);
+
+ ++work_cnt;
+ /* may need to pause if we have done too much work in this
+ * loop */
+ if (work_cnt >= work_max_cnt) {
+ break;
+ }
+ } while ((s = stream_fifo_head(mp->obuf)) != NULL);
+ pim_msdp_write_proceed_actions(mp);
+
+ sockopt_cork(mp->fd, 0);
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets", mp->key_str, work_cnt);
+ }
+
+ return 0;
+}
+
+static void
+pim_msdp_pkt_send(struct pim_msdp_peer *mp, struct stream *s)
+{
+ /* Add packet to the end of list. */
+ pim_msdp_pkt_add(mp, s);
+
+ PIM_MSDP_PEER_WRITE_ON(mp);
+}
+
+void
+pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp)
+{
+ struct stream *s;
+
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ /* don't tx anything unless a session is established */
+ return;
+ }
+ s = stream_new(PIM_MSDP_KA_TLV_MAX_SIZE);
+ stream_putc(s, PIM_MSDP_KEEPALIVE);
+ stream_putw(s, PIM_MSDP_KA_TLV_MAX_SIZE);
+
+ pim_msdp_pkt_send(mp, s);
+}
+
+static void
+pim_msdp_pkt_sa_push_to_one_peer(struct pim_msdp_peer *mp)
+{
+ struct stream *s;
+
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ /* don't tx anything unless a session is established */
+ return;
+ }
+ s = stream_dup(msdp->work_obuf);
+ if (s) {
+ pim_msdp_pkt_send(mp, s);
+ mp->flags |= PIM_MSDP_PEERF_SA_JUST_SENT;
+ }
+}
+
+/* push the stream into the obuf fifo of all the peers */
+static void
+pim_msdp_pkt_sa_push(struct pim_msdp_peer *mp)
+{
+ struct listnode *mpnode;
+
+ if (mp) {
+ pim_msdp_pkt_sa_push_to_one_peer(mp);
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push", mp->key_str);
+ }
+ pim_msdp_pkt_sa_push_to_one_peer(mp);
+ }
+ }
+}
+
+static int
+pim_msdp_pkt_sa_fill_hdr(int local_cnt)
+{
+ int curr_tlv_ecnt;
+
+ stream_reset(msdp->work_obuf);
+ curr_tlv_ecnt = local_cnt>PIM_MSDP_SA_MAX_ENTRY_CNT?PIM_MSDP_SA_MAX_ENTRY_CNT:local_cnt;
+ local_cnt -= curr_tlv_ecnt;
+ stream_putc(msdp->work_obuf, PIM_MSDP_V4_SOURCE_ACTIVE);
+ stream_putw(msdp->work_obuf, PIM_MSDP_SA_ENTRY_CNT2SIZE(curr_tlv_ecnt));
+ stream_putc(msdp->work_obuf, curr_tlv_ecnt);
+ stream_put_ipv4(msdp->work_obuf, msdp->originator_id.s_addr);
+
+ return local_cnt;
+}
+
+static void
+pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa *sa)
+{
+ stream_put3(msdp->work_obuf, 0 /* reserved */);
+ stream_putc(msdp->work_obuf, 32 /* sprefix len */);
+ stream_put_ipv4(msdp->work_obuf, sa->sg.grp.s_addr);
+ stream_put_ipv4(msdp->work_obuf, sa->sg.src.s_addr);
+}
+
+static void
+pim_msdp_pkt_sa_gen(struct pim_msdp_peer *mp)
+{
+ struct listnode *sanode;
+ struct pim_msdp_sa *sa;
+ int sa_count;
+ int local_cnt = msdp->local_cnt;
+
+ sa_count = 0;
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug(" sa gen %d", local_cnt);
+ }
+
+ local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt);
+
+ for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
+ /* current implementation of MSDP is for anycast i.e. full mesh. so
+ * no re-forwarding of SAs that we learnt from other peers */
+ continue;
+ }
+ /* add sa into scratch pad */
+ pim_msdp_pkt_sa_fill_one(sa);
+ ++sa_count;
+ if (sa_count >= PIM_MSDP_SA_MAX_ENTRY_CNT) {
+ pim_msdp_pkt_sa_push(mp);
+ /* reset headers */
+ sa_count = 0;
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug(" sa gen for remainder %d", local_cnt);
+ }
+ local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt);
+ }
+ }
+
+ if (sa_count) {
+ pim_msdp_pkt_sa_push(mp);
+ }
+ return;
+}
+
+static void
+pim_msdp_pkt_sa_tx_done(void)
+{
+ struct listnode *mpnode;
+ struct pim_msdp_peer *mp;
+
+ /* if SA were sent to the peers we restart ka timer and avoid
+ * unnecessary ka noise */
+ for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
+ if (mp->flags & PIM_MSDP_PEERF_SA_JUST_SENT) {
+ mp->flags &= ~PIM_MSDP_PEERF_SA_JUST_SENT;
+ pim_msdp_peer_pkt_txed(mp);
+ }
+ }
+}
+
+void
+pim_msdp_pkt_sa_tx(void)
+{
+ pim_msdp_pkt_sa_gen(NULL /* mp */);
+ pim_msdp_pkt_sa_tx_done();
+}
+
+void
+pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa)
+{
+ pim_msdp_pkt_sa_fill_hdr(1 /* cnt */);
+ pim_msdp_pkt_sa_fill_one(sa);
+ pim_msdp_pkt_sa_push(NULL);
+ pim_msdp_pkt_sa_tx_done();
+}
+
+/* when a connection is first established we push all SAs immediately */
+void
+pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp)
+{
+ pim_msdp_pkt_sa_gen(mp);
+ pim_msdp_pkt_sa_tx_done();
+}
+
+static void
+pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer *mp)
+{
+ pim_msdp_peer_reset_tcp_conn(mp, "invalid-pkt-rx");
+}
+
+static void
+pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len)
+{
+ mp->ka_rx_cnt++;
+ if (len != PIM_MSDP_KA_TLV_MAX_SIZE) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+ pim_msdp_peer_pkt_rxed(mp);
+}
+
+static void
+pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp)
+{
+ int prefix_len;
+ struct prefix_sg sg;
+
+ /* just throw away the three reserved bytes */
+ stream_get3(mp->ibuf);
+ prefix_len = stream_getc(mp->ibuf);
+
+ memset(&sg, 0, sizeof (struct prefix_sg));
+ sg.grp.s_addr = stream_get_ipv4(mp->ibuf);
+ sg.src.s_addr = stream_get_ipv4(mp->ibuf);
+
+ if (prefix_len != 32) {
+ /* ignore SA update if the prefix length is not 32 */
+ zlog_err("rxed sa update with invalid prefix length %d", prefix_len);
+ return;
+ }
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ zlog_debug(" sg %s", pim_str_sg_dump(&sg));
+ }
+ pim_msdp_sa_ref(mp, &sg, rp);
+}
+
+static void
+pim_msdp_pkt_sa_rx(struct pim_msdp_peer *mp, int len)
+{
+ int entry_cnt;
+ int i;
+ struct in_addr rp; /* Last RP address associated with this SA */
+
+ mp->sa_rx_cnt++;
+
+ if (len < PIM_MSDP_SA_TLV_MIN_SIZE) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+
+ entry_cnt = stream_getc(mp->ibuf);
+ /* some vendors include the actual multicast data in the tlv (at the end).
+ * we will ignore such data. in the future we may consider pushing it down
+ * the RPT */
+ if (len < PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt)) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+ rp.s_addr = stream_get_ipv4(mp->ibuf);
+
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ char rp_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<rp?>", rp, rp_str, sizeof(rp_str));
+ zlog_debug(" entry_cnt %d rp %s", entry_cnt, rp_str);
+ }
+
+ if (!pim_msdp_peer_rpf_check(mp, rp)) {
+ /* if peer-RPF check fails don't process the packet any further */
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ zlog_debug(" peer RPF check failed");
+ }
+ return;
+ }
+
+ pim_msdp_peer_pkt_rxed(mp);
+
+ /* update SA cache */
+ for (i = 0; i < entry_cnt; ++i) {
+ pim_msdp_pkt_sa_rx_one(mp, rp);
+ }
+}
+
+static void
+pim_msdp_pkt_rx(struct pim_msdp_peer *mp)
+{
+ enum pim_msdp_tlv type;
+ int len;
+
+ /* re-read type and len */
+ type = stream_getc_from(mp->ibuf, 0);
+ len = stream_getw_from(mp->ibuf, 1);
+ if (len < PIM_MSDP_HEADER_SIZE) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+
+ if (len > PIM_MSDP_SA_TLV_MAX_SIZE) {
+ /* if tlv size if greater than max just ignore the tlv */
+ return;
+ }
+
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ pim_msdp_pkt_dump(mp, type, len, true /*rx*/, NULL /*s*/);
+ }
+
+ switch(type) {
+ case PIM_MSDP_KEEPALIVE:
+ pim_msdp_pkt_ka_rx(mp, len);
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ mp->sa_rx_cnt++;
+ pim_msdp_pkt_sa_rx(mp, len);
+ break;
+ default:
+ mp->unk_rx_cnt++;
+ }
+}
+
+/* pim msdp read utility function. */
+static int
+pim_msdp_read_packet(struct pim_msdp_peer *mp)
+{
+ int nbytes;
+ int readsize;
+ int old_endp;
+ int new_endp;
+
+ old_endp = stream_get_endp(mp->ibuf);
+ readsize = mp->packet_size - old_endp;
+ if (!readsize) {
+ return 0;
+ }
+
+ /* Read packet from fd */
+ nbytes = stream_read_try(mp->ibuf, mp->fd, readsize);
+ new_endp = stream_get_endp(mp->ibuf);
+ if (nbytes < 0) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes);
+ }
+ if (nbytes == -2) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_read io retry old_end: %d new_end: %d", mp->key_str, old_endp, new_endp);
+ }
+ /* transient error retry */
+ return -1;
+ }
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return -1;
+ }
+
+ if (!nbytes) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes);
+ }
+ pim_msdp_peer_reset_tcp_conn(mp, "peer-down");
+ return -1;
+ }
+
+ /* We read partial packet. */
+ if (stream_get_endp(mp->ibuf) != mp->packet_size) {
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s read partial len %d old_endp %d new_endp %d", mp->key_str, mp->packet_size, old_endp, new_endp);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+pim_msdp_read(struct thread *thread)
+{
+ struct pim_msdp_peer *mp;
+ int rc;
+ uint32_t len;
+
+ mp = THREAD_ARG(thread);
+ mp->t_read = NULL;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s pim_msdp_read", mp->key_str);
+ }
+
+ if (mp->fd < 0) {
+ return -1;
+ }
+
+ /* check if TCP connection is established */
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ pim_msdp_connect_check(mp);
+ return 0;
+ }
+
+ PIM_MSDP_PEER_READ_ON(mp);
+
+ if (!mp->packet_size) {
+ mp->packet_size = PIM_MSDP_HEADER_SIZE;
+ }
+
+ if (stream_get_endp(mp->ibuf) < PIM_MSDP_HEADER_SIZE) {
+ /* start by reading the TLV header */
+ rc = pim_msdp_read_packet(mp);
+ if (rc < 0) {
+ goto pim_msdp_read_end;
+ }
+
+ /* Find TLV type and len */
+ stream_getc(mp->ibuf);
+ len = stream_getw(mp->ibuf);
+ if (len < PIM_MSDP_HEADER_SIZE) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ goto pim_msdp_read_end;
+ }
+ /* read complete TLV */
+ mp->packet_size = len;
+ }
+
+ rc = pim_msdp_read_packet(mp);
+ if (rc < 0) {
+ goto pim_msdp_read_end;
+ }
+
+ pim_msdp_pkt_rx(mp);
+
+ /* reset input buffers and get ready for the next packet */
+ mp->packet_size = 0;
+ stream_reset(mp->ibuf);
+
+pim_msdp_read_end:
+ return 0;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * IP MSDP socket management
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <lib/log.h>
+#include <lib/network.h>
+#include <lib/sockunion.h>
+#include <lib/thread.h>
+#include <lib/vty.h>
+
+#include "pimd.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_socket.h"
+
+extern struct zebra_privs_t pimd_privs;
+
+/* increase socket send buffer size */
+static void
+pim_msdp_update_sock_send_buffer_size (int fd)
+{
+ int size = PIM_MSDP_SOCKET_SNDBUF_SIZE;
+ int optval;
+ socklen_t optlen = sizeof(optval);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) {
+ zlog_err("getsockopt of SO_SNDBUF failed %s\n", safe_strerror(errno));
+ return;
+ }
+
+ if (optval < size) {
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) {
+ zlog_err("Couldn't increase send buffer: %s\n", safe_strerror(errno));
+ }
+ }
+}
+
+/* passive peer socket accept */
+static int
+pim_msdp_sock_accept(struct thread *thread)
+{
+ union sockunion su;
+ struct pim_msdp_listener *listener = THREAD_ARG(thread);
+ int accept_sock;
+ int msdp_sock;
+ struct pim_msdp_peer *mp;
+ char buf[SU_ADDRSTRLEN];
+
+ sockunion_init(&su);
+
+ /* re-register accept thread */
+ accept_sock = THREAD_FD(thread);
+ if (accept_sock < 0) {
+ zlog_err ("accept_sock is negative value %d", accept_sock);
+ return -1;
+ }
+ listener->thread = thread_add_read(master, pim_msdp_sock_accept,
+ listener, accept_sock);
+
+ /* accept client connection. */
+ msdp_sock = sockunion_accept(accept_sock, &su);
+ if (msdp_sock < 0) {
+ zlog_err ("pim_msdp_sock_accept failed (%s)", safe_strerror (errno));
+ return -1;
+ }
+
+ /* see if have peer config for this */
+ mp = pim_msdp_peer_find(su.sin.sin_addr);
+ if (!mp || !PIM_MSDP_PEER_IS_LISTENER(mp)) {
+ ++msdp->rejected_accepts;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_err("msdp peer connection refused from %s",
+ sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ }
+ close(msdp_sock);
+ return -1;
+ }
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s accept success%s", mp->key_str, mp->fd>=0?"(dup)":"");
+ }
+
+ /* if we have an existing connection we need to kill that one
+ * with this one */
+ if (mp->fd >= 0) {
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_err("msdp peer new connection from %s stop old connection",
+ sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ }
+ pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
+ }
+ mp->fd = msdp_sock;
+ set_nonblocking(mp->fd);
+ pim_msdp_update_sock_send_buffer_size(mp->fd);
+ pim_msdp_peer_established(mp);
+ return 0;
+}
+
+/* global listener for the MSDP well know TCP port */
+int
+pim_msdp_sock_listen(void)
+{
+ int sock;
+ int socklen;
+ struct sockaddr_in sin;
+ int rc;
+ struct pim_msdp_listener *listener = &msdp->listener;
+
+ if (msdp->flags & PIM_MSDPF_LISTENER) {
+ /* listener already setup */
+ return 0;
+ }
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ zlog_err ("socket: %s", safe_strerror (errno));
+ return sock;
+ }
+
+ memset(&sin, 0, sizeof(struct sockaddr_in));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(PIM_MSDP_TCP_PORT);
+ socklen = sizeof(struct sockaddr_in);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin.sin_len = socklen;
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+ sockopt_reuseaddr(sock);
+ sockopt_reuseport(sock);
+
+ if (pimd_privs.change(ZPRIVS_RAISE)) {
+ zlog_err ("pim_msdp_socket: could not raise privs, %s",
+ safe_strerror (errno));
+ }
+
+ /* bind to well known TCP port */
+ rc = bind(sock, (struct sockaddr *)&sin, socklen);
+
+ if (pimd_privs.change(ZPRIVS_LOWER)) {
+ zlog_err ("pim_msdp_socket: could not lower privs, %s",
+ safe_strerror (errno));
+ }
+
+ if (rc < 0) {
+ zlog_err ("pim_msdp_socket bind to port %d: %s", ntohs(sin.sin_port), safe_strerror (errno));
+ close(sock);
+ return rc;
+ }
+
+ rc = listen(sock, 3 /* backlog */);
+ if (rc < 0) {
+ zlog_err ("pim_msdp_socket listen: %s", safe_strerror (errno));
+ close(sock);
+ return rc;
+ }
+
+ /* add accept thread */
+ listener->fd = sock;
+ memcpy(&listener->su, &sin, socklen);
+ listener->thread = thread_add_read(msdp->master, pim_msdp_sock_accept, listener, sock);
+
+ msdp->flags |= PIM_MSDPF_LISTENER;
+ return 0;
+}
+
+/* active peer socket setup */
+int
+pim_msdp_sock_connect(struct pim_msdp_peer *mp)
+{
+ int rc;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ zlog_debug("MSDP peer %s attempt connect%s", mp->key_str, mp->fd<0?"":"(dup)");
+ }
+
+ /* if we have an existing connection we need to kill that one
+ * with this one */
+ if (mp->fd >= 0) {
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ zlog_err("msdp duplicate connect to %s nuke old connection", mp->key_str);
+ }
+ pim_msdp_peer_stop_tcp_conn(mp, false /* chg_state */);
+ }
+
+ /* Make socket for the peer. */
+ mp->fd = sockunion_socket(&mp->su_peer);
+ if (mp->fd < 0) {
+ zlog_err ("pim_msdp_socket socket failure: %s", safe_strerror (errno));
+ return -1;
+ }
+
+ set_nonblocking(mp->fd);
+
+ /* Set socket send buffer size */
+ pim_msdp_update_sock_send_buffer_size(mp->fd);
+ sockopt_reuseaddr(mp->fd);
+ sockopt_reuseport(mp->fd);
+
+ /* source bind */
+ rc = sockunion_bind(mp->fd, &mp->su_local, 0, &mp->su_local);
+ if (rc < 0) {
+ zlog_err ("pim_msdp_socket connect bind failure: %s", safe_strerror (errno));
+ close(mp->fd);
+ mp->fd = -1;
+ return rc;
+ }
+
+ /* Connect to the remote mp. */
+ return (sockunion_connect(mp->fd, &mp->su_peer, htons(PIM_MSDP_TCP_PORT), 0));
+}
+
--- /dev/null
+/*
+ * 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
#include <zebra.h>
#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
#include "pimd.h"
+#include "pim_vty.h"
#include "pim_pim.h"
#include "pim_msg.h"
#include "pim_util.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_rp.h"
+#include "pim_rpf.h"
void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size,
uint8_t pim_msg_type)
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;
buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
buf[1] = '\0'; /* native encoding */
- buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */
+ buf[2] = bits;
buf[3] = 32; /* mask len */
memcpy(buf+4, &addr, sizeof(struct in_addr));
return buf + ENCODED_IPV4_SOURCE_SIZE;
}
+
+int
+pim_msg_join_prune_encode (uint8_t *buf, int buf_size, int is_join,
+ struct pim_upstream *up,
+ struct in_addr upstream, int holdtime)
+{
+ uint8_t *pim_msg = buf;
+ uint8_t *pim_msg_curr = buf + PIM_MSG_HEADER_LEN;
+ uint8_t *end = buf + buf_size;
+ uint16_t *prunes = NULL;
+ uint16_t *joins = NULL;
+ struct in_addr stosend;
+ uint8_t bits;
+ int remain;
+
+ remain = end - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_ucast (pim_msg_curr, buf_size - PIM_MSG_HEADER_LEN, upstream);
+ if (!pim_msg_curr) {
+ char dst_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<dst?>", upstream, dst_str, sizeof(dst_str));
+ zlog_warn("%s: failure encoding destination address %s: space left=%d",
+ __PRETTY_FUNCTION__, dst_str, remain);
+ return -3;
+ }
+
+ remain = end - pim_msg_curr;
+ if (remain < 4) {
+ zlog_warn("%s: group will not fit: space left=%d",
+ __PRETTY_FUNCTION__, remain);
+ return -4;
+ }
+
+ *pim_msg_curr = 0; /* reserved */
+ ++pim_msg_curr;
+ *pim_msg_curr = 1; /* number of groups */
+ ++pim_msg_curr;
+
+ *((uint16_t *) pim_msg_curr) = htons(holdtime);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = end - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_group (pim_msg_curr, remain,
+ up->sg.grp);
+ if (!pim_msg_curr) {
+ char group_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<grp?>", up->sg.grp, group_str, sizeof(group_str));
+ zlog_warn("%s: failure encoding group address %s: space left=%d",
+ __PRETTY_FUNCTION__, group_str, remain);
+ return -5;
+ }
+
+ remain = end - pim_msg_curr;
+ if (remain < 4) {
+ zlog_warn("%s: sources will not fit: space left=%d",
+ __PRETTY_FUNCTION__, remain);
+ return -6;
+ }
+
+ /* number of joined sources */
+ joins = (uint16_t *)pim_msg_curr;
+ *joins = htons(is_join ? 1 : 0);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ /* number of pruned sources */
+ prunes = (uint16_t *)pim_msg_curr;
+ *prunes = htons(is_join ? 0 : 1);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = end - pim_msg_curr;
+ if (up->sg.src.s_addr == INADDR_ANY)
+ {
+ struct pim_rpf *rpf = pim_rp_g (up->sg.grp);
+ bits = PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_WC_BIT | PIM_ENCODE_RPT_BIT;
+ stosend = rpf->rpf_addr.u.prefix4;
+ }
+ else
+ {
+ bits = PIM_ENCODE_SPARSE_BIT;
+ stosend = up->sg.src;
+ }
+ pim_msg_curr = pim_msg_addr_encode_ipv4_source (pim_msg_curr, remain, stosend, bits);
+ if (!pim_msg_curr) {
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", up->sg.src, source_str, sizeof(source_str));
+ zlog_warn("%s: failure encoding source address %s: space left=%d",
+ __PRETTY_FUNCTION__, source_str, remain);
+ return -7;
+ }
+ remain = pim_msg_curr - pim_msg;
+
+ /*
+ * This is not implemented correctly at this point in time
+ * Make it stop.
+ */
+#if 0
+ if (up->sg.src.s_addr == INADDR_ANY)
+ {
+ struct pim_upstream *child;
+ struct listnode *up_node;
+ int send_prune = 0;
+
+ zlog_debug ("%s: Considering (%s) children for (S,G,rpt) prune",
+ __PRETTY_FUNCTION__, up->sg_str);
+ for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
+ {
+ if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
+ {
+ if (!pim_rpf_is_same(&up->rpf, &child->rpf))
+ {
+ send_prune = 1;
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message",
+ __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+ }
+ else
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)",
+ __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+ }
+ else if (pim_upstream_is_sg_rpt (child))
+ {
+ if (pim_upstream_empty_inherited_olist (child))
+ {
+ send_prune = 1;
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message",
+ __PRETTY_FUNCTION__, child->sg_str);
+ }
+ else if (!pim_rpf_is_same (&up->rpf, &child->rpf))
+ {
+ send_prune = 1;
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message",
+ __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+ }
+ else
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message",
+ __PRETTY_FUNCTION__, up->sg_str, child->sg_str);
+ }
+ else
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("%s: SPT bit is not set for (%s)",
+ __PRETTY_FUNCTION__, child->sg_str);
+ if (send_prune)
+ {
+ pim_msg_curr = pim_msg_addr_encode_ipv4_source (pim_msg_curr, remain,
+ child->sg.src,
+ PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_RPT_BIT);
+ remain = pim_msg_curr - pim_msg;
+ *prunes = htons(ntohs(*prunes) + 1);
+ send_prune = 0;
+ }
+ }
+ }
+#endif
+ pim_msg_build_header (pim_msg, remain, PIM_MSG_TYPE_JOIN_PRUNE);
+
+ return remain;
+}
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 */
#include "prefix.h"
#include "memory.h"
#include "if.h"
+#include "vty.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_neighbor.h"
#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)
{
if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) {
if (PIM_DEBUG_PIM_EVENTS) {
- char dr_old_str[100];
- char dr_new_str[100];
+ char dr_old_str[INET_ADDRSTRLEN];
+ char dr_new_str[INET_ADDRSTRLEN];
pim_inet4_dump("<old_dr?>", old_dr_addr, dr_old_str, sizeof(dr_old_str));
pim_inet4_dump("<new_dr?>", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str));
zlog_debug("%s: DR was %s now is %s on interface %s",
struct interface *ifp;
char msg[100];
- zassert(t);
neigh = THREAD_ARG(t);
- zassert(neigh);
ifp = neigh->interface;
if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s",
neigh->holdtime, src_str, ifp->name);
}
- neigh->t_expire_timer = 0;
+ neigh->t_expire_timer = NULL;
snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime);
pim_neighbor_delete(ifp, neigh, msg);
return 0;
}
-static void neighbor_timer_off(struct pim_neighbor *neigh)
-{
- if (PIM_DEBUG_PIM_TRACE_DETAIL) {
- if (neigh->t_expire_timer) {
- char src_str[100];
- pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
- zlog_debug("%s: cancelling timer for neighbor %s on %s",
- __PRETTY_FUNCTION__,
- src_str, neigh->interface->name);
- }
- }
- THREAD_OFF(neigh->t_expire_timer);
- zassert(!neigh->t_expire_timer);
-}
-
void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime)
{
neigh->holdtime = holdtime;
- neighbor_timer_off(neigh);
+ THREAD_OFF(neigh->t_expire_timer);
/*
0xFFFF is request for no holdtime
}
if (PIM_DEBUG_PIM_TRACE_DETAIL) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
zlog_debug("%s: starting %u sec timer for neighbor %s on %s",
__PRETTY_FUNCTION__,
{
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;
}
neigh->dr_priority = dr_priority;
neigh->generation_id = generation_id;
neigh->prefix_list = addr_list;
- neigh->t_expire_timer = 0;
+ neigh->t_expire_timer = NULL;
neigh->interface = ifp;
pim_neighbor_timer_reset(neigh, holdtime);
+ /*
+ * The pim_ifstat_hello_sent variable is used to decide if
+ * we should expedite a hello out the interface. If we
+ * establish a new neighbor, we unfortunately need to
+ * reset the value so that we can know to hurry up and
+ * hello
+ */
+ pim_ifp->pim_ifstat_hello_sent = 0;
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
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) {
}
}
- 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,
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;
message with a new GenID is received from an existing neighbor, a
new Hello message should be sent on this interface after a
randomized delay between 0 and Triggered_Hello_Delay.
+
+ This is a bit silly to do it that way. If I get a new
+ genid we need to send the hello *now* because we've
+ lined up a bunch of join/prune messages to go out the
+ interface.
*/
- pim_hello_restart_triggered(neigh->interface);
+ if (send_hello_now)
+ pim_hello_restart_now (ifp);
+ else
+ pim_hello_restart_triggered(neigh->interface);
+
+ pim_upstream_find_new_rpf();
+ pim_rp_setup ();
+
+ pim_neighbor_rpf_update();
return neigh;
}
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);
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);
listnode_delete(pim_ifp->pim_neighbor_list, neigh);
pim_neighbor_free(neigh);
+
+ pim_neighbor_rpf_update();
}
void pim_neighbor_delete_all(struct interface *ifp,
{
struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4);
if (p) {
- char addr_str[100];
- char this_neigh_str[100];
- char other_neigh_str[100];
+ char addr_str[INET_ADDRSTRLEN];
+ char this_neigh_str[INET_ADDRSTRLEN];
+ char other_neigh_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr->u.prefix4, addr_str, sizeof(addr_str));
pim_inet4_dump("<neigh1?>", neigh_addr, this_neigh_str, sizeof(this_neigh_str));
#include "if.h"
#include "linklist.h"
+#include "prefix.h"
#include "pim_tlv.h"
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,
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);
#include "memory.h"
#include "linklist.h"
#include "if.h"
+#include "hash.h"
+#include "jhash.h"
#include "pimd.h"
#include "pim_oil.h"
#include "pim_iface.h"
#include "pim_time.h"
+struct list *pim_channel_oil_list = NULL;
+struct hash *pim_channel_oil_hash = NULL;
+
+static int
+pim_channel_oil_compare (struct channel_oil *c1, struct channel_oil *c2)
+{
+ if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
+ return -1;
+
+ if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
+ return 1;
+
+ if (ntohl(c1->oil.mfcc_origin.s_addr) < ntohl(c2->oil.mfcc_origin.s_addr))
+ return -1;
+
+ if (ntohl(c1->oil.mfcc_origin.s_addr) > ntohl(c2->oil.mfcc_origin.s_addr))
+ return 1;
+
+ return 0;
+}
+
+static int
+pim_oil_equal (const void *arg1, const void *arg2)
+{
+ const struct channel_oil *c1 = (const struct channel_oil *)arg1;
+ const struct channel_oil *c2 = (const struct channel_oil *)arg2;
+
+ if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr) &&
+ (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
+ return 1;
+
+ return 0;
+}
+
+static unsigned int
+pim_oil_hash_key (void *arg)
+{
+ struct channel_oil *oil = (struct channel_oil *)arg;
+
+ return jhash_2words (oil->oil.mfcc_mcastgrp.s_addr, oil->oil.mfcc_origin.s_addr, 0);
+}
+
+void
+pim_oil_init (void)
+{
+ pim_channel_oil_hash = hash_create_size (8192, pim_oil_hash_key,
+ pim_oil_equal);
+
+ pim_channel_oil_list = list_new();
+ if (!pim_channel_oil_list) {
+ zlog_err("%s %s: failure: channel_oil_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return;
+ }
+ pim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
+ pim_channel_oil_list->cmp = (int (*)(void *, void *)) pim_channel_oil_compare;
+}
+
+void
+pim_oil_terminate (void)
+{
+ if (pim_channel_oil_list)
+ list_free(pim_channel_oil_list);
+ pim_channel_oil_list = NULL;
+
+ if (pim_channel_oil_hash)
+ hash_free (pim_channel_oil_hash);
+ pim_channel_oil_hash = NULL;
+}
+
void pim_channel_oil_free(struct channel_oil *c_oil)
{
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
}
-static void pim_channel_oil_delete(struct channel_oil *c_oil)
+static void
+pim_del_channel_oil (struct channel_oil *c_oil)
{
/*
notice that listnode_delete() can't be moved
into pim_channel_oil_free() because the later is
called by list_delete_all_node()
*/
- listnode_delete(qpim_channel_oil_list, c_oil);
+ listnode_delete(pim_channel_oil_list, c_oil);
+ hash_release (pim_channel_oil_hash, c_oil);
pim_channel_oil_free(c_oil);
}
-static struct channel_oil *channel_oil_new(struct in_addr group_addr,
- struct in_addr source_addr,
- int input_vif_index)
+static struct channel_oil *
+pim_add_channel_oil (struct prefix_sg *sg,
+ int input_vif_index)
{
struct channel_oil *c_oil;
- struct interface *ifp_in;
+ struct interface *ifp;
- ifp_in = pim_if_find_by_vif_index(input_vif_index);
- if (!ifp_in) {
+ ifp = pim_if_find_by_vif_index(input_vif_index);
+ if (!ifp) {
/* warning only */
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source_addr, source_str, sizeof(source_str));
- zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+ zlog_warn("%s: (S,G)=%s could not find input interface for input_vif_index=%d",
__PRETTY_FUNCTION__,
- source_str, group_str, input_vif_index);
+ pim_str_sg_dump (sg), input_vif_index);
}
c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
if (!c_oil) {
zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
- return 0;
+ return NULL;
}
- c_oil->oil.mfcc_mcastgrp = group_addr;
- c_oil->oil.mfcc_origin = source_addr;
+ c_oil->oil.mfcc_mcastgrp = sg->grp;
+ c_oil->oil.mfcc_origin = sg->src;
+ c_oil = hash_get (pim_channel_oil_hash, c_oil, hash_alloc_intern);
+
c_oil->oil.mfcc_parent = input_vif_index;
c_oil->oil_ref_count = 1;
c_oil->installed = 0;
- zassert(c_oil->oil_size == 0);
+ listnode_add_sort(pim_channel_oil_list, c_oil);
return c_oil;
}
-static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr,
- struct in_addr source_addr,
- int input_vif_index)
+static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg)
{
- struct channel_oil *c_oil;
+ struct channel_oil *c_oil = NULL;
+ struct channel_oil lookup;
- c_oil = channel_oil_new(group_addr, source_addr, input_vif_index);
- if (!c_oil) {
- zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
- return 0;
- }
+ lookup.oil.mfcc_mcastgrp = sg->grp;
+ lookup.oil.mfcc_origin = sg->src;
- listnode_add(qpim_channel_oil_list, c_oil);
+ c_oil = hash_lookup (pim_channel_oil_hash, &lookup);
return c_oil;
}
-static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr,
- struct in_addr source_addr)
-{
- struct listnode *node;
- struct channel_oil *c_oil;
-
- for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
- if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
- (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr))
- return c_oil;
- }
-
- return 0;
-}
-
-struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
- struct in_addr source_addr,
+struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
int input_vif_index)
{
struct channel_oil *c_oil;
- c_oil = pim_find_channel_oil(group_addr, source_addr);
+ c_oil = pim_find_channel_oil(sg);
if (c_oil) {
+ if (c_oil->oil.mfcc_parent != input_vif_index)
+ {
+ c_oil->oil_inherited_rescan = 1;
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug ("%s: Existing channel oil %s points to %d, modifying to point at %d",
+ __PRETTY_FUNCTION__, pim_str_sg_dump(sg), c_oil->oil.mfcc_parent, input_vif_index);
+ }
+ c_oil->oil.mfcc_parent = input_vif_index;
++c_oil->oil_ref_count;
return c_oil;
}
- return pim_add_channel_oil(group_addr, source_addr, input_vif_index);
+ return pim_add_channel_oil(sg, input_vif_index);
}
void pim_channel_oil_del(struct channel_oil *c_oil)
--c_oil->oil_ref_count;
if (c_oil->oil_ref_count < 1) {
- pim_channel_oil_delete(c_oil);
+ pim_del_channel_oil(c_oil);
}
}
+int
+pim_channel_del_oif (struct channel_oil *channel_oil,
+ struct interface *oif,
+ uint32_t proto_mask)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert (channel_oil);
+ zassert (oif);
+
+ pim_ifp = oif->info;
+
+ /*
+ * Don't do anything if we've been asked to remove a source
+ * that is not actually on it.
+ */
+ if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask))
+ {
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, channel_oil->oif_flags[pim_ifp->mroute_vif_index],
+ oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ }
+ return 0;
+ }
+
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
+
+ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index])
+ {
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ }
+ return 0;
+ }
+
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+
+ if (pim_mroute_add (channel_oil, __PRETTY_FUNCTION__)) {
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+ }
+ return -1;
+ }
+
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index);
+ }
+
+ return 0;
+}
+
+
int pim_channel_add_oif(struct channel_oil *channel_oil,
struct interface *oif,
uint32_t proto_mask)
struct pim_interface *pim_ifp;
int old_ttl;
- zassert(channel_oil);
+ /*
+ * If we've gotten here we've gone bad, but let's
+ * not take down pim
+ */
+ if (!channel_oil)
+ {
+ zlog_warn ("Attempt to Add OIF for non-existent channel oil");
+ return -1;
+ }
pim_ifp = oif->info;
- if (PIM_DEBUG_MROUTE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
- __FILE__, __PRETTY_FUNCTION__,
- source_str, group_str,
- proto_mask, oif->name, pim_ifp->mroute_vif_index);
- }
-
#ifdef PIM_ENFORCE_LOOPFREE_MFC
/*
Prevent creating MFC entry with OIF=IIF.
TODO T22.
*/
if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
+ channel_oil->oil_inherited_rescan = 1;
if (PIM_DEBUG_MROUTE)
{
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
if (PIM_DEBUG_MROUTE)
{
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
if (PIM_DEBUG_MROUTE)
{
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
if (old_ttl > 0) {
if (PIM_DEBUG_MROUTE)
{
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
- if (pim_mroute_add(channel_oil)) {
+ if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE)
{
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
if (PIM_DEBUG_MROUTE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
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));
+}
struct channel_counts
{
+ unsigned long long lastused;
unsigned long pktcnt;
unsigned long oldpktcnt;
unsigned long bytecnt;
struct channel_oil {
struct mfcctl oil;
int installed;
+ int oil_inherited_rescan;
int oil_size;
int oil_ref_count;
time_t oif_creation[MAXVIFS];
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 */
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;
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));
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)
{
struct ip *ip_hdr;
size_t ip_hlen; /* ip header length in bytes */
- char src_str[100];
- char dst_str[100];
+ char src_str[INET_ADDRSTRLEN];
+ char dst_str[INET_ADDRSTRLEN];
uint8_t *pim_msg;
int pim_msg_len;
uint8_t pim_version;
- uint8_t pim_type;
+ enum pim_msg_type pim_type;
uint16_t pim_checksum; /* received checksum */
uint16_t checksum; /* computed checksum */
struct pim_neighbor *neigh;
- if (!ifp->info) {
- zlog_warn("%s: PIM not enabled on interface %s",
- __PRETTY_FUNCTION__, ifp->name);
- return -1;
- }
-
if (len < sizeof(*ip_hdr)) {
- zlog_warn("PIM packet size=%zu shorter than minimum=%zu",
- len, sizeof(*ip_hdr));
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("PIM packet size=%zu shorter than minimum=%zu",
+ len, sizeof(*ip_hdr));
return -1;
}
ip_hdr = (struct ip *) buf;
-
- pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
- pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
-
ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
- if (PIM_DEBUG_PIM_PACKETS) {
- zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
- src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p);
- }
-
if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) {
- zlog_warn("IP packet protocol=%d is not PIM=%d",
- ip_hdr->ip_p, PIM_IP_PROTO_PIM);
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("IP packet protocol=%d is not PIM=%d",
+ ip_hdr->ip_p, PIM_IP_PROTO_PIM);
return -1;
}
if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
- zlog_warn("IP packet header size=%zu shorter than minimum=%d",
- ip_hlen, PIM_IP_HEADER_MIN_LEN);
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("IP packet header size=%zu shorter than minimum=%d",
+ ip_hlen, PIM_IP_HEADER_MIN_LEN);
return -1;
}
if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
- zlog_warn("IP packet header size=%zu greater than maximum=%d",
- ip_hlen, PIM_IP_HEADER_MAX_LEN);
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("IP packet header size=%zu greater than maximum=%d",
+ ip_hlen, PIM_IP_HEADER_MAX_LEN);
return -1;
}
pim_msg = buf + ip_hlen;
pim_msg_len = len - ip_hlen;
- if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
- pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
- }
-
if (pim_msg_len < PIM_PIM_MIN_LEN) {
- zlog_warn("PIM message size=%d shorter than minimum=%d",
- pim_msg_len, PIM_PIM_MIN_LEN);
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("PIM message size=%d shorter than minimum=%d",
+ pim_msg_len, PIM_PIM_MIN_LEN);
return -1;
}
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;
}
checksum = in_cksum(pim_msg, pim_msg_len);
if (checksum != pim_checksum) {
- zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
- ifp->name, pim_checksum, checksum);
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
+ ifp->name, pim_checksum, checksum);
return -1;
}
if (PIM_DEBUG_PIM_PACKETS) {
- zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x",
- src_str, dst_str, ifp->name, ip_hdr->ip_ttl,
- pim_version, pim_type, pim_msg_len, checksum);
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
+ zlog_debug("Recv PIM %s packet from %s to %s on %s: ttl=%d pim_version=%d pim_msg_size=%d checksum=%x",
+ pim_pim_msgtype2str (pim_type), src_str, dst_str, ifp->name,
+ ip_hdr->ip_ttl, pim_version, pim_msg_len, checksum);
+ if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+ pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
+ }
}
- if (pim_type == PIM_MSG_TYPE_REG_STOP ||
- pim_type == PIM_MSG_TYPE_BOOTSTRAP ||
- pim_type == PIM_MSG_TYPE_GRAFT ||
- pim_type == PIM_MSG_TYPE_GRAFT_ACK ||
- pim_type == PIM_MSG_TYPE_CANDIDATE)
+ switch (pim_type)
{
+ case PIM_MSG_TYPE_HELLO:
+ return pim_hello_recv (ifp,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ break;
+ case PIM_MSG_TYPE_REGISTER:
+ return pim_register_recv (ifp,
+ ip_hdr->ip_dst,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ break;
+ case PIM_MSG_TYPE_REG_STOP:
+ return pim_register_stop_recv (pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ break;
+ case PIM_MSG_TYPE_JOIN_PRUNE:
+ neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
+ if (!neigh) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ pim_type, src_str, ifp->name);
+ return -1;
+ }
+ pim_neighbor_timer_reset(neigh, neigh->holdtime);
+ return pim_joinprune_recv(ifp, neigh,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ break;
+ case PIM_MSG_TYPE_ASSERT:
+ neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
+ if (!neigh) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ pim_type, src_str, ifp->name);
+ return -1;
+ }
+ pim_neighbor_timer_reset(neigh, neigh->holdtime);
+ return pim_assert_recv(ifp, neigh,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ break;
+ default:
if (PIM_DEBUG_PIM_PACKETS) {
zlog_debug("Recv PIM packet type %d which is not currently understood",
pim_type);
}
return -1;
}
-
- if (pim_type == PIM_MSG_TYPE_HELLO) {
- return pim_hello_recv(ifp,
- ip_hdr->ip_src,
- pim_msg + PIM_MSG_HEADER_LEN,
- pim_msg_len - PIM_MSG_HEADER_LEN);
- } else if (pim_type == PIM_MSG_TYPE_REGISTER) {
- return pim_register_recv(ifp,
- ip_hdr->ip_dst,
- ip_hdr->ip_src,
- pim_msg + PIM_MSG_HEADER_LEN,
- pim_msg_len - PIM_MSG_HEADER_LEN);
- }
-
- neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
- if (!neigh) {
- zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
- __FILE__, __PRETTY_FUNCTION__,
- pim_type, src_str, ifp->name);
- return -1;
- }
-
- switch (pim_type) {
- case PIM_MSG_TYPE_JOIN_PRUNE:
- return pim_joinprune_recv(ifp, neigh,
- ip_hdr->ip_src,
- pim_msg + PIM_MSG_HEADER_LEN,
- pim_msg_len - PIM_MSG_HEADER_LEN);
- break;
- case PIM_MSG_TYPE_ASSERT:
- return pim_assert_recv(ifp, neigh,
- ip_hdr->ip_src,
- pim_msg + PIM_MSG_HEADER_LEN,
- pim_msg_len - PIM_MSG_HEADER_LEN);
- break;
- default:
- zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s",
- __FILE__, __PRETTY_FUNCTION__,
- pim_type, src_str, ifp->name);
- break;
- }
-
return -1;
}
int len;
ifindex_t ifindex = -1;
int result = -1; /* defaults to bad */
-
- zassert(t);
+ static long long count = 0;
+ int cont = 1;
ifp = THREAD_ARG(t);
- zassert(ifp);
-
fd = THREAD_FD(t);
pim_ifp = ifp->info;
- zassert(pim_ifp);
-
- zassert(fd == pim_ifp->pim_sock_fd);
- len = pim_socket_recvfromto(fd, buf, sizeof(buf),
- &from, &fromlen,
- &to, &tolen,
- &ifindex);
- if (len < 0) {
- zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s",
- fd, errno, safe_strerror(errno));
- goto done;
- }
-
- if (PIM_DEBUG_PIM_PACKETS) {
- char from_str[100];
- char to_str[100];
-
- if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
- sprintf(from_str, "<from?>");
- if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
- sprintf(to_str, "<to?>");
-
- zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
- len, from_str, to_str, fd, ifindex, ifp->ifindex);
- }
-
- if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
- pim_pkt_dump(__PRETTY_FUNCTION__, buf, len);
- }
+ while (cont)
+ {
+ len = pim_socket_recvfromto(fd, buf, sizeof(buf),
+ &from, &fromlen,
+ &to, &tolen,
+ &ifindex);
+ if (len < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ cont = 0;
+ break;
+ }
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug ("Received errno: %d %s", errno, safe_strerror (errno));
+ goto done;
+ }
#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
- /* ifindex sanity check */
- if (ifindex != (int) ifp->ifindex) {
- char from_str[100];
- char to_str[100];
- struct interface *recv_ifp;
-
- if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
- sprintf(from_str, "<from?>");
- if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
- sprintf(to_str, "<to?>");
-
- recv_ifp = if_lookup_by_index(ifindex);
- if (recv_ifp) {
- zassert(ifindex == (int) recv_ifp->ifindex);
- }
+ /* ifindex sanity check */
+ if (ifindex != (int) ifp->ifindex) {
+ char from_str[INET_ADDRSTRLEN];
+ char to_str[INET_ADDRSTRLEN];
+ struct interface *recv_ifp;
+
+ if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
+ sprintf(from_str, "<from?>");
+ if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
+ sprintf(to_str, "<to?>");
+
+ recv_ifp = if_lookup_by_index(ifindex);
+ if (recv_ifp) {
+ zassert(ifindex == (int) recv_ifp->ifindex);
+ }
#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
- zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
- from_str, to_str, fd,
- ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
- ifp->ifindex, ifp->name);
+ zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
+ from_str, to_str, fd,
+ ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
+ ifp->ifindex, ifp->name);
#endif
- goto done;
- }
+ goto done;
+ }
#endif
- int fail = pim_pim_packet(ifp, buf, len);
- if (fail) {
- if (PIM_DEBUG_PIM_PACKETS)
- zlog_debug("%s: pim_pim_packet() return=%d",
- __PRETTY_FUNCTION__, fail);
- goto done;
- }
+ int fail = pim_pim_packet(ifp, buf, len);
+ if (fail) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug("%s: pim_pim_packet() return=%d",
+ __PRETTY_FUNCTION__, fail);
+ goto done;
+ }
+
+ count++;
+ if (count % qpim_packet_process == 0)
+ cont = 0;
+ }
result = 0; /* good */
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);
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;
pim_ifstat_reset(ifp);
}
-int pim_msg_send(int fd,
- struct in_addr dst,
- uint8_t *pim_msg,
- int pim_msg_size,
- const char *ifname)
+static uint16_t ip_id = 0;
+
+
+static int
+pim_msg_send_frame (int fd, char *buf, size_t len,
+ struct sockaddr *dst, size_t salen)
+{
+ struct ip *ip = (struct ip *)buf;
+
+ while (sendto (fd, buf, len, MSG_DONTWAIT, dst, salen) < 0)
+ {
+ char dst_str[INET_ADDRSTRLEN];
+
+ switch (errno)
+ {
+ case EMSGSIZE:
+ {
+ size_t hdrsize = sizeof (struct ip);
+ size_t newlen1 = ((len - hdrsize) / 2 ) & 0xFFF8;
+ size_t sendlen = newlen1 + hdrsize;
+ size_t offset = ntohs (ip->ip_off);
+
+ ip->ip_len = htons (sendlen);
+ ip->ip_off = htons (offset | IP_MF);
+ if (pim_msg_send_frame (fd, buf, sendlen, dst, salen) == 0)
+ {
+ struct ip *ip2 = (struct ip *)(buf + newlen1);
+ size_t newlen2 = len - sendlen;
+ sendlen = newlen2 + hdrsize;
+
+ memcpy (ip2, ip, hdrsize);
+ ip2->ip_len = htons (sendlen);
+ ip2->ip_off = htons (offset + (newlen1 >> 3));
+ return pim_msg_send_frame (fd, (char *)ip2, sendlen, dst, salen);
+ }
+ }
+
+ return -1;
+ break;
+ default:
+ if (PIM_DEBUG_PIM_PACKETS)
+ {
+ pim_inet4_dump ("<dst?>", ip->ip_dst, dst_str, sizeof (dst_str));
+ zlog_warn ("%s: sendto() failure to %s: fd=%d msg_size=%zd: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ dst_str, fd, len,
+ errno, safe_strerror(errno));
+ }
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+pim_msg_send(int fd, struct in_addr src,
+ struct in_addr dst, uint8_t *pim_msg,
+ int pim_msg_size, const char *ifname)
{
- ssize_t sent;
struct sockaddr_in to;
socklen_t tolen;
+ unsigned char buffer[10000];
+ unsigned char *msg_start;
+ struct ip *ip;
+
+ memset (buffer, 0, 10000);
+ int sendlen = sizeof (struct ip) + pim_msg_size;
+
+ msg_start = buffer + sizeof (struct ip);
+ memcpy (msg_start, pim_msg, pim_msg_size);
+
+ ip = (struct ip *)buffer;
+ ip->ip_id = htons (++ip_id);
+ ip->ip_hl = 5;
+ ip->ip_v = 4;
+ ip->ip_p = PIM_IP_PROTO_PIM;
+ ip->ip_src = src;
+ ip->ip_dst = dst;
+ ip->ip_ttl = MAXTTL;
+ ip->ip_len = htons (sendlen);
if (PIM_DEBUG_PIM_PACKETS) {
- char dst_str[100];
+ char dst_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x",
__PRETTY_FUNCTION__,
pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size);
}
- sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT,
- (struct sockaddr *)&to, tolen);
- if (sent != (ssize_t) pim_msg_size) {
- int e = errno;
- char dst_str[100];
- pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
- if (sent < 0) {
- zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s",
- __PRETTY_FUNCTION__,
- dst_str, ifname, fd, pim_msg_size,
- e, safe_strerror(e));
- }
- else {
- zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd",
- __PRETTY_FUNCTION__,
- dst_str, ifname, fd,
- pim_msg_size, sent);
- }
- return -1;
- }
-
+ pim_msg_send_frame (fd, (char *)buffer, sendlen,
+ (struct sockaddr *)&to, tolen);
return 0;
}
pim_ifp = ifp->info;
if (PIM_DEBUG_PIM_HELLO) {
- char dst_str[100];
+ char dst_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str));
zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
__PRETTY_FUNCTION__,
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,
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);
/*
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);
}
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",
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;
}
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();
/*
#define PIM_PIM_BUFSIZE_READ (20000)
#define PIM_PIM_BUFSIZE_WRITE (20000)
-#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20)
-
#define PIM_DEFAULT_HELLO_PERIOD (30) /* seconds, RFC 4601: 4.11 */
#define PIM_DEFAULT_TRIGGERED_HELLO_DELAY (5) /* seconds, RFC 4601: 4.11 */
#define PIM_DEFAULT_DR_PRIORITY (1) /* RFC 4601: 4.3.1 */
#define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0) /* boolean */
#define PIM_DEFAULT_T_PERIODIC (60) /* RFC 4601: 4.11. Timer Values */
-#define PIM_MSG_TYPE_HELLO (0)
-#define PIM_MSG_TYPE_REGISTER (1)
-#define PIM_MSG_TYPE_REG_STOP (2)
-#define PIM_MSG_TYPE_JOIN_PRUNE (3)
-#define PIM_MSG_TYPE_BOOTSTRAP (4)
-#define PIM_MSG_TYPE_ASSERT (5)
-#define PIM_MSG_TYPE_GRAFT (6)
-#define PIM_MSG_TYPE_GRAFT_ACK (7)
-#define PIM_MSG_TYPE_CANDIDATE (8)
+enum pim_msg_type {
+ PIM_MSG_TYPE_HELLO = 0,
+ PIM_MSG_TYPE_REGISTER,
+ PIM_MSG_TYPE_REG_STOP,
+ PIM_MSG_TYPE_JOIN_PRUNE,
+ PIM_MSG_TYPE_BOOTSTRAP,
+ PIM_MSG_TYPE_ASSERT,
+ PIM_MSG_TYPE_GRAFT,
+ PIM_MSG_TYPE_GRAFT_ACK,
+ PIM_MSG_TYPE_CANDIDATE
+};
#define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg)
#define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg)
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,
#include "log.h"
#include "if.h"
#include "thread.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_mroute.h"
#include "pim_oil.h"
#include "pim_zebra.h"
#include "pim_join.h"
+#include "pim_util.h"
struct thread *send_test_packet_timer = NULL;
-/*
- * This seems stupidly expensive. A list lookup. Why is this
- * not a hash?
- */
-static int
-pim_check_is_my_ip_address (struct in_addr dest_addr)
+void
+pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg,
+ struct in_addr src, struct in_addr originator)
{
- /*
- * See if we can short-cut some?
- * This might not make sense if we ever leave a static RP
- * type of configuration.
- * Note - Premature optimization might bite our patooeys' here.
- */
- if (I_am_RP(dest_addr) && (dest_addr.s_addr == qpim_rp.rpf_addr.s_addr))
- return 1;
+ struct pim_interface *pinfo;
+ unsigned char buffer[10000];
+ unsigned int b1length = 0;
+ unsigned int length;
+ uint8_t *b1;
+ struct prefix p;
+
+ if (PIM_DEBUG_PIM_REG)
+ {
+ zlog_debug ("Sending Register stop for %s to %s on %s",
+ pim_str_sg_dump (sg), inet_ntoa(originator), ifp->name);
+ }
- if (if_lookup_exact_address (&dest_addr, AF_INET))
- return 1;
+ memset (buffer, 0, 10000);
+ b1 = (uint8_t *)buffer + PIM_MSG_REGISTER_STOP_LEN;
- return 0;
+ length = pim_encode_addr_group (b1, AFI_IP, 0, 0, sg->grp);
+ b1length += length;
+ b1 += length;
+
+ p.family = AF_INET;
+ p.u.prefix4 = sg->src;
+ p.prefixlen = 32;
+ length = pim_encode_addr_ucast (b1, &p);
+ b1length += length;
+
+ pim_msg_build_header (buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, PIM_MSG_TYPE_REG_STOP);
+
+ pinfo = (struct pim_interface *)ifp->info;
+ if (!pinfo)
+ {
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug ("%s: No pinfo!\n", __PRETTY_FUNCTION__);
+ return;
+ }
+ if (pim_msg_send (pinfo->pim_sock_fd, src, originator,
+ buffer, b1length + PIM_MSG_REGISTER_STOP_LEN,
+ ifp->name))
+ {
+ if (PIM_DEBUG_PIM_TRACE)
+ {
+ zlog_debug ("%s: could not send PIM register stop message on interface %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ }
+ }
}
-static void
-pim_register_stop_send (struct in_addr src)
+int
+pim_register_stop_recv (uint8_t *buf, int buf_size)
{
- return;
+ struct pim_upstream *upstream = NULL;
+ struct prefix source;
+ struct prefix_sg sg;
+ int l;
+
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ l = pim_parse_addr_group (&sg, buf, buf_size);
+ buf += l;
+ buf_size -= l;
+ pim_parse_addr_ucast (&source, buf, buf_size);
+ sg.src = source.u.prefix4;
+
+ upstream = pim_upstream_find (&sg);
+ if (!upstream)
+ {
+ return 0;
+ }
+
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug ("Received Register stop for %s",
+ upstream->sg_str);
+
+ switch (upstream->join_state)
+ {
+ case PIM_UPSTREAM_NOTJOINED:
+ case PIM_UPSTREAM_PRUNE:
+ return 0;
+ break;
+ case PIM_UPSTREAM_JOINED:
+ upstream->join_state = PIM_UPSTREAM_PRUNE;
+ pim_channel_del_oif (upstream->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+ pim_upstream_start_register_stop_timer (upstream, 0);
+ case PIM_UPSTREAM_JOIN_PENDING:
+ upstream->join_state = PIM_UPSTREAM_PRUNE;
+ pim_upstream_start_register_stop_timer (upstream, 0);
+ return 0;
+ break;
+ }
+
+ return 0;
}
void
-pim_register_send (const struct ip *ip_hdr, struct pim_rpf *rpg)
+pim_register_send (const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, struct pim_upstream *up)
{
- unsigned char buffer[3000];
+ unsigned char buffer[10000];
unsigned char *b1;
struct pim_interface *pinfo;
struct interface *ifp;
- uint32_t plen;
+
+ if (PIM_DEBUG_PIM_REG)
+ {
+ char rp_str[INET_ADDRSTRLEN];
+ strcpy (rp_str, inet_ntoa (rpg->rpf_addr.u.prefix4));
+ zlog_debug ("Sending %s %sRegister Packet to %s",
+ up->sg_str, null_register ? "NULL " : "", rp_str);
+ }
ifp = rpg->source_nexthop.interface;
+ if (!ifp)
+ {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug ("%s: No interface to transmit register on", __PRETTY_FUNCTION__);
+ return;
+ }
pinfo = (struct pim_interface *)ifp->info;
if (!pinfo) {
- zlog_debug("%s: No pinfo!\n", __PRETTY_FUNCTION__);
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("%s: Interface: %s not configured for pim to trasmit on!\n", __PRETTY_FUNCTION__, ifp->name);
return;
}
- memset(buffer, 0, 3000);
+ memset(buffer, 0, 10000);
+ b1 = buffer + PIM_MSG_HEADER_LEN;
+ *b1 |= null_register << 6;
b1 = buffer + PIM_MSG_REGISTER_LEN;
- plen = ntohs(ip_hdr->ip_len);
- memcpy(b1, (const unsigned char *)ip_hdr, plen);
+ memcpy(b1, (const unsigned char *)buf, buf_size);
- pim_msg_build_header(buffer, plen + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER);
+ pim_msg_build_header(buffer, buf_size + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER);
if (pim_msg_send(pinfo->pim_sock_fd,
- rpg->rpf_addr,
+ src,
+ rpg->rpf_addr.u.prefix4,
buffer,
- plen + PIM_MSG_REGISTER_LEN,
+ buf_size + PIM_MSG_REGISTER_LEN,
ifp->name)) {
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("%s: could not send PIM register message on interface %s",
{
int sentRegisterStop = 0;
struct ip *ip_hdr;
- //size_t hlen;
- struct in_addr group = { .s_addr = 0 };
- struct in_addr source = { .s_addr = 0 };
- //uint8_t *msg;
+ struct prefix_sg sg;
uint32_t *bits;
+ int i_am_rp = 0;
- if (!pim_check_is_my_ip_address (dest_addr)) {
- if (PIM_DEBUG_PIM_PACKETS) {
- char dest[100];
+#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4
+ ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN);
+
+ if (!pim_rp_check_is_my_ip_address (ip_hdr->ip_dst, dest_addr)) {
+ if (PIM_DEBUG_PIM_REG) {
+ char dest[INET_ADDRSTRLEN];
pim_inet4_dump ("<dst?>", dest_addr, dest, sizeof(dest));
zlog_debug ("%s: Received Register message for %s that I do not own", __func__,
return 0;
}
-#define inherited_olist(S,G) NULL
/*
* Please note this is not drawn to get the correct bit/data size
*
* Line above. So we need to add 4 bytes to get to the
* start of the actual Encapsulated data.
*/
-#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4
- ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN);
- //hlen = (ip_hdr->ip_hl << 2) | PIM_MSG_REGISTER_LEN;
- //msg = (uint8_t *)tlv_buf + hlen;
- source = ip_hdr->ip_src;
- group = ip_hdr->ip_dst;
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = ip_hdr->ip_src;
+ sg.grp = ip_hdr->ip_dst;
- if (I_am_RP (group) && (dest_addr.s_addr == ((RP (group))->rpf_addr.s_addr))) {
+ i_am_rp = I_am_RP (sg.grp);
+
+ if (PIM_DEBUG_PIM_REG)
+ {
+ char src_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump ("<src?>", src_addr, src_str, sizeof (src_str));
+ zlog_debug ("Received Register message(%s) from %s on %s, rp: %d",
+ pim_str_sg_dump (&sg), src_str, ifp->name, i_am_rp);
+ }
+
+ if (i_am_rp && (dest_addr.s_addr == ((RP (sg.grp))->rpf_addr.u.prefix4.s_addr))) {
sentRegisterStop = 0;
if (*bits & PIM_REGISTER_BORDER_BIT) {
- struct in_addr pimbr = pim_br_get_pmbr (source, group);
+ struct in_addr pimbr = pim_br_get_pmbr (&sg);
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug("%s: Received Register message with Border bit set", __func__);
if (pimbr.s_addr == pim_br_unknown.s_addr)
- pim_br_set_pmbr(source, group, src_addr);
+ pim_br_set_pmbr(&sg, src_addr);
else if (src_addr.s_addr != pimbr.s_addr) {
- pim_register_stop_send(src_addr);
+ pim_register_stop_send (ifp, &sg, dest_addr, src_addr);
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug("%s: Sending register-Stop to %s and dropping mr. packet",
__func__, "Sender");
}
}
- 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;
-}
#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
*/
#include <zebra.h>
+#include "lib/json.h"
#include "log.h"
#include "network.h"
#include "if.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "memory.h"
+#include "vty.h"
+#include "vrf.h"
+#include "plist.h"
#include "pimd.h"
+#include "pim_vty.h"
#include "pim_str.h"
+#include "pim_iface.h"
#include "pim_rp.h"
#include "pim_str.h"
#include "pim_rpf.h"
+#include "pim_sock.h"
+#include "pim_memory.h"
+#include "pim_iface.h"
+#include "pim_msdp.h"
-static int i_am_rp = 0;
+struct rp_info
+{
+ struct prefix group;
+ struct pim_rpf rp;
+ int i_am_rp;
+ char *plist;
+};
+
+static struct list *qpim_rp_list = NULL;
+static struct rp_info *tail = NULL;
+
+static void
+pim_rp_info_free (struct rp_info *rp_info)
+{
+ XFREE (MTYPE_PIM_RP, rp_info);
+}
+
+static int
+pim_rp_list_cmp (void *v1, void *v2)
+{
+ struct rp_info *rp1 = (struct rp_info *)v1;
+ struct rp_info *rp2 = (struct rp_info *)v2;
+
+ if (rp1 == rp2)
+ return 0;
+
+ if (!rp1 && rp2)
+ return -1;
+
+ if (rp1 && !rp2)
+ return 1;
+
+ /*
+ * Sort by RP IP address
+ */
+ if (rp1->rp.rpf_addr.u.prefix4.s_addr < rp2->rp.rpf_addr.u.prefix4.s_addr)
+ return -1;
+
+ if (rp1->rp.rpf_addr.u.prefix4.s_addr > rp2->rp.rpf_addr.u.prefix4.s_addr)
+ return 1;
+
+ /*
+ * Sort by group IP address
+ */
+ if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr)
+ return -1;
+
+ if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr)
+ return 1;
+
+ if (rp1 == tail)
+ return 1;
+
+ return -1;
+}
+
+void
+pim_rp_init (void)
+{
+ struct rp_info *rp_info;
+
+ qpim_rp_list = list_new ();
+ qpim_rp_list->del = (void (*)(void *))pim_rp_info_free;
+ qpim_rp_list->cmp = pim_rp_list_cmp;
+
+ rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info));
+
+ if (!rp_info)
+ return;
+
+ str2prefix ("224.0.0.0/4", &rp_info->group);
+ rp_info->group.family = AF_INET;
+ rp_info->rp.rpf_addr.family = AF_INET;
+ rp_info->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE;
+ tail = rp_info;
+
+ listnode_add (qpim_rp_list, rp_info);
+}
+
+void
+pim_rp_free (void)
+{
+ if (qpim_rp_list)
+ list_free (qpim_rp_list);
+}
+
+/*
+ * Given an RP's prefix-list, return the RP's rp_info for that prefix-list
+ */
+static struct rp_info *
+pim_rp_find_prefix_list (struct in_addr rp, const char *plist)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+ {
+ if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr &&
+ rp_info->plist && strcmp(rp_info->plist, plist) == 0)
+ {
+ return rp_info;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Return true if plist is used by any rp_info
+ */
+static int
+pim_rp_prefix_list_used (const char *plist)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+ {
+ if (rp_info->plist && strcmp(rp_info->plist, plist) == 0)
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
/*
- * Checks to see if we should elect ourself the actual RP
+ * Given an RP's address, return the RP's rp_info that is an exact match for 'group'
*/
+static struct rp_info *
+pim_rp_find_exact (struct in_addr rp, struct prefix *group)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+ {
+ if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr &&
+ prefix_same (&rp_info->group, group))
+ return rp_info;
+ }
+
+ return NULL;
+}
+
+/*
+ * Given a group, return the rp_info for that group
+ */
+static struct rp_info *
+pim_rp_find_match_group (struct prefix *group)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+ struct prefix_list *plist;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+ {
+ if (rp_info->plist)
+ {
+ plist = prefix_list_lookup (AFI_IP, rp_info->plist);
+
+ if (plist && prefix_list_apply (plist, group) == PREFIX_PERMIT)
+ return rp_info;
+ }
+ else
+ {
+ if (prefix_match (&rp_info->group, group))
+ return rp_info;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * When the user makes "ip pim rp" configuration changes or if they change the
+ * prefix-list(s) used by these statements we must tickle the upstream state
+ * for each group to make them re-lookup who their RP should be.
+ *
+ * This is a placeholder function for now.
+ */
+static void
+pim_rp_refresh_group_to_rp_mapping()
+{
+ pim_msdp_i_am_rp_changed();
+}
+
void
-pim_rp_check_rp (struct in_addr old, struct in_addr new)
-{
- if (PIM_DEBUG_ZEBRA) {
- char sold[100];
- char snew[100];
- char rp[100];
- pim_inet4_dump("<rp?>", qpim_rp.rpf_addr, rp, sizeof(rp));
- pim_inet4_dump("<old?>", old, sold, sizeof(sold));
- pim_inet4_dump("<new?>", new, snew, sizeof(snew));
- zlog_debug("%s: %s for old %s new %s", __func__, rp, sold, snew );
+pim_rp_prefix_list_update (struct prefix_list *plist)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+ int refresh_needed = 0;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+ {
+ if (rp_info->plist && strcmp(rp_info->plist, prefix_list_name (plist)) == 0)
+ {
+ refresh_needed = 1;
+ break;
+ }
+ }
+
+ if (refresh_needed)
+ pim_rp_refresh_group_to_rp_mapping();
+}
+
+static int
+pim_rp_check_interface_addrs(struct rp_info *rp_info,
+ struct pim_interface *pim_ifp)
+{
+ struct listnode *node;
+ struct pim_secondary_addr *sec_addr;
+
+ if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
+ return 1;
+
+ if (!pim_ifp->sec_addr_list) {
+ return 0;
}
- if (qpim_rp.rpf_addr.s_addr == INADDR_NONE)
- return;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+ if (sec_addr->addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+pim_rp_check_interfaces (struct rp_info *rp_info)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ rp_info->i_am_rp = 0;
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
+ {
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) {
+ rp_info->i_am_rp = 1;
+ }
+ }
+}
+
+int
+pim_rp_new (const char *rp, const char *group_range, const char *plist)
+{
+ int result;
+ struct rp_info *rp_info;
+ struct rp_info *rp_all;
+ struct prefix group_all;
+ struct listnode *node, *nnode;
+ struct rp_info *tmp_rp_info;
+ char buffer[BUFSIZ];
+
+ rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info));
+ if (!rp_info)
+ return PIM_MALLOC_FAIL;
+
+ if (group_range == NULL)
+ result = str2prefix ("224.0.0.0/4", &rp_info->group);
+ else
+ result = str2prefix (group_range, &rp_info->group);
+
+ if (!result)
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_GROUP_BAD_ADDRESS;
+ }
+
+ rp_info->rp.rpf_addr.family = AF_INET;
+ result = inet_pton (rp_info->rp.rpf_addr.family, rp, &rp_info->rp.rpf_addr.u.prefix4);
+
+ if (result <= 0)
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_RP_BAD_ADDRESS;
+ }
+
+ if (plist)
+ {
+ /*
+ * Return if the prefix-list is already configured for this RP
+ */
+ if (pim_rp_find_prefix_list (rp_info->rp.rpf_addr.u.prefix4, plist))
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_SUCCESS;
+ }
+
+ /*
+ * Barf if the prefix-list is already configured for an RP
+ */
+ if (pim_rp_prefix_list_used (plist))
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_RP_PFXLIST_IN_USE;
+ }
+
+ /*
+ * Free any existing rp_info entries for this RP
+ */
+ for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info))
+ {
+ if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr)
+ {
+ if (tmp_rp_info->plist)
+ pim_rp_del (rp, NULL, tmp_rp_info->plist);
+ else
+ pim_rp_del (rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), NULL);
+ }
+ }
+
+ rp_info->plist = XSTRDUP(MTYPE_PIM_FILTER_NAME, plist);
+ }
+ else
+ {
+ str2prefix ("224.0.0.0/4", &group_all);
+ rp_all = pim_rp_find_match_group(&group_all);
+
+ /*
+ * Barf if group is a non-multicast subnet
+ */
+ if (! prefix_match (&rp_all->group, &rp_info->group))
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_GROUP_BAD_ADDRESS;
+ }
- if (new.s_addr == qpim_rp.rpf_addr.s_addr)
+ /*
+ * Remove any prefix-list rp_info entries for this RP
+ */
+ for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info))
+ {
+ if (tmp_rp_info->plist &&
+ rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr)
+ {
+ pim_rp_del (rp, NULL, tmp_rp_info->plist);
+ }
+ }
+
+ /*
+ * Take over the 224.0.0.0/4 group if the rp is INADDR_NONE
+ */
+ if (prefix_same (&rp_all->group, &rp_info->group) &&
+ pim_rpf_addr_is_inaddr_none (&rp_all->rp))
+ {
+ rp_all->rp.rpf_addr = rp_info->rp.rpf_addr;
+ XFREE (MTYPE_PIM_RP, rp_info);
+
+ if (pim_nexthop_lookup (&rp_all->rp.source_nexthop, rp_all->rp.rpf_addr.u.prefix4, 1) != 0)
+ return PIM_RP_NO_PATH;
+
+ pim_rp_check_interfaces (rp_all);
+ pim_rp_refresh_group_to_rp_mapping();
+ return PIM_SUCCESS;
+ }
+
+ /*
+ * Return if the group is already configured for this RP
+ */
+ if (pim_rp_find_exact (rp_info->rp.rpf_addr.u.prefix4, &rp_info->group))
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_SUCCESS;
+ }
+
+ /*
+ * Barf if this group is already covered by some other RP
+ */
+ tmp_rp_info = pim_rp_find_match_group (&rp_info->group);
+
+ if (tmp_rp_info)
+ {
+ if (tmp_rp_info->plist)
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_GROUP_PFXLIST_OVERLAP;
+ }
+ else
+ {
+ /*
+ * If the only RP that covers this group is an RP configured for
+ * 224.0.0.0/4 that is fine, ignore that one. For all others
+ * though we must return PIM_GROUP_OVERLAP
+ */
+ if (! prefix_same (&group_all, &tmp_rp_info->group))
+ {
+ XFREE (MTYPE_PIM_RP, rp_info);
+ return PIM_GROUP_OVERLAP;
+ }
+ }
+ }
+ }
+
+ listnode_add_sort (qpim_rp_list, rp_info);
+
+ if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0)
+ return PIM_RP_NO_PATH;
+
+ pim_rp_check_interfaces (rp_info);
+ pim_rp_refresh_group_to_rp_mapping();
+ return PIM_SUCCESS;
+}
+
+int
+pim_rp_del (const char *rp, const char *group_range, const char *plist)
+{
+ struct prefix group;
+ struct in_addr rp_addr;
+ struct prefix g_all;
+ struct rp_info *rp_info;
+ struct rp_info *rp_all;
+ int result;
+
+ if (group_range == NULL)
+ result = str2prefix ("224.0.0.0/4", &group);
+ else
+ result = str2prefix (group_range, &group);
+
+ if (!result)
+ return PIM_GROUP_BAD_ADDRESS;
+
+ result = inet_pton (AF_INET, rp, &rp_addr);
+ if (result <= 0)
+ return PIM_RP_BAD_ADDRESS;
+
+ if (plist)
+ rp_info = pim_rp_find_prefix_list (rp_addr, plist);
+ else
+ rp_info = pim_rp_find_exact (rp_addr, &group);
+
+ if (!rp_info)
+ return PIM_RP_NOT_FOUND;
+
+ if (rp_info->plist)
{
- i_am_rp = 1;
- return;
+ XFREE(MTYPE_PIM_FILTER_NAME, rp_info->plist);
+ rp_info->plist = NULL;
}
- if (old.s_addr == qpim_rp.rpf_addr.s_addr)
+ str2prefix ("224.0.0.0/4", &g_all);
+ rp_all = pim_rp_find_match_group (&g_all);
+
+ if (rp_all == rp_info)
{
- i_am_rp = 0;
- return;
+ rp_all->rp.rpf_addr.family = AF_INET;
+ rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE;
+ rp_all->i_am_rp = 0;
+ return PIM_SUCCESS;
+ }
+
+ listnode_delete (qpim_rp_list, rp_info);
+ pim_rp_refresh_group_to_rp_mapping();
+ return PIM_SUCCESS;
+}
+
+int
+pim_rp_setup (void)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+ int ret = 0;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
+ {
+ if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
+ continue;
+
+ if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0)
+ {
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug ("Unable to lookup nexthop for rp specified");
+ ret++;
+ }
}
+
+ if (ret)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Checks to see if we should elect ourself the actual RP when new if
+ * addresses are added against an interface.
+ */
+void
+pim_rp_check_on_if_add(struct pim_interface *pim_ifp)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+ bool i_am_rp_changed = false;
+
+ if (qpim_rp_list == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) {
+ if (pim_rpf_addr_is_inaddr_none (&rp_info->rp))
+ continue;
+
+ /* if i_am_rp is already set nothing to be done (adding new addresses
+ * is not going to make a difference). */
+ if (rp_info->i_am_rp) {
+ continue;
+ }
+
+ if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) {
+ i_am_rp_changed = true;
+ rp_info->i_am_rp = 1;
+ if (PIM_DEBUG_ZEBRA) {
+ char rp[PREFIX_STRLEN];
+ pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp));
+ zlog_debug("%s: %s: i am rp", __func__, rp);
+ }
+ }
+ }
+
+ if (i_am_rp_changed) {
+ pim_msdp_i_am_rp_changed();
+ }
+}
+
+/* up-optimized re-evaluation of "i_am_rp". this is used when ifaddresses
+ * are removed. Removing numbers is an uncommon event in an active network
+ * so I have made no attempt to optimize it. */
+void
+pim_i_am_rp_re_evaluate(void)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+ bool i_am_rp_changed = false;
+ int old_i_am_rp;
+
+ if (qpim_rp_list == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_rp_list, node, rp_info)) {
+ if (pim_rpf_addr_is_inaddr_none(&rp_info->rp))
+ continue;
+
+ old_i_am_rp = rp_info->i_am_rp;
+ pim_rp_check_interfaces(rp_info);
+
+ if (old_i_am_rp != rp_info->i_am_rp) {
+ i_am_rp_changed = true;
+ if (PIM_DEBUG_ZEBRA) {
+ char rp[PREFIX_STRLEN];
+ pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp));
+ if (rp_info->i_am_rp) {
+ zlog_debug("%s: %s: i am rp", __func__, rp);
+ } else {
+ zlog_debug("%s: %s: i am no longer rp", __func__, rp);
+ }
+ }
+ }
+ }
+
+ if (i_am_rp_changed) {
+ pim_msdp_i_am_rp_changed();
+ }
}
/*
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;
}
/*
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;
}
/*
*
*/
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);
+ }
+}
#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
#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];
- int num_ifindex;
- struct interface *ifp;
- int first_ifindex;
+ 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);
+}
- memset (nexthop_tab, 0, sizeof (struct pim_zlookup_nexthop) * PIM_NEXTHOP_IFINDEX_TAB_SIZE);
+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 = NULL;
+ ifindex_t first_ifindex = 0;
+ int found = 0;
+ int i = 0;
- if (!incoming)
+ if ((nexthop->last_lookup.s_addr == addr.s_addr) &&
+ (nexthop->last_lookup_time > last_route_change_time))
{
- num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
- PIM_NEXTHOP_IFINDEX_TAB_SIZE,
- addr, PIM_NEXTHOP_LOOKUP_MAX);
- if (num_ifindex < 1) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: could not find nexthop ifindex for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- addr_str);
- return -1;
- }
-
- first_ifindex = nexthop_tab[0].ifindex;
-
- if (num_ifindex > 1) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
- __FILE__, __PRETTY_FUNCTION__,
- num_ifindex, addr_str, first_ifindex);
- /* debug warning only, do not return */
- }
-
- ifp = if_lookup_by_index(first_ifindex);
- if (!ifp) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: could not find interface for ifindex %d (address %s)",
- __FILE__, __PRETTY_FUNCTION__,
- first_ifindex, addr_str);
- return -2;
- }
+ if (PIM_DEBUG_TRACE)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug ("%s: Using last lookup for %s at %lld, %lld",
+ __PRETTY_FUNCTION__,
+ addr_str,
+ nexthop->last_lookup_time,
+ last_route_change_time);
+ }
+ nexthop_lookups_avoided++;
+ return 0;
}
else
{
- ifp = incoming;
- first_ifindex = ifp->ifindex;
+ if (PIM_DEBUG_TRACE)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug ("%s: Looking up: %s, last lookup time: %lld, %lld",
+ __PRETTY_FUNCTION__,
+ addr_str,
+ nexthop->last_lookup_time,
+ last_route_change_time);
+ }
}
- if (!ifp->info) {
- char addr_str[100];
+ memset (nexthop_tab, 0, sizeof (struct pim_zlookup_nexthop) * MULTIPATH_NUM);
+ num_ifindex = zclient_lookup_nexthop(nexthop_tab,
+ MULTIPATH_NUM,
+ addr, PIM_NEXTHOP_LOOKUP_MAX);
+ if (num_ifindex < 1) {
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
- __PRETTY_FUNCTION__,
- ifp->name, first_ifindex, addr_str);
- /* debug warning only, do not return */
+ zlog_warn("%s %s: could not find nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ addr_str);
+ return -1;
}
- if (PIM_DEBUG_PIM_TRACE) {
- char nexthop_str[100];
- char addr_str[100];
- pim_inet4_dump("<nexthop?>", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str));
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
- __FILE__, __PRETTY_FUNCTION__,
- nexthop_str, addr_str,
- ifp->name, first_ifindex,
- nexthop_tab[0].route_metric,
- nexthop_tab[0].protocol_distance);
- }
+ while (!found && (i < num_ifindex))
+ {
+ first_ifindex = nexthop_tab[i].ifindex;
- /* update nextop data */
- nexthop->interface = ifp;
- nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr;
- nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance;
- nexthop->mrib_route_metric = nexthop_tab[0].route_metric;
+ ifp = if_lookup_by_index(first_ifindex);
+ if (!ifp)
+ {
+ if (PIM_DEBUG_ZEBRA)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: could not find interface for ifindex %d (address %s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ first_ifindex, addr_str);
+ }
+ i++;
+ continue;
+ }
+
+ if (!ifp->info)
+ {
+ if (PIM_DEBUG_ZEBRA)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
+ __PRETTY_FUNCTION__,
+ ifp->name, first_ifindex, addr_str);
+ }
+ i++;
+ }
+ else if (neighbor_needed && !pim_if_connected_to_source (ifp, addr))
+ {
+ struct pim_neighbor *nbr;
+
+ nbr = pim_neighbor_find (ifp, nexthop_tab[i].nexthop_addr.u.prefix4);
+ if (PIM_DEBUG_PIM_TRACE_DETAIL)
+ zlog_debug ("ifp name: %s, pim nbr: %p", ifp->name, nbr);
+ if (!nbr && !if_is_loopback (ifp))
+ i++;
+ else
+ found = 1;
+ }
+ else
+ found = 1;
+ }
- return 0;
+ if (found)
+ {
+ if (PIM_DEBUG_ZEBRA) {
+ char nexthop_str[PREFIX_STRLEN];
+ char addr_str[INET_ADDRSTRLEN];
+ pim_addr_dump("<nexthop?>", &nexthop_tab[i].nexthop_addr, nexthop_str, sizeof(nexthop_str));
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ nexthop_str, addr_str,
+ ifp->name, first_ifindex,
+ nexthop_tab[i].route_metric,
+ nexthop_tab[i].protocol_distance);
+ }
+ /* update nextop data */
+ nexthop->interface = ifp;
+ nexthop->mrib_nexthop_addr = nexthop_tab[i].nexthop_addr;
+ nexthop->mrib_metric_preference = nexthop_tab[i].protocol_distance;
+ nexthop->mrib_route_metric = nexthop_tab[i].route_metric;
+ nexthop->last_lookup = addr;
+ nexthop->last_lookup_time = pim_time_monotonic_usec();
+ return 0;
+ }
+ else
+ return -1;
}
static int nexthop_mismatch(const struct pim_nexthop *nh1,
const struct pim_nexthop *nh2)
{
- return (nh1->interface != nh2->interface)
- ||
- (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr)
- ||
- (nh1->mrib_metric_preference != nh2->mrib_metric_preference)
- ||
+ return (nh1->interface != nh2->interface) ||
+ (nh1->mrib_nexthop_addr.u.prefix4.s_addr != nh2->mrib_nexthop_addr.u.prefix4.s_addr) ||
+ (nh1->mrib_metric_preference != nh2->mrib_metric_preference) ||
(nh1->mrib_route_metric != nh2->mrib_route_metric);
}
-enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
- struct in_addr *old_rpf_addr,
- struct interface *incoming)
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, struct in_addr *old_rpf_addr)
{
- struct in_addr save_rpf_addr;
+ struct prefix save_rpf_addr;
struct pim_nexthop save_nexthop;
struct pim_rpf *rpf = &up->rpf;
save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */
if (pim_nexthop_lookup(&rpf->source_nexthop,
- up->upstream_addr, incoming)) {
+ up->upstream_addr,
+ !PIM_UPSTREAM_FLAG_TEST_FHR (up->flags) &&
+ !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))) {
return PIM_RPF_FAILURE;
}
- rpf->rpf_addr = pim_rpf_find_rpf_addr(up);
- if (PIM_INADDR_IS_ANY(rpf->rpf_addr) && PIM_DEBUG_PIM_EVENTS) {
+ rpf->rpf_addr.family = AF_INET;
+ rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up);
+ if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) {
/* RPF'(S,G) not found */
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s %s: RPF'(%s,%s) not found: won't send join upstream",
+ zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str);
+ up->sg_str);
/* warning only */
}
/* detect change in pim_nexthop */
if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) {
- if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- char nhaddr_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<addr?>", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
- zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d",
+ if (PIM_DEBUG_ZEBRA) {
+ char nhaddr_str[PREFIX_STRLEN];
+ pim_addr_dump("<addr?>", &rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
+ zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str,
+ up->sg_str,
rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
nhaddr_str,
rpf->source_nexthop.mrib_metric_preference,
/* detect change in RPF_interface(S) */
if (save_nexthop.interface != rpf->source_nexthop.interface) {
- if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s",
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str,
+ up->sg_str,
save_nexthop.interface ? save_nexthop.interface->name : "<oldif?>",
rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
/* warning only */
}
/* 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;
}
struct in_addr rpf_addr;
if (!up->rpf.source_nexthop.interface) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)",
+ zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
__PRETTY_FUNCTION__,
- src_str, grp_str);
+ up->sg_str);
rpf_addr.s_addr = PIM_NET_INADDR_ANY;
return rpf_addr;
}
rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface,
- up->source_addr, up->group_addr);
+ &up->sg);
if (rpf_ch) {
if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
return rpf_ch->ifassert_winner;
/* 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
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;
+}
#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 */
/* GLOBAL VARS */
extern struct zebra_privs_t pimd_privs;
-int pim_socket_raw(int protocol)
+int
+pim_socket_raw (int protocol)
{
int fd;
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);
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",
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;
ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
if (ret) {
- char group_str[100];
- char ifaddr_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char ifaddr_str[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
sprintf(group_str, "<group?>");
if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
}
if (PIM_DEBUG_TRACE) {
- char group_str[100];
- char ifaddr_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char ifaddr_str[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
sprintf(group_str, "<group?>");
if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
const char *ifname)
{
if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
- int e = errno;
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
__PRETTY_FUNCTION__,
fd, group_str, source_str, ifindex, ifname,
- e, safe_strerror(e));
+ errno, safe_strerror(errno));
return -1;
}
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);
if (ifindex)
*ifindex = i->ipi_ifindex;
- if (to && PIM_DEBUG_PACKETS) {
- char to_str[100];
- pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
- zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
- __PRETTY_FUNCTION__,
- to_str, ntohs(to->sin_port));
- }
-
break;
}
#endif
if (tolen)
*tolen = sizeof(struct sockaddr_in);
- if (to && PIM_DEBUG_PACKETS) {
- char to_str[100];
- pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
- zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
- __PRETTY_FUNCTION__,
- to_str, ntohs(to->sin_port));
- }
-
break;
}
#endif
#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,
#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";
sockaddr.sin_port = htons(port);
if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
__PRETTY_FUNCTION__,
THREAD_OFF(ss->t_sock_read);
if (close(ss->sock_fd)) {
- int e = errno;
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
__PRETTY_FUNCTION__,
- ss->sock_fd, source_str, e, safe_strerror(e));
+ ss->sock_fd, source_str, errno, safe_strerror(errno));
/* warning only */
}
sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != len) {
- int e = errno;
- char to_str[100];
+ char to_str[INET_ADDRSTRLEN];
pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
if (sent < 0) {
zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
__PRETTY_FUNCTION__,
to_str, ntohs(to.sin_port), ss->sock_fd, len,
- e, safe_strerror(e));
+ errno, safe_strerror(errno));
}
else {
zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
&to, &tolen,
&ifindex);
if (len < 0) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
ifp = if_lookup_by_index(ifindex);
if (buf[0] != PIM_SSMPINGD_REQUEST) {
- char source_str[100];
- char from_str[100];
- char to_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char from_str[INET_ADDRSTRLEN];
+ char to_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
}
if (PIM_DEBUG_SSMPINGD) {
- char source_str[100];
- char from_str[100];
- char to_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char from_str[INET_ADDRSTRLEN];
+ char to_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
int sock_fd;
int result;
- zassert(t);
-
ss = THREAD_ARG(t);
- zassert(ss);
sock_fd = THREAD_FD(t);
zassert(sock_fd == ss->sock_fd);
sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
if (sock_fd < 0) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: ssmpingd_socket() failure for source %s",
__PRETTY_FUNCTION__, source_str);
return 0;
}
- ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
+ ss = XCALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
if (!ss) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
- zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
+ zlog_err("%s: XCALLOC(%zu) failure for ssmpingd source %s",
__PRETTY_FUNCTION__,
sizeof(*ss), source_str);
close(sock_fd);
}
{
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_info("%s: starting ssmpingd for source %s",
__PRETTY_FUNCTION__, source_str);
ss = ssmpingd_new(source_addr);
if (!ss) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: ssmpingd_new() failure for source %s",
__PRETTY_FUNCTION__, source_str);
ss = ssmpingd_find(source_addr);
if (!ss) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: could not find ssmpingd for source %s",
__PRETTY_FUNCTION__, source_str);
}
{
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_info("%s: stopping ssmpingd for source %s",
__PRETTY_FUNCTION__, source_str);
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;
s_route->source.s_addr == source.s_addr) {
if (s_route->iif == iif_index &&
s_route->oif_ttls[oif_index]) {
- char gifaddr_str[100];
- char sifaddr_str[100];
+ char gifaddr_str[INET_ADDRSTRLEN];
+ char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
listnode_add(qpim_static_route_list, s_route);
}
- if (pim_mroute_add(&s_route->c_oil))
+ if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__))
{
- char gifaddr_str[100];
- char sifaddr_str[100];
+ char gifaddr_str[INET_ADDRSTRLEN];
+ char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
}
if (PIM_DEBUG_STATIC) {
- char gifaddr_str[100];
- char sifaddr_str[100];
+ char gifaddr_str[INET_ADDRSTRLEN];
+ char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
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;
/* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */
if (s_route->c_oil.oil_ref_count <= 0 ?
- pim_mroute_del(&s_route->c_oil) : pim_mroute_add(&s_route->c_oil)) {
- char gifaddr_str[100];
- char sifaddr_str[100];
- pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
- pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
- zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
+ pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__) : pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
+ char gifaddr_str[INET_ADDRSTRLEN];
+ char sifaddr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+ pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+ zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
__FILE__, __PRETTY_FUNCTION__,
iif_index,
oif_index,
gifaddr_str,
sifaddr_str);
- s_route->oif_ttls[oif_index] = 1;
- s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
- ++s_route->c_oil.oil_ref_count;
+ s_route->oif_ttls[oif_index] = 1;
+ s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
+ ++s_route->c_oil.oil_ref_count;
- return -1;
+ return -1;
}
s_route->c_oil.oif_creation[oif_index] = 0;
}
if (PIM_DEBUG_STATIC) {
- char gifaddr_str[100];
- char sifaddr_str[100];
+ char gifaddr_str[INET_ADDRSTRLEN];
+ char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
}
if (!node) {
- char gifaddr_str[100];
- char sifaddr_str[100];
+ char gifaddr_str[INET_ADDRSTRLEN];
+ char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
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))
{
#include "pim_str.h"
-void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
+void pim_addr_dump (const char *onfail, struct prefix *p, char *buf, int buf_size)
{
int save_errno = errno;
- if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
- int e = errno;
- zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
- buf_size, e, safe_strerror(e));
+ if (!inet_ntop(p->family, &p->u.prefix, buf, buf_size)) {
+ zlog_warn("pim_addr_dump: inet_ntop(buf_size=%d): errno=%d: %s",
+ buf_size, errno, safe_strerror(errno));
if (onfail)
snprintf(buf, buf_size, "%s", onfail);
}
errno = save_errno;
}
+
+void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
+{
+ int save_errno = errno;
+
+ if (addr.s_addr == INADDR_ANY)
+ strcpy(buf, "*");
+ else
+ {
+ if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
+ zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
+ buf_size, errno, safe_strerror(errno));
+ if (onfail)
+ snprintf(buf, buf_size, "%s", onfail);
+ }
+ }
+
+ errno = save_errno;
+}
+
+char *
+pim_str_sg_dump (const struct prefix_sg *sg)
+{
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ static char sg_str[PIM_SG_LEN];
+
+ pim_inet4_dump ("<src?>", sg->src, src_str, sizeof(src_str));
+ pim_inet4_dump ("<grp?>", sg->grp, grp_str, sizeof(grp_str));
+ snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str);
+
+ return sg_str;
+}
+
+char *
+pim_str_sg_set (const struct prefix_sg *sg, char *sg_str)
+{
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump ("<src?>", sg->src, src_str, sizeof(src_str));
+ pim_inet4_dump ("<grp?>", sg->grp, grp_str, sizeof(grp_str));
+ snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str);
+
+ return sg_str;
+}
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <prefix.h>
+
+/*
+ * Longest possible length of a (S,G) string is 36 bytes
+ * 123.123.123.123 = 16 * 2
+ * (,) = 3
+ * NULL Character at end = 1
+ * (123.123.123.123,123,123,123,123)
+ */
+#define PIM_SG_LEN 36
+
+void pim_addr_dump (const char *onfail, struct prefix *p, char *buf, int buf_size);
void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size);
+char *pim_str_sg_dump (const struct prefix_sg *sg);
+char *pim_str_sg_set (const struct prefix_sg *sg, char *sg_str);
#endif
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;
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);
#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)
{
}
}
+#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)
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;
}
int correct_len, int option_len)
{
if (option_len != correct_len) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s",
label, tlv_name,
uint16_t new, uint16_t old)
{
if (PIM_OPTION_IS_SET(options, opt_mask)) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
label, tlv_name,
uint32_t new, uint32_t old)
{
if (PIM_OPTION_IS_SET(options, opt_mask)) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
label, tlv_name,
uint32_t new, uint32_t old)
{
if (PIM_OPTION_IS_SET(options, opt_mask)) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s",
label, tlv_name,
}
int
-pim_parse_addr_group (struct prefix *p,
+pim_parse_addr_group (struct prefix_sg *sg,
const uint8_t *buf,
int buf_size)
{
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;
}
}
}
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)
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
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;
}
*/
addr_offset = pim_parse_addr_ucast(&tmp, addr, pastend - addr);
if (addr_offset < 1) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
__PRETTY_FUNCTION__,
switch (tmp.family) {
case AF_INET:
{
- char addr_str[100];
- char src_str[100];
+ char addr_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str));
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s",
break;
default:
{
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s",
__PRETTY_FUNCTION__,
*/
if (tmp.family == AF_INET) {
if (tmp.u.prefix4.s_addr == src_addr.s_addr) {
- char src_str[100];
+ char src_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: ignoring primary address in secondary list from %s on %s",
__PRETTY_FUNCTION__,
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);
#include "memory.h"
#include "thread.h"
#include "linklist.h"
+#include "vty.h"
+#include "plist.h"
+#include "hash.h"
+#include "jhash.h"
+#include "wheel.h"
#include "pimd.h"
#include "pim_pim.h"
#include "pim_macro.h"
#include "pim_rp.h"
#include "pim_br.h"
+#include "pim_register.h"
+#include "pim_msdp.h"
+
+struct hash *pim_upstream_hash = NULL;
+struct list *pim_upstream_list = NULL;
+struct timer_wheel *pim_upstream_sg_wheel = NULL;
static void join_timer_start(struct pim_upstream *up);
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
+/*
+ * A (*,G) or a (*,*) is going away
+ * remove the parent pointer from
+ * those pointing at us
+ */
+static void
+pim_upstream_remove_children (struct pim_upstream *up)
+{
+ struct pim_upstream *child;
+
+ if (!up->sources)
+ return;
+
+ while (!list_isempty (up->sources))
+ {
+ child = listnode_head (up->sources);
+ child->parent = NULL;
+ listnode_delete (up->sources, child);
+ }
+}
+
+/*
+ * A (*,G) or a (*,*) is being created
+ * Find the children that would point
+ * at us.
+ */
+static void
+pim_upstream_find_new_children (struct pim_upstream *up)
+{
+ struct pim_upstream *child;
+ struct listnode *ch_node;
+
+ if ((up->sg.src.s_addr != INADDR_ANY) &&
+ (up->sg.grp.s_addr != INADDR_ANY))
+ return;
+
+ if ((up->sg.src.s_addr == INADDR_ANY) &&
+ (up->sg.grp.s_addr == INADDR_ANY))
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
+ {
+ if ((up->sg.grp.s_addr != INADDR_ANY) &&
+ (child->sg.grp.s_addr == up->sg.grp.s_addr) &&
+ (child != up))
+ {
+ child->parent = up;
+ listnode_add_sort (up->sources, child);
+ }
+ }
+}
+
+/*
+ * If we have a (*,*) || (S,*) there is no parent
+ * If we have a (S,G), find the (*,G)
+ * If we have a (*,G), find the (*,*)
+ */
+static struct pim_upstream *
+pim_upstream_find_parent (struct pim_upstream *child)
+{
+ struct prefix_sg any = child->sg;
+ struct pim_upstream *up = NULL;
+
+ // (S,G)
+ if ((child->sg.src.s_addr != INADDR_ANY) &&
+ (child->sg.grp.s_addr != INADDR_ANY))
+ {
+ any.src.s_addr = INADDR_ANY;
+ up = pim_upstream_find (&any);
+
+ if (up)
+ listnode_add (up->sources, child);
+
+ return up;
+ }
+
+ return NULL;
+}
+
void pim_upstream_free(struct pim_upstream *up)
{
XFREE(MTYPE_PIM_UPSTREAM, up);
}
}
-void pim_upstream_delete(struct pim_upstream *up)
+void
+pim_upstream_del(struct pim_upstream *up, const char *name)
{
+ bool notify_msdp = false;
+
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("%s(%s): Delete %s ref count: %d",
+ __PRETTY_FUNCTION__, name, up->sg_str, up->ref_count);
+
+ --up->ref_count;
+
+ if (up->ref_count >= 1)
+ return;
+
THREAD_OFF(up->t_join_timer);
THREAD_OFF(up->t_ka_timer);
+ THREAD_OFF(up->t_rs_timer);
+ THREAD_OFF(up->t_msdp_reg_timer);
+
+ if (up->join_state == PIM_UPSTREAM_JOINED) {
+ pim_joinprune_send (up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr.u.prefix4,
+ up, 0);
+ if (up->sg.src.s_addr == INADDR_ANY) {
+ /* if a (*, G) entry in the joined state is being deleted we
+ * need to notify MSDP */
+ notify_msdp = true;
+ }
+ }
+
+ if (up->sg.src.s_addr != INADDR_ANY) {
+ wheel_remove_item (pim_upstream_sg_wheel, up);
+ notify_msdp = true;
+ }
+ pim_upstream_remove_children (up);
+ pim_mroute_del (up->channel_oil, __PRETTY_FUNCTION__);
upstream_channel_oil_detach(up);
+ if (up->sources)
+ list_delete (up->sources);
+ up->sources = NULL;
+
/*
notice that listnode_delete() can't be moved
into pim_upstream_free() because the later is
called by list_delete_all_node()
*/
- listnode_delete(qpim_upstream_list, up);
+ if (up->parent)
+ {
+ listnode_delete (up->parent->sources, up);
+ up->parent = NULL;
+ }
+ listnode_delete (pim_upstream_list, up);
+ hash_release (pim_upstream_hash, up);
+ if (notify_msdp) {
+ pim_msdp_up_del(&up->sg);
+ }
pim_upstream_free(up);
}
-static void send_join(struct pim_upstream *up)
+void
+pim_upstream_send_join (struct pim_upstream *up)
{
- zassert(up->join_state == PIM_UPSTREAM_JOINED);
-
-
- if (PIM_DEBUG_PIM_TRACE) {
- if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
- char src_str[100];
- char grp_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, rpf_str);
+ if (PIM_DEBUG_TRACE) {
+ char rpf_str[PREFIX_STRLEN];
+ pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+ zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__,
+ up->sg_str, rpf_str, pim_upstream_state2str (up->join_state),
+ up->rpf.source_nexthop.interface->name);
+ if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
+ zlog_debug("%s: can't send join upstream: RPF'%s=%s",
+ __PRETTY_FUNCTION__,
+ up->sg_str, rpf_str);
/* warning only */
}
}
-
+
/* send Join(S,G) to the current upstream neighbor */
pim_joinprune_send(up->rpf.source_nexthop.interface,
- up->rpf.rpf_addr,
- up->source_addr,
- up->group_addr,
+ up->rpf.rpf_addr.u.prefix4,
+ up,
1 /* join */);
}
{
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;
static void join_timer_start(struct pim_upstream *up)
{
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
+ zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
__PRETTY_FUNCTION__,
qpim_t_periodic,
- src_str, grp_str);
+ up->sg_str);
}
- zassert(!up->t_join_timer);
-
+ THREAD_OFF (up->t_join_timer);
THREAD_TIMER_ON(master, up->t_join_timer,
on_join_timer,
up, qpim_t_periodic);
int interval_msec)
{
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
+ zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
__PRETTY_FUNCTION__,
interval_msec,
- src_str, grp_str);
+ up->sg_str);
}
THREAD_OFF(up->t_join_timer);
join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ if (PIM_DEBUG_TRACE) {
+ char rpf_str[INET_ADDRSTRLEN];
pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
+ zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str,
+ up->sg_str,
rpf_str,
join_timer_remain_msec, t_joinsuppress_msec);
}
if (join_timer_remain_msec < t_joinsuppress_msec) {
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
+ if (PIM_DEBUG_TRACE) {
+ zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec",
__FILE__, __PRETTY_FUNCTION__,
- src_str, grp_str, t_joinsuppress_msec);
+ up->sg_str, t_joinsuppress_msec);
}
pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- char rpf_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ if (PIM_DEBUG_TRACE) {
+ char rpf_str[INET_ADDRSTRLEN];
pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
- zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
+ zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
debug_label,
- src_str, grp_str, rpf_str,
+ up->sg_str, rpf_str,
join_timer_remain_msec, t_override_msec);
}
if (join_timer_remain_msec > t_override_msec) {
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
+ if (PIM_DEBUG_TRACE) {
+ zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec",
debug_label,
- src_str, grp_str,
+ up->sg_str,
t_override_msec);
}
static void forward_on(struct pim_upstream *up)
{
- struct listnode *ifnode;
- struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
- struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
+ /* scan (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-
- if (ch->upstream != up)
- continue;
+ if (ch->upstream != up)
+ continue;
- if (pim_macro_chisin_oiflist(ch))
- pim_forward_start(ch);
+ if (pim_macro_chisin_oiflist(ch))
+ pim_forward_start(ch);
- } /* scan iface channel list */
- } /* scan iflist */
+ } /* scan iface channel list */
}
static void forward_off(struct pim_upstream *up)
{
- struct listnode *ifnode;
- struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
- struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+ if (ch->upstream != up)
+ continue;
- if (ch->upstream != up)
- continue;
+ pim_forward_stop(ch);
- pim_forward_stop(ch);
+ } /* scan iface channel list */
+}
- } /* scan iface channel list */
- } /* scan iflist */
+static int
+pim_upstream_could_register (struct pim_upstream *up)
+{
+ struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info;
+
+ if (pim_ifp && PIM_I_am_DR (pim_ifp) &&
+ pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
+ return 1;
+
+ return 0;
}
-static void pim_upstream_switch(struct pim_upstream *up,
- enum pim_upstream_state new_state)
+void
+pim_upstream_switch(struct pim_upstream *up,
+ enum pim_upstream_state new_state)
{
enum pim_upstream_state old_state = up->join_state;
- zassert(old_state != new_state);
-
- up->join_state = new_state;
- up->state_transition = pim_time_monotonic_sec();
-
if (PIM_DEBUG_PIM_EVENTS) {
- char src_str[100];
- char grp_str[100];
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
+ zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
__PRETTY_FUNCTION__,
- ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
- src_str, grp_str);
+ up->sg_str,
+ pim_upstream_state2str (up->join_state),
+ pim_upstream_state2str (new_state));
}
+ /*
+ * This code still needs work.
+ */
+ switch (up->join_state)
+ {
+ case PIM_UPSTREAM_PRUNE:
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+ {
+ up->join_state = new_state;
+ up->state_transition = pim_time_monotonic_sec ();
+ }
+ break;
+ case PIM_UPSTREAM_JOIN_PENDING:
+ break;
+ case PIM_UPSTREAM_NOTJOINED:
+ case PIM_UPSTREAM_JOINED:
+ up->join_state = new_state;
+ if (old_state != new_state)
+ up->state_transition = pim_time_monotonic_sec();
+
+ break;
+ }
+
pim_upstream_update_assert_tracking_desired(up);
if (new_state == PIM_UPSTREAM_JOINED) {
- forward_on(up);
- send_join(up);
- join_timer_start(up);
+ if (old_state != PIM_UPSTREAM_JOINED)
+ {
+ int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
+ forward_on(up);
+ pim_msdp_up_join_state_changed(up);
+ if (pim_upstream_could_register (up))
+ {
+ PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+ if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
+ {
+ pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
+ pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+ }
+ }
+ else
+ {
+ pim_upstream_send_join (up);
+ join_timer_start (up);
+ }
+ }
+ else
+ {
+ forward_on (up);
+ }
}
else {
forward_off(up);
+ if (old_state == PIM_UPSTREAM_JOINED)
+ pim_msdp_up_join_state_changed(up);
pim_joinprune_send(up->rpf.source_nexthop.interface,
- up->rpf.rpf_addr,
- up->source_addr,
- up->group_addr,
+ up->rpf.rpf_addr.u.prefix4,
+ up,
0 /* prune */);
- zassert(up->t_join_timer);
- THREAD_OFF(up->t_join_timer);
+ if (up->t_join_timer)
+ THREAD_OFF(up->t_join_timer);
}
-
}
+static int
+pim_upstream_compare (void *arg1, void *arg2)
+{
+ const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
+ const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
+
+ if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
+ return -1;
+
+ if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
+ return 1;
+
+ if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
+ return -1;
+
+ if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
+ return 1;
-static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
- struct in_addr group_addr,
- struct interface *incoming)
+ return 0;
+}
+
+static struct pim_upstream *
+pim_upstream_new (struct prefix_sg *sg,
+ struct interface *incoming,
+ int flags)
{
- struct pim_upstream *up;
enum pim_rpf_result rpf_result;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up;
- up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+ up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
if (!up) {
- zlog_err("%s: PIM XMALLOC(%zu) failure",
+ zlog_err("%s: PIM XCALLOC(%zu) failure",
__PRETTY_FUNCTION__, sizeof(*up));
return NULL;
}
- up->source_addr = source_addr;
- if (!pim_rp_set_upstream_addr (&up->upstream_addr, source_addr))
+ up->sg = *sg;
+ pim_str_sg_set (sg, up->sg_str);
+ up = hash_get (pim_upstream_hash, up, hash_alloc_intern);
+ if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp))
{
- if (PIM_DEBUG_PIM_TRACE)
+ if (PIM_DEBUG_TRACE)
zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
+ hash_release (pim_upstream_hash, up);
XFREE (MTYPE_PIM_UPSTREAM, up);
return NULL;
}
- up->group_addr = group_addr;
- up->flags = 0;
+ up->parent = pim_upstream_find_parent (up);
+ if (up->sg.src.s_addr == INADDR_ANY)
+ {
+ up->sources = list_new ();
+ up->sources->cmp = pim_upstream_compare;
+ }
+ else
+ up->sources = NULL;
+
+ pim_upstream_find_new_children (up);
+ up->flags = flags;
up->ref_count = 1;
up->t_join_timer = NULL;
up->t_ka_timer = NULL;
+ up->t_rs_timer = NULL;
+ up->t_msdp_reg_timer = NULL;
up->join_state = 0;
up->state_transition = pim_time_monotonic_sec();
up->channel_oil = NULL;
up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
- up->rpf.source_nexthop.interface = 0;
- up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+ up->rpf.source_nexthop.interface = NULL;
+ up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET;
+ up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
- up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
+ up->rpf.rpf_addr.family = AF_INET;
+ up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
+
+ if (up->sg.src.s_addr != INADDR_ANY)
+ wheel_add_item (pim_upstream_sg_wheel, up);
- rpf_result = pim_rpf_update(up, 0, incoming);
+ rpf_result = pim_rpf_update(up, NULL);
if (rpf_result == PIM_RPF_FAILURE) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__,
+ up->sg_str);
+
+ if (up->parent)
+ {
+ listnode_delete (up->parent->sources, up);
+ up->parent = NULL;
+ }
+
+ if (up->sg.src.s_addr != INADDR_ANY)
+ wheel_remove_item (pim_upstream_sg_wheel, up);
+
+ pim_upstream_remove_children (up);
+ if (up->sources)
+ list_delete (up->sources);
+
+ hash_release (pim_upstream_hash, up);
XFREE(MTYPE_PIM_UPSTREAM, up);
return NULL;
}
- listnode_add(qpim_upstream_list, up);
+ pim_ifp = up->rpf.source_nexthop.interface->info;
+ if (pim_ifp)
+ up->channel_oil = pim_channel_oil_add(&up->sg, pim_ifp->mroute_vif_index);
+
+ listnode_add_sort(pim_upstream_list, up);
+
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("%s: Created Upstream %s", __PRETTY_FUNCTION__, up->sg_str);
return up;
}
-struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
- struct in_addr group_addr)
+struct pim_upstream *pim_upstream_find(struct prefix_sg *sg)
{
- struct listnode *up_node;
- struct pim_upstream *up;
-
- for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
- if (group_addr.s_addr == up->group_addr.s_addr) {
- if ((up->source_addr.s_addr == INADDR_ANY) ||
- (source_addr.s_addr == up->source_addr.s_addr)) {
- return up;
- }
- }
- }
+ struct pim_upstream lookup;
+ struct pim_upstream *up = NULL;
- return 0;
+ lookup.sg = *sg;
+ up = hash_lookup (pim_upstream_hash, &lookup);
+ return up;
}
-struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
- struct in_addr group_addr,
- struct interface *incoming)
+static void pim_upstream_ref(struct pim_upstream *up, int flags)
{
- struct pim_upstream *up;
+ up->flags |= flags;
+ ++up->ref_count;
+}
- up = pim_upstream_find(source_addr, group_addr);
+struct pim_upstream *pim_upstream_add(struct prefix_sg *sg,
+ struct interface *incoming,
+ int flags, const char *name)
+{
+ struct pim_upstream *up = NULL;
+ int found = 0;
+ up = pim_upstream_find(sg);
if (up) {
- ++up->ref_count;
+ pim_upstream_ref(up, flags);
+ found = 1;
}
else {
- up = pim_upstream_new(source_addr, group_addr, incoming);
+ up = pim_upstream_new(sg, incoming, flags);
}
+ if (PIM_DEBUG_TRACE)
+ {
+ if (up)
+ zlog_debug("%s(%s): %s, found: %d: ref_count: %d",
+ __PRETTY_FUNCTION__, name,
+ up->sg_str, found,
+ up->ref_count);
+ else
+ zlog_debug("%s(%s): (%s) failure to create",
+ __PRETTY_FUNCTION__, name,
+ pim_str_sg_dump (sg));
+ }
+
return up;
}
-void pim_upstream_del(struct pim_upstream *up)
+static int
+pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
+ struct pim_ifchannel *ch)
{
- --up->ref_count;
+ struct pim_upstream *parent = up->parent;
- if (up->ref_count < 1) {
- pim_upstream_delete(up);
- }
+ if (ch->upstream == up)
+ {
+ if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
+ return 1;
+
+ if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+ return 0;
+ }
+
+ /*
+ * joins (*,G)
+ */
+ if (parent && ch->upstream == parent)
+ {
+ if (!pim_macro_ch_lost_assert (ch) && pim_macro_chisin_joins_or_include (ch))
+ return 1;
+ }
+
+ return 0;
}
/*
*/
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 */
}
/*
/* 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;
}
struct pim_upstream *up;
/*
- Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
- */
- for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+ * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
+ */
+ for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
- if (PIM_DEBUG_PIM_TRACE) {
- char neigh_str[100];
- char src_str[100];
- char grp_str[100];
- char rpf_addr_str[100];
+ if (PIM_DEBUG_TRACE) {
+ char neigh_str[INET_ADDRSTRLEN];
+ char rpf_addr_str[PREFIX_STRLEN];
pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
- pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
- pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
- zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
+ pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+ zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
__PRETTY_FUNCTION__,
- neigh_str, src_str, grp_str,
+ neigh_str, up->sg_str,
up->join_state == PIM_UPSTREAM_JOINED,
rpf_addr_str);
}
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",
void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
struct interface *old_rpf_ifp)
{
- struct listnode *ifnode;
- struct listnode *ifnextnode;
- struct interface *ifp;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
- struct listnode *chnode;
- struct listnode *chnextnode;
- struct pim_ifchannel *ch;
- struct pim_interface *pim_ifp;
+ /* search all ifchannels */
+ for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
- pim_ifp = ifp->info;
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* search all ifchannels */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
- if (ch->upstream != up)
- continue;
+ if (ch->upstream != up)
+ continue;
- if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
- if (
- /* RPF_interface(S) was NOT I */
- (old_rpf_ifp == ch->interface)
- &&
- /* RPF_interface(S) stopped being I */
- (ch->upstream->rpf.source_nexthop.interface != ch->interface)
- ) {
- assert_action_a5(ch);
- }
- } /* PIM_IFASSERT_I_AM_LOSER */
+ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+ if (
+ /* RPF_interface(S) was NOT I */
+ (old_rpf_ifp == ch->interface)
+ &&
+ /* RPF_interface(S) stopped being I */
+ (ch->upstream->rpf.source_nexthop.interface != ch->interface)
+ ) {
+ assert_action_a5(ch);
+ }
+ } /* PIM_IFASSERT_I_AM_LOSER */
- pim_ifchannel_update_assert_tracking_desired(ch);
- }
+ pim_ifchannel_update_assert_tracking_desired(ch);
}
}
void pim_upstream_update_could_assert(struct pim_upstream *up)
{
- struct listnode *ifnode;
- struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
- struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-
- if (ch->upstream != up)
- continue;
-
- pim_ifchannel_update_could_assert(ch);
+ if (ch->upstream != up)
+ continue;
- } /* scan iface channel list */
- } /* scan iflist */
+ pim_ifchannel_update_could_assert(ch);
+ } /* scan iface channel list */
}
void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
{
- struct listnode *ifnode;
- struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
- struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
-
- if (ch->upstream != up)
- continue;
+ if (ch->upstream != up)
+ continue;
- pim_ifchannel_update_my_assert_metric(ch);
+ pim_ifchannel_update_my_assert_metric(ch);
- } /* scan iface channel list */
- } /* scan iflist */
+ } /* scan iface channel list */
}
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
{
- struct listnode *ifnode;
- struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
- struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
- /* scan all interfaces */
- for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
- pim_ifp = ifp->info;
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
+ pim_ifp = ch->interface->info;
if (!pim_ifp)
continue;
- /* scan per-interface (S,G) state */
- for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+ if (ch->upstream != up)
+ continue;
- if (ch->upstream != up)
- continue;
+ pim_ifchannel_update_assert_tracking_desired(ch);
- pim_ifchannel_update_assert_tracking_desired(ch);
+ } /* scan iface channel list */
+}
- } /* scan iface channel list */
- } /* scan iflist */
+/* When kat is stopped CouldRegister goes to false so we need to
+ * transition the (S, G) on FHR to NI state and remove reg tunnel
+ * from the OIL */
+static void pim_upstream_fhr_kat_expiry(struct pim_upstream *up)
+{
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
+ return;
+
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("kat expired on %s; clear fhr reg state", up->sg_str);
+
+ /* stop reg-stop timer */
+ THREAD_OFF(up->t_rs_timer);
+ /* remove regiface from the OIL if it is there*/
+ pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+ /* move to "not-joined" */
+ up->join_state = PIM_UPSTREAM_NOTJOINED;
+ PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
+}
+
+/* When kat is started CouldRegister can go to true. And if it does we
+ * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
+ * to the OIL */
+static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
+{
+ if (pim_upstream_could_register(up)) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str);
+
+ PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+ if (up->join_state == PIM_UPSTREAM_NOTJOINED) {
+ pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
+ up->join_state = PIM_UPSTREAM_JOINED;
+ }
+ }
}
/*
* On an RP, the PMBR value must be cleared when the
* Keepalive Timer expires
+ * KAT expiry indicates that flow is inactive. If the flow was created or
+ * maintained by activity now is the time to deref it.
*/
static int
pim_upstream_keep_alive_timer (struct thread *t)
struct pim_upstream *up;
up = THREAD_ARG(t);
+ up->t_ka_timer = NULL;
+
+ if (I_am_RP (up->sg.grp))
+ {
+ pim_br_clear_pmbr (&up->sg);
+ /*
+ * We need to do more here :)
+ * But this is the start.
+ */
+ }
- if (I_am_RP (up->group_addr))
- {
- pim_br_clear_pmbr (up->source_addr, up->group_addr);
- /*
- * We need to do more here :)
- * But this is the start.
- */
- }
- else
- {
- pim_mroute_update_counters (up->channel_oil);
+ /* source is no longer active - pull the SA from MSDP's cache */
+ pim_msdp_sa_local_del(&up->sg);
+
+ /* if entry was created because of activity we need to deref it */
+ if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
+ {
+ pim_upstream_fhr_kat_expiry(up);
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("kat expired on %s; remove stream reference", up->sg_str);
+ PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
+ pim_upstream_del(up, __PRETTY_FUNCTION__);
+ }
- if (up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt)
- {
- pim_mroute_del (up->channel_oil);
- pim_upstream_delete (up);
- }
- else
- {
- up->t_ka_timer = NULL;
- pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
- }
- }
- return 1;
+ return 0;
}
void
pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
uint32_t time)
{
+ if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug ("kat start on %s with no stream reference", up->sg_str);
+ }
+ THREAD_OFF (up->t_ka_timer);
THREAD_TIMER_ON (master,
up->t_ka_timer,
pim_upstream_keep_alive_timer,
up, time);
+
+ /* any time keepalive is started against a SG we will have to
+ * re-evaluate our active source database */
+ pim_msdp_sa_local_update(up);
+}
+
+/* MSDP on RP needs to know if a source is registerable to this RP */
+static int
+pim_upstream_msdp_reg_timer(struct thread *t)
+{
+ struct pim_upstream *up;
+
+ up = THREAD_ARG(t);
+ up->t_msdp_reg_timer = NULL;
+
+ /* source is no longer active - pull the SA from MSDP's cache */
+ pim_msdp_sa_local_del(&up->sg);
+ return 1;
+}
+void
+pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
+{
+ THREAD_OFF(up->t_msdp_reg_timer);
+ THREAD_TIMER_ON(master, up->t_msdp_reg_timer,
+ pim_upstream_msdp_reg_timer, up, PIM_MSDP_REG_RXED_PERIOD);
+
+ pim_msdp_sa_local_update(up);
}
/*
* 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;
+
+}
#define PIM_UPSTREAM_H
#include <zebra.h>
+#include <prefix.h>
+
+#include <pimd/pim_rpf.h>
#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED (1 << 0)
-#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0)
+#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (1 << 1)
+#define PIM_UPSTREAM_FLAG_MASK_FHR (1 << 2)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_IGMP (1 << 3)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_PIM (1 << 4)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_STREAM (1 << 5)
+#define PIM_UPSTREAM_FLAG_MASK_SRC_MSDP (1 << 6)
#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+#define PIM_UPSTREAM_FLAG_TEST_FHR(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_FHR)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_PIM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+#define PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+#define PIM_UPSTREAM_FLAG_SET_FHR(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_FHR)
+#define PIM_UPSTREAM_FLAG_SET_SRC_IGMP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+#define PIM_UPSTREAM_FLAG_SET_SRC_PIM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+#define PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+#define PIM_UPSTREAM_FLAG_SET_SRC_MSDP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
-
-/*
- RFC 4601:
-
- Metric Preference
- Preference value assigned to the unicast routing protocol that
- provided the route to the multicast source or Rendezvous-Point.
-
- Metric
- The unicast routing table metric associated with the route used to
- reach the multicast source or Rendezvous-Point. The metric is in
- units applicable to the unicast routing protocol used.
-*/
-struct pim_nexthop {
- struct interface *interface; /* RPF_interface(S) */
- struct in_addr mrib_nexthop_addr; /* MRIB.next_hop(S) */
- uint32_t mrib_metric_preference; /* MRIB.pref(S) */
- uint32_t mrib_route_metric; /* MRIB.metric(S) */
-};
-
-struct pim_rpf {
- struct pim_nexthop source_nexthop;
- struct in_addr rpf_addr; /* RPF'(S,G) */
-};
-
-enum pim_rpf_result {
- PIM_RPF_OK = 0,
- PIM_RPF_CHANGED,
- PIM_RPF_FAILURE
-};
+#define PIM_UPSTREAM_FLAG_UNSET_FHR(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_FHR)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
+#define PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
enum pim_upstream_state {
PIM_UPSTREAM_NOTJOINED,
- PIM_UPSTREAM_JOINED
+ PIM_UPSTREAM_JOINED,
+ PIM_UPSTREAM_JOIN_PENDING,
+ PIM_UPSTREAM_PRUNE,
};
enum pim_upstream_sptbit {
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;
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)
*/
#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);
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 */
#include <zebra.h>
#include "log.h"
+#include "prefix.h"
#include "pim_util.h"
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);
+}
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 */
#include "if.h"
#include "linklist.h"
+#include "prefix.h"
+#include "vty.h"
#include "vrf.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_vty.h"
#include "pim_pim.h"
#include "pim_oil.h"
#include "pim_static.h"
+#include "pim_rp.h"
+#include "pim_msdp.h"
-int pim_debug_config_write(struct vty *vty)
+int
+pim_debug_config_write (struct vty *vty)
{
int writes = 0;
+ if (PIM_DEBUG_MSDP_EVENTS) {
+ vty_out(vty, "debug msdp events%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ vty_out(vty, "debug msdp packets%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ vty_out(vty, "debug msdp internal%s", VTY_NEWLINE);
+ ++writes;
+ }
if (PIM_DEBUG_IGMP_EVENTS) {
vty_out(vty, "debug igmp events%s", VTY_NEWLINE);
++writes;
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;
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);
++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;
vty_out(vty, "!%s", VTY_NEWLINE);
++writes;
for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
- char source_str[100];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE);
++writes;
vty_out(vty, "%s", VTY_NEWLINE);
}
+ /* update source */
+ if (PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) {
+ char src_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", pim_ifp->update_source, src_str,
+ sizeof(src_str));
+ vty_out(vty, " ip pim use-source %s%s", src_str, VTY_NEWLINE);
+ ++writes;
+ }
+
/* IF ip igmp */
if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
vty_out(vty, " ip igmp%s", VTY_NEWLINE);
++writes;
}
+ /* ip igmp version */
+ if (pim_ifp->igmp_version != IGMP_DEFAULT_VERSION)
+ {
+ vty_out(vty, " ip igmp version %d%s",
+ pim_ifp->igmp_version,
+ VTY_NEWLINE);
+ ++writes;
+ }
+
/* IF ip igmp query-interval */
if (pim_ifp->igmp_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL)
{
/* 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;
struct listnode *node;
struct igmp_join *ij;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", ij->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<src?>", ij->source_addr, source_str, sizeof(source_str));
+ inet_ntop(AF_INET, &ij->source_addr, source_str, sizeof(source_str));
vty_out(vty, " ip igmp join %s %s%s",
group_str, source_str,
VTY_NEWLINE);
#include "zclient.h"
#include "stream.h"
#include "network.h"
+#include "vty.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_pim.h"
}
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
}
}
+ if (ifp->info)
+ pim_if_del_vif(ifp);
+
return 0;
}
{
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
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);
#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 */
/* but we had a primary address already */
char buf[BUFSIZ];
- char old[100];
prefix2str(p, buf, BUFSIZ);
- pim_inet4_dump("<old?>", primary_addr, old, sizeof(old));
- zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s",
+ zlog_warn("%s: %s : forcing secondary flag on %s",
__PRETTY_FUNCTION__,
- c->ifp->name, old, buf);
+ c->ifp->name, buf);
}
SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
}
}
pim_if_addr_add(c);
+ if (pim_ifp)
+ pim_rp_check_on_if_add(pim_ifp);
+
+ if (if_is_loopback (c->ifp))
+ {
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
+ {
+ if (!if_is_loopback (ifp) && if_is_operative (ifp))
+ pim_if_addr_add_all (ifp);
+ }
+ }
return 0;
}
{
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
#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;
}
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
/* 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);
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);
{
if (PIM_DEBUG_ZEBRA)
{
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_debug("%s %s: could not find input interface(%d) for (S,G)=(%s,%s)",
__FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent,
source_str, group_str);
}
- pim_mroute_del (c_oil);
+ pim_mroute_del (c_oil, __PRETTY_FUNCTION__);
return;
}
if (input_iface_vif_index == c_oil->oil.mfcc_parent)
{
+ if (!c_oil->installed)
+ pim_mroute_add (c_oil, __PRETTY_FUNCTION__);
+
/* RPF unchanged */
return;
}
{
struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
__FILE__, __PRETTY_FUNCTION__,
source_str, group_str,
- old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
- new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ old_iif->name, c_oil->oil.mfcc_parent,
+ new_iif->name, input_iface_vif_index);
}
/* new iif loops to existing oif ? */
struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
if (PIM_DEBUG_ZEBRA) {
- char source_str[100];
- char group_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
__FILE__, __PRETTY_FUNCTION__,
source_str, group_str,
- new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ new_iif->name, input_iface_vif_index);
}
- del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
+ //del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
}
/* update iif vif_index */
old_vif_index = c_oil->oil.mfcc_parent;
c_oil->oil.mfcc_parent = input_iface_vif_index;
- zlog_debug ("FF");
/* update kernel multicast forwarding cache (MFC) */
- if (pim_mroute_add(c_oil))
+ if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__))
{
- /* just log warning */
- struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
- struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
- __FILE__, __PRETTY_FUNCTION__,
- source_str, group_str,
- old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
- new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ if (PIM_DEBUG_MROUTE)
+ {
+ /* just log warning */
+ struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
+ struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_debug("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str,
+ old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
+ new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ }
}
}
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;
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;
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));
sched_rpf_cache_refresh();
+ pim_rp_setup ();
return 0;
}
{
zclient_send_reg_requests (zclient, VRF_DEFAULT);
}
+
void pim_zebra_init(char *zebra_sock_path)
{
int i;
__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)
static int fib_lookup_if_vif_index(struct in_addr addr)
{
- struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+ struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
int num_ifindex;
int vif_index;
ifindex_t first_ifindex;
- num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
- PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr,
+ num_ifindex = zclient_lookup_nexthop(nexthop_tab,
+ MULTIPATH_NUM, addr,
PIM_NEXTHOP_LOOKUP_MAX);
if (num_ifindex < 1) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: could not find nexthop ifindex for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- addr_str);
+ if (PIM_DEBUG_ZEBRA)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: could not find nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ addr_str);
+ }
return -1;
}
first_ifindex = nexthop_tab[0].ifindex;
if (num_ifindex > 1) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
- __FILE__, __PRETTY_FUNCTION__,
- num_ifindex, addr_str, first_ifindex);
+ if (PIM_DEBUG_ZEBRA)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
+ __FILE__, __PRETTY_FUNCTION__,
+ num_ifindex, addr_str, first_ifindex);
+ }
/* debug warning only, do not return */
}
if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s",
__FILE__, __PRETTY_FUNCTION__,
vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex);
if (vif_index < 0) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- vif_index, addr_str);
+ if (PIM_DEBUG_ZEBRA)
+ {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: low vif_index=%d < 1 nexthop for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ vif_index, addr_str);
+ }
return -2;
}
- zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
-
- if (vif_index > qpim_mroute_oif_highest_vif_index) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- vif_index, qpim_mroute_oif_highest_vif_index, addr_str);
-
- zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?",
- __FILE__, __PRETTY_FUNCTION__,
- ifindex2ifname(vif_index),
- vif_index);
-
- return -3;
- }
-
return vif_index;
}
struct pim_interface *pim_ifp;
int old_ttl;
- zassert(channel_oil);
-
pim_ifp = oif->info;
- zassert(pim_ifp->mroute_vif_index >= 1);
- zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
- zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
-
if (PIM_DEBUG_MROUTE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
/* Prevent single protocol from unsubscribing same interface from
channel (S,G) multiple times */
if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- proto_mask, oif->name, pim_ifp->mroute_vif_index,
- channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
- source_str, group_str);
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ }
return -2;
}
/* Check the OIF keeps existing before returning, and only log
warning otherwise */
if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- proto_mask, oif->name, pim_ifp->mroute_vif_index,
- channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
- source_str, group_str);
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ }
}
return 0;
old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
if (old_ttl < 1) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- oif->name, pim_ifp->mroute_vif_index,
- source_str, group_str);
+ if (PIM_DEBUG_MROUTE)
+ {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+ }
return -3;
}
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
- if (pim_mroute_add(channel_oil)) {
- char group_str[100];
- char source_str[100];
+ if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)",
--channel_oil->oil_size;
if (channel_oil->oil_size < 1) {
- if (pim_mroute_del(channel_oil)) {
- /* just log a warning in case of failure */
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
- zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- source_str, group_str);
+ if (pim_mroute_del(channel_oil, __PRETTY_FUNCTION__)) {
+ if (PIM_DEBUG_MROUTE)
+ {
+ /* just log a warning in case of failure */
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_debug("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ }
}
}
if (PIM_DEBUG_MROUTE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
void igmp_source_forward_start(struct igmp_source *source)
{
struct igmp_group *group;
+ struct prefix_sg sg;
int result;
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = source->source_addr;
+ sg.grp = source->source_group->group_addr;
+
if (PIM_DEBUG_IGMP_TRACE) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+ zlog_debug("%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d",
__PRETTY_FUNCTION__,
- source_str, group_str,
+ pim_str_sg_dump (&sg),
source->source_group->group_igmp_sock->fd,
source->source_group->group_igmp_sock->interface->name,
IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
struct in_addr vif_source;
struct pim_interface *pim_oif;
- if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr))
+ if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr, sg.grp))
return;
int input_iface_vif_index = fib_lookup_if_vif_index(vif_source);
if (input_iface_vif_index < 1) {
- char source_str[100];
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_warn("%s %s: could not find input interface for source %s",
- __FILE__, __PRETTY_FUNCTION__,
- source_str);
+ if (PIM_DEBUG_IGMP_TRACE)
+ {
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_debug("%s %s: could not find input interface for source %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str);
+ }
return;
}
*/
pim_oif = source->source_group->group_igmp_sock->interface->info;
if (!pim_oif) {
- zlog_warn("%s: multicast not enabled on oif=%s ?",
- __PRETTY_FUNCTION__,
- source->source_group->group_igmp_sock->interface->name);
- return;
- }
- if (pim_oif->mroute_vif_index < 1) {
- zlog_warn("%s %s: oif=%s vif_index=%d < 1",
- __FILE__, __PRETTY_FUNCTION__,
- source->source_group->group_igmp_sock->interface->name,
- pim_oif->mroute_vif_index);
+ if (PIM_DEBUG_IGMP_TRACE)
+ {
+ zlog_debug("%s: multicast not enabled on oif=%s ?",
+ __PRETTY_FUNCTION__,
+ source->source_group->group_igmp_sock->interface->name);
+ }
return;
}
+
if (input_iface_vif_index == pim_oif->mroute_vif_index) {
/* ignore request for looped MFC entry */
if (PIM_DEBUG_IGMP_TRACE) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d",
+ zlog_debug("%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d",
__PRETTY_FUNCTION__,
- source_str, group_str,
+ pim_str_sg_dump (&sg),
source->source_group->group_igmp_sock->fd,
source->source_group->group_igmp_sock->interface->name,
input_iface_vif_index);
return;
}
- source->source_channel_oil = pim_channel_oil_add(group->group_addr,
- source->source_addr,
+ source->source_channel_oil = pim_channel_oil_add(&sg,
input_iface_vif_index);
if (!source->source_channel_oil) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- source_str, group_str);
+ if (PIM_DEBUG_IGMP_TRACE)
+ {
+ zlog_debug("%s %s: could not create OIL for channel (S,G)=%s",
+ __FILE__, __PRETTY_FUNCTION__,
+ pim_str_sg_dump (&sg));
+ }
return;
}
}
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;
}
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);
}
void igmp_source_forward_stop(struct igmp_source *source)
{
struct igmp_group *group;
+ struct prefix_sg sg;
int result;
+ memset (&sg, 0, sizeof (struct prefix_sg));
+ sg.src = source->source_addr;
+ sg.grp = source->source_group->group_addr;
+
if (PIM_DEBUG_IGMP_TRACE) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+ zlog_debug("%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d",
__PRETTY_FUNCTION__,
- source_str, group_str,
+ pim_str_sg_dump (&sg),
source->source_group->group_igmp_sock->fd,
source->source_group->group_igmp_sock->interface->name,
IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
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;
}
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);
}
struct pim_upstream *up = ch->upstream;
if (PIM_DEBUG_PIM_TRACE) {
- char source_str[100];
- char group_str[100];
- char upstream_str[100];
+ char source_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
+ char upstream_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", ch->sg.src, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", ch->sg.grp, group_str, sizeof(group_str));
pim_inet4_dump("<upstream?>", up->upstream_addr, upstream_str, sizeof(upstream_str));
zlog_debug("%s: (S,G)=(%s,%s) oif=%s(%s)",
__PRETTY_FUNCTION__,
if (!up->channel_oil) {
int input_iface_vif_index = fib_lookup_if_vif_index(up->upstream_addr);
if (input_iface_vif_index < 1) {
- char source_str[100];
- pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
- zlog_warn("%s %s: could not find input interface for source %s",
- __FILE__, __PRETTY_FUNCTION__,
- source_str);
+ if (PIM_DEBUG_PIM_TRACE)
+ {
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<source?>", up->sg.src, source_str, sizeof(source_str));
+ zlog_debug("%s %s: could not find input interface for source %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str);
+ }
return;
}
- up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr,
+ up->channel_oil = pim_channel_oil_add(&up->sg,
input_iface_vif_index);
if (!up->channel_oil) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", up->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
- zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- source_str, group_str);
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("%s %s: could not create OIL for channel (S,G)=%s",
+ __FILE__, __PRETTY_FUNCTION__,
+ up->sg_str);
return;
}
}
struct pim_upstream *up = ch->upstream;
if (PIM_DEBUG_PIM_TRACE) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
+ zlog_debug("%s: (S,G)=%s oif=%s",
__PRETTY_FUNCTION__,
- source_str, group_str, ch->interface->name);
+ ch->sg_str, ch->interface->name);
}
if (!up->channel_oil) {
- char source_str[100];
- char group_str[100];
- pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL",
- __PRETTY_FUNCTION__,
- source_str, group_str, ch->interface->name);
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("%s: (S,G)=%s oif=%s missing channel OIL",
+ __PRETTY_FUNCTION__,
+ ch->sg_str, ch->interface->name);
return;
}
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 */
#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);
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;
zlog_notice("%s: zclient lookup socket initialized",
__PRETTY_FUNCTION__);
- return zlookup;
}
static int zclient_read_nexthop(struct zclient *zlookup,
int nexthop_num;
int i, err;
- if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
+ if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s: addr=%s",
__PRETTY_FUNCTION__,
raddr.s_addr = stream_get_ipv4(s);
if (raddr.s_addr != addr.s_addr) {
- char addr_str[100];
- char raddr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
+ char raddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
zlog_warn("%s: address mismatch: addr=%s raddr=%s",
return -6;
}
- length -= MIN_LEN;
-
for (i = 0; i < nexthop_num; ++i) {
enum nexthop_types_t nexthop_type;
+ struct pim_neighbor *nbr;
- if (length < 1) {
- zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
- __func__, zlookup->sock, length);
- return -7;
- }
-
nexthop_type = stream_getc(s);
- --length;
-
+ if (num_ifindex >= tab_size) {
+ char addr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ (num_ifindex + 1), tab_size, addr_str);
+ return num_ifindex;
+ }
switch (nexthop_type) {
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_IPV4_IFINDEX:
- if (num_ifindex >= tab_size) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- (num_ifindex + 1), tab_size, addr_str);
- return num_ifindex;
- }
- if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX) {
- if (length < 4) {
- zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
- __func__, zlookup->sock, length);
- return -8;
- }
- nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
- length -= 4;
+ case NEXTHOP_TYPE_IPV4:
+ nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET;
+ if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX ||
+ nexthop_type == NEXTHOP_TYPE_IPV4) {
+ nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = stream_get_ipv4(s);
}
else {
- nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+ nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
}
nexthop_tab[num_ifindex].ifindex = stream_getl(s);
nexthop_tab[num_ifindex].protocol_distance = distance;
nexthop_tab[num_ifindex].route_metric = metric;
++num_ifindex;
break;
- case NEXTHOP_TYPE_IPV4:
- if (num_ifindex >= tab_size) {
- char addr_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- (num_ifindex + 1), tab_size, addr_str);
- return num_ifindex;
- }
- nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
- length -= 4;
- nexthop_tab[num_ifindex].ifindex = 0;
- nexthop_tab[num_ifindex].protocol_distance = distance;
- nexthop_tab[num_ifindex].route_metric = metric;
- if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
- char nexthop_str[100];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
- zlog_debug("%s %s: zebra returned recursive nexthop %s for address %s",
- __FILE__, __PRETTY_FUNCTION__,
- nexthop_str, addr_str);
- }
- ++num_ifindex;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET6;
+ stream_get (&nexthop_tab[num_ifindex].nexthop_addr.u.prefix6, s, 16);
+ nexthop_tab[num_ifindex].ifindex = stream_getl (s);
+ nbr = pim_neighbor_find_if (if_lookup_by_index_vrf (nexthop_tab[num_ifindex].ifindex, VRF_DEFAULT));
+ if (nbr)
+ {
+ nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET;
+ nexthop_tab[num_ifindex].nexthop_addr.u.prefix4 = nbr->source_addr;
+ }
+ ++num_ifindex;
break;
default:
/* do nothing */
{
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
__FILE__, __PRETTY_FUNCTION__,
return num_ifindex;
}
-static int zclient_lookup_nexthop_once(struct zclient *zlookup,
- struct pim_zlookup_nexthop nexthop_tab[],
- const int tab_size,
- struct in_addr addr)
+static int
+zclient_lookup_nexthop_once (struct pim_zlookup_nexthop nexthop_tab[],
+ const int tab_size,
+ struct in_addr addr)
{
struct stream *s;
int ret;
- if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
+ if (PIM_DEBUG_PIM_TRACE_DETAIL) {
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s: addr=%s",
__PRETTY_FUNCTION__,
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;
}
tab_size, addr);
}
-int zclient_lookup_nexthop(struct zclient *zlookup,
- struct pim_zlookup_nexthop nexthop_tab[],
- const int tab_size,
- struct in_addr addr,
- int max_lookup)
+int
+zclient_lookup_nexthop (struct pim_zlookup_nexthop nexthop_tab[],
+ const int tab_size,
+ struct in_addr addr,
+ int max_lookup)
{
int lookup;
uint32_t route_metric = 0xFFFFFFFF;
uint8_t protocol_distance = 0xFF;
+ qpim_nexthop_lookups++;
+
for (lookup = 0; lookup < max_lookup; ++lookup) {
int num_ifindex;
int first_ifindex;
- struct in_addr nexthop_addr;
+ struct prefix nexthop_addr;
- num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
- PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
+ num_ifindex = zclient_lookup_nexthop_once(nexthop_tab,
+ tab_size, addr);
if (num_ifindex < 1) {
if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
__FILE__, __PRETTY_FUNCTION__,
}
/*
- 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;
if (lookup > 0) {
/* Report non-recursive success after first lookup */
if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d",
__FILE__, __PRETTY_FUNCTION__,
}
/* 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;
}
if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
- char nexthop_str[100];
+ char addr_str[INET_ADDRSTRLEN];
+ char nexthop_str[PREFIX_STRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
+ pim_addr_dump("<nexthop?>", &nexthop_addr, nexthop_str, sizeof(nexthop_str));
zlog_debug("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d",
__FILE__, __PRETTY_FUNCTION__,
lookup, max_lookup, nexthop_str, addr_str,
nexthop_tab[0].route_metric);
}
- addr = nexthop_addr; /* use nexthop addr for recursive lookup */
+ addr = nexthop_addr.u.prefix4; /* use nexthop addr for recursive lookup */
} /* for (max_lookup) */
if (PIM_DEBUG_ZEBRA) {
- char addr_str[100];
+ char addr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
__FILE__, __PRETTY_FUNCTION__,
return -2;
}
+
+void
+pim_zlookup_show_ip_multicast (struct vty *vty)
+{
+ vty_out(vty, "Zclient lookup socket: ");
+ if (zlookup) {
+ vty_out(vty, "%d failures=%d%s", zlookup->sock,
+ zlookup->fail, VTY_NEWLINE);
+ }
+ else {
+ vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
+ }
+}
+
+int
+pim_zlookup_sg_statistics (struct channel_oil *c_oil)
+{
+ struct stream *s = zlookup->obuf;
+ uint16_t command = 0;
+ unsigned long long lastused;
+ struct prefix_sg sg;
+ int count = 0;
+ int ret;
+ struct interface *ifp = pim_if_find_by_vif_index (c_oil->oil.mfcc_parent);
+
+ if (PIM_DEBUG_ZEBRA)
+ {
+ struct prefix_sg more;
+
+ more.src = c_oil->oil.mfcc_origin;
+ more.grp = c_oil->oil.mfcc_mcastgrp;
+ zlog_debug ("Sending Request for New Channel Oil Information(%s)", pim_str_sg_dump (&more));
+ }
+
+ if (!ifp)
+ return -1;
+
+ stream_reset (s);
+ zclient_create_header (s, ZEBRA_IPMR_ROUTE_STATS, VRF_DEFAULT);
+ stream_put_in_addr (s, &c_oil->oil.mfcc_origin);
+ stream_put_in_addr (s, &c_oil->oil.mfcc_mcastgrp);
+ stream_putl (s, ifp->ifindex);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ count = stream_get_endp (s);
+ ret = writen (zlookup->sock, s->data, count);
+ if (ret <= 0)
+ {
+ zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket",
+ __FILE__, __PRETTY_FUNCTION__, errno);
+ return -1;
+ }
+
+ s = zlookup->ibuf;
+
+ while (command != ZEBRA_IPMR_ROUTE_STATS)
+ {
+ int err;
+ uint16_t length = 0;
+ vrf_id_t vrf_id;
+ u_char marker;
+ u_char version;
+
+ stream_reset (s);
+ err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
+ &vrf_id, &command);
+ if (err < 0)
+ {
+ zlog_err ("%s %s: zclient_read_header() failed",
+ __FILE__, __PRETTY_FUNCTION__);
+ zclient_lookup_failed(zlookup);
+ return -1;
+ }
+ }
+
+ sg.src.s_addr = stream_get_ipv4 (s);
+ sg.grp.s_addr = stream_get_ipv4 (s);
+ if (sg.src.s_addr != c_oil->oil.mfcc_origin.s_addr ||
+ sg.grp.s_addr != c_oil->oil.mfcc_mcastgrp.s_addr)
+ {
+ zlog_err ("%s: Received wrong %s information",
+ __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
+ zclient_lookup_failed (zlookup);
+ return -3;
+ }
+
+ stream_get (&lastused, s, sizeof (lastused));
+ ret = stream_getl (s);
+
+ if (PIM_DEBUG_ZEBRA)
+ zlog_debug ("Received %lld for %s success: %d", lastused, pim_str_sg_dump (&sg), ret);
+
+ c_oil->cc.lastused = lastused;
+
+ return 0;
+
+}
#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 */
#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"
#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;
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;
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;
{
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__,
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) {
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;
/*
#include <stdint.h>
+#include "pim_str.h"
#include "pim_memory.h"
#include "pim_assert.h"
#define PIMD_PROGNAME "pimd"
#define PIMD_DEFAULT_CONFIG "pimd.conf"
#define PIMD_VTY_PORT 2611
-#define PIMD_BUG_ADDRESS "https://github.com/udhos/qpimd"
#define PIM_IP_HEADER_MIN_LEN (20)
#define PIM_IP_HEADER_MAX_LEN (60)
#define PIM_INADDR_IS_ANY(addr) (addr).s_addr == PIM_NET_INADDR_ANY
#define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
#define PIM_MASK_PIM_EVENTS (1 << 0)
#define PIM_MASK_PIM_EVENTS_DETAIL (1 << 1)
#define PIM_MASK_PIM_PACKETS (1 << 2)
#define PIM_MASK_ZEBRA (1 << 11)
#define PIM_MASK_SSMPINGD (1 << 12)
#define PIM_MASK_MROUTE (1 << 13)
-#define PIM_MASK_PIM_HELLO (1 << 14)
-#define PIM_MASK_PIM_J_P (1 << 15)
-#define PIM_MASK_STATIC (1 << 16)
+#define PIM_MASK_MROUTE_DETAIL (1 << 14)
+#define PIM_MASK_PIM_HELLO (1 << 15)
+#define PIM_MASK_PIM_J_P (1 << 16)
+#define PIM_MASK_STATIC (1 << 17)
+#define PIM_MASK_PIM_REG (1 << 18)
+#define PIM_MASK_MSDP_EVENTS (1 << 19)
+#define PIM_MASK_MSDP_PACKETS (1 << 20)
+#define PIM_MASK_MSDP_INTERNAL (1 << 21)
+
+
+/* PIM error codes */
+#define PIM_SUCCESS 0
+#define PIM_MALLOC_FAIL -1
+#define PIM_GROUP_BAD_ADDRESS -2
+#define PIM_GROUP_OVERLAP -3
+#define PIM_GROUP_PFXLIST_OVERLAP -4
+#define PIM_RP_BAD_ADDRESS -5
+#define PIM_RP_NO_PATH -6
+#define PIM_RP_NOT_FOUND -7
+#define PIM_RP_PFXLIST_IN_USE -8
+#define PIM_IFACE_NOT_FOUND -9
+#define PIM_UPDATE_SOURCE_DUP -10
const char *const PIM_ALL_SYSTEMS;
const char *const PIM_ALL_ROUTERS;
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;
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)
#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)
#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)
#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);
$ignore{'"address-family ipv4 <unicast|multicast>"'} = "ignore";
$ignore{'"address-family ipv6"'} = "ignore";
$ignore{'"address-family ipv6 <unicast|multicast>"'} = "ignore";
+$ignore{'"address-family ipv4 vpn"'} = "ignore";
$ignore{'"address-family vpnv4"'} = "ignore";
$ignore{'"address-family vpnv4 unicast"'} = "ignore";
$ignore{'"address-family ipv4 vrf NAME"'} = "ignore";
$ignore{'"address-family <encap|encapv4>"'} = "ignore";
$ignore{'"address-family encapv6"'} = "ignore";
+$ignore{'"address-family ipv4 encap"'} = "ignore";
+$ignore{'"address-family ipv6 encap"'} = "ignore";
+$ignore{'"address-family ipv6 vpn"'} = "ignore";
$ignore{'"address-family vpnv6"'} = "ignore";
$ignore{'"address-family vpnv6 unicast"'} = "ignore";
$ignore{'"exit-address-family"'} = "ignore";
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 \
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)
#ifdef OPEN_BSD
for (n = 0; n < ifconf.ifc_len; )
{
- int size;
+ unsigned int size;
ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n);
ifp = if_get_by_name_len(ifreq->ifr_name,
}
}
+
+//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))
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; }
int ret;
if (! IS_ZEBRA_DEBUG_RIB)
return;
- ret = rib_lookup_ipv4_route (&p, &gate, VRF_DEFAULT);
+ ret = rib_lookup_ipv4_route ((struct prefix_ipv4 *)&p, &gate, VRF_DEFAULT);
prefix2str (&p, buf, sizeof(buf));
switch (rtm->rtm_type)
{
case ZEBRA_RIB_FOUND_EXACT: /* RIB RR == FIB RR */
zlog_debug ("%s: %s %s: done Ok",
__func__, lookup (rtm_type_str, rtm->rtm_type), buf);
- rib_lookup_and_dump (&p, VRF_DEFAULT);
+ rib_lookup_and_dump ((struct prefix_ipv4 *)&p, VRF_DEFAULT);
return;
break;
}
case ZEBRA_RIB_FOUND_EXACT:
zlog_debug ("%s: %s %s: desync: RR is still in RIB, while already not in FIB",
__func__, lookup (rtm_type_str, rtm->rtm_type), buf);
- rib_lookup_and_dump (&p, VRF_DEFAULT);
+ rib_lookup_and_dump ((struct prefix_ipv4 *)&p, VRF_DEFAULT);
break;
case ZEBRA_RIB_FOUND_CONNECTED:
case ZEBRA_RIB_FOUND_NOGATE:
zlog_debug ("%s: %s %s: desync: RR is still in RIB, plus gate differs",
__func__, lookup (rtm_type_str, rtm->rtm_type), buf);
- rib_lookup_and_dump (&p, VRF_DEFAULT);
+ rib_lookup_and_dump ((struct prefix_ipv4 *)&p, VRF_DEFAULT);
break;
case ZEBRA_RIB_NOTFOUND: /* RIB RR == FIB RR */
zlog_debug ("%s: %s %s: done Ok",
__func__, lookup (rtm_type_str, rtm->rtm_type), buf);
- rib_lookup_and_dump (&p, VRF_DEFAULT);
+ rib_lookup_and_dump ((struct prefix_ipv4 *)&p, VRF_DEFAULT);
return;
break;
}
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 */
#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
#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
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];
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));
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])
{
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;
}
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)
{
/* TODO */
return 0;
}
+
+extern int
+kernel_get_ipmr_sg_stats (void *mroute)
+{
+ return 0;
+}
size_t in_buf_len, fpm_msg_type_e *msg_type)
{
size_t len;
+#ifdef HAVE_NETLINK
int cmd;
+#endif
len = 0;
*msg_type = FPM_MSG_TYPE_NONE;
#include "log.h"
#include "rib.h"
#include "vty.h"
+#include "prefix.h"
#include "zebra/zserv.h"
#include "zebra/zebra_ns.h"
--- /dev/null
+/* zebra_mroute code
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "stream.h"
+#include "prefix.h"
+#include "vrf.h"
+#include "rib.h"
+
+#include "zebra/zserv.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_mroute.h"
+#include "zebra/rt.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;
+}
--- /dev/null
+/* 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
+
#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 };
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);
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;
return NULL;
}
-
+#ifdef HAVE_NETLINK
/* Display default rtm_table for all clients. */
DEFUN (show_table,
show_table_cmd,
zebrad.rtm_table_default = 0;
return CMD_SUCCESS;
}
+#endif
DEFUN (ip_forwarding,
ip_forwarding_cmd,