summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/base-branch-label.yml19
-rw-r--r--babeld/message.c8
-rw-r--r--bfdd/bfd_packet.c8
-rw-r--r--bfdd/bfdd.c2
-rw-r--r--bgpd/bgp_advertise.h18
-rw-r--r--bgpd/bgp_aspath.c2
-rw-r--r--bgpd/bgp_aspath.h79
-rw-r--r--bgpd/bgp_attr.c6
-rw-r--r--bgpd/bgp_attr.h25
-rw-r--r--bgpd/bgp_attr_evpn.c2
-rw-r--r--bgpd/bgp_bmp.c2
-rw-r--r--bgpd/bgp_clist.h21
-rw-r--r--bgpd/bgp_community.h31
-rw-r--r--bgpd/bgp_damp.h16
-rw-r--r--bgpd/bgp_debug.c53
-rw-r--r--bgpd/bgp_debug.h12
-rw-r--r--bgpd/bgp_dump.c6
-rw-r--r--bgpd/bgp_dump.h2
-rw-r--r--bgpd/bgp_errors.c12
-rw-r--r--bgpd/bgp_errors.h2
-rw-r--r--bgpd/bgp_evpn.c71
-rw-r--r--bgpd/bgp_evpn_mh.c27
-rw-r--r--bgpd/bgp_evpn_mh.h2
-rw-r--r--bgpd/bgp_evpn_private.h3
-rw-r--r--bgpd/bgp_evpn_vty.c71
-rw-r--r--bgpd/bgp_flowspec.c2
-rw-r--r--bgpd/bgp_flowspec_util.c6
-rw-r--r--bgpd/bgp_fsm.c21
-rw-r--r--bgpd/bgp_io.c10
-rw-r--r--bgpd/bgp_keepalives.c4
-rw-r--r--bgpd/bgp_label.c4
-rw-r--r--bgpd/bgp_memory.c2
-rw-r--r--bgpd/bgp_mpath.c6
-rw-r--r--bgpd/bgp_mpath.h18
-rw-r--r--bgpd/bgp_mplsvpn.c2
-rw-r--r--bgpd/bgp_network.c11
-rw-r--r--bgpd/bgp_nexthop.c8
-rw-r--r--bgpd/bgp_nht.c11
-rw-r--r--bgpd/bgp_open.c11
-rw-r--r--bgpd/bgp_packet.c100
-rw-r--r--bgpd/bgp_packet.h7
-rw-r--r--bgpd/bgp_pbr.c8
-rw-r--r--bgpd/bgp_route.c75
-rw-r--r--bgpd/bgp_routemap.c10
-rw-r--r--bgpd/bgp_rpki.c546
-rw-r--r--bgpd/bgp_snmp.c6
-rw-r--r--bgpd/bgp_updgrp.c54
-rw-r--r--bgpd/bgp_vty.c197
-rw-r--r--bgpd/bgp_zebra.c12
-rw-r--r--bgpd/bgp_zebra.h58
-rw-r--r--bgpd/bgpd.c26
-rw-r--r--bgpd/bgpd.h18
-rw-r--r--bgpd/rfapi/rfapi_rib.c5
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c20
-rw-r--r--bgpd/rfp-example/librfp/rfp_example.c2
-rw-r--r--configure.ac6
-rw-r--r--debian/changelog6
-rw-r--r--debian/control2
-rw-r--r--debian/frr-pythontools.install1
-rw-r--r--doc/developer/workflow.rst8
-rw-r--r--doc/user/bgp.rst37
-rw-r--r--doc/user/installation.rst3
-rw-r--r--doc/user/isisd.rst2
-rw-r--r--doc/user/ospfd.rst10
-rw-r--r--doc/user/overview.rst6
-rw-r--r--doc/user/pim.rst4
-rw-r--r--doc/user/pimv6.rst91
-rw-r--r--doc/user/routemap.rst5
-rw-r--r--doc/user/rpki.rst26
-rw-r--r--docker/alpine/Dockerfile7
-rw-r--r--docker/centos-7/Dockerfile8
-rw-r--r--docker/centos-8/Dockerfile6
-rw-r--r--docker/ubi-8/Dockerfile6
-rw-r--r--eigrpd/eigrp_packet.c4
-rw-r--r--eigrpd/eigrp_snmp.c2
-rw-r--r--eigrpd/eigrpd.c2
-rw-r--r--include/linux/if_link.h28
-rw-r--r--include/linux/mroute6.h3
-rw-r--r--include/linux/rtnetlink.h17
-rw-r--r--isisd/isis_bpf.c2
-rw-r--r--isisd/isis_cli.c47
-rw-r--r--isisd/isis_mt.c6
-rw-r--r--isisd/isis_mt.h3
-rw-r--r--isisd/isis_nb.c6
-rw-r--r--isisd/isis_nb.h7
-rw-r--r--isisd/isis_nb_config.c12
-rw-r--r--isisd/isis_pfpacket.c10
-rw-r--r--isisd/isis_spf.c20
-rw-r--r--isisd/isis_sr.c5
-rw-r--r--isisd/isis_tlvs.c12
-rw-r--r--isisd/isisd.c2
-rw-r--r--lib/keychain.c2
-rw-r--r--lib/linklist.c7
-rw-r--r--lib/routemap.c12
-rw-r--r--lib/sockunion.c2
-rw-r--r--lib/table.c4
-rw-r--r--lib/thread.c8
-rw-r--r--lib/thread.h9
-rw-r--r--lib/vty.c6
-rw-r--r--nhrpd/vici.c2
-rw-r--r--ospf6d/ospf6_asbr.c6
-rw-r--r--ospf6d/ospf6_auth_trailer.c4
-rw-r--r--ospf6d/ospf6_gr.c1
-rw-r--r--ospf6d/ospf6_gr_helper.c15
-rw-r--r--ospf6d/ospf6_interface.c45
-rw-r--r--ospf6d/ospf6_intra.c2
-rw-r--r--ospf6d/ospf6_message.c34
-rw-r--r--ospf6d/ospf6_neighbor.c57
-rw-r--r--ospf6d/ospf6_network.c4
-rw-r--r--ospf6d/ospf6_nssa.c2
-rw-r--r--ospf6d/ospf6_route.c8
-rw-r--r--ospf6d/ospf6_spf.c6
-rw-r--r--ospf6d/ospf6_top.c30
-rw-r--r--ospf6d/ospf6_zebra.c2
-rw-r--r--ospfclient/ospf_apiclient.c4
-rwxr-xr-xospfclient/ospfclient.py1133
-rw-r--r--ospfclient/subdir.am8
-rw-r--r--ospfd/ospf_api.c31
-rw-r--r--ospfd/ospf_api.h18
-rw-r--r--ospfd/ospf_apiserver.c360
-rw-r--r--ospfd/ospf_apiserver.h21
-rw-r--r--ospfd/ospf_asbr.c4
-rw-r--r--ospfd/ospf_dump.c46
-rw-r--r--ospfd/ospf_dump.h4
-rw-r--r--ospfd/ospf_ext.c2
-rw-r--r--ospfd/ospf_ldp_sync.c2
-rw-r--r--ospfd/ospf_lsa.c7
-rw-r--r--ospfd/ospf_lsdb.c4
-rw-r--r--ospfd/ospf_opaque.c96
-rw-r--r--ospfd/ospf_opaque.h2
-rw-r--r--ospfd/ospf_packet.c4
-rw-r--r--ospfd/ospf_ri.c2
-rw-r--r--ospfd/ospf_route.c23
-rw-r--r--ospfd/ospf_route.h5
-rw-r--r--ospfd/ospf_snmp.c38
-rw-r--r--ospfd/ospf_spf.c44
-rw-r--r--ospfd/ospf_spf.h3
-rw-r--r--ospfd/ospf_sr.c2
-rw-r--r--ospfd/ospf_te.c2
-rw-r--r--ospfd/ospf_ti_lfa.c13
-rw-r--r--ospfd/ospf_vty.c128
-rw-r--r--ospfd/ospf_zebra.c2
-rw-r--r--ospfd/ospfd.c2
-rw-r--r--ospfd/ospfd.h3
-rw-r--r--pathd/path_pcep_cli.c2
-rw-r--r--pceplib/pcep_msg_tlvs_encoding.c3
-rw-r--r--pceplib/pcep_pcc.c2
-rw-r--r--pimd/pim6_cmd.c290
-rw-r--r--pimd/pim6_cmd.h13
-rw-r--r--pimd/pim6_main.c4
-rw-r--r--pimd/pim6_mld.c3011
-rw-r--r--pimd/pim6_mld.h367
-rw-r--r--pimd/pim6_mld_protocol.h125
-rw-r--r--pimd/pim6_mroute_msg.c9
-rw-r--r--pimd/pim6_stubs.c1
-rw-r--r--pimd/pim_assert.c15
-rw-r--r--pimd/pim_bsm.c14
-rw-r--r--pimd/pim_cmd.c436
-rw-r--r--pimd/pim_cmd_common.c198
-rw-r--r--pimd/pim_cmd_common.h17
-rw-r--r--pimd/pim_hello.c9
-rw-r--r--pimd/pim_iface.c17
-rw-r--r--pimd/pim_iface.h7
-rw-r--r--pimd/pim_ifchannel.c1
-rw-r--r--pimd/pim_igmp.c1
-rw-r--r--pimd/pim_igmp_mtrace.c1
-rw-r--r--pimd/pim_igmpv2.c1
-rw-r--r--pimd/pim_igmpv3.c9
-rw-r--r--pimd/pim_instance.c3
-rw-r--r--pimd/pim_instance.h6
-rw-r--r--pimd/pim_join.c22
-rw-r--r--pimd/pim_jp_agg.c1
-rw-r--r--pimd/pim_jp_agg.h2
-rw-r--r--pimd/pim_macro.c1
-rw-r--r--pimd/pim_mroute.c12
-rw-r--r--pimd/pim_mroute.h7
-rw-r--r--pimd/pim_mroute_msg.c9
-rw-r--r--pimd/pim_msdp.c1
-rw-r--r--pimd/pim_msdp_packet.c1
-rw-r--r--pimd/pim_msdp_socket.c1
-rw-r--r--pimd/pim_msg.c1
-rw-r--r--pimd/pim_nb.c6
-rw-r--r--pimd/pim_nb.h2
-rw-r--r--pimd/pim_nb_config.c70
-rw-r--r--pimd/pim_neighbor.c23
-rw-r--r--pimd/pim_nht.c3
-rw-r--r--pimd/pim_pim.c26
-rw-r--r--pimd/pim_pim.h2
-rw-r--r--pimd/pim_register.c29
-rw-r--r--pimd/pim_rp.c33
-rw-r--r--pimd/pim_rpf.c1
-rw-r--r--pimd/pim_rpf.h2
-rw-r--r--pimd/pim_sock.c3
-rw-r--r--pimd/pim_sock.h2
-rw-r--r--pimd/pim_ssm.c1
-rw-r--r--pimd/pim_ssm.h2
-rw-r--r--pimd/pim_ssmpingd.c1
-rw-r--r--pimd/pim_static.c1
-rw-r--r--pimd/pim_static.h1
-rw-r--r--pimd/pim_tib.c1
-rw-r--r--pimd/pim_tlv.c1
-rw-r--r--pimd/pim_vty.c25
-rw-r--r--pimd/pim_vty.h2
-rw-r--r--pimd/pim_vxlan.h2
-rw-r--r--pimd/pim_zlookup.c1
-rw-r--r--pimd/pim_zlookup.h2
-rw-r--r--pimd/pim_zpthread.c1
-rw-r--r--pimd/pimd.h1
-rw-r--r--pimd/subdir.am5
-rw-r--r--python/makefile.py45
-rw-r--r--redhat/frr.spec.in101
-rw-r--r--ripd/rip_interface.c2
-rw-r--r--ripd/rip_nb_config.c2
-rw-r--r--ripd/rip_snmp.c6
-rw-r--r--ripd/ripd.c17
-rw-r--r--ripngd/ripngd.c12
-rw-r--r--snapcraft/snapcraft.yaml.in10
-rw-r--r--tests/lib/test_srcdest_table.c2
-rw-r--r--tests/ospfd/test_ospf_spf.c6
-rw-r--r--tests/topotests/bfd_topo1/test_bfd_topo1.py2
-rw-r--r--tests/topotests/bfd_topo2/test_bfd_topo2.py2
-rw-r--r--tests/topotests/bgp_auth/bgp_auth_common.py279
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth.py587
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth1.py245
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth2.py253
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth3.py234
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth4.py251
-rw-r--r--tests/topotests/bgp_gr_notification/__init__.py0
-rw-r--r--tests/topotests/bgp_gr_notification/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_gr_notification/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_gr_notification/r2/bgpd.conf11
-rw-r--r--tests/topotests/bgp_gr_notification/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py224
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py1
-rw-r--r--tests/topotests/cspf_topo1/reference/sharp-ted.json2
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref40
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref168
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff142
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff124
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff247
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff142
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff124
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff247
-rw-r--r--tests/topotests/lib/common_config.py10
-rw-r--r--tests/topotests/lib/micronet.py5
-rw-r--r--tests/topotests/lib/pim.py71
-rw-r--r--tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py12
-rw-r--r--tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py12
-rwxr-xr-xtests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py44
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py294
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py (renamed from tests/topotests/ospf_gr_helper/test_ospf_gr_helper.py)336
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py373
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py325
-rwxr-xr-xtests/topotests/ospfapi/ctester.py137
l---------tests/topotests/ospfapi/lib1
-rw-r--r--tests/topotests/ospfapi/r1/ospfd.conf10
-rw-r--r--tests/topotests/ospfapi/r1/zebra.conf4
-rw-r--r--tests/topotests/ospfapi/r2/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r2/zebra.conf7
-rw-r--r--tests/topotests/ospfapi/r3/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r3/zebra.conf7
-rw-r--r--tests/topotests/ospfapi/r4/ospfd.conf10
-rw-r--r--tests/topotests/ospfapi/r4/zebra.conf4
-rw-r--r--tests/topotests/ospfapi/test_ospf_clientapi.py470
-rw-r--r--tests/topotests/pbr_topo1/test_pbr_topo1.py12
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py2
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py2
-rw-r--r--tools/coccinelle/argv_find.cocci7
-rw-r--r--tools/coccinelle/memset.cocci21
-rw-r--r--tools/etc/frr/support_bundle_commands.conf1
-rw-r--r--tools/frr-llvm-cg.c6
-rwxr-xr-xtools/frr-reload.py1
-rwxr-xr-x[-rw-r--r--]tools/frrcommon.sh.in4
-rwxr-xr-x[-rw-r--r--]tools/releasedate.py0
-rw-r--r--vtysh/vtysh.c2
-rw-r--r--watchfrr/watchfrr.c2
-rw-r--r--yang/frr-gmp.yang1
-rw-r--r--yang/frr-isisd.yang4
-rw-r--r--yang/frr-pim.yang7
-rw-r--r--zebra/connected.c8
-rw-r--r--zebra/debug_nl.c107
-rw-r--r--zebra/interface.c86
-rw-r--r--zebra/interface.h215
-rw-r--r--zebra/ioctl.c37
-rw-r--r--zebra/kernel_netlink.c94
-rw-r--r--zebra/kernel_netlink.h37
-rw-r--r--zebra/kernel_socket.c2
-rw-r--r--zebra/main.c9
-rw-r--r--zebra/rib.h29
-rw-r--r--zebra/rt_netlink.c10
-rw-r--r--zebra/rtadv.c162
-rw-r--r--zebra/rtadv.h291
-rw-r--r--zebra/table_manager.c2
-rw-r--r--zebra/zapi_msg.c19
-rw-r--r--zebra/zebra_dplane.c22
-rw-r--r--zebra/zebra_evpn.c15
-rw-r--r--zebra/zebra_evpn_mac.c9
-rw-r--r--zebra/zebra_evpn_mh.c11
-rw-r--r--zebra/zebra_evpn_mh.h2
-rw-r--r--zebra/zebra_evpn_neigh.c9
-rw-r--r--zebra/zebra_fpm.c2
-rw-r--r--zebra/zebra_l2.h1
-rw-r--r--zebra/zebra_mpls.c4
-rw-r--r--zebra/zebra_mpls_vty.c2
-rw-r--r--zebra/zebra_nb_config.c7
-rw-r--r--zebra/zebra_nb_state.c2
-rw-r--r--zebra/zebra_netns_notify.c2
-rw-r--r--zebra/zebra_opaque.c1
-rw-r--r--zebra/zebra_pbr.c7
-rw-r--r--zebra/zebra_pbr.h3
-rw-r--r--zebra/zebra_ptm.c6
-rw-r--r--zebra/zebra_rib.c11
-rw-r--r--zebra/zebra_vrf.c5
-rw-r--r--zebra/zebra_vrf.h3
-rw-r--r--zebra/zebra_vty.c1
-rw-r--r--zebra/zebra_vxlan.c96
-rw-r--r--zebra/zebra_vxlan.h7
-rw-r--r--zebra/zserv.h4
319 files changed, 12920 insertions, 3582 deletions
diff --git a/.github/workflows/base-branch-label.yml b/.github/workflows/base-branch-label.yml
new file mode 100644
index 0000000000..01da280911
--- /dev/null
+++ b/.github/workflows/base-branch-label.yml
@@ -0,0 +1,19 @@
+name: Add base branch label
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+
+jobs:
+ label:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - uses: actions-ecosystem/action-add-labels@v1
+ with:
+ labels: |
+ ${{ github.event.pull_request.base.ref }}
diff --git a/babeld/message.c b/babeld/message.c
index 559b8c4e4a..0ddfda8d7b 100644
--- a/babeld/message.c
+++ b/babeld/message.c
@@ -286,7 +286,7 @@ channels_len(unsigned char *channels)
followed by a sequence of TLVs. TLVs of known types are also checked to meet
minimum length constraints defined for each. Return 0 for no errors. */
static int
-babel_packet_examin(const unsigned char *packet, int packetlen)
+babel_packet_examin(const unsigned char *packet, int packetlen, int *blength)
{
int i = 0, bodylen;
const unsigned char *message;
@@ -323,6 +323,8 @@ babel_packet_examin(const unsigned char *packet, int packetlen)
}
i += len + 2;
}
+
+ *blength = bodylen;
return 0;
}
@@ -356,7 +358,7 @@ parse_packet(const unsigned char *from, struct interface *ifp,
return;
}
- if (babel_packet_examin (packet, packetlen)) {
+ if (babel_packet_examin (packet, packetlen, &bodylen)) {
flog_err(EC_BABEL_PACKET,
"Received malformed packet on %s from %s.",
ifp->name, format_address(from));
@@ -369,8 +371,6 @@ parse_packet(const unsigned char *from, struct interface *ifp,
return;
}
- DO_NTOHS(bodylen, packet + 2);
-
i = 0;
while(i < bodylen) {
message = packet + 4 + i;
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index eff81d4b63..c717a333a6 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -639,6 +639,14 @@ void bfd_recv_cb(struct thread *t)
return;
}
+ /* Ensure that existing good sessions are not overridden. */
+ if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN &&
+ bfd->ses_state != PTM_BFD_ADM_DOWN) {
+ cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
+ "'remote discriminator' is zero, not overridden");
+ return;
+ }
+
/*
* Multi hop: validate packet TTL.
*/
diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c
index c764941513..7ef229da1b 100644
--- a/bfdd/bfdd.c
+++ b/bfdd/bfdd.c
@@ -337,6 +337,8 @@ int main(int argc, char *argv[])
bool ctlsockused = false;
int opt;
+ bglobal.bg_use_dplane = false;
+
/* Initialize system sockets. */
bg_init();
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
index ef4f626111..4b032ba9c6 100644
--- a/bgpd/bgp_advertise.h
+++ b/bgpd/bgp_advertise.h
@@ -142,14 +142,16 @@ struct bgp_synchronize {
#define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in)
/* Prototypes. */
-extern bool bgp_adj_out_lookup(struct peer *, struct bgp_dest *, uint32_t);
-extern void bgp_adj_in_set(struct bgp_dest *, struct peer *, struct attr *,
- uint32_t);
-extern bool bgp_adj_in_unset(struct bgp_dest *, struct peer *, uint32_t);
-extern void bgp_adj_in_remove(struct bgp_dest *, struct bgp_adj_in *);
-
-extern void bgp_sync_init(struct peer *);
-extern void bgp_sync_delete(struct peer *);
+extern bool bgp_adj_out_lookup(struct peer *peer, struct bgp_dest *dest,
+ uint32_t addpath_tx_id);
+extern void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer,
+ struct attr *attr, uint32_t addpath_id);
+extern bool bgp_adj_in_unset(struct bgp_dest *dest, struct peer *peer,
+ uint32_t addpath_id);
+extern void bgp_adj_in_remove(struct bgp_dest *dest, struct bgp_adj_in *bai);
+
+extern void bgp_sync_init(struct peer *peer);
+extern void bgp_sync_delete(struct peer *peer);
extern unsigned int baa_hash_key(const void *p);
extern bool baa_hash_cmp(const void *p1, const void *p2);
extern void bgp_advertise_add(struct bgp_advertise_attr *baa,
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 41166de050..39886337f3 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -854,7 +854,7 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit)
if (length % AS16_VALUE_SIZE)
return NULL;
- memset(&as, 0, sizeof(struct aspath));
+ memset(&as, 0, sizeof(as));
if (assegments_parse(s, length, &as.segments, use32bit) < 0)
return NULL;
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index 912db7b254..5caab73c4d 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -79,36 +79,40 @@ struct aspath {
/* Prototypes. */
extern void aspath_init(void);
extern void aspath_finish(void);
-extern struct aspath *aspath_parse(struct stream *, size_t, int);
-extern struct aspath *aspath_dup(struct aspath *);
-extern struct aspath *aspath_aggregate(struct aspath *, struct aspath *);
-extern struct aspath *aspath_prepend(struct aspath *, struct aspath *);
-extern struct aspath *aspath_filter_exclude(struct aspath *, struct aspath *);
-extern struct aspath *aspath_add_seq_n(struct aspath *, as_t, unsigned);
-extern struct aspath *aspath_add_seq(struct aspath *, as_t);
-extern struct aspath *aspath_add_confed_seq(struct aspath *, as_t);
+extern struct aspath *aspath_parse(struct stream *s, size_t length,
+ int use32bit);
+extern struct aspath *aspath_dup(struct aspath *aspath);
+extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2);
+extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2);
+extern struct aspath *aspath_filter_exclude(struct aspath *source,
+ struct aspath *exclude_list);
+extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno,
+ unsigned num);
+extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno);
+extern struct aspath *aspath_add_confed_seq(struct aspath *aspath, as_t asno);
extern bool aspath_cmp(const void *as1, const void *as2);
-extern bool aspath_cmp_left(const struct aspath *, const struct aspath *);
+extern bool aspath_cmp_left(const struct aspath *aspath1,
+ const struct aspath *aspath2);
extern bool aspath_cmp_left_confed(const struct aspath *as1,
- const struct aspath *as2xs);
-extern struct aspath *aspath_delete_confed_seq(struct aspath *);
+ const struct aspath *as2);
+extern struct aspath *aspath_delete_confed_seq(struct aspath *aspath);
extern struct aspath *aspath_empty(void);
extern struct aspath *aspath_empty_get(void);
-extern struct aspath *aspath_str2aspath(const char *);
+extern struct aspath *aspath_str2aspath(const char *str);
extern void aspath_str_update(struct aspath *as, bool make_json);
-extern void aspath_free(struct aspath *);
-extern struct aspath *aspath_intern(struct aspath *);
-extern void aspath_unintern(struct aspath **);
-extern const char *aspath_print(struct aspath *);
-extern void aspath_print_vty(struct vty *, const char *, struct aspath *,
- const char *);
-extern void aspath_print_all_vty(struct vty *);
-extern unsigned int aspath_key_make(const void *);
-extern unsigned int aspath_get_first_as(struct aspath *);
-extern unsigned int aspath_get_last_as(struct aspath *);
-extern int aspath_loop_check(struct aspath *, as_t);
-extern bool aspath_private_as_check(struct aspath *);
-extern bool aspath_single_asn_check(struct aspath *, as_t asn);
+extern void aspath_free(struct aspath *aspath);
+extern struct aspath *aspath_intern(struct aspath *aspath);
+extern void aspath_unintern(struct aspath **aspath);
+extern const char *aspath_print(struct aspath *aspath);
+extern void aspath_print_vty(struct vty *vty, const char *format,
+ struct aspath *aspath, const char *suffix);
+extern void aspath_print_all_vty(struct vty *vty);
+extern unsigned int aspath_key_make(const void *p);
+extern unsigned int aspath_get_first_as(struct aspath *aspath);
+extern unsigned int aspath_get_last_as(struct aspath *aspath);
+extern int aspath_loop_check(struct aspath *aspath, as_t asno);
+extern bool aspath_private_as_check(struct aspath *aspath);
+extern bool aspath_single_asn_check(struct aspath *aspath, as_t asn);
extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
as_t target_asn,
as_t our_asn);
@@ -118,24 +122,25 @@ extern struct aspath *aspath_replace_private_asns(struct aspath *aspath,
as_t asn, as_t peer_asn);
extern struct aspath *aspath_remove_private_asns(struct aspath *aspath,
as_t peer_asn);
-extern bool aspath_firstas_check(struct aspath *, as_t);
-extern bool aspath_confed_check(struct aspath *);
-extern bool aspath_left_confed_check(struct aspath *);
+extern bool aspath_firstas_check(struct aspath *aspath, as_t asno);
+extern bool aspath_confed_check(struct aspath *aspath);
+extern bool aspath_left_confed_check(struct aspath *aspath);
extern unsigned long aspath_count(void);
-extern unsigned int aspath_count_hops(const struct aspath *);
+extern unsigned int aspath_count_hops(const struct aspath *aspath);
extern bool aspath_check_as_sets(struct aspath *aspath);
extern bool aspath_check_as_zero(struct aspath *aspath);
-extern unsigned int aspath_count_confeds(struct aspath *);
-extern unsigned int aspath_size(struct aspath *);
-extern as_t aspath_highest(struct aspath *);
-extern as_t aspath_leftmost(struct aspath *);
-extern size_t aspath_put(struct stream *, struct aspath *, int);
+extern unsigned int aspath_count_confeds(struct aspath *aspath);
+extern unsigned int aspath_size(struct aspath *aspath);
+extern as_t aspath_highest(struct aspath *aspath);
+extern as_t aspath_leftmost(struct aspath *aspath);
+extern size_t aspath_put(struct stream *s, struct aspath *aspath, int use32bit);
-extern struct aspath *aspath_reconcile_as4(struct aspath *, struct aspath *);
-extern bool aspath_has_as4(struct aspath *);
+extern struct aspath *aspath_reconcile_as4(struct aspath *aspath,
+ struct aspath *as4path);
+extern bool aspath_has_as4(struct aspath *aspath);
/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */
-extern uint8_t *aspath_snmp_pathseg(struct aspath *, size_t *);
+extern uint8_t *aspath_snmp_pathseg(struct aspath *aspath, size_t *varlen);
extern void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate,
struct aspath *aspath);
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index f45362f811..d57281a700 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -980,7 +980,7 @@ struct attr *bgp_attr_aggregate_intern(
struct attr *new;
int ret;
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
/* Origin attribute. */
attr.origin = origin;
@@ -1043,7 +1043,7 @@ struct attr *bgp_attr_aggregate_intern(
struct attr attr_tmp = attr;
struct bgp_path_info rmap_path;
- memset(&rmap_path, 0, sizeof(struct bgp_path_info));
+ memset(&rmap_path, 0, sizeof(rmap_path));
rmap_path.peer = bgp->peer_self;
rmap_path.attr = &attr_tmp;
@@ -4430,7 +4430,7 @@ void bgp_packet_mpunreach_prefix(struct stream *s, const struct prefix *p,
bool addpath_capable, uint32_t addpath_tx_id,
struct attr *attr)
{
- uint8_t wlabel[3] = {0x80, 0x00, 0x00};
+ uint8_t wlabel[4] = {0x80, 0x00, 0x00};
if (safi == SAFI_LABELED_UNICAST) {
label = (mpls_label_t *)wlabel;
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index ac5734ede6..01d993dabd 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -386,14 +386,14 @@ struct bpacket_attr_vec_arr;
/* Prototypes. */
extern void bgp_attr_init(void);
extern void bgp_attr_finish(void);
-extern enum bgp_attr_parse_ret bgp_attr_parse(struct peer *, struct attr *,
- bgp_size_t, struct bgp_nlri *,
- struct bgp_nlri *);
+extern enum bgp_attr_parse_ret
+bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_size_t size,
+ struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw);
extern struct attr *bgp_attr_intern(struct attr *attr);
-extern void bgp_attr_unintern_sub(struct attr *);
-extern void bgp_attr_unintern(struct attr **);
-extern void bgp_attr_flush(struct attr *);
-extern struct attr *bgp_attr_default_set(struct attr *attr, uint8_t);
+extern void bgp_attr_unintern_sub(struct attr *attr);
+extern void bgp_attr_unintern(struct attr **pattr);
+extern void bgp_attr_flush(struct attr *attr);
+extern struct attr *bgp_attr_default_set(struct attr *attr, uint8_t origin);
extern struct attr *bgp_attr_aggregate_intern(
struct bgp *bgp, uint8_t origin, struct aspath *aspath,
struct community *community, struct ecommunity *ecommunity,
@@ -410,13 +410,14 @@ extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
extern void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
const struct prefix *p);
extern bool attrhash_cmp(const void *arg1, const void *arg2);
-extern unsigned int attrhash_key_make(const void *);
-extern void attr_show_all(struct vty *);
+extern unsigned int attrhash_key_make(const void *p);
+extern void attr_show_all(struct vty *vty);
extern unsigned long int attr_count(void);
extern unsigned long int attr_unknown_count(void);
/* Cluster list prototypes. */
-extern bool cluster_loop_check(struct cluster_list *, struct in_addr);
+extern bool cluster_loop_check(struct cluster_list *cluster,
+ struct in_addr originator);
/* Below exported for unit-test purposes only */
struct bgp_attr_parser_args {
@@ -429,9 +430,9 @@ struct bgp_attr_parser_args {
uint8_t *startp;
};
extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
- struct bgp_nlri *);
+ struct bgp_nlri *mp_update);
extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
- struct bgp_nlri *);
+ struct bgp_nlri *mp_withdraw);
extern enum bgp_attr_parse_ret
bgp_attr_prefix_sid(struct bgp_attr_parser_args *args);
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index e528fadedd..d379f40a88 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -49,7 +49,7 @@ void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
struct ecommunity_val routermac_ecom;
struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
- memset(&routermac_ecom, 0, sizeof(struct ecommunity_val));
+ memset(&routermac_ecom, 0, sizeof(routermac_ecom));
routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN;
routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN);
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index 47922985d9..109f70f78f 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -1189,7 +1189,7 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
if (adjin->peer == peer)
break;
}
- bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, prd,
+ bmp_monitor(bmp, peer, 0, &bqe->p, prd,
adjin ? adjin->attr : NULL, afi, safi,
adjin ? adjin->uptime : monotime(NULL));
written = true;
diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h
index bb7c1363b2..fb80787618 100644
--- a/bgpd/bgp_clist.h
+++ b/bgpd/bgp_clist.h
@@ -137,7 +137,7 @@ extern struct community_list_handler *bgp_clist;
/* Prototypes. */
extern struct community_list_handler *community_list_init(void);
-extern void community_list_terminate(struct community_list_handler *);
+extern void community_list_terminate(struct community_list_handler *ch);
extern int community_list_set(struct community_list_handler *ch,
const char *name, const char *str,
@@ -160,21 +160,24 @@ extern int lcommunity_list_unset(struct community_list_handler *ch,
const char *seq, int direct, int style);
extern struct community_list_master *
-community_list_master_lookup(struct community_list_handler *, int);
+community_list_master_lookup(struct community_list_handler *ch, int master);
extern struct community_list *
community_list_lookup(struct community_list_handler *c, const char *name,
uint32_t name_hash, int master);
-extern bool community_list_match(struct community *, struct community_list *);
-extern bool ecommunity_list_match(struct ecommunity *, struct community_list *);
-extern bool lcommunity_list_match(struct lcommunity *, struct community_list *);
-extern bool community_list_exact_match(struct community *,
- struct community_list *);
+extern bool community_list_match(struct community *com,
+ struct community_list *list);
+extern bool ecommunity_list_match(struct ecommunity *ecom,
+ struct community_list *list);
+extern bool lcommunity_list_match(struct lcommunity *lcom,
+ struct community_list *list);
+extern bool community_list_exact_match(struct community *com,
+ struct community_list *list);
extern bool lcommunity_list_exact_match(struct lcommunity *lcom,
struct community_list *list);
-extern struct community *community_list_match_delete(struct community *,
- struct community_list *);
+extern struct community *
+community_list_match_delete(struct community *com, struct community_list *list);
extern struct lcommunity *
lcommunity_list_match_delete(struct lcommunity *lcom,
struct community_list *list);
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
index 6f0ae0235c..616ddb4405 100644
--- a/bgpd/bgp_community.h
+++ b/bgpd/bgp_community.h
@@ -72,25 +72,26 @@ struct community {
extern void community_init(void);
extern void community_finish(void);
extern void community_free(struct community **comm);
-extern struct community *community_uniq_sort(struct community *);
-extern struct community *community_parse(uint32_t *, unsigned short);
-extern struct community *community_intern(struct community *);
-extern void community_unintern(struct community **);
-extern char *community_str(struct community *, bool make_json,
+extern struct community *community_uniq_sort(struct community *com);
+extern struct community *community_parse(uint32_t *pnt, unsigned short length);
+extern struct community *community_intern(struct community *com);
+extern void community_unintern(struct community **com);
+extern char *community_str(struct community *com, bool make_json,
bool translate_alias);
-extern unsigned int community_hash_make(const struct community *);
-extern struct community *community_str2com(const char *);
-extern bool community_match(const struct community *, const struct community *);
+extern unsigned int community_hash_make(const struct community *com);
+extern struct community *community_str2com(const char *str);
+extern bool community_match(const struct community *com1,
+ const struct community *com2);
extern bool community_cmp(const struct community *c1,
const struct community *c2);
-extern struct community *community_merge(struct community *,
- struct community *);
-extern struct community *community_delete(struct community *,
- struct community *);
-extern struct community *community_dup(struct community *);
-extern bool community_include(struct community *, uint32_t);
+extern struct community *community_merge(struct community *com1,
+ struct community *com2);
+extern struct community *community_delete(struct community *com1,
+ struct community *com2);
+extern struct community *community_dup(struct community *com);
+extern bool community_include(struct community *com, uint32_t val);
extern void community_add_val(struct community *com, uint32_t val);
-extern void community_del_val(struct community *, uint32_t *);
+extern void community_del_val(struct community *com, uint32_t *val);
extern unsigned long community_count(void);
extern struct hash *community_hash(void);
extern uint32_t community_val_get(struct community *com, int i);
diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h
index 0c70abef84..f7deea5385 100644
--- a/bgpd/bgp_damp.h
+++ b/bgpd/bgp_damp.h
@@ -132,9 +132,10 @@ struct bgp_damp_config {
#define REUSE_LIST_SIZE 256
#define REUSE_ARRAY_SIZE 1024
-extern int bgp_damp_enable(struct bgp *, afi_t, safi_t, time_t, unsigned int,
- unsigned int, time_t);
-extern int bgp_damp_disable(struct bgp *, afi_t, safi_t);
+extern int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
+ unsigned int reuse, unsigned int suppress,
+ time_t max);
+extern int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi);
extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
afi_t afi, safi_t safi, int attr_change);
extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
@@ -142,8 +143,9 @@ extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
extern void bgp_damp_info_free(struct bgp_damp_info *path, int withdraw,
afi_t afi, safi_t safi);
extern void bgp_damp_info_clean(afi_t afi, safi_t safi);
-extern int bgp_damp_decay(time_t, int, struct bgp_damp_config *damp);
-extern void bgp_config_write_damp(struct vty *, afi_t afi, safi_t safi);
+extern int bgp_damp_decay(time_t tdiff, int penalty,
+ struct bgp_damp_config *damp);
+extern void bgp_config_write_damp(struct vty *vty, afi_t afi, safi_t safi);
extern void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path,
afi_t afi, safi_t safi, json_object *json_path);
extern const char *bgp_damp_reuse_time_vty(struct vty *vty,
@@ -151,7 +153,7 @@ extern const char *bgp_damp_reuse_time_vty(struct vty *vty,
char *timebuf, size_t len, afi_t afi,
safi_t safi, bool use_json,
json_object *json);
-extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t,
- uint16_t);
+extern int bgp_show_dampening_parameters(struct vty *vty, afi_t afi,
+ safi_t safi, uint16_t show_flags);
#endif /* _QUAGGA_BGP_DAMP_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 0993d6de57..cd6ba00846 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -627,8 +627,8 @@ static int bgp_debug_parse_evpn_prefix(struct vty *vty, struct cmd_token **argv,
int argc, struct prefix **argv_pp)
{
struct prefix *argv_p;
- struct ethaddr mac;
- struct ipaddr ip;
+ struct ethaddr mac = {};
+ struct ipaddr ip = {};
int evpn_type = 0;
int mac_idx = 0;
int ip_idx = 0;
@@ -639,28 +639,37 @@ static int bgp_debug_parse_evpn_prefix(struct vty *vty, struct cmd_token **argv,
return CMD_WARNING;
if (evpn_type == BGP_EVPN_MAC_IP_ROUTE) {
- memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&ip, 0, sizeof(ip));
- argv_find(argv, argc, "mac", &mac_idx);
- (void)prefix_str2mac(argv[mac_idx + 1]->arg, &mac);
+ if (argv_find(argv, argc, "mac", &mac_idx))
+ if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
- argv_find(argv, argc, "ip", &ip_idx);
- str2ipaddr(argv[ip_idx + 1]->arg, &ip);
+ if (argv_find(argv, argc, "ip", &ip_idx))
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
build_evpn_type2_prefix((struct prefix_evpn *)argv_p,
&mac, &ip);
} else if (evpn_type == BGP_EVPN_IMET_ROUTE) {
- memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&ip, 0, sizeof(ip));
- argv_find(argv, argc, "ip", &ip_idx);
- str2ipaddr(argv[ip_idx + 1]->arg, &ip);
+ if (argv_find(argv, argc, "ip", &ip_idx))
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
build_evpn_type3_prefix((struct prefix_evpn *)argv_p,
ip.ipaddr_v4);
} else if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
struct prefix ip_prefix;
- memset(&ip_prefix, 0, sizeof(struct prefix));
+ memset(&ip_prefix, 0, sizeof(ip_prefix));
if (argv_find(argv, argc, "ip", &ip_idx)) {
(void)str2prefix(argv[ip_idx + 1]->arg, &ip_prefix);
apply_mask(&ip_prefix);
@@ -2567,9 +2576,9 @@ static int bgp_debug_per_prefix(const struct prefix *p,
/* Return true if this peer is on the per_peer_list of peers to debug
* for BGP_DEBUG_TYPE
*/
-static int bgp_debug_per_peer(char *host, unsigned long term_bgp_debug_type,
- unsigned int BGP_DEBUG_TYPE,
- struct list *per_peer_list)
+static bool bgp_debug_per_peer(char *host, unsigned long term_bgp_debug_type,
+ unsigned int BGP_DEBUG_TYPE,
+ struct list *per_peer_list)
{
struct bgp_debug_filter *filter;
struct listnode *node, *nnode;
@@ -2577,25 +2586,25 @@ static int bgp_debug_per_peer(char *host, unsigned long term_bgp_debug_type,
if (term_bgp_debug_type & BGP_DEBUG_TYPE) {
/* We are debugging all peers so return true */
if (!per_peer_list || list_isempty(per_peer_list))
- return 1;
+ return true;
else {
if (!host)
- return 0;
+ return false;
for (ALL_LIST_ELEMENTS(per_peer_list, node, nnode,
filter))
if (strcmp(filter->host, host) == 0)
- return 1;
+ return true;
- return 0;
+ return false;
}
}
- return 0;
+ return false;
}
-int bgp_debug_neighbor_events(struct peer *peer)
+bool bgp_debug_neighbor_events(const struct peer *peer)
{
char *host = NULL;
@@ -2607,7 +2616,7 @@ int bgp_debug_neighbor_events(struct peer *peer)
bgp_debug_neighbor_events_peers);
}
-int bgp_debug_keepalive(struct peer *peer)
+bool bgp_debug_keepalive(const struct peer *peer)
{
char *host = NULL;
@@ -2619,7 +2628,7 @@ int bgp_debug_keepalive(struct peer *peer)
bgp_debug_keepalive_peers);
}
-bool bgp_debug_update(struct peer *peer, const struct prefix *p,
+bool bgp_debug_update(const struct peer *peer, const struct prefix *p,
struct update_group *updgrp, unsigned int inbound)
{
char *host = NULL;
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index cf6325ba36..62f5340dfd 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -166,17 +166,17 @@ struct bgp_debug_filter {
extern const char *const bgp_type_str[];
-extern bool bgp_dump_attr(struct attr *, char *, size_t);
+extern bool bgp_dump_attr(struct attr *attr, char *buf, size_t size);
extern bool bgp_debug_peer_updout_enabled(char *host);
-extern const char *bgp_notify_code_str(char);
-extern const char *bgp_notify_subcode_str(char, char);
+extern const char *bgp_notify_code_str(char code);
+extern const char *bgp_notify_subcode_str(char code, char subcode);
extern void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
const char *direct, bool hard_reset);
extern const struct message bgp_status_msg[];
-extern int bgp_debug_neighbor_events(struct peer *peer);
-extern int bgp_debug_keepalive(struct peer *peer);
-extern bool bgp_debug_update(struct peer *peer, const struct prefix *p,
+extern bool bgp_debug_neighbor_events(const struct peer *peer);
+extern bool bgp_debug_keepalive(const struct peer *peer);
+extern bool bgp_debug_update(const struct peer *peer, const struct prefix *p,
struct update_group *updgrp, unsigned int inbound);
extern bool bgp_debug_bestpath(struct bgp_dest *dest);
extern bool bgp_debug_zebra(const struct prefix *p);
diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
index c389fec5f4..e57f449f78 100644
--- a/bgpd/bgp_dump.c
+++ b/bgpd/bgp_dump.c
@@ -851,9 +851,9 @@ static int config_write_bgp_dump(struct vty *vty)
/* Initialize BGP packet dump functionality. */
void bgp_dump_init(void)
{
- memset(&bgp_dump_all, 0, sizeof(struct bgp_dump));
- memset(&bgp_dump_updates, 0, sizeof(struct bgp_dump));
- memset(&bgp_dump_routes, 0, sizeof(struct bgp_dump));
+ memset(&bgp_dump_all, 0, sizeof(bgp_dump_all));
+ memset(&bgp_dump_updates, 0, sizeof(bgp_dump_updates));
+ memset(&bgp_dump_routes, 0, sizeof(bgp_dump_routes));
bgp_dump_obuf =
stream_new((BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE * 2)
diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h
index a8cbd8ed2c..70e430a9b8 100644
--- a/bgpd/bgp_dump.h
+++ b/bgpd/bgp_dump.h
@@ -59,6 +59,6 @@
extern void bgp_dump_init(void);
extern void bgp_dump_finish(void);
-extern int bgp_dump_state(struct peer *);
+extern int bgp_dump_state(struct peer *peer);
#endif /* _QUAGGA_BGP_DUMP_H */
diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c
index 193c96a169..9f87f8ce7a 100644
--- a/bgpd/bgp_errors.c
+++ b/bgpd/bgp_errors.c
@@ -182,6 +182,12 @@ static struct log_ref ferr_bgp_warn[] = {
.suggestion = "Gather log files and open an Issue, restart FRR",
},
{
+ .code = EC_BGP_SENDQ_STUCK_WARN,
+ .title = "BGP has been unable to send anything to a peer for an extended time",
+ .description = "The BGP peer does not seem to be receiving or processing any data received from us, causing updates to be delayed.",
+ .suggestion = "Check connectivity to the peer and that it is not overloaded",
+ },
+ {
.code = END_FERR,
}
};
@@ -481,6 +487,12 @@ static struct log_ref ferr_bgp_err[] = {
.suggestion = "Add a v6 LL address to the outgoing interfaces as per RFC",
},
{
+ .code = EC_BGP_SENDQ_STUCK_PROPER,
+ .title = "BGP is shutting down a peer due to being unable to send anything for an extended time",
+ .description = "No BGP updates were successfully sent to the peer for more than twice the holdtime.",
+ .suggestion = "Check connectivity to the peer and that it is not overloaded",
+ },
+ {
.code = END_FERR,
}
};
diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h
index 0b71af3fc6..0c0917c49e 100644
--- a/bgpd/bgp_errors.h
+++ b/bgpd/bgp_errors.h
@@ -102,6 +102,8 @@ enum bgp_log_refs {
EC_BGP_INVALID_BGP_INSTANCE,
EC_BGP_INVALID_ROUTE,
EC_BGP_NO_LL_ADDRESS_AVAILABLE,
+ EC_BGP_SENDQ_STUCK_WARN,
+ EC_BGP_SENDQ_STUCK_PROPER,
};
extern void bgp_error_init(void);
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index bfe7452fad..74395bb0e2 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -209,7 +209,7 @@ static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt)
return NULL;
}
- memset(&tmp, 0, sizeof(struct vrf_irt_node));
+ memset(&tmp, 0, sizeof(tmp));
memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
irt = hash_lookup(bgp_evpn->vrf_import_rt_hash, &tmp);
return irt;
@@ -260,9 +260,6 @@ static struct irt_node *import_rt_new(struct bgp *bgp,
{
struct irt_node *irt;
- if (!bgp)
- return NULL;
-
irt = XCALLOC(MTYPE_BGP_EVPN_IMPORT_RT, sizeof(struct irt_node));
irt->rt = *rt;
@@ -294,7 +291,7 @@ static struct irt_node *lookup_import_rt(struct bgp *bgp,
struct irt_node *irt;
struct irt_node tmp;
- memset(&tmp, 0, sizeof(struct irt_node));
+ memset(&tmp, 0, sizeof(tmp));
memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
irt = hash_lookup(bgp->import_rt_hash, &tmp);
return irt;
@@ -436,10 +433,8 @@ static void map_vni_to_rt(struct bgp *bgp, struct bgpevpn *vpn,
/* Already mapped. */
return;
- if (!irt) {
+ if (!irt)
irt = import_rt_new(bgp, &eval_tmp);
- assert(irt);
- }
/* Add VNI to the hash list for this RT. */
listnode_add(irt->vnis, vpn);
@@ -1296,7 +1291,7 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
if (src_attr)
attr = *src_attr;
else {
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
}
@@ -1733,7 +1728,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
int route_change;
bool old_is_sync = false;
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
/* Build path-attribute for this route. */
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
@@ -2147,7 +2142,7 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
* Delete all type-2 (MACIP) local routes for this VNI - only from the
* global routing table. These are also scheduled for withdraw from peers.
*/
-static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
+static void delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
{
afi_t afi;
safi_t safi;
@@ -2160,7 +2155,7 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
rddest = bgp_node_lookup(bgp->rib[afi][safi],
(struct prefix *)&vpn->prd);
- if (rddest && bgp_dest_has_bgp_path_info_data(rddest)) {
+ if (rddest) {
table = bgp_dest_get_bgp_table_info(rddest);
for (dest = bgp_table_top(table); dest;
dest = bgp_route_next(dest)) {
@@ -2175,13 +2170,10 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (pi)
bgp_process(bgp, dest, afi, safi);
}
- }
- /* Unlock RD node. */
- if (rddest)
+ /* Unlock RD node. */
bgp_dest_unlock_node(rddest);
-
- return 0;
+ }
}
/*
@@ -3686,8 +3678,8 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
es = bgp_evpn_es_find(&evp->prefix.ead_addr.esi);
bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi,
- global_dest, attr, 1,
- &global_pi, &route_changed);
+ global_dest, attr, &global_pi,
+ &route_changed);
}
/* Schedule for processing and unlock node. */
@@ -3704,7 +3696,6 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
*/
static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
{
- int ret;
struct prefix_evpn p;
struct bgp_dest *global_dest;
struct bgp_path_info *pi;
@@ -3714,9 +3705,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
/* Delete and withdraw locally learnt type-2 routes (MACIP)
* for this VNI - from the global table.
*/
- ret = delete_global_type2_routes(bgp, vpn);
- if (ret)
- return ret;
+ delete_global_type2_routes(bgp, vpn);
/* Remove type-3 route for this VNI from global table. */
build_evpn_type3_prefix(&p, vpn->originator_ip);
@@ -3988,7 +3977,7 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
pfx += 8;
/* Make EVPN prefix. */
- memset(&p, 0, sizeof(struct prefix_evpn));
+ memset(&p, 0, sizeof(p));
p.family = AF_EVPN;
p.prefixlen = EVPN_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
@@ -4058,7 +4047,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
pfx += 8;
/* Make EVPN prefix. */
- memset(&p, 0, sizeof(struct prefix_evpn));
+ memset(&p, 0, sizeof(p));
p.family = AF_EVPN;
p.prefixlen = EVPN_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
@@ -4178,7 +4167,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p,
char temp[16];
const struct evpn_addr *p_evpn_p;
- memset(&temp, 0, 16);
+ memset(&temp, 0, sizeof(temp));
if (p->family != AF_EVPN)
return;
p_evpn_p = &(p->u.prefix_evpn);
@@ -4438,7 +4427,7 @@ void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf, afi_t afi,
struct prefix ip_prefix;
/* form the default prefix 0.0.0.0/0 */
- memset(&ip_prefix, 0, sizeof(struct prefix));
+ memset(&ip_prefix, 0, sizeof(ip_prefix));
ip_prefix.family = afi2family(afi);
if (add) {
@@ -5001,7 +4990,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
for (; pnt < lim; pnt += psize) {
/* Clear prefix structure. */
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
/* Deal with path-id if AddPath is supported. */
if (addpath_capable) {
@@ -5285,7 +5274,7 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni)
struct bgpevpn *vpn;
struct bgpevpn tmp;
- memset(&tmp, 0, sizeof(struct bgpevpn));
+ memset(&tmp, 0, sizeof(tmp));
tmp.vni = vni;
vpn = hash_lookup(bgp->vnihash, &tmp);
return vpn;
@@ -5302,9 +5291,6 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
{
struct bgpevpn *vpn;
- if (!bgp)
- return NULL;
-
vpn = XCALLOC(MTYPE_BGP_EVPN, sizeof(struct bgpevpn));
/* Set values - RD and RT set to defaults. */
@@ -5882,19 +5868,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
*/
if (is_vni_live(vpn))
update_routes_for_vni(bgp, vpn);
- }
-
- /* Create or update as appropriate. */
- if (!vpn) {
+ } else {
+ /* Create or update as appropriate. */
vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id,
- mcast_grp, svi_ifindex);
- if (!vpn) {
- flog_err(
- EC_BGP_VNI,
- "%u: Failed to allocate VNI entry for VNI %u - at Add",
- bgp->vrf_id, vni);
- return -1;
- }
+ mcast_grp, svi_ifindex);
}
/* if the VNI is live already, there is nothing more to do */
@@ -6329,7 +6306,7 @@ static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp,
struct bgpevpn *vpn;
struct bgpevpn tmp;
- memset(&tmp, 0, sizeof(struct bgpevpn));
+ memset(&tmp, 0, sizeof(tmp));
tmp.svi_ifindex = svi;
vpn = hash_lookup(bgp->vni_svi_hash, &tmp);
return vpn;
@@ -6399,7 +6376,7 @@ bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc)
* which stores all the remote IP addresses received via MAC/IP routes
* in this EVI
*/
- memset(&tmp, 0, sizeof(struct evpn_remote_ip));
+ memset(&tmp, 0, sizeof(tmp));
p = &bnc->prefix;
if (p->family == AF_INET) {
@@ -6432,7 +6409,7 @@ static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
if (!vpn->bgp_vrf || vpn->svi_ifindex == 0)
return;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
if (addr->ipa_type == IPADDR_V4) {
afi = AFI_IP;
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index 39b31c0c1a..92456080ed 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -351,7 +351,7 @@ static void bgp_evpn_es_route_del_all(struct bgp *bgp, struct bgp_evpn_es *es)
*/
int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
struct bgpevpn *vpn, afi_t afi, safi_t safi,
- struct bgp_dest *dest, struct attr *attr, int add,
+ struct bgp_dest *dest, struct attr *attr,
struct bgp_path_info **ri, int *route_changed)
{
struct bgp_path_info *tmp_pi = NULL;
@@ -390,9 +390,6 @@ int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
return -1;
}
- if (!local_pi && !add)
- return 0;
-
/* create or update the entry */
if (!local_pi) {
@@ -602,7 +599,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es,
bgp_attr_set_ecommunity(attr, ecommunity_dup(&ecom_encap));
/* ES import RT */
- memset(&mac, 0, sizeof(struct ethaddr));
+ memset(&mac, 0, sizeof(mac));
memset(&ecom_es_rt, 0, sizeof(ecom_es_rt));
es_get_system_mac(&es->esi, &mac);
encode_es_rt_extcomm(&eval_es_rt, &mac);
@@ -636,7 +633,7 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp,
struct bgp_dest *dest = NULL;
struct bgp_path_info *pi = NULL;
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
/* Build path-attribute for this route. */
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
@@ -652,7 +649,7 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp,
dest = bgp_node_get(es->route_table, (struct prefix *)p);
/* Create or update route entry. */
- ret = bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest, &attr, 1,
+ ret = bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest, &attr,
&pi, &route_changed);
if (ret != 0)
flog_err(
@@ -681,8 +678,7 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp,
dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
p, &es->es_base_frag->prd);
bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest,
- attr_new, 1, &global_pi,
- &route_changed);
+ attr_new, &global_pi, &route_changed);
/* Schedule for processing and unlock node. */
bgp_process(bgp, dest, afi, safi);
@@ -947,7 +943,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
int route_changed = 0;
struct prefix_rd *global_rd;
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
/* Build path-attribute for this route. */
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
@@ -968,7 +964,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
/* Create or update route entry. */
ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest,
- &attr, 1, &pi, &route_changed);
+ &attr, &pi, &route_changed);
if (ret != 0)
flog_err(
EC_BGP_ES_INVALID,
@@ -990,11 +986,11 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
/* Create or update route entry. */
ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest,
- &attr, 1, &pi, &route_changed);
+ &attr, &pi, &route_changed);
if (ret != 0) {
flog_err(
EC_BGP_ES_INVALID,
- "%u ERROR: Failed to updated EAD-EVI route ESI: %s VTEP %pI4",
+ "%u ERROR: Failed to updated EAD-ES route ESI: %s VTEP %pI4",
bgp->vrf_id, es->esi_str, &es->originator_ip);
}
global_rd = &es_frag->prd;
@@ -1022,8 +1018,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
p, global_rd);
bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest,
- attr_new, 1, &global_pi,
- &route_changed);
+ attr_new, &global_pi, &route_changed);
/* Schedule for processing and unlock node. */
bgp_process(bgp, dest, afi, safi);
@@ -4512,7 +4507,7 @@ static struct bgp_evpn_nh *bgp_evpn_nh_add(struct bgp *bgp_vrf,
struct bgp_evpn_nh tmp_n;
struct bgp_evpn_nh *n = NULL;
- memset(&tmp_n, 0, sizeof(struct bgp_evpn_nh));
+ memset(&tmp_n, 0, sizeof(tmp_n));
memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
n = hash_get(bgp_vrf->evpn_nh_table, &tmp_n, bgp_evpn_nh_alloc);
ipaddr2str(ip, n->nh_str, sizeof(n->nh_str));
diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h
index dc3fe44776..11030e323f 100644
--- a/bgpd/bgp_evpn_mh.h
+++ b/bgpd/bgp_evpn_mh.h
@@ -420,7 +420,7 @@ extern int delete_global_ead_evi_routes(struct bgp *bgp, struct bgpevpn *vpn);
extern int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
struct bgpevpn *vpn, afi_t afi, safi_t safi,
struct bgp_dest *dest, struct attr *attr,
- int add, struct bgp_path_info **ri,
+ struct bgp_path_info **ri,
int *route_changed);
int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
struct attr *attr, uint8_t *pfx, int psize,
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index 763408782f..64fdc29704 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -450,8 +450,7 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN);
p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE;
- if (ip)
- memcpy(&p->prefix.macip_addr.ip, ip, sizeof(*ip));
+ memcpy(&p->prefix.macip_addr.ip, ip, sizeof(*ip));
}
static inline void
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 109de1efb2..d94dcdd362 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -1419,8 +1419,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd,
struct prefix_rd prd;
int rd_all = 0;
- argv_find(argv, argc, "all", &rd_all);
- if (rd_all)
+ if (argv_find(argv, argc, "all", &rd_all))
return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
NULL, SHOW_DISPLAY_STANDARD,
use_json(argc, argv));
@@ -1469,8 +1468,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags,
struct prefix_rd prd;
int rd_all = 0;
- argv_find(argv, argc, "all", &rd_all);
- if (rd_all)
+ if (argv_find(argv, argc, "all", &rd_all))
return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
NULL, SHOW_DISPLAY_TAGS, 0);
@@ -1573,7 +1571,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
int ret;
struct peer *peer;
char *peerstr = NULL;
- struct prefix_rd prd;
+ struct prefix_rd prd = {};
bool uj = use_json(argc, argv);
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
@@ -1587,8 +1585,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
return CMD_WARNING;
}
- argv_find(argv, argc, "all", &rd_all);
- if (!rd_all) {
+ if (argv_find(argv, argc, "all", &rd_all)) {
argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
&idx_ext_community);
ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
@@ -1797,8 +1794,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
return CMD_WARNING;
}
- argv_find(argv, argc, "all", &rd_all);
- if (rd_all)
+ if (argv_find(argv, argc, "all", &rd_all))
return show_adj_route_vpn(vty, peer, NULL, AFI_L2VPN, SAFI_EVPN,
uj);
else {
@@ -1860,8 +1856,7 @@ DEFUN(show_ip_bgp_evpn_rd_overlay,
struct prefix_rd prd;
int rd_all = 0;
- argv_find(argv, argc, "all", &rd_all);
- if (rd_all)
+ if (argv_find(argv, argc, "all", &rd_all))
return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
NULL, SHOW_DISPLAY_OVERLAY,
use_json(argc, argv));
@@ -2261,9 +2256,6 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
struct bgpevpn *vpn;
struct in_addr mcast_grp = {INADDR_ANY};
- if (!bgp->vnihash)
- return NULL;
-
vpn = bgp_evpn_lookup_vni(bgp, vni);
if (!vpn) {
/* Check if this L2VNI is already configured as L3VNI */
@@ -2279,13 +2271,6 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
* zebra
*/
vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0);
- if (!vpn) {
- flog_err(
- EC_BGP_VNI,
- "%u: Failed to allocate VNI entry for VNI %u - at Config",
- bgp->vrf_id, vni);
- return NULL;
- }
}
/* Mark as configured. */
@@ -2302,8 +2287,6 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
*/
static void evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn)
{
- assert(bgp->vnihash);
-
if (!is_vni_live(vpn)) {
bgp_evpn_free(bgp, vpn);
return;
@@ -2374,7 +2357,7 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp,
num_vnis = hashcount(bgp->vnihash);
if (!num_vnis)
return;
- memset(&wctx, 0, sizeof(struct vni_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.bgp = bgp;
wctx.vty = vty;
wctx.vtep_ip = vtep_ip;
@@ -4700,7 +4683,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
JSON_STR)
{
struct bgp *bgp;
- int ret;
+ int ret = 0;
struct prefix_rd prd;
int type = 0;
bool uj = false;
@@ -4717,15 +4700,16 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
if (uj)
json = json_object_new_object();
- argv_find(argv, argc, "all", &rd_all);
- if (!rd_all) {
+ if (argv_find(argv, argc, "all", &rd_all)) {
/* get the RD */
- argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
- &idx_ext_community);
- ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
- if (!ret) {
- vty_out(vty, "%% Malformed Route Distinguisher\n");
- return CMD_WARNING;
+ if (argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
+ &idx_ext_community)) {
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
}
}
@@ -4764,7 +4748,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
JSON_STR)
{
struct bgp *bgp;
- int ret;
+ int ret = 0;
struct prefix_rd prd;
struct ethaddr mac;
struct ipaddr ip;
@@ -4788,14 +4772,15 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
json = json_object_new_object();
/* get the prd */
- argv_find(argv, argc, "all", &rd_all);
- if (!rd_all) {
- argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
- &idx_ext_community);
- ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
- if (!ret) {
- vty_out(vty, "%% Malformed Route Distinguisher\n");
- return CMD_WARNING;
+ if (argv_find(argv, argc, "all", &rd_all)) {
+ if (argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
+ &idx_ext_community)) {
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
}
}
@@ -6301,7 +6286,7 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
if (bgp->advertise_all_vni)
vty_out(vty, " advertise-all-vni\n");
- if (bgp->vnihash) {
+ if (hashcount(bgp->vnihash)) {
struct list *vnilist = hash_to_list(bgp->vnihash);
struct listnode *ln;
struct bgpevpn *data;
diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c
index de3f2a9d47..39c0cfe514 100644
--- a/bgpd/bgp_flowspec.c
+++ b/bgpd/bgp_flowspec.c
@@ -121,7 +121,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
for (; pnt < lim; pnt += psize) {
/* Clear prefix structure. */
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
/* All FlowSpec NLRI begin with length. */
if (pnt + 1 > lim)
diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c
index 348dc7c9d1..9f3ea499ff 100644
--- a/bgpd/bgp_flowspec_util.c
+++ b/bgpd/bgp_flowspec_util.c
@@ -97,7 +97,7 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
switch (type) {
case FLOWSPEC_DEST_PREFIX:
case FLOWSPEC_SRC_PREFIX:
- memset(&compare, 0, sizeof(struct prefix));
+ memset(&compare, 0, sizeof(compare));
ret = bgp_flowspec_ip_address(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content+offset,
@@ -185,7 +185,7 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
uint8_t prefix_offset = 0;
*error = 0;
- memset(&prefix_local, 0, sizeof(struct prefix));
+ memset(&prefix_local, 0, sizeof(prefix_local));
/* read the prefix length */
prefix_local.prefixlen = nlri_ptr[offset];
psize = PSIZE(prefix_local.prefixlen);
@@ -665,7 +665,7 @@ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
struct bgp_dest *dest = pi->net;
struct bgp_pbr_entry_action *api_action;
- memset(&api, 0, sizeof(struct bgp_pbr_entry_main));
+ memset(&api, 0, sizeof(api));
if (bgp_pbr_build_and_validate_entry(bgp_dest_get_prefix(dest), pi,
&api)
< 0)
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index cc3505333b..fe4ab272ac 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -1701,6 +1701,12 @@ static int bgp_connect_success(struct peer *peer)
return -1;
}
+ /*
+ * If we are doing nht for a peer that ls v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+
bgp_reads_on(peer);
if (bgp_debug_neighbor_events(peer)) {
@@ -1739,6 +1745,12 @@ static int bgp_connect_success_w_delayopen(struct peer *peer)
return -1;
}
+ /*
+ * If we are doing nht for a peer that ls v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+
bgp_reads_on(peer);
if (bgp_debug_neighbor_events(peer)) {
@@ -1961,6 +1973,15 @@ static int bgp_fsm_holdtime_expire(struct peer *peer)
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [FSM] Hold timer expire", peer->host);
+ /* RFC8538 updates RFC 4724 by defining an extension that permits
+ * the Graceful Restart procedures to be performed when the BGP
+ * speaker receives a BGP NOTIFICATION message or the Hold Time expires.
+ */
+ if (peer_established(peer) &&
+ bgp_has_graceful_restart_notification(peer))
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE))
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+
return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0);
}
diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c
index bd0dfb3a6d..75d34a84e0 100644
--- a/bgpd/bgp_io.c
+++ b/bgpd/bgp_io.c
@@ -298,6 +298,7 @@ static uint16_t bgp_write(struct peer *peer)
unsigned int iovsz;
unsigned int strmsz;
unsigned int total_written;
+ time_t now;
wpkt_quanta_old = atomic_load_explicit(&peer->bgp->wpkt_quanta,
memory_order_relaxed);
@@ -430,19 +431,22 @@ static uint16_t bgp_write(struct peer *peer)
}
done : {
+ now = bgp_clock();
/*
* Update last_update if UPDATEs were written.
* Note: that these are only updated at end,
* not per message (i.e., per loop)
*/
if (uo)
- atomic_store_explicit(&peer->last_update, bgp_clock(),
+ atomic_store_explicit(&peer->last_update, now,
memory_order_relaxed);
/* If we TXed any flavor of packet */
- if (update_last_write)
- atomic_store_explicit(&peer->last_write, bgp_clock(),
+ if (update_last_write) {
+ atomic_store_explicit(&peer->last_write, now,
memory_order_relaxed);
+ peer->last_sendq_ok = now;
+ }
}
return status;
diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c
index 4cb7bd788a..86202a0e3d 100644
--- a/bgpd/bgp_keepalives.c
+++ b/bgpd/bgp_keepalives.c
@@ -121,7 +121,7 @@ static void peer_process(struct hash_bucket *hb, void *arg)
bgp_keepalive_send(pkat->peer);
monotime(&pkat->last);
- memset(&elapsed, 0x00, sizeof(struct timeval));
+ memset(&elapsed, 0, sizeof(elapsed));
diff = ka;
}
@@ -220,7 +220,7 @@ void *bgp_keepalives_start(void *arg)
hash_iterate(peerhash, peer_process, &next_update);
if (next_update.tv_sec == -1)
- memset(&next_update, 0x00, sizeof(next_update));
+ memset(&next_update, 0, sizeof(next_update));
monotime_since(&currtime, &aftertime);
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
index f53deb63b3..a9c4b4c678 100644
--- a/bgpd/bgp_label.c
+++ b/bgpd/bgp_label.c
@@ -55,7 +55,7 @@ int bgp_parse_fec_update(void)
s = zclient->ibuf;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
p.family = stream_getw(s);
p.prefixlen = stream_getc(s);
stream_get(p.u.val, s, PSIZE(p.prefixlen));
@@ -350,7 +350,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
for (; pnt < lim; pnt += psize) {
/* Clear prefix structure. */
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
if (addpath_capable) {
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 1dc992fb94..b9f1ba3971 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -65,7 +65,7 @@ DEFINE_MTYPE(BGPD, AS_LIST, "BGP AS list");
DEFINE_MTYPE(BGPD, AS_FILTER, "BGP AS filter");
DEFINE_MTYPE(BGPD, AS_FILTER_STR, "BGP AS filter str");
-DEFINE_MTYPE(BGPD, COMMUNITY_ALIAS, "community");
+DEFINE_MTYPE(BGPD, COMMUNITY_ALIAS, "community alias");
DEFINE_MTYPE(BGPD, COMMUNITY, "community");
DEFINE_MTYPE(BGPD, COMMUNITY_VAL, "community val");
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index 6cd6ddd9dd..64af8a5411 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -46,7 +46,7 @@
* Record maximum-paths configuration for BGP instance
*/
int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi, int peertype,
- uint16_t maxpaths, uint16_t options)
+ uint16_t maxpaths, bool same_clusterlen)
{
if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
return -1;
@@ -54,7 +54,7 @@ int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi, int peertype,
switch (peertype) {
case BGP_PEER_IBGP:
bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths;
- bgp->maxpaths[afi][safi].ibgp_flags |= options;
+ bgp->maxpaths[afi][safi].same_clusterlen = same_clusterlen;
break;
case BGP_PEER_EBGP:
bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths;
@@ -80,7 +80,7 @@ int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
switch (peertype) {
case BGP_PEER_IBGP:
bgp->maxpaths[afi][safi].maxpaths_ibgp = multipath_num;
- bgp->maxpaths[afi][safi].ibgp_flags = 0;
+ bgp->maxpaths[afi][safi].same_clusterlen = false;
break;
case BGP_PEER_EBGP:
bgp->maxpaths[afi][safi].maxpaths_ebgp = multipath_num;
diff --git a/bgpd/bgp_mpath.h b/bgpd/bgp_mpath.h
index 5476297c3d..4925f16dc1 100644
--- a/bgpd/bgp_mpath.h
+++ b/bgpd/bgp_mpath.h
@@ -19,8 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef _QUAGGA_BGP_MPATH_H
-#define _QUAGGA_BGP_MPATH_H
+#ifndef _FRR_BGP_MPATH_H
+#define _FRR_BGP_MPATH_H
/* Supplemental information linked to bgp_path_info for keeping track of
* multipath selections, lazily allocated to save memory
@@ -51,17 +51,19 @@ struct bgp_path_info_mpath {
};
/* Functions to support maximum-paths configuration */
-extern int bgp_maximum_paths_set(struct bgp *, afi_t, safi_t, int, uint16_t,
- uint16_t);
-extern int bgp_maximum_paths_unset(struct bgp *, afi_t, safi_t, int);
+extern int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi,
+ int peertype, uint16_t maxpaths,
+ bool clusterlen);
+extern int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
+ int peertype);
/* Functions used by bgp_best_selection to record current
* multipath selections
*/
extern int bgp_path_info_nexthop_cmp(struct bgp_path_info *bpi1,
struct bgp_path_info *bpi2);
-extern void bgp_mp_list_init(struct list *);
-extern void bgp_mp_list_clear(struct list *);
+extern void bgp_mp_list_init(struct list *mp_list);
+extern void bgp_mp_list_clear(struct list *mp_list);
extern void bgp_mp_list_add(struct list *mp_list, struct bgp_path_info *mpinfo);
extern void bgp_mp_dmed_deselect(struct bgp_path_info *dmed_best);
extern void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest,
@@ -90,4 +92,4 @@ extern bool bgp_path_info_mpath_chkwtd(struct bgp *bgp,
struct bgp_path_info *path);
extern uint64_t bgp_path_info_mpath_cumbw(struct bgp_path_info *path);
-#endif /* _QUAGGA_BGP_MPATH_H */
+#endif /* _FRR_BGP_MPATH_H */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 4d8c4ac2ac..d1a6daa8f5 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -131,7 +131,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */
while (STREAM_READABLE(data) > 0) {
/* Clear prefix structure. */
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
if (addpath_capable) {
STREAM_GET(&addpath_id, data, BGP_ADDPATH_ID_LEN);
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index e702ee4fdd..77e2a0f53f 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -741,11 +741,9 @@ int bgp_connect(struct peer *peer)
#ifdef IPTOS_PREC_INTERNETCONTROL
frr_with_privs(&bgpd_privs) {
if (sockunion_family(&peer->su) == AF_INET)
- setsockopt_ipv4_tos(peer->fd,
- IPTOS_PREC_INTERNETCONTROL);
+ setsockopt_ipv4_tos(peer->fd, bm->tcp_dscp);
else if (sockunion_family(&peer->su) == AF_INET6)
- setsockopt_ipv6_tclass(peer->fd,
- IPTOS_PREC_INTERNETCONTROL);
+ setsockopt_ipv6_tclass(peer->fd, bm->tcp_dscp);
}
#endif
@@ -822,10 +820,9 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen,
#ifdef IPTOS_PREC_INTERNETCONTROL
if (sa->sa_family == AF_INET)
- setsockopt_ipv4_tos(sock, IPTOS_PREC_INTERNETCONTROL);
+ setsockopt_ipv4_tos(sock, bm->tcp_dscp);
else if (sa->sa_family == AF_INET6)
- setsockopt_ipv6_tclass(sock,
- IPTOS_PREC_INTERNETCONTROL);
+ setsockopt_ipv6_tclass(sock, bm->tcp_dscp);
#endif
sockopt_v6only(sa->sa_family, sock);
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index 25174ce654..c6043807dd 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -396,8 +396,7 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc)
bgp_address_add(bgp, ifc, addr);
- dest = bgp_node_get(bgp->connected_table[AFI_IP],
- (struct prefix *)&p);
+ dest = bgp_node_get(bgp->connected_table[AFI_IP], &p);
bc = bgp_dest_get_bgp_connected_ref_info(dest);
if (bc)
bc->refcnt++;
@@ -430,8 +429,7 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc)
bgp_address_add(bgp, ifc, addr);
- dest = bgp_node_get(bgp->connected_table[AFI_IP6],
- (struct prefix *)&p);
+ dest = bgp_node_get(bgp->connected_table[AFI_IP6], &p);
bc = bgp_dest_get_bgp_connected_ref_info(dest);
if (bc)
@@ -562,7 +560,7 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type,
return true;
if (new_afi == AF_INET && hashcount(bgp->tip_hash)) {
- memset(&tmp_tip, 0, sizeof(struct tip_addr));
+ memset(&tmp_tip, 0, sizeof(tmp_tip));
tmp_tip.addr = attr->nexthop;
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) {
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 3433e1471c..3ab67a6181 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -720,9 +720,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
zlog_debug(
"parse nexthop update(%pFX(%u)(%s)): bnc info not found for import check",
&nhr.prefix, nhr.srte_color, bgp->name_pretty);
- return;
- }
- bgp_process_nexthop_update(bnc_import, &nhr, true);
+ } else
+ bgp_process_nexthop_update(bnc_import, &nhr, true);
/*
* HACK: if any BGP route is dependant on an SR-policy that doesn't
@@ -740,9 +739,9 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi],
bnc_iter) {
- if (!prefix_same(&bnc_import->prefix, &bnc_iter->prefix)
- || bnc_iter->srte_color == 0
- || CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID))
+ if (!prefix_same(&bnc_nhc->prefix, &bnc_iter->prefix) ||
+ bnc_iter->srte_color == 0 ||
+ CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID))
continue;
bgp_process_nexthop_update(bnc_iter, &nhr, false);
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index bfa1506afe..fa3fa3fcee 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -1435,12 +1435,17 @@ static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer,
restart_time = peer->bgp->restart_time;
if (peer->bgp->t_startup) {
SET_FLAG(restart_time, GRACEFUL_RESTART_R_BIT);
- SET_FLAG(restart_time, GRACEFUL_RESTART_N_BIT);
SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV);
- SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending R-Bit for peer: %s",
+ peer->host);
+ }
+ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION)) {
+ SET_FLAG(restart_time, GRACEFUL_RESTART_N_BIT);
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
- zlog_debug("[BGP_GR] Sending R-Bit/N-Bit for peer: %s",
+ zlog_debug("[BGP_GR] Sending N-Bit for peer: %s",
peer->host);
}
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 88eeab6ed1..5560469838 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -122,8 +122,47 @@ void bgp_packet_set_size(struct stream *s)
*/
static void bgp_packet_add(struct peer *peer, struct stream *s)
{
+ intmax_t delta;
+ uint32_t holdtime;
+
frr_with_mutex(&peer->io_mtx) {
+ /* if the queue is empty, reset the "last OK" timestamp to
+ * now, otherwise if we write another packet immediately
+ * after it'll get confused
+ */
+ if (!stream_fifo_count_safe(peer->obuf))
+ peer->last_sendq_ok = bgp_clock();
+
stream_fifo_push(peer->obuf, s);
+
+ delta = bgp_clock() - peer->last_sendq_ok;
+ holdtime = atomic_load_explicit(&peer->holdtime,
+ memory_order_relaxed);
+
+ /* Note that when we're here, we're adding some packet to the
+ * OutQ. That includes keepalives when there is nothing to
+ * do, so there's a guarantee we pass by here once in a while.
+ *
+ * That implies there is no need to go set up another separate
+ * timer that ticks down SendHoldTime, as we'll be here sooner
+ * or later anyway and will see the checks below failing.
+ */
+ if (!holdtime) {
+ /* no holdtime, do nothing. */
+ } else if (delta > 2 * (intmax_t)holdtime) {
+ flog_err(
+ EC_BGP_SENDQ_STUCK_PROPER,
+ "%s has not made any SendQ progress for 2 holdtimes, terminating session",
+ peer->host);
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ } else if (delta > (intmax_t)holdtime &&
+ bgp_clock() - peer->last_sendq_warn > 5) {
+ flog_warn(
+ EC_BGP_SENDQ_STUCK_WARN,
+ "%s has not made any SendQ progress for 1 holdtime, peer overloaded?",
+ peer->host);
+ peer->last_sendq_warn = bgp_clock();
+ }
}
}
@@ -694,7 +733,6 @@ static void bgp_write_notify(struct peer *peer)
/* Type should be notify. */
atomic_fetch_add_explicit(&peer->notify_out, 1, memory_order_relaxed);
- peer->notify_out++;
/* Double start timer. */
peer->v_start *= 2;
@@ -748,28 +786,44 @@ struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify)
return bn;
}
+/* Check if Graceful-Restart N-bit is exchanged */
+bool bgp_has_graceful_restart_notification(struct peer *peer)
+{
+ return CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) &&
+ CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
+}
+
/*
* Check if to send BGP CEASE Notification/Hard Reset?
*/
-bool bgp_notify_is_hard_reset(struct peer *peer, uint8_t code, uint8_t subcode)
+bool bgp_notify_send_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode)
{
/* When the "N" bit has been exchanged, a Hard Reset message is used to
* indicate to the peer that the session is to be fully terminated.
*/
- if (!CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) ||
- !CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV))
+ if (!bgp_has_graceful_restart_notification(peer))
return false;
/*
* https://datatracker.ietf.org/doc/html/rfc8538#section-5.1
*/
- if (code == BGP_NOTIFY_CEASE || code == BGP_NOTIFY_HOLD_ERR) {
+ if (code == BGP_NOTIFY_CEASE) {
switch (subcode) {
case BGP_NOTIFY_CEASE_MAX_PREFIX:
case BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN:
case BGP_NOTIFY_CEASE_PEER_UNCONFIG:
case BGP_NOTIFY_CEASE_HARD_RESET:
return true;
+ case BGP_NOTIFY_CEASE_ADMIN_RESET:
+ /* Provide user control:
+ * `bgp hard-adminstrative-reset`
+ */
+ if (CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_HARD_ADMIN_RESET))
+ return true;
+ else
+ return false;
default:
break;
}
@@ -779,6 +833,24 @@ bool bgp_notify_is_hard_reset(struct peer *peer, uint8_t code, uint8_t subcode)
}
/*
+ * Check if received BGP CEASE Notification/Hard Reset?
+ */
+bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode)
+{
+ /* When the "N" bit has been exchanged, a Hard Reset message is used to
+ * indicate to the peer that the session is to be fully terminated.
+ */
+ if (!bgp_has_graceful_restart_notification(peer))
+ return false;
+
+ if (code == BGP_NOTIFY_CEASE && subcode == BGP_NOTIFY_CEASE_HARD_RESET)
+ return true;
+
+ return false;
+}
+
+/*
* Creates a BGP Notify and appends it to the peer's output queue.
*
* This function attempts to write the packet from the thread it is called
@@ -802,7 +874,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
uint8_t sub_code, uint8_t *data, size_t datalen)
{
struct stream *s;
- bool hard_reset = bgp_notify_is_hard_reset(peer, code, sub_code);
+ bool hard_reset = bgp_notify_send_hard_reset(peer, code, sub_code);
/* Lock I/O mutex to prevent other threads from pushing packets */
frr_mutex_lock_autounlock(&peer->io_mtx);
@@ -1681,7 +1753,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
}
/* Set initial values. */
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
attr.label_index = BGP_INVALID_LABEL_INDEX;
attr.label = MPLS_INVALID_LABEL;
memset(&nlris, 0, sizeof(nlris));
@@ -1980,6 +2052,7 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
if (peer->notify.data) {
XFREE(MTYPE_BGP_NOTIFICATION, peer->notify.data);
peer->notify.length = 0;
+ peer->notify.hard_reset = false;
}
outer.code = stream_getc(peer->curr);
@@ -1992,7 +2065,8 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
memcpy(outer.raw_data, stream_pnt(peer->curr), outer.length);
}
- hard_reset = bgp_notify_is_hard_reset(peer, outer.code, outer.subcode);
+ hard_reset =
+ bgp_notify_received_hard_reset(peer, outer.code, outer.subcode);
if (hard_reset && outer.length) {
inner = bgp_notify_decapsulate_hard_reset(&outer);
peer->notify.hard_reset = true;
@@ -2063,6 +2137,13 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
inner.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM)
UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+ /* If Graceful-Restart N-bit (Notification) is exchanged,
+ * and it's not a Hard Reset, let's retain the routes.
+ */
+ if (bgp_has_graceful_restart_notification(peer) && !hard_reset &&
+ CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE))
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+
bgp_peer_gr_flags_update(peer);
BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
peer->bgp->peer);
@@ -2218,8 +2299,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
* to maximise debug information.
*/
int ok;
- memset(&orfp, 0,
- sizeof(struct orf_prefix));
+ memset(&orfp, 0, sizeof(orfp));
common = *p_pnt++;
/* after ++: p_pnt <= p_end */
if (common
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index 2fd7ff64d4..a0eb579db7 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -88,7 +88,10 @@ extern void bgp_send_delayed_eor(struct bgp *bgp);
void bgp_packet_process_error(struct thread *thread);
extern struct bgp_notify
bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify);
-extern bool bgp_notify_is_hard_reset(struct peer *peer, uint8_t code,
- uint8_t subcode);
+extern bool bgp_has_graceful_restart_notification(struct peer *peer);
+extern bool bgp_notify_send_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode);
+extern bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode);
#endif /* _QUAGGA_BGP_PACKET_H */
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
index 352670e6c1..7b5e287242 100644
--- a/bgpd/bgp_pbr.c
+++ b/bgpd/bgp_pbr.c
@@ -2679,9 +2679,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
struct bgp_pbr_val_mask bpvm;
memset(&range, 0, sizeof(range));
- memset(&nh, 0, sizeof(struct nexthop));
- memset(&bpf, 0, sizeof(struct bgp_pbr_filter));
- memset(&bpof, 0, sizeof(struct bgp_pbr_or_filter));
+ memset(&nh, 0, sizeof(nh));
+ memset(&bpf, 0, sizeof(bpf));
+ memset(&bpof, 0, sizeof(bpof));
if (api->match_bitmask & PREFIX_SRC_PRESENT ||
(api->type == BGP_PBR_IPRULE &&
api->match_bitmask_iprule & PREFIX_SRC_PRESENT))
@@ -2692,7 +2692,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
dst = &api->dst_prefix;
if (api->type == BGP_PBR_IPRULE)
bpf.type = api->type;
- memset(&nh, 0, sizeof(struct nexthop));
+ memset(&nh, 0, sizeof(nh));
nh.vrf_id = VRF_UNKNOWN;
if (api->match_protocol_num) {
proto = (uint8_t)api->protocol[0].value;
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 2544ea5208..5df45aa315 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1079,12 +1079,9 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
pair (newm, existm) with the cluster list length. Prefer the
path with smaller cluster list length. */
if (newm == existm) {
- if (peer_sort_lookup(new->peer) == BGP_PEER_IBGP
- && peer_sort_lookup(exist->peer) == BGP_PEER_IBGP
- && (mpath_cfg == NULL
- || CHECK_FLAG(
- mpath_cfg->ibgp_flags,
- BGP_FLAG_IBGP_MULTIPATH_SAME_CLUSTERLEN))) {
+ if (peer_sort_lookup(new->peer) == BGP_PEER_IBGP &&
+ peer_sort_lookup(exist->peer) == BGP_PEER_IBGP &&
+ (mpath_cfg == NULL || mpath_cfg->same_clusterlen)) {
newm = BGP_CLUSTER_LIST_LENGTH(new->attr);
existm = BGP_CLUSTER_LIST_LENGTH(exist->attr);
@@ -1597,7 +1594,7 @@ static int bgp_input_modifier(struct peer *peer, const struct prefix *p,
/* Route map apply. */
if (rmap) {
- memset(&rmap_path, 0, sizeof(struct bgp_path_info));
+ memset(&rmap_path, 0, sizeof(rmap_path));
/* Duplicate current value to new structure for modification. */
rmap_path.peer = peer;
rmap_path.attr = attr;
@@ -1653,7 +1650,7 @@ static int bgp_output_modifier(struct peer *peer, const struct prefix *p,
if (rmap == NULL)
return RMAP_DENY;
- memset(&rmap_path, 0, sizeof(struct bgp_path_info));
+ memset(&rmap_path, 0, sizeof(rmap_path));
/* Route map apply. */
/* Duplicate current value to new structure for modification. */
rmap_path.peer = peer;
@@ -2698,7 +2695,7 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp,
PEER_STATUS_ORF_WAIT_REFRESH))
return;
- memset(&attr, 0, sizeof(struct attr));
+ memset(&attr, 0, sizeof(attr));
/* It's initialized in bgp_announce_check() */
/* Announcement to the subgroup. If the route is filtered withdraw it.
@@ -3758,7 +3755,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (orig_safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
- memset(&new_attr, 0, sizeof(struct attr));
+ memset(&new_attr, 0, sizeof(new_attr));
new_attr.label_index = BGP_INVALID_LABEL_INDEX;
new_attr.label = MPLS_INVALID_LABEL;
@@ -5656,7 +5653,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
then the Error Subcode is set to Invalid Network Field. */
for (; pnt < lim; pnt += psize) {
/* Clear prefix structure. */
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
if (addpath_capable) {
@@ -5697,12 +5694,12 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
}
/* Defensive coding, double-check the psize fits in a struct
- * prefix */
- if (psize > (ssize_t)sizeof(p.u)) {
+ * prefix for the v4 and v6 afi's and unicast/multicast */
+ if (psize > (ssize_t)sizeof(p.u.val)) {
flog_err(
EC_BGP_UPDATE_RCV,
"%s [Error] Update packet error (prefix length %d too large for prefix storage %zu)",
- peer->host, p.prefixlen, sizeof(p.u));
+ peer->host, p.prefixlen, sizeof(p.u.val));
return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
}
@@ -5833,7 +5830,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
if (bgp_static->rmap.name) {
struct attr attr_tmp = attr;
- memset(&rmap_path, 0, sizeof(struct bgp_path_info));
+ memset(&rmap_path, 0, sizeof(rmap_path));
rmap_path.peer = bgp->peer_self;
rmap_path.attr = &attr_tmp;
@@ -6145,7 +6142,7 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
memcpy(&attr.esi, bgp_static->eth_s_id, sizeof(esi_t));
if (bgp_static->encap_tunneltype == BGP_ENCAP_TYPE_VXLAN) {
struct bgp_encap_type_vxlan bet;
- memset(&bet, 0, sizeof(struct bgp_encap_type_vxlan));
+ memset(&bet, 0, sizeof(bet));
bet.vnid = p->u.prefix_evpn.prefix_addr.eth_tag;
bgp_encap_type_vxlan_to_tlv(&bet, &attr);
}
@@ -6645,7 +6642,7 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty,
return CMD_WARNING_CONFIG_FAILED;
}
if (gwip) {
- memset(&gw_ip, 0, sizeof(struct prefix));
+ memset(&gw_ip, 0, sizeof(gw_ip));
ret = str2prefix(gwip, &gw_ip);
if (!ret) {
vty_out(vty, "%% Malformed GatewayIp\n");
@@ -8372,7 +8369,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
/* Apply route-map. */
if (red->rmap.name) {
- memset(&rmap_path, 0, sizeof(struct bgp_path_info));
+ memset(&rmap_path, 0, sizeof(rmap_path));
rmap_path.peer = bgp->peer_self;
rmap_path.attr = &attr_new;
@@ -13472,7 +13469,7 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix,
RPKI_NOT_BEING_USED, use_json(argc, argv));
}
-static void show_adj_route_header(struct vty *vty, struct bgp *bgp,
+static void show_adj_route_header(struct vty *vty, struct peer *peer,
struct bgp_table *table, int *header1,
int *header2, json_object *json,
json_object *json_scode,
@@ -13484,10 +13481,13 @@ static void show_adj_route_header(struct vty *vty, struct bgp *bgp,
if (json) {
json_object_int_add(json, "bgpTableVersion", version);
json_object_string_addf(json, "bgpLocalRouterId",
- "%pI4", &bgp->router_id);
+ "%pI4", &peer->bgp->router_id);
json_object_int_add(json, "defaultLocPrf",
- bgp->default_local_pref);
- json_object_int_add(json, "localAS", bgp->as);
+ peer->bgp->default_local_pref);
+ json_object_int_add(json, "localAS",
+ peer->change_local_as
+ ? peer->change_local_as
+ : peer->local_as);
json_object_object_add(json, "bgpStatusCodes",
json_scode);
json_object_object_add(json, "bgpOriginCodes",
@@ -13496,15 +13496,17 @@ static void show_adj_route_header(struct vty *vty, struct bgp *bgp,
vty_out(vty,
"BGP table version is %" PRIu64
", local router ID is %pI4, vrf id ",
- version, &bgp->router_id);
- if (bgp->vrf_id == VRF_UNKNOWN)
+ version, &peer->bgp->router_id);
+ if (peer->bgp->vrf_id == VRF_UNKNOWN)
vty_out(vty, "%s", VRFID_NONE_STR);
else
- vty_out(vty, "%u", bgp->vrf_id);
+ vty_out(vty, "%u", peer->bgp->vrf_id);
vty_out(vty, "\n");
vty_out(vty, "Default local pref %u, ",
- bgp->default_local_pref);
- vty_out(vty, "local AS %u\n", bgp->as);
+ peer->bgp->default_local_pref);
+ vty_out(vty, "local AS %u\n",
+ peer->change_local_as ? peer->change_local_as
+ : peer->local_as);
vty_out(vty, BGP_SHOW_SCODE_HEADER);
vty_out(vty, BGP_SHOW_NCODE_HEADER);
vty_out(vty, BGP_SHOW_OCODE_HEADER);
@@ -13557,7 +13559,10 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
"%pI4", &bgp->router_id);
json_object_int_add(json, "defaultLocPrf",
bgp->default_local_pref);
- json_object_int_add(json, "localAS", bgp->as);
+ json_object_int_add(json, "localAS",
+ peer->change_local_as
+ ? peer->change_local_as
+ : peer->local_as);
json_object_object_add(json, "bgpStatusCodes",
json_scode);
json_object_object_add(json, "bgpOriginCodes",
@@ -13577,7 +13582,9 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
vty_out(vty, "\n");
vty_out(vty, "Default local pref %u, ",
bgp->default_local_pref);
- vty_out(vty, "local AS %u\n", bgp->as);
+ vty_out(vty, "local AS %u\n",
+ peer->change_local_as ? peer->change_local_as
+ : peer->local_as);
vty_out(vty, BGP_SHOW_SCODE_HEADER);
vty_out(vty, BGP_SHOW_NCODE_HEADER);
vty_out(vty, BGP_SHOW_OCODE_HEADER);
@@ -13596,7 +13603,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
if (ain->peer != peer)
continue;
- show_adj_route_header(vty, bgp, table, header1,
+ show_adj_route_header(vty, peer, table, header1,
header2, json, json_scode,
json_ocode, wide);
@@ -13653,7 +13660,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
if (paf->peer != peer || !adj->attr)
continue;
- show_adj_route_header(vty, bgp, table,
+ show_adj_route_header(vty, peer, table,
header1, header2,
json, json_scode,
json_ocode, wide);
@@ -13697,9 +13704,9 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
} else if (type == bgp_show_adj_route_bestpath) {
struct bgp_path_info *pi;
- show_adj_route_header(vty, bgp, table, header1, header2,
- json, json_scode, json_ocode,
- wide);
+ show_adj_route_header(vty, peer, table, header1,
+ header2, json, json_scode,
+ json_ocode, wide);
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 20ee2e4d49..c7f5e0433b 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -263,10 +263,14 @@ route_match_peer(void *rule, const struct prefix *prefix, void *object)
peer = ((struct bgp_path_info *)object)->peer;
if (pc->interface) {
- if (!peer->conf_if)
+ if (!peer->conf_if || !peer->group)
return RMAP_NOMATCH;
- if (strcmp(peer->conf_if, pc->interface) == 0)
+ if (peer->conf_if && strcmp(peer->conf_if, pc->interface) == 0)
+ return RMAP_MATCH;
+
+ if (peer->group &&
+ strcmp(peer->group->name, pc->interface) == 0)
return RMAP_MATCH;
return RMAP_NOMATCH;
@@ -4567,7 +4571,7 @@ DEFPY_YANG (match_peer,
"Match peer address\n"
"IP address of peer\n"
"IPv6 address of peer\n"
- "Interface name of peer\n")
+ "Interface name of peer or peer group name\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:peer']";
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index eb9d5f3f73..1c7dc7cb0a 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -63,9 +63,6 @@
#include "bgpd/bgp_rpki_clippy.c"
#endif
-static struct thread *t_rpki;
-static struct thread *t_rpki_start;
-
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib");
@@ -75,6 +72,8 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib");
#define RETRY_INTERVAL_DEFAULT 600
#define BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT 3
+static struct thread *t_rpki_sync;
+
#define RPKI_DEBUG(...) \
if (rpki_debug) { \
zlog_debug("RPKI: " __VA_ARGS__); \
@@ -99,13 +98,14 @@ struct rpki_for_each_record_arg {
struct vty *vty;
unsigned int *prefix_amount;
as_t as;
+ json_object *json;
};
static int start(void);
static void stop(void);
static int reset(bool force);
static struct rtr_mgr_group *get_connected_group(void);
-static void print_prefix_table(struct vty *vty);
+static void print_prefix_table(struct vty *vty, json_object *json);
static void install_cli_commands(void);
static int config_write(struct vty *vty);
static int config_on_exit(struct vty *vty);
@@ -122,10 +122,11 @@ static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket);
static struct cache *find_cache(const uint8_t preference);
static int add_tcp_cache(const char *host, const char *port,
const uint8_t preference, const char *bindaddr);
-static void print_record(const struct pfx_record *record, struct vty *vty);
-static int is_synchronized(void);
-static int is_running(void);
-static int is_stopping(void);
+static void print_record(const struct pfx_record *record, struct vty *vty,
+ json_object *json);
+static bool is_synchronized(void);
+static bool is_running(void);
+static bool is_stopping(void);
static void route_match_free(void *rule);
static enum route_map_cmd_result_t route_match(void *rule,
const struct prefix *prefix,
@@ -137,10 +138,11 @@ static void revalidate_all_routes(void);
static struct rtr_mgr_config *rtr_config;
static struct list *cache_list;
-static int rtr_is_running;
-static int rtr_is_stopping;
+static bool rtr_is_running;
+static bool rtr_is_stopping;
+static bool rtr_is_synced;
static _Atomic int rtr_update_overflow;
-static int rpki_debug;
+static bool rpki_debug;
static unsigned int polling_period;
static unsigned int expire_interval;
static unsigned int retry_interval;
@@ -274,13 +276,27 @@ static struct cache *find_cache(const uint8_t preference)
return NULL;
}
-static void print_record(const struct pfx_record *record, struct vty *vty)
+static void print_record(const struct pfx_record *record, struct vty *vty,
+ json_object *json)
{
char ip[INET6_ADDRSTRLEN];
+ json_object *json_record = NULL;
lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip));
- vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len,
- record->max_len, record->asn);
+
+ if (!json) {
+ vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len,
+ record->max_len, record->asn);
+ } else {
+ json_record = json_object_new_object();
+ json_object_string_add(json_record, "prefix", ip);
+ json_object_int_add(json_record, "prefixLenMin",
+ record->min_len);
+ json_object_int_add(json_record, "prefixLenMax",
+ record->max_len);
+ json_object_int_add(json_record, "asn", record->asn);
+ json_object_array_add(json, json_record);
+ }
}
static void print_record_by_asn(const struct pfx_record *record, void *data)
@@ -290,7 +306,7 @@ static void print_record_by_asn(const struct pfx_record *record, void *data)
if (record->asn == arg->as) {
(*arg->prefix_amount)++;
- print_record(record, vty);
+ print_record(record, vty, arg->json);
}
}
@@ -301,7 +317,7 @@ static void print_record_cb(const struct pfx_record *record, void *data)
(*arg->prefix_amount)++;
- print_record(record, vty);
+ print_record(record, vty, arg->json);
}
static struct rtr_mgr_group *get_groups(void)
@@ -333,17 +349,17 @@ static struct rtr_mgr_group *get_groups(void)
return rtr_mgr_groups;
}
-inline int is_synchronized(void)
+inline bool is_synchronized(void)
{
- return is_running() && rtr_mgr_conf_in_sync(rtr_config);
+ return rtr_is_synced;
}
-inline int is_running(void)
+inline bool is_running(void)
{
return rtr_is_running;
}
-inline int is_stopping(void)
+inline bool is_stopping(void)
{
return rtr_is_stopping;
}
@@ -372,13 +388,13 @@ static void bgpd_sync_callback(struct thread *thread)
struct listnode *node;
struct prefix *prefix;
struct pfx_record rec;
- int retval;
- int socket = THREAD_FD(thread);
- thread_add_read(bm->master, bgpd_sync_callback, NULL, socket, &t_rpki);
+ thread_add_read(bm->master, bgpd_sync_callback, NULL,
+ rpki_sync_socket_bgpd, NULL);
if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
- while (read(socket, &rec, sizeof(struct pfx_record)) != -1)
+ while (read(rpki_sync_socket_bgpd, &rec,
+ sizeof(struct pfx_record)) != -1)
;
atomic_store_explicit(&rtr_update_overflow, 0,
@@ -387,20 +403,12 @@ static void bgpd_sync_callback(struct thread *thread)
return;
}
- retval = read(socket, &rec, sizeof(struct pfx_record));
+ int retval =
+ read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
if (retval != sizeof(struct pfx_record)) {
- RPKI_DEBUG("Could not read from socket");
+ RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
return;
}
-
- /* RTR-Server crashed/terminated, let's handle and switch
- * to the second available RTR-Server according to preference.
- */
- if (rec.socket && rec.socket->state == RTR_ERROR_FATAL) {
- reset(true);
- return;
- }
-
prefix = pfx_record_to_prefix(&rec);
afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
@@ -458,53 +466,29 @@ static void revalidate_all_routes(void)
{
struct bgp *bgp;
struct listnode *node;
- afi_t afi;
- safi_t safi;
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
struct peer *peer;
struct listnode *peer_listnode;
for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
- FOREACH_AFI_SAFI (afi, safi) {
- if (!peer->afc_nego[afi][safi])
- continue;
- if (!peer->bgp->rib[afi][safi])
- continue;
+ for (size_t i = 0; i < 2; i++) {
+ safi_t safi;
+ afi_t afi = (i == 0) ? AFI_IP : AFI_IP6;
+
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX;
+ safi++) {
+ if (!peer->bgp->rib[afi][safi])
+ continue;
- bgp_soft_reconfig_in(peer, afi, safi);
+ bgp_soft_reconfig_in(peer, afi, safi);
+ }
}
}
}
}
-static void rpki_connection_status_cb(const struct rtr_mgr_group *group
- __attribute__((unused)),
- enum rtr_mgr_status status,
- const struct rtr_socket *socket
- __attribute__((unused)),
- void *data __attribute__((unused)))
-{
- struct pfx_record rec = {0};
- int retval;
-
- if (is_stopping() ||
- atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
- return;
-
- if (status == RTR_MGR_ERROR)
- rec.socket = socket;
-
- retval = write(rpki_sync_socket_rtr, &rec, sizeof(rec));
- if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
- atomic_store_explicit(&rtr_update_overflow, 1,
- memory_order_seq_cst);
-
- else if (retval != sizeof(rec))
- RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
-}
-
static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
const struct pfx_record rec,
const bool added __attribute__((unused)))
@@ -546,8 +530,9 @@ static void rpki_init_sync_socket(void)
goto err;
}
+
thread_add_read(bm->master, bgpd_sync_callback, NULL,
- rpki_sync_socket_bgpd, &t_rpki);
+ rpki_sync_socket_bgpd, NULL);
return;
@@ -559,9 +544,10 @@ err:
static int bgp_rpki_init(struct thread_master *master)
{
- rpki_debug = 0;
- rtr_is_running = 0;
- rtr_is_stopping = 0;
+ rpki_debug = false;
+ rtr_is_running = false;
+ rtr_is_stopping = false;
+ rtr_is_synced = false;
cache_list = list_new();
cache_list->del = (void (*)(void *)) & free_cache;
@@ -596,23 +582,27 @@ static int bgp_rpki_module_init(void)
return 0;
}
-static void start_expired(struct thread *thread)
+static void sync_expired(struct thread *thread)
{
if (!rtr_mgr_conf_in_sync(rtr_config)) {
- thread_add_timer(bm->master, start_expired, NULL,
+ RPKI_DEBUG("rtr_mgr is not synced, retrying.");
+ thread_add_timer(bm->master, sync_expired, NULL,
BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT,
- &t_rpki_start);
+ &t_rpki_sync);
return;
}
- rtr_is_running = 1;
+ RPKI_DEBUG("rtr_mgr sync is done.");
+
+ rtr_is_synced = true;
}
static int start(void)
{
int ret;
- rtr_is_stopping = 0;
+ rtr_is_stopping = false;
+ rtr_is_synced = false;
rtr_update_overflow = 0;
if (list_isempty(cache_list)) {
@@ -627,8 +617,7 @@ static int start(void)
RPKI_DEBUG("Polling period: %d", polling_period);
ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
expire_interval, retry_interval,
- rpki_update_cb_sync_rtr, NULL,
- rpki_connection_status_cb, NULL);
+ rpki_update_cb_sync_rtr, NULL, NULL, NULL);
if (ret == RTR_ERROR) {
RPKI_DEBUG("Init rtr_mgr failed.");
return ERROR;
@@ -642,21 +631,23 @@ static int start(void)
return ERROR;
}
- thread_add_timer(bm->master, start_expired, NULL, 0, &t_rpki_start);
+ thread_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync);
XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
+ rtr_is_running = true;
+
return SUCCESS;
}
static void stop(void)
{
- rtr_is_stopping = 1;
+ rtr_is_stopping = true;
if (is_running()) {
- THREAD_OFF(t_rpki_start);
+ THREAD_OFF(t_rpki_sync);
rtr_mgr_stop(rtr_config);
rtr_mgr_free(rtr_config);
- rtr_is_running = 0;
+ rtr_is_running = false;
}
}
@@ -665,9 +656,6 @@ static int reset(bool force)
if (is_running() && !force)
return SUCCESS;
- if (thread_is_scheduled(t_rpki_start))
- return SUCCESS;
-
RPKI_DEBUG("Resetting RPKI Session");
stop();
return start();
@@ -681,25 +669,36 @@ static struct rtr_mgr_group *get_connected_group(void)
return rtr_mgr_get_first_group(rtr_config);
}
-static void print_prefix_table_by_asn(struct vty *vty, as_t as)
+static void print_prefix_table_by_asn(struct vty *vty, as_t as,
+ json_object *json)
{
unsigned int number_of_ipv4_prefixes = 0;
unsigned int number_of_ipv6_prefixes = 0;
struct rtr_mgr_group *group = get_connected_group();
struct rpki_for_each_record_arg arg;
+ json_object *json_records = NULL;
arg.vty = vty;
arg.as = as;
+ arg.json = NULL;
if (!group) {
- vty_out(vty, "Cannot find a connected group.\n");
+ if (!json)
+ vty_out(vty, "Cannot find a connected group.\n");
return;
}
struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
- vty_out(vty, "RPKI/RTR prefix table\n");
- vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", "Origin-AS");
+ if (!json) {
+ vty_out(vty, "RPKI/RTR prefix table\n");
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
+ "Origin-AS");
+ } else {
+ json_records = json_object_new_array();
+ json_object_object_add(json, "prefixes", json_records);
+ arg.json = json_records;
+ }
arg.prefix_amount = &number_of_ipv4_prefixes;
pfx_table_for_each_ipv4_record(pfx_table, print_record_by_asn, &arg);
@@ -707,27 +706,51 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as)
arg.prefix_amount = &number_of_ipv6_prefixes;
pfx_table_for_each_ipv6_record(pfx_table, print_record_by_asn, &arg);
- vty_out(vty, "Number of IPv4 Prefixes: %u\n", number_of_ipv4_prefixes);
- vty_out(vty, "Number of IPv6 Prefixes: %u\n", number_of_ipv6_prefixes);
+ if (!json) {
+ vty_out(vty, "Number of IPv4 Prefixes: %u\n",
+ number_of_ipv4_prefixes);
+ vty_out(vty, "Number of IPv6 Prefixes: %u\n",
+ number_of_ipv6_prefixes);
+ } else {
+ json_object_int_add(json, "ipv4PrefixCount",
+ number_of_ipv4_prefixes);
+ json_object_int_add(json, "ipv6PrefixCount",
+ number_of_ipv6_prefixes);
+ }
+
+ if (json)
+ vty_json(vty, json);
}
-static void print_prefix_table(struct vty *vty)
+static void print_prefix_table(struct vty *vty, json_object *json)
{
struct rpki_for_each_record_arg arg;
unsigned int number_of_ipv4_prefixes = 0;
unsigned int number_of_ipv6_prefixes = 0;
struct rtr_mgr_group *group = get_connected_group();
+ json_object *json_records = NULL;
arg.vty = vty;
+ arg.json = NULL;
- if (!group)
+ if (!group) {
+ if (!json)
+ vty_out(vty, "Cannot find a connected group.\n");
return;
+ }
struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
- vty_out(vty, "RPKI/RTR prefix table\n");
- vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", "Origin-AS");
+ if (!json) {
+ vty_out(vty, "RPKI/RTR prefix table\n");
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
+ "Origin-AS");
+ } else {
+ json_records = json_object_new_array();
+ json_object_object_add(json, "prefixes", json_records);
+ arg.json = json_records;
+ }
arg.prefix_amount = &number_of_ipv4_prefixes;
pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg);
@@ -735,8 +758,20 @@ static void print_prefix_table(struct vty *vty)
arg.prefix_amount = &number_of_ipv6_prefixes;
pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg);
- vty_out(vty, "Number of IPv4 Prefixes: %u\n", number_of_ipv4_prefixes);
- vty_out(vty, "Number of IPv6 Prefixes: %u\n", number_of_ipv6_prefixes);
+ if (!json) {
+ vty_out(vty, "Number of IPv4 Prefixes: %u\n",
+ number_of_ipv4_prefixes);
+ vty_out(vty, "Number of IPv6 Prefixes: %u\n",
+ number_of_ipv6_prefixes);
+ } else {
+ json_object_int_add(json, "ipv4PrefixCount",
+ number_of_ipv4_prefixes);
+ json_object_int_add(json, "ipv6PrefixCount",
+ number_of_ipv6_prefixes);
+ }
+
+ if (json)
+ vty_json(vty, json);
}
static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
@@ -1064,10 +1099,11 @@ DEFPY (rpki_polling_period,
DEFUN (no_rpki_polling_period,
no_rpki_polling_period_cmd,
- "no rpki polling_period",
+ "no rpki polling_period [(1-86400)]",
NO_STR
RPKI_OUTPUT_STRING
- "Set polling period back to default\n")
+ "Set polling period back to default\n"
+ "Polling period value\n")
{
polling_period = POLLING_PERIOD_DEFAULT;
return CMD_SUCCESS;
@@ -1091,10 +1127,11 @@ DEFPY (rpki_expire_interval,
DEFUN (no_rpki_expire_interval,
no_rpki_expire_interval_cmd,
- "no rpki expire_interval",
+ "no rpki expire_interval [(600-172800)]",
NO_STR
RPKI_OUTPUT_STRING
- "Set expire interval back to default\n")
+ "Set expire interval back to default\n"
+ "Expire interval value\n")
{
expire_interval = polling_period * 2;
return CMD_SUCCESS;
@@ -1113,10 +1150,11 @@ DEFPY (rpki_retry_interval,
DEFUN (no_rpki_retry_interval,
no_rpki_retry_interval_cmd,
- "no rpki retry_interval",
+ "no rpki retry_interval [(1-7200)]",
NO_STR
RPKI_OUTPUT_STRING
- "Set retry interval back to default\n")
+ "Set retry interval back to default\n"
+ "retry interval value\n")
{
retry_interval = RETRY_INTERVAL_DEFAULT;
return CMD_SUCCESS;
@@ -1212,57 +1250,70 @@ DEFPY (no_rpki_cache,
return CMD_SUCCESS;
}
-DEFUN (show_rpki_prefix_table,
+DEFPY (show_rpki_prefix_table,
show_rpki_prefix_table_cmd,
- "show rpki prefix-table",
+ "show rpki prefix-table [json$uj]",
SHOW_STR
RPKI_OUTPUT_STRING
- "Show validated prefixes which were received from RPKI Cache\n")
+ "Show validated prefixes which were received from RPKI Cache\n"
+ JSON_STR)
{
- struct listnode *cache_node;
- struct cache *cache;
+ struct json_object *json = NULL;
- for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
- vty_out(vty, "host: %s port: %s\n",
- cache->tr_config.tcp_config->host,
- cache->tr_config.tcp_config->port);
+ if (!is_synchronized()) {
+ if (!uj)
+ vty_out(vty, "No connection to RPKI cache server.\n");
+ return CMD_WARNING;
}
- if (is_synchronized())
- print_prefix_table(vty);
- else
- vty_out(vty, "No connection to RPKI cache server.\n");
+ if (uj)
+ json = json_object_new_object();
+
+ print_prefix_table(vty, json);
return CMD_SUCCESS;
}
-DEFPY (show_rpki_as_number, show_rpki_as_number_cmd,
- "show rpki as-number (1-4294967295)$by_asn",
- SHOW_STR RPKI_OUTPUT_STRING
- "Lookup by ASN in prefix table\n"
- "AS Number\n")
+DEFPY (show_rpki_as_number,
+ show_rpki_as_number_cmd,
+ "show rpki as-number (1-4294967295)$by_asn [json$uj]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Lookup by ASN in prefix table\n"
+ "AS Number\n"
+ JSON_STR)
{
+ struct json_object *json = NULL;
+
if (!is_synchronized()) {
- vty_out(vty, "No Connection to RPKI cache server.\n");
+ if (!uj)
+ vty_out(vty, "No Connection to RPKI cache server.\n");
return CMD_WARNING;
}
- print_prefix_table_by_asn(vty, by_asn);
+ if (uj)
+ json = json_object_new_object();
+
+ print_prefix_table_by_asn(vty, by_asn, json);
return CMD_SUCCESS;
}
DEFPY (show_rpki_prefix,
show_rpki_prefix_cmd,
- "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn]",
+ "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn] [json$uj]",
SHOW_STR
RPKI_OUTPUT_STRING
"Lookup IP prefix and optionally ASN in prefix table\n"
"IPv4 prefix\n"
"IPv6 prefix\n"
- "AS Number\n")
+ "AS Number\n"
+ JSON_STR)
{
+ json_object *json = NULL;
+ json_object *json_records = NULL;
if (!is_synchronized()) {
- vty_out(vty, "No Connection to RPKI cache server.\n");
+ if (!uj)
+ vty_out(vty, "No Connection to RPKI cache server.\n");
return CMD_WARNING;
}
@@ -1274,7 +1325,8 @@ DEFPY (show_rpki_prefix,
memcpy(addr_str, prefix_str, addr_len);
if (lrtr_ip_str_to_addr(addr_str, &addr) != 0) {
- vty_out(vty, "Invalid IP prefix\n");
+ if (!json)
+ vty_out(vty, "Invalid IP prefix\n");
return CMD_WARNING;
}
@@ -1285,110 +1337,247 @@ DEFPY (show_rpki_prefix,
if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count,
asn, &addr, prefix->prefixlen, &result)
!= PFX_SUCCESS) {
- vty_out(vty, "Prefix lookup failed\n");
+ if (!json)
+ vty_out(vty, "Prefix lookup failed\n");
return CMD_WARNING;
}
- vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", "Origin-AS");
+ if (uj)
+ json = json_object_new_object();
+
+ if (!json) {
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
+ "Origin-AS");
+ } else {
+ json_records = json_object_new_array();
+ json_object_object_add(json, "prefixes", json_records);
+ }
+
for (size_t i = 0; i < match_count; ++i) {
const struct pfx_record *record = &matches[i];
if (record->max_len >= prefix->prefixlen
&& ((asn != 0 && (uint32_t)asn == record->asn)
|| asn == 0)) {
- print_record(&matches[i], vty);
+ print_record(&matches[i], vty, json_records);
}
}
+ if (json)
+ vty_json(vty, json);
+
return CMD_SUCCESS;
}
-DEFUN (show_rpki_cache_server,
+DEFPY (show_rpki_cache_server,
show_rpki_cache_server_cmd,
- "show rpki cache-server",
+ "show rpki cache-server [json$uj]",
SHOW_STR
RPKI_OUTPUT_STRING
- "SHOW configured cache server\n")
+ "Show configured cache server\n"
+ JSON_STR)
{
+ struct json_object *json = NULL;
+ struct json_object *json_server = NULL;
+ struct json_object *json_servers = NULL;
struct listnode *cache_node;
struct cache *cache;
+ if (uj) {
+ json = json_object_new_object();
+ json_servers = json_object_new_array();
+ json_object_object_add(json, "servers", json_servers);
+ }
+
for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
if (cache->type == TCP) {
- vty_out(vty, "host: %s port: %s\n",
- cache->tr_config.tcp_config->host,
- cache->tr_config.tcp_config->port);
+ if (!json) {
+ vty_out(vty, "host: %s port: %s\n",
+ cache->tr_config.tcp_config->host,
+ cache->tr_config.tcp_config->port);
+ } else {
+ json_server = json_object_new_object();
+ json_object_string_add(json_server, "mode",
+ "tcp");
+ json_object_string_add(
+ json_server, "host",
+ cache->tr_config.tcp_config->host);
+ json_object_string_add(
+ json_server, "port",
+ cache->tr_config.tcp_config->port);
+ json_object_array_add(json_servers,
+ json_server);
+ }
#if defined(FOUND_SSH)
} else if (cache->type == SSH) {
- vty_out(vty,
- "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s\n",
- cache->tr_config.ssh_config->host,
- cache->tr_config.ssh_config->port,
- cache->tr_config.ssh_config->username,
- cache->tr_config.ssh_config
- ->server_hostkey_path,
- cache->tr_config.ssh_config
- ->client_privkey_path);
+ if (!json) {
+ vty_out(vty,
+ "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s\n",
+ cache->tr_config.ssh_config->host,
+ cache->tr_config.ssh_config->port,
+ cache->tr_config.ssh_config->username,
+ cache->tr_config.ssh_config
+ ->server_hostkey_path,
+ cache->tr_config.ssh_config
+ ->client_privkey_path);
+ } else {
+ json_server = json_object_new_object();
+ json_object_string_add(json_server, "mode",
+ "ssh");
+ json_object_string_add(
+ json_server, "host",
+ cache->tr_config.ssh_config->host);
+ json_object_int_add(
+ json_server, "port",
+ cache->tr_config.ssh_config->port);
+ json_object_string_add(
+ json_server, "username",
+ cache->tr_config.ssh_config->username);
+ json_object_string_add(
+ json_server, "serverHostkeyPath",
+ cache->tr_config.ssh_config
+ ->server_hostkey_path);
+ json_object_string_add(
+ json_server, "clientPrivkeyPath",
+ cache->tr_config.ssh_config
+ ->client_privkey_path);
+ json_object_array_add(json_servers,
+ json_server);
+ }
#endif
}
}
+ if (json)
+ vty_json(vty, json);
+
return CMD_SUCCESS;
}
-DEFUN (show_rpki_cache_connection,
+DEFPY (show_rpki_cache_connection,
show_rpki_cache_connection_cmd,
- "show rpki cache-connection",
+ "show rpki cache-connection [json$uj]",
SHOW_STR
RPKI_OUTPUT_STRING
- "Show to which RPKI Cache Servers we have a connection\n")
+ "Show to which RPKI Cache Servers we have a connection\n"
+ JSON_STR)
{
+ struct json_object *json = NULL;
+ struct json_object *json_conn = NULL;
+ struct json_object *json_conns = NULL;
+ struct listnode *cache_node;
+ struct cache *cache;
+ struct rtr_mgr_group *group;
+
+ if (uj)
+ json = json_object_new_object();
+
if (!is_synchronized()) {
- vty_out(vty, "No connection to RPKI cache server.\n");
+ if (!json)
+ vty_out(vty, "No connection to RPKI cache server.\n");
+ else
+ vty_json(vty, json);
return CMD_SUCCESS;
}
- struct listnode *cache_node;
- struct cache *cache;
- struct rtr_mgr_group *group = get_connected_group();
-
+ group = get_connected_group();
if (!group) {
- vty_out(vty, "Cannot find a connected group.\n");
+ if (!json)
+ vty_out(vty, "Cannot find a connected group.\n");
+ else
+ vty_json(vty, json);
+
return CMD_SUCCESS;
}
- vty_out(vty, "Connected to group %d\n", group->preference);
+
+ if (!json) {
+ vty_out(vty, "Connected to group %d\n", group->preference);
+ } else {
+ json_conns = json_object_new_array();
+ json_object_int_add(json, "connectedGroup", group->preference);
+ json_object_object_add(json, "connections", json_conns);
+ }
+
for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
- if (cache->preference == group->preference) {
- struct tr_tcp_config *tcp_config;
+ struct tr_tcp_config *tcp_config;
#if defined(FOUND_SSH)
- struct tr_ssh_config *ssh_config;
+ struct tr_ssh_config *ssh_config;
#endif
+ switch (cache->type) {
+ case TCP:
+ tcp_config = cache->tr_config.tcp_config;
- switch (cache->type) {
- case TCP:
- tcp_config = cache->tr_config.tcp_config;
- vty_out(vty, "rpki tcp cache %s %s pref %hhu\n",
+ if (!json) {
+ vty_out(vty,
+ "rpki tcp cache %s %s pref %hhu%s\n",
tcp_config->host, tcp_config->port,
- cache->preference);
- break;
-
+ cache->preference,
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? " (connected)"
+ : "");
+ } else {
+ json_conn = json_object_new_object();
+ json_object_string_add(json_conn, "mode",
+ "tcp");
+ json_object_string_add(json_conn, "host",
+ tcp_config->host);
+ json_object_string_add(json_conn, "port",
+ tcp_config->port);
+ json_object_int_add(json_conn, "preference",
+ cache->preference);
+ json_object_string_add(
+ json_conn, "state",
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? "connected"
+ : "disconnected");
+ json_object_array_add(json_conns, json_conn);
+ }
+ break;
#if defined(FOUND_SSH)
- case SSH:
- ssh_config = cache->tr_config.ssh_config;
- vty_out(vty, "rpki ssh cache %s %u pref %hhu\n",
- ssh_config->host, ssh_config->port,
- cache->preference);
- break;
-#endif
+ case SSH:
+ ssh_config = cache->tr_config.ssh_config;
- default:
- break;
+ if (!json) {
+ vty_out(vty,
+ "rpki ssh cache %s %u pref %hhu%s\n",
+ ssh_config->host, ssh_config->port,
+ cache->preference,
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? " (connected)"
+ : "");
+ } else {
+ json_conn = json_object_new_object();
+ json_object_string_add(json_conn, "mode",
+ "ssh");
+ json_object_string_add(json_conn, "host",
+ ssh_config->host);
+ json_object_int_add(json_conn, "port",
+ ssh_config->port);
+ json_object_int_add(json_conn, "preference",
+ cache->preference);
+ json_object_string_add(
+ json_conn, "state",
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? "connected"
+ : "disconnected");
+ json_object_array_add(json_conns, json_conn);
}
+ break;
+#endif
+ default:
+ break;
}
}
+ if (json)
+ vty_json(vty, json);
+
return CMD_SUCCESS;
}
@@ -1413,7 +1602,7 @@ DEFUN (debug_rpki,
DEBUG_STR
"Enable debugging for rpki\n")
{
- rpki_debug = 1;
+ rpki_debug = true;
return CMD_SUCCESS;
}
@@ -1424,7 +1613,7 @@ DEFUN (no_debug_rpki,
DEBUG_STR
"Disable debugging for rpki\n")
{
- rpki_debug = 0;
+ rpki_debug = false;
return CMD_SUCCESS;
}
@@ -1478,6 +1667,7 @@ static void install_cli_commands(void)
install_element(ENABLE_NODE, &bgp_rpki_stop_cmd);
/* Install rpki reset command */
+ install_element(ENABLE_NODE, &rpki_reset_cmd);
install_element(RPKI_NODE, &rpki_reset_cmd);
/* Install rpki polling period commands */
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index 868801c14e..e25d8d90db 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -461,7 +461,7 @@ static int write_bgpPeerTable(int action, uint8_t *var_val,
intval = *(long *)var_val;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
peer = bgpPeerTable_lookup(NULL, name, &length, &addr, 1);
if (!peer)
@@ -518,7 +518,7 @@ static uint8_t *bgpPeerTable(struct variable *v, oid name[], size_t *length,
if (smux_header_table(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
peer = bgpPeerTable_lookup(v, name, length, &addr, exact);
if (!peer)
@@ -802,7 +802,7 @@ static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[],
if (smux_header_table(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct prefix_ipv4));
+ memset(&addr, 0, sizeof(addr));
path = bgp4PathAttrLookup(v, name, length, bgp, &addr, exact);
if (!path)
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index fb3f9aae43..c5d049f363 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -398,7 +398,6 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word(
(peer->shared_network && peer_afi_active_nego(peer, AFI_IP6)),
key);
-
/*
* There are certain peers that must get their own update-group:
* - lonesoul peers
@@ -413,6 +412,59 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word(jhash(peer->host, strlen(peer->host), SEED2),
key);
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug(
+ "%pBP Update Group Hash: sort: %d UpdGrpFlags: %u UpdGrpAFFlags: %u",
+ peer, peer->sort, peer->flags & PEER_UPDGRP_FLAGS,
+ flags & PEER_UPDGRP_AF_FLAGS);
+ zlog_debug(
+ "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u",
+ peer, (uint32_t)peer->addpath_type[afi][safi],
+ peer->cap & PEER_UPDGRP_CAP_FLAGS,
+ peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS,
+ peer->v_routeadv, peer->change_local_as);
+ zlog_debug(
+ "%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s",
+ peer, peer->max_packet_size, peer->pmax_out[afi][safi],
+ peer->group ? peer->group->name : "(NONE)",
+ ROUTE_MAP_OUT_NAME(filter) ? ROUTE_MAP_OUT_NAME(filter)
+ : "(NONE)");
+ zlog_debug(
+ "%pBP Update Group Hash: dlist out: %s plist out: %s aslist out: %s usmap out: %s advmap: %s",
+ peer,
+ DISTRIBUTE_OUT_NAME(filter)
+ ? DISTRIBUTE_OUT_NAME(filter)
+ : "(NONE)",
+ PREFIX_LIST_OUT_NAME(filter)
+ ? PREFIX_LIST_OUT_NAME(filter)
+ : "(NONE)",
+ FILTER_LIST_OUT_NAME(filter)
+ ? FILTER_LIST_OUT_NAME(filter)
+ : "(NONE)",
+ UNSUPPRESS_MAP_NAME(filter)
+ ? UNSUPPRESS_MAP_NAME(filter)
+ : "(NONE)",
+ ADVERTISE_MAP_NAME(filter) ? ADVERTISE_MAP_NAME(filter)
+ : "(NONE)");
+ zlog_debug(
+ "%pBP Update Group Hash: default rmap: %s shared network and afi active network: %d",
+ peer,
+ peer->default_rmap[afi][safi].name
+ ? peer->default_rmap[afi][safi].name
+ : "(NONE)",
+ peer->shared_network &&
+ peer_afi_active_nego(peer, AFI_IP6));
+ zlog_debug(
+ "%pBP Update Group Hash: Lonesoul: %u ORF prefix: %u ORF old: %u max prefix out: %u",
+ peer, CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL),
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV),
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV),
+ CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT));
+ zlog_debug("%pBP Update Group Hash key: %u", peer, key);
+ }
return key;
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 65a053990e..0a4083e5f6 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -123,6 +123,14 @@ FRR_CFG_DEFAULT_BOOL(BGP_SUPPRESS_DUPLICATES,
{ .val_bool = false, .match_version = "< 7.6", },
{ .val_bool = true },
);
+FRR_CFG_DEFAULT_BOOL(BGP_GRACEFUL_NOTIFICATION,
+ { .val_bool = false, .match_version = "< 8.3", },
+ { .val_bool = true },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_HARD_ADMIN_RESET,
+ { .val_bool = false, .match_version = "< 8.3", },
+ { .val_bool = true },
+);
DEFINE_HOOK(bgp_inst_config_write,
(struct bgp *bgp, struct vty *vty),
@@ -569,6 +577,10 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
SET_FLAG((*bgp)->flags, BGP_FLAG_EBGP_REQUIRES_POLICY);
if (DFLT_BGP_SUPPRESS_DUPLICATES)
SET_FLAG((*bgp)->flags, BGP_FLAG_SUPPRESS_DUPLICATES);
+ if (DFLT_BGP_GRACEFUL_NOTIFICATION)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_GRACEFUL_NOTIFICATION);
+ if (DFLT_BGP_HARD_ADMIN_RESET)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_HARD_ADMIN_RESET);
ret = BGP_SUCCESS;
}
@@ -1542,7 +1554,7 @@ DEFUN (no_router_bgp,
BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) ||
CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) ||
- (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) {
+ (hashcount(tmp_bgp->vnihash))) {
vty_out(vty,
"%% Cannot delete default BGP instance. Dependent VRF instances exist\n");
return CMD_WARNING_CONFIG_FAILED;
@@ -1556,6 +1568,32 @@ DEFUN (no_router_bgp,
return CMD_SUCCESS;
}
+/* bgp session-dscp */
+
+DEFPY (bgp_session_dscp,
+ bgp_session_dscp_cmd,
+ "bgp session-dscp (0-63)$dscp",
+ BGP_STR
+ "Override default (C6) bgp TCP session DSCP value\n"
+ "Manually configured dscp parameter\n")
+{
+ bm->tcp_dscp = dscp << 2;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_session_dscp,
+ no_bgp_session_dscp_cmd,
+ "no bgp session-dscp [(0-63)]",
+ NO_STR
+ BGP_STR
+ "Override default (C6) bgp TCP session DSCP value\n"
+ "Manually configured dscp parameter\n")
+{
+ bm->tcp_dscp = IPTOS_PREC_INTERNETCONTROL;
+
+ return CMD_SUCCESS;
+}
/* BGP router-id. */
@@ -1602,49 +1640,72 @@ DEFPY(bgp_community_alias, bgp_community_alias_cmd,
"Community (AA:BB or AA:BB:CC)\n"
"Alias name\n")
{
- struct community_alias ca1;
- struct community_alias ca2;
+ struct community_alias ca = {};
struct community_alias *lookup_community;
struct community_alias *lookup_alias;
+ struct community *comm;
+ struct lcommunity *lcomm;
+ uint8_t invalid = 0;
+
+ comm = community_str2com(community);
+ if (!comm)
+ invalid++;
+ community_free(&comm);
- if (!community_str2com(community) && !lcommunity_str2com(community)) {
+ lcomm = lcommunity_str2com(community);
+ if (!lcomm)
+ invalid++;
+ lcommunity_free(&lcomm);
+
+ if (invalid > 1) {
vty_out(vty, "Invalid community format\n");
return CMD_WARNING;
}
- memset(&ca1, 0, sizeof(ca1));
- memset(&ca2, 0, sizeof(ca2));
- strlcpy(ca1.community, community, sizeof(ca1.community));
- strlcpy(ca1.alias, alias_name, sizeof(ca1.alias));
+ strlcpy(ca.community, community, sizeof(ca.community));
+ strlcpy(ca.alias, alias_name, sizeof(ca.alias));
- lookup_community = bgp_ca_community_lookup(&ca1);
- lookup_alias = bgp_ca_alias_lookup(&ca1);
+ lookup_community = bgp_ca_community_lookup(&ca);
+ lookup_alias = bgp_ca_alias_lookup(&ca);
if (no) {
- bgp_ca_alias_delete(&ca1);
- bgp_ca_community_delete(&ca1);
+ bgp_ca_alias_delete(&ca);
+ bgp_ca_community_delete(&ca);
} else {
if (lookup_alias) {
/* Lookup if community hash table has an item
* with the same alias name.
*/
- strlcpy(ca2.community, lookup_alias->community,
- sizeof(ca2.community));
- if (bgp_ca_community_lookup(&ca2)) {
+ strlcpy(ca.community, lookup_alias->community,
+ sizeof(ca.community));
+ if (bgp_ca_community_lookup(&ca)) {
vty_out(vty,
"community (%s) already has this alias (%s)\n",
lookup_alias->community,
lookup_alias->alias);
return CMD_WARNING;
}
- bgp_ca_alias_delete(&ca1);
+ bgp_ca_alias_delete(&ca);
}
- if (lookup_community)
- bgp_ca_community_delete(&ca1);
+ if (lookup_community) {
+ /* Lookup if alias hash table has an item
+ * with the same community.
+ */
+ strlcpy(ca.alias, lookup_community->alias,
+ sizeof(ca.alias));
+ if (bgp_ca_alias_lookup(&ca)) {
+ vty_out(vty,
+ "alias (%s) already has this community (%s)\n",
+ lookup_community->alias,
+ lookup_community->community);
+ return CMD_WARNING;
+ }
+ bgp_ca_community_delete(&ca);
+ }
- bgp_ca_alias_insert(&ca1);
- bgp_ca_community_insert(&ca1);
+ bgp_ca_alias_insert(&ca);
+ bgp_ca_community_insert(&ca);
}
return CMD_SUCCESS;
@@ -1962,8 +2023,8 @@ DEFUN (bgp_maxmed_onstartup,
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx = 0;
- argv_find(argv, argc, "(5-86400)", &idx);
- bgp->v_maxmed_onstartup = strtoul(argv[idx]->arg, NULL, 10);
+ if (argv_find(argv, argc, "(5-86400)", &idx))
+ bgp->v_maxmed_onstartup = strtoul(argv[idx]->arg, NULL, 10);
if (argv_find(argv, argc, "(0-4294967295)", &idx))
bgp->maxmed_onstartup_value = strtoul(argv[idx]->arg, NULL, 10);
else
@@ -2263,9 +2324,12 @@ DEFUN (bgp_coalesce_time,
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx = 0;
- argv_find(argv, argc, "(0-4294967295)", &idx);
+
bgp->heuristic_coalesce = false;
- bgp->coalesce_time = strtoul(argv[idx]->arg, NULL, 10);
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ bgp->coalesce_time = strtoul(argv[idx]->arg, NULL, 10);
+
return CMD_SUCCESS;
}
@@ -2327,9 +2391,8 @@ DEFUN (bgp_maxpaths_ibgp_cluster,
"Match the cluster length\n")
{
int idx_number = 2;
- return bgp_maxpaths_config_vty(
- vty, BGP_PEER_IBGP, argv[idx_number]->arg,
- BGP_FLAG_IBGP_MULTIPATH_SAME_CLUSTERLEN, 1);
+ return bgp_maxpaths_config_vty(vty, BGP_PEER_IBGP,
+ argv[idx_number]->arg, true, 1);
}
ALIAS_HIDDEN(bgp_maxpaths_ibgp_cluster, bgp_maxpaths_ibgp_cluster_hidden_cmd,
@@ -2387,8 +2450,7 @@ static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp,
if (bgp->maxpaths[afi][safi].maxpaths_ibgp != multipath_num) {
vty_out(vty, " maximum-paths ibgp %d",
bgp->maxpaths[afi][safi].maxpaths_ibgp);
- if (CHECK_FLAG(bgp->maxpaths[afi][safi].ibgp_flags,
- BGP_FLAG_IBGP_MULTIPATH_SAME_CLUSTERLEN))
+ if (bgp->maxpaths[afi][safi].same_clusterlen)
vty_out(vty, " equal-cluster-length");
vty_out(vty, "\n");
}
@@ -2868,6 +2930,41 @@ DEFUN (no_bgp_graceful_restart_preserve_fw,
return CMD_SUCCESS;
}
+DEFPY (bgp_graceful_restart_notification,
+ bgp_graceful_restart_notification_cmd,
+ "[no$no] bgp graceful-restart notification",
+ NO_STR
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Indicate Graceful Restart support for BGP NOTIFICATION messages\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ UNSET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION);
+ else
+ SET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_administrative_reset,
+ bgp_administrative_reset_cmd,
+ "[no$no] bgp hard-administrative-reset",
+ NO_STR
+ BGP_STR
+ "Send Hard Reset CEASE Notification for 'Administrative Reset'\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ UNSET_FLAG(bgp->flags, BGP_FLAG_HARD_ADMIN_RESET);
+ else
+ SET_FLAG(bgp->flags, BGP_FLAG_HARD_ADMIN_RESET);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (bgp_graceful_restart_disable,
bgp_graceful_restart_disable_cmd,
"bgp graceful-restart-disable",
@@ -17078,6 +17175,10 @@ int bgp_config_write(struct vty *vty)
if (CHECK_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA))
vty_out(vty, "bgp send-extra-data zebra\n");
+ /* BGP session DSCP value */
+ if (bm->tcp_dscp != IPTOS_PREC_INTERNETCONTROL)
+ vty_out(vty, "bgp session-dscp %u\n", bm->tcp_dscp >> 2);
+
/* BGP configuration. */
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
@@ -17143,6 +17244,16 @@ int bgp_config_write(struct vty *vty)
? ""
: "no ");
+ /* Send Hard Reset CEASE Notification for 'Administrative Reset'
+ */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_HARD_ADMIN_RESET) !=
+ SAVE_BGP_HARD_ADMIN_RESET)
+ vty_out(vty, " %sbgp hard-administrative-reset\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_HARD_ADMIN_RESET)
+ ? ""
+ : "no ");
+
/* BGP default <afi>-<safi> */
FOREACH_AFI_SAFI (afi, safi) {
if (afi == AFI_IP && safi == SAFI_UNICAST) {
@@ -17275,6 +17386,14 @@ int bgp_config_write(struct vty *vty)
vty_out(vty, " bgp graceful-restart restart-time %u\n",
bgp->restart_time);
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION) !=
+ SAVE_BGP_GRACEFUL_NOTIFICATION)
+ vty_out(vty, " %sbgp graceful-restart notification\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_GRACEFUL_NOTIFICATION)
+ ? ""
+ : "no ");
+
if (bgp->select_defer_time != BGP_DEFAULT_SELECT_DEFERRAL_TIME)
vty_out(vty,
" bgp graceful-restart select-defer-time %u\n",
@@ -17763,6 +17882,10 @@ void bgp_vty_init(void)
/* "no router bgp" commands. */
install_element(CONFIG_NODE, &no_router_bgp_cmd);
+ /* "bgp session-dscp command */
+ install_element(CONFIG_NODE, &bgp_session_dscp_cmd);
+ install_element(CONFIG_NODE, &no_bgp_session_dscp_cmd);
+
/* "bgp router-id" commands. */
install_element(BGP_NODE, &bgp_router_id_cmd);
install_element(BGP_NODE, &no_bgp_router_id_cmd);
@@ -17907,6 +18030,7 @@ void bgp_vty_init(void)
&no_bgp_graceful_restart_select_defer_time_cmd);
install_element(BGP_NODE, &bgp_graceful_restart_preserve_fw_cmd);
install_element(BGP_NODE, &no_bgp_graceful_restart_preserve_fw_cmd);
+ install_element(BGP_NODE, &bgp_graceful_restart_notification_cmd);
install_element(BGP_NODE, &bgp_graceful_restart_disable_eor_cmd);
install_element(BGP_NODE, &no_bgp_graceful_restart_disable_eor_cmd);
@@ -17917,6 +18041,9 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &bgp_graceful_shutdown_cmd);
install_element(BGP_NODE, &no_bgp_graceful_shutdown_cmd);
+ /* "bgp hard-administrative-reset" commands */
+ install_element(BGP_NODE, &bgp_administrative_reset_cmd);
+
/* "bgp long-lived-graceful-restart" commands */
install_element(BGP_NODE, &bgp_llgr_stalepath_time_cmd);
install_element(BGP_NODE, &no_bgp_llgr_stalepath_time_cmd);
@@ -19176,8 +19303,7 @@ DEFUN (community_list_standard,
int style = COMMUNITY_LIST_STANDARD;
int idx = 0;
- argv_find(argv, argc, "(0-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
seq = argv[idx]->arg;
idx = 0;
@@ -19225,8 +19351,7 @@ DEFUN (no_community_list_standard_all,
char *seq = NULL;
int idx = 0;
- argv_find(argv, argc, "(0-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
seq = argv[idx]->arg;
idx = 0;
@@ -19289,8 +19414,7 @@ DEFUN (community_list_expanded_all,
int style = COMMUNITY_LIST_EXPANDED;
int idx = 0;
- argv_find(argv, argc, "(0-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
seq = argv[idx]->arg;
idx = 0;
@@ -19339,8 +19463,7 @@ DEFUN (no_community_list_expanded_all,
int style = COMMUNITY_LIST_EXPANDED;
int idx = 0;
- argv_find(argv, argc, "(0-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
seq = argv[idx]->arg;
idx = 0;
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 77b8a8ab96..2c1d561721 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -2890,8 +2890,8 @@ static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS)
ifindex_t svi_ifindex;
bool is_anycast_mac = false;
- memset(&svi_rmac, 0, sizeof(struct ethaddr));
- memset(&originator_ip, 0, sizeof(struct in_addr));
+ memset(&svi_rmac, 0, sizeof(svi_rmac));
+ memset(&originator_ip, 0, sizeof(originator_ip));
s = zclient->ibuf;
l3vni = stream_getl(s);
if (cmd == ZEBRA_L3VNI_ADD) {
@@ -3048,7 +3048,7 @@ static int bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS)
struct bgp *bgp_vrf = NULL;
struct prefix p;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
s = zclient->ibuf;
stream_get(&p, s, sizeof(struct prefix));
@@ -3519,7 +3519,7 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
if (!vrf_is_backend_netns() && bgp->vrf_id != nh->vrf_id)
return;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
if (afi != AFI_IP && afi != AFI_IP6)
return;
p.family = afi2family(afi);
@@ -3620,7 +3620,7 @@ int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable)
/* Check if capability is already sent. If the flag force is set
* send the capability since this can be initial bgp configuration
*/
- memset(&api, 0, sizeof(struct zapi_cap));
+ memset(&api, 0, sizeof(api));
if (disable) {
api.cap = ZEBRA_CLIENT_GR_DISABLE;
api.vrf_id = bgp->vrf_id;
@@ -3700,7 +3700,7 @@ int bgp_zebra_stale_timer_update(struct bgp *bgp)
return BGP_GR_FAILURE;
}
- memset(&api, 0, sizeof(struct zapi_cap));
+ memset(&api, 0, sizeof(api));
api.cap = ZEBRA_CLIENT_RIB_STALE_TIME;
api.stale_removal_time = bgp->rib_stale_time;
api.vrf_id = bgp->vrf_id;
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
index eee3d36931..17f46e49cc 100644
--- a/bgpd/bgp_zebra.h
+++ b/bgpd/bgp_zebra.h
@@ -45,7 +45,7 @@ extern int bgp_if_update_all(void);
extern void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
struct bgp_path_info *path, struct bgp *bgp,
afi_t afi, safi_t safi);
-extern void bgp_zebra_announce_table(struct bgp *, afi_t, safi_t);
+extern void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi);
extern void bgp_zebra_withdraw(const struct prefix *p,
struct bgp_path_info *path, struct bgp *bgp,
safi_t safi);
@@ -61,44 +61,54 @@ extern void bgp_zebra_withdraw_table_all_subtypes(struct bgp *bgp, afi_t afi,
extern void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer);
extern void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer);
-extern void bgp_zebra_instance_register(struct bgp *);
-extern void bgp_zebra_instance_deregister(struct bgp *);
+extern void bgp_zebra_instance_register(struct bgp *bgp);
+extern void bgp_zebra_instance_deregister(struct bgp *bgp);
extern void bgp_redistribute_redo(struct bgp *bgp);
-extern struct bgp_redist *bgp_redist_lookup(struct bgp *, afi_t, uint8_t,
- unsigned short);
-extern struct bgp_redist *bgp_redist_add(struct bgp *, afi_t, uint8_t,
- unsigned short);
-extern int bgp_redistribute_set(struct bgp *, afi_t, int, unsigned short,
- bool changed);
-extern int bgp_redistribute_resend(struct bgp *, afi_t, int, unsigned short);
+extern struct bgp_redist *bgp_redist_lookup(struct bgp *bgp, afi_t afi,
+ uint8_t type,
+ unsigned short instance);
+extern struct bgp_redist *bgp_redist_add(struct bgp *bgp, afi_t afi,
+ uint8_t type, unsigned short instance);
+extern int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance, bool changed);
+extern int bgp_redistribute_resend(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance);
extern bool bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name,
struct route_map *route_map);
-extern bool bgp_redistribute_metric_set(struct bgp *, struct bgp_redist *,
- afi_t, int, uint32_t);
-extern int bgp_redistribute_unset(struct bgp *, afi_t, int, unsigned short);
-extern int bgp_redistribute_unreg(struct bgp *, afi_t, int, unsigned short);
+extern bool bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red,
+ afi_t afi, int type, uint32_t metric);
+extern int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance);
+extern int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance);
-extern struct interface *if_lookup_by_ipv4(struct in_addr *, vrf_id_t);
-extern struct interface *if_lookup_by_ipv4_exact(struct in_addr *, vrf_id_t);
-extern struct interface *if_lookup_by_ipv6(struct in6_addr *, ifindex_t,
- vrf_id_t);
-extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *, ifindex_t,
- vrf_id_t);
+extern struct interface *if_lookup_by_ipv4(struct in_addr *addr,
+ vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_ipv4_exact(struct in_addr *addr,
+ vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_ipv6(struct in6_addr *addr,
+ ifindex_t ifindex, vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *addr,
+ ifindex_t ifindex,
+ vrf_id_t vrf_id);
extern int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise,
vni_t vni);
-extern int bgp_zebra_advertise_gw_macip(struct bgp *, int, vni_t);
+extern int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise,
+ vni_t vni);
extern int bgp_zebra_advertise_svi_macip(struct bgp *bgp, int advertise,
vni_t vni);
-extern int bgp_zebra_advertise_all_vni(struct bgp *, int);
+extern int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise);
extern int bgp_zebra_dup_addr_detection(struct bgp *bgp);
extern int bgp_zebra_vxlan_flood_control(struct bgp *bgp,
enum vxlan_flood_control flood_ctrl);
extern int bgp_zebra_num_connects(void);
-extern bool bgp_zebra_nexthop_set(union sockunion *, union sockunion *,
- struct bgp_nexthop *, struct peer *);
+extern bool bgp_zebra_nexthop_set(union sockunion *local,
+ union sockunion *remote,
+ struct bgp_nexthop *nexthop,
+ struct peer *peer);
struct bgp_pbr_action;
struct bgp_pbr_match;
struct bgp_pbr_rule;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 33ed7d1d94..7d284f28b3 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -232,8 +232,17 @@ void bgp_option_norib_set_runtime(void)
zlog_info("Disabled BGP route installation to RIB (Zebra)");
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
- FOREACH_AFI_SAFI(afi, safi)
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * Stop a crash, more work is needed
+ * here to properly add/remove these types of
+ * routes from zebra.
+ */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
bgp_zebra_withdraw_table_all_subtypes(bgp, afi, safi);
+ }
}
zlog_info("All routes have been withdrawn from RIB (Zebra)");
@@ -255,8 +264,17 @@ void bgp_option_norib_unset_runtime(void)
zlog_info("Enabled BGP route installation to RIB (Zebra)");
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
- FOREACH_AFI_SAFI(afi, safi)
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * Stop a crash, more work is needed
+ * here to properly add/remove these types
+ * of routes from zebra
+ */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
bgp_zebra_announce_table_all_subtypes(bgp, afi, safi);
+ }
}
zlog_info("All routes have been installed in RIB (Zebra)");
@@ -3241,7 +3259,6 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->evpn_info = XCALLOC(MTYPE_BGP_EVPN_INFO,
sizeof(struct bgp_evpn_info));
-
bgp_evpn_init(bgp);
bgp_evpn_vrf_es_init(bgp);
bgp_pbr_init(bgp);
@@ -7820,7 +7837,7 @@ void bgp_master_init(struct thread_master *master, const int buffer_size,
{
qobj_init();
- memset(&bgp_master, 0, sizeof(struct bgp_master));
+ memset(&bgp_master, 0, sizeof(bgp_master));
bm = &bgp_master;
bm->bgp = list_new();
@@ -7836,6 +7853,7 @@ void bgp_master_init(struct thread_master *master, const int buffer_size,
bm->terminating = false;
bm->socket_buffer = buffer_size;
bm->wait_for_fib = false;
+ bm->tcp_dscp = IPTOS_PREC_INTERNETCONTROL;
bgp_mac_init();
/* init the rd id space.
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 59f05daf18..d380d9d678 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -171,6 +171,10 @@ struct bgp_master {
#define BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA (1 << 1)
bool terminating; /* global flag that sigint terminate seen */
+
+ /* DSCP value for TCP sessions */
+ uint8_t tcp_dscp;
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(bgp_master);
@@ -453,7 +457,7 @@ struct bgp {
#define BGP_LINK_BW_REF_BW 1
/* BGP flags. */
- uint32_t flags;
+ uint64_t flags;
#define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0)
#define BGP_FLAG_DETERMINISTIC_MED (1 << 1)
#define BGP_FLAG_MED_MISSING_AS_WORST (1 << 2)
@@ -488,6 +492,10 @@ struct bgp {
#define BGP_FLAG_SUPPRESS_FIB_PENDING (1 << 26)
#define BGP_FLAG_SUPPRESS_DUPLICATES (1 << 27)
#define BGP_FLAG_PEERTYPE_MULTIPATH_RELAX (1 << 29)
+/* Indicate Graceful Restart support for BGP NOTIFICATION messages */
+#define BGP_FLAG_GRACEFUL_NOTIFICATION (1 << 30)
+/* Send Hard Reset CEASE Notification for 'Administrative Reset' */
+#define BGP_FLAG_HARD_ADMIN_RESET (1 << 31)
/* BGP default address-families.
* New peers inherit enabled afi/safis from bgp instance.
@@ -616,8 +624,7 @@ struct bgp {
struct bgp_maxpaths_cfg {
uint16_t maxpaths_ebgp;
uint16_t maxpaths_ibgp;
- uint16_t ibgp_flags;
-#define BGP_FLAG_IBGP_MULTIPATH_SAME_CLUSTERLEN (1 << 0)
+ bool same_clusterlen;
} maxpaths[AFI_MAX][SAFI_MAX];
_Atomic uint32_t wpkt_quanta; // max # packets to write per i/o cycle
@@ -1526,6 +1533,11 @@ struct peer {
/* timestamp when the last msg was written */
_Atomic time_t last_update;
+ /* only updated under io_mtx.
+ * last_sendq_warn is only for ratelimiting log warning messages.
+ */
+ time_t last_sendq_ok, last_sendq_warn;
+
/* Notify data. */
struct bgp_notify notify;
diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c
index ba0c576f1e..44eebe961c 100644
--- a/bgpd/rfapi/rfapi_rib.c
+++ b/bgpd/rfapi/rfapi_rib.c
@@ -2108,10 +2108,9 @@ void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it,
sl);
for (cursor = NULL,
- rc = skiplist_next(sl, NULL, (void **)&m,
- (void **)&cursor);
+ rc = skiplist_next(sl, NULL, (void **)&m, &cursor);
!rc; rc = skiplist_next(sl, NULL, (void **)&m,
- (void **)&cursor)) {
+ &cursor)) {
#if DEBUG_PENDING_DELETE_ROUTE
vnc_zlog_debug_verbose("%s: eth monitor rfd=%p",
diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c
index 994de7d729..acbc413917 100644
--- a/bgpd/rfapi/vnc_import_bgp.c
+++ b/bgpd/rfapi/vnc_import_bgp.c
@@ -238,7 +238,7 @@ static void vnc_rhnck(char *tag)
struct prefix pfx_orig_nexthop;
memset(&pfx_orig_nexthop, 0,
- sizeof(struct prefix)); /* keep valgrind happy */
+ sizeof(pfx_orig_nexthop)); /* keep valgrind happy */
pkey = p->key;
pb = p->value;
@@ -303,7 +303,7 @@ static int process_unicast_route(struct bgp *bgp, /* in */
struct prefix pfx_orig_nexthop;
memset(&pfx_orig_nexthop, 0,
- sizeof(struct prefix)); /* keep valgrind happy */
+ sizeof(pfx_orig_nexthop)); /* keep valgrind happy */
/*
* prefix list check
@@ -346,7 +346,7 @@ static int process_unicast_route(struct bgp *bgp, /* in */
* must be freed before we return. It's easier to put it after
* all of the possible returns above.
*/
- memset(&hattr, 0, sizeof(struct attr));
+ memset(&hattr, 0, sizeof(hattr));
/* hattr becomes a ghost attr */
hattr = *attr;
@@ -773,7 +773,7 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp,
* must be freed before we return. It's easier to put it after
* all of the possible returns above.
*/
- memset(&hattr, 0, sizeof(struct attr));
+ memset(&hattr, 0, sizeof(hattr));
/* hattr becomes a ghost attr */
hattr = *attr;
@@ -966,7 +966,7 @@ static void vnc_import_bgp_add_route_mode_nvegroup(
* must be freed before we return. It's easier to put it after
* all of the possible returns above.
*/
- memset(&hattr, 0, sizeof(struct attr));
+ memset(&hattr, 0, sizeof(hattr));
/* hattr becomes a ghost attr */
hattr = *attr;
@@ -1420,7 +1420,7 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
uint32_t local_pref;
memset(&pfx_unicast_nexthop, 0,
- sizeof(struct prefix)); /* keep valgrind happy */
+ sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */
if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
vnc_zlog_debug_any(
@@ -1538,7 +1538,7 @@ void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
struct prefix pfx_unicast_nexthop;
memset(&pfx_unicast_nexthop, 0,
- sizeof(struct prefix)); /* keep valgrind happy */
+ sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */
if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom,
&pfx_unicast_nexthop)) {
@@ -1713,7 +1713,7 @@ static void vnc_import_bgp_exterior_add_route_it(
prd = NULL;
/* use local_pref from unicast route */
- memset(&new_attr, 0, sizeof(struct attr));
+ memset(&new_attr, 0, sizeof(new_attr));
new_attr = *bpi_interior->attr;
if (info->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
@@ -1807,7 +1807,7 @@ void vnc_import_bgp_exterior_del_route(
return;
memset(&pfx_orig_nexthop, 0,
- sizeof(struct prefix)); /* keep valgrind happy */
+ sizeof(pfx_orig_nexthop)); /* keep valgrind happy */
h = bgp_default->rfapi;
hc = bgp_default->rfapi_cfg;
@@ -2473,7 +2473,7 @@ void vnc_import_bgp_exterior_del_route_interior(
prd = NULL;
/* use local_pref from unicast route */
- memset(&new_attr, 0, sizeof(struct attr));
+ memset(&new_attr, 0, sizeof(new_attr));
new_attr = *bpi->attr;
if (bpi_exterior
&& (bpi_exterior->attr->flag
diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c
index 060fc76550..9a4ec7dbf3 100644
--- a/bgpd/rfp-example/librfp/rfp_example.c
+++ b/bgpd/rfp-example/librfp/rfp_example.c
@@ -287,7 +287,7 @@ static int rfp_cfg_write_cb(struct vty *vty, void *rfp_start_val)
void *rfp_start(struct thread_master *master, struct rfapi_rfp_cfg **cfgp,
struct rfapi_rfp_cb_methods **cbmp)
{
- memset(&global_rfi, 0, sizeof(struct rfp_instance_t));
+ memset(&global_rfi, 0, sizeof(global_rfi));
global_rfi.master = master; /* for BGPD threads */
/* initilize struct rfapi_rfp_cfg, see rfapi.h */
diff --git a/configure.ac b/configure.ac
index 463c69c043..29d13b16cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1601,7 +1601,7 @@ AC_CHECK_HEADER([net/if_media.h],
])
AC_CHECK_MEMBERS([struct ifmediareq.ifm_status],
AC_DEFINE([HAVE_BSD_LINK_DETECT], [1], [BSD link-detect]),
- [], LINK_DETECT_INCLUDES)],
+ [], LINK_DETECT_INCLUDES)],
[],
FRR_INCLUDES)
@@ -1617,7 +1617,7 @@ dnl ------------------------
dnl TCP_MD5SIG socket option
dnl ------------------------
-AC_CHECK_HEADER([netinet/tcp.h],
+AC_CHECK_HEADER([netinet/tcp.h],
[m4_define([MD5_INCLUDES],
FRR_INCLUDES
[#include <netinet/tcp.h>
@@ -2041,7 +2041,7 @@ dnl ------------------------------------
dnl Enable RPKI and add librtr to libs
dnl ------------------------------------
if test "$enable_rpki" = "yes"; then
- PKG_CHECK_MODULES([RTRLIB], [rtrlib >= 0.5.0],
+ PKG_CHECK_MODULES([RTRLIB], [rtrlib >= 0.8.0],
[RPKI=true],
[RPKI=false
AC_MSG_ERROR([rtrlib was not found on your system or is too old.])]
diff --git a/debian/changelog b/debian/changelog
index f3e42199de..0661ffbb97 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,12 @@ frr (8.3~dev-1) UNRELEASED; urgency=medium
-- Donatas Abraitis <donatas.abraitis@gmail.com> Tue, 01 Feb 2022 11:00:00 +0200
+frr (8.2-0) UNRELEASED; urgency=medium
+
+ * New upstream release FRR 8.2
+
+ -- Jafar Al-Gharaibeh <jafar@atcorp.com> Tue, 01 Mar 2022 10:00:00 +0500
+
frr (8.1-0) unstable; urgency=medium
* New upstream release FRR 8.1
diff --git a/debian/control b/debian/control
index 2bc144e798..7c90979a22 100644
--- a/debian/control
+++ b/debian/control
@@ -19,7 +19,7 @@ Build-Depends: bison,
libpcre2-dev,
libpython3-dev,
libreadline-dev,
- librtr-dev <!pkg.frr.nortrlib>,
+ librtr-dev (>= 0.8.0) <!pkg.frr.nortrlib>,
libsnmp-dev,
libssh-dev <!pkg.frr.nortrlib>,
libyang2-dev,
diff --git a/debian/frr-pythontools.install b/debian/frr-pythontools.install
index 820895ce68..662fbe0f51 100644
--- a/debian/frr-pythontools.install
+++ b/debian/frr-pythontools.install
@@ -1,3 +1,4 @@
usr/lib/frr/frr-reload.py
usr/lib/frr/generate_support_bundle.py
usr/lib/frr/frr_babeltrace.py
+usr/lib/frr/ospfclient.py
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index adab9725d9..688ce545fb 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -163,13 +163,13 @@ as early as possible, i.e. the first 2-week window.
For reference, the expected release schedule according to the above is:
+---------+------------+------------+------------+------------+------------+
-| Release | 2021-11-02 | 2022-03-01 | 2022-07-05 | 2022-11-01 | 2023-03-07 |
+| Release | 2022-07-05 | 2022-11-01 | 2023-03-07 | 2023-07-04 | 2023-10-31 |
+---------+------------+------------+------------+------------+------------+
-| RC | 2021-10-19 | 2022-02-15 | 2022-06-21 | 2022-10-18 | 2023-02-21 |
+| RC | 2022-06-21 | 2022-10-18 | 2023-02-21 | 2023-06-20 | 2023-10-17 |
+---------+------------+------------+------------+------------+------------+
-| dev/X.Y | 2021-10-05 | 2022-02-01 | 2022-06-07 | 2022-10-04 | 2023-02-07 |
+| dev/X.Y | 2022-06-07 | 2022-10-04 | 2023-02-07 | 2023-06-06 | 2023-10-03 |
+---------+------------+------------+------------+------------+------------+
-| freeze | 2021-09-21 | 2022-01-18 | 2022-05-24 | 2022-09-20 | 2023-01-24 |
+| freeze | 2022-05-24 | 2022-09-20 | 2023-01-24 | 2023-05-23 | 2023-09-19 |
+---------+------------+------------+------------+------------+------------+
Each release is managed by one or more volunteer release managers from the FRR
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 6f99b41140..d4abf2c34d 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -501,6 +501,19 @@ Suppress duplicate updates
Suppress duplicate updates if the route actually not changed.
Default: enabled.
+Send Hard Reset CEASE Notification for Administrative Reset
+-----------------------------------------------------------
+
+.. clicmd:: bgp hard-administrative-reset
+
+ Send Hard Reset CEASE Notification for 'Administrative Reset' events.
+
+ When disabled, and Graceful Restart Notification capability is exchanged
+ between the peers, Graceful Restart procedures apply, and routes will be
+ retained.
+
+ Enabled by default.
+
Disable checking if nexthop is connected on EBGP sessions
---------------------------------------------------------
@@ -947,6 +960,22 @@ However, it MUST defer route selection for an address family until it either.
expires. The stale path timer is started when the router receives a Route-Refresh
BoRR message.
+.. clicmd:: bgp graceful-restart notification
+
+ Indicate Graceful Restart support for BGP NOTIFICATION messages.
+
+ After changing this parameter, you have to reset the peers in order to advertise
+ N-bit in Graceful Restart capability.
+
+ Without Graceful-Restart Notification capability (N-bit not set), GR is not
+ activated when receiving CEASE/HOLDTIME expire notifications.
+
+ When sending ``CEASE/Administrative Reset`` (``clear bgp``), the session is closed
+ and routes are not retained. When N-bit is set and ``bgp hard-administrative-reset``
+ is turned off Graceful-Restart is activated and routes are retained.
+
+ Enabled by default.
+
.. _bgp-per-peer-graceful-restart:
BGP Per Peer Graceful Restart
@@ -3974,7 +4003,8 @@ the daemons RIB from Zebra and unsetting it will announce all routes in the
daemons RIB to Zebra. If the option is passed as a command line argument when
starting the daemon and the configuration gets saved, the option will persist
unless removed from the configuration with the negating command prior to the
-configuration write operation.
+configuration write operation. At this point in time non SAFI_UNICAST BGP
+data is not properly withdrawn from zebra when this command is issued.
.. clicmd:: bgp send-extra-data zebra
@@ -3984,6 +4014,11 @@ behavior in BGP is not to send this data. If the routes were sent to zebra and
the option is changed, bgpd doesn't reinstall the routes to comply with the new
setting.
+.. clicmd:: bgp session-dscp (0-63)
+
+This command allows bgp to control, at a global level, the TCP dscp values
+in the TCP header.
+
.. _bgp-suppress-fib:
Suppressing routes not installed in FIB
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index b24a9fb471..401a1f2721 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -212,7 +212,8 @@ options from the list below.
.. option:: --disable-ospfclient
- Disable building of the example OSPF-API client.
+ Disable installation of the python ospfclient and building of the example
+ OSPF-API client.
.. option:: --disable-isisd
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index d2859670dd..9ccb5ba4b5 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -411,7 +411,7 @@ Known limitations:
clear the Node flag that is set by default for Prefix-SIDs associated to
loopback addresses. This option is necessary to configure Anycast-SIDs.
-.. clicmd:: show isis segment-routing nodes
+.. clicmd:: show isis segment-routing node
Show detailed information about all learned Segment Routing Nodes.
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index d1a0bb6f7b..068bb8ba31 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -867,6 +867,12 @@ Opaque LSA
Show Opaque LSA from the database.
+.. clicmd:: show ip ospf (1-65535) reachable-routers
+
+.. clicmd:: show ip ospf [vrf <NAME|all>] reachable-routers
+
+ Show routing table of reachable routers.
+
.. _ospf-traffic-engineering:
Traffic Engineering
@@ -1064,6 +1070,10 @@ Debugging OSPF
library messages and OSPF BFD integration messages that are mostly state
transitions and validation problems.
+.. clicmd:: debug ospf client-api
+
+ Show debug information for the OSPF opaque data client API.
+
.. clicmd:: debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index a24e3eb7f2..9bcd2af346 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -375,6 +375,8 @@ BGP
:t:`Default External BGP (EBGP) Route Propagation Behavior without Policies. J. Mauch, J. Snijders, G. Hankins. July 2017`
- :rfc:`8277`
:t:`Using BGP to Bind MPLS Labels to Address Prefixes. E. Rosen. October 2017`
+- :rfc:`8538`
+ :t:`Notification Message Support for BGP Graceful Restart. K. Patel, R. Fernando, J. Scudder, J. Haas. March 2019`
- :rfc:`8654`
:t:`Extended Message Support for BGP. R. Bush, K. Patel, D. Ward. October 2019`
- :rfc:`9003`
@@ -517,7 +519,7 @@ Bug Reports
For information on reporting bugs, please see :ref:`bug-reports`.
-.. _frr: |package-url|
+.. _frr: https://frrouting.org
.. _github: https://github.com/frrouting/frr/
.. _github issues: https://github.com/frrouting/frr/issues
-.. _slack: https://frrouting.org/#participate
+.. _slack: https://frrouting.org/community
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index dcea709503..ae39f4220d 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -234,6 +234,10 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
and would like pim to use a specific source address associated with
that interface.
+.. clicmd:: ip pim passive
+
+ Disable sending and receiving pim control packets on the interface.
+
.. clicmd:: ip igmp
Tell pim to receive IGMP reports and Query on this interface. The default
diff --git a/doc/user/pimv6.rst b/doc/user/pimv6.rst
index 0456e77b21..c932c17a3d 100644
--- a/doc/user/pimv6.rst
+++ b/doc/user/pimv6.rst
@@ -158,6 +158,10 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
and would like pim to use a specific source address associated with
that interface.
+.. clicmd:: ipv6 pim passive
+
+ Disable sending and receiving pim control packets on the interface.
+
.. clicmd:: ipv6 mld
Tell pim to receive MLD reports and Query on this interface. The default
@@ -214,6 +218,9 @@ vrf is specified then the default vrf is assumed. Finally the special keyword
'all' allows you to look at all vrfs for the command. Naming a vrf 'all' will
cause great confusion.
+PIM protocol state
+------------------
+
.. clicmd:: show ipv6 pim [vrf NAME] group-type [json]
Display SSM group ranges.
@@ -285,6 +292,39 @@ cause great confusion.
Display upstream information for S,G's and the RPF data associated with them.
+
+MLD state
+---------
+
+.. clicmd:: show ipv6 mld [vrf NAME] interface [IFNAME] [detail|json]
+
+ Display per-interface MLD state, elected querier and related timers. Use
+ the ``detail`` or ``json`` options for further information (the JSON output
+ always contains all details.)
+
+.. clicmd:: show ipv6 mld [vrf NAME] statistics [interface IFNAME] [json]
+
+ Display packet and error counters for MLD interfaces. All counters are
+ packet counters (not bytes) and wrap at 64 bit. In some rare cases,
+ malformed received MLD reports may be partially processed and counted on
+ multiple counters.
+
+.. clicmd:: show ipv6 mld [vrf NAME] joins [{interface IFNAME|groups X:X::X:X/M|sources X:X::X:X/M|detail}] [json]
+
+ Display joined groups tracked by MLD. ``interface``, ``groups`` and
+ ``sources`` options may be used to limit output to a subset (note ``sources``
+ refers to the multicast traffic sender, not the host that joined to receive
+ the traffic.)
+
+ The ``detail`` option also reports which hosts have joined (subscribed) to
+ particular ``S,G``. This information is only available for MLDv2 hosts with
+ a MLDv2 querier. MLDv1 joins are recorded as "untracked" and shown in the
+ ``NonTrkSeen`` output column.
+
+
+General multicast routing state
+-------------------------------
+
.. clicmd:: show ipv6 multicast
Display various information about the interfaces used in this pim instance.
@@ -326,6 +366,25 @@ cause great confusion.
Display total number of S,G mroutes and number of S,G mroutes
installed into the kernel for all vrfs.
+PIMv6 Clear Commands
+====================
+
+Clear commands reset various variables.
+
+.. clicmd:: clear ipv6 mroute
+
+ Reset multicast routes.
+
+.. clicmd:: clear ipv6 mroute [vrf NAME] count
+
+ When this command is issued, reset the counts of data shown for
+ packet count, byte count and wrong interface to 0 and start count
+ up from this spot.
+
+.. clicmd:: clear ipv6 pim oil
+
+ Rescan PIMv6 OIL (output interface list).
+
PIMv6 Debug Commands
====================
@@ -335,3 +394,35 @@ configure CLI mode. If you specify debug commands in the configuration cli
mode, the debug commands can be persistent across restarts of the FRR pim6d if
the config was written out.
+.. clicmd:: debug pimv6 events
+
+ This turns on debugging for PIMv6 system events. Especially timers.
+
+.. clicmd:: debug pimv6 nht
+
+ This turns on debugging for PIMv6 nexthop tracking. It will display
+ information about RPF lookups and information about when a nexthop changes.
+
+.. clicmd:: debug pimv6 nht detail
+
+ This turns on debugging for PIMv6 nexthop in detail. This is not enabled
+ by default.
+
+.. clicmd:: debug pimv6 packet-dump
+
+ This turns on an extraordinary amount of data. Each pim packet sent and
+ received is dumped for debugging purposes. This should be considered a
+ developer only command.
+
+.. clicmd:: debug pimv6 packets
+
+ This turns on information about packet generation for sending and about
+ packet handling from a received packet.
+
+.. clicmd:: debug pimv6 trace
+
+ This traces pim code and how it is running.
+
+.. clicmd:: debug pimv6 zebra
+
+ This gathers data about events from zebra that come up through the ZAPI.
diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst
index ef7aef9c5e..754b709173 100644
--- a/doc/user/routemap.rst
+++ b/doc/user/routemap.rst
@@ -205,6 +205,11 @@ Route Map Match Command
interface name specified if the neighbor was specified
in this manner.
+.. clicmd:: match peer PEER_GROUP_NAME
+
+ This is a BGP specific match command. Matches the peer
+ group name specified for the peer in question.
+
.. clicmd:: match source-protocol PROTOCOL_NAME
This is a ZEBRA specific match command. Matches the
diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst
index c1308913a4..e5bd59d9cb 100644
--- a/doc/user/rpki.rst
+++ b/doc/user/rpki.rst
@@ -102,13 +102,23 @@ The following commands are independent of a specific cache server.
.. clicmd:: rpki polling_period (1-3600)
-
Set the number of seconds the router waits until the router asks the cache
again for updated data.
The default value is 300 seconds.
- The following commands configure one or multiple cache servers.
+.. clicmd:: rpki expire_interval (600-172800)
+
+ Set the number of seconds the router waits until the router expires the cache.
+
+ The default value is 7200 seconds.
+
+.. clicmd:: rpki retry_interval (1-7200)
+
+ Set the number of seconds the router waits until retrying to connect to the
+ cache server.
+
+ The default value is 600 seconds.
.. clicmd:: rpki cache (A.B.C.D|WORD) PORT [SSH_USERNAME] [SSH_PRIVKEY_PATH] [SSH_PUBKEY_PATH] [KNOWN_HOSTS_PATH] [source A.B.C.D] PREFERENCE
@@ -190,26 +200,30 @@ Debugging
Displaying RPKI
---------------
-.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)]
+.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)] [json]
Display validated prefixes received from the cache servers filtered
by the specified prefix.
-.. clicmd:: show rpki as-number ASN
+.. clicmd:: show rpki as-number ASN [json]
Display validated prefixes received from the cache servers filtered
by ASN.
-.. clicmd:: show rpki prefix-table
+.. clicmd:: show rpki prefix-table [json]
Display all validated prefix to origin AS mappings/records which have been
received from the cache servers and stored in the router. Based on this data,
the router validates BGP Updates.
-.. clicmd:: show rpki cache-connection
+.. clicmd:: show rpki cache-server [json]
Display all configured cache servers, whether active or not.
+.. clicmd:: show rpki cache-connection [json]
+
+ Display all cache connections, and show which is connected or not.
+
.. clicmd:: show bgp [afi] [safi] <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> rpki <valid|invalid|notfound>
Display for the specified prefix or address the bgp paths that match the given rpki state.
diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile
index 79ae315679..fa4b9859b9 100644
--- a/docker/alpine/Dockerfile
+++ b/docker/alpine/Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Create a basic stage set up to build APKs
-FROM alpine:3.13 as alpine-builder
+FROM alpine:3.15 as alpine-builder
RUN apk add \
--update-cache \
abuild \
@@ -22,7 +22,7 @@ RUN cd /libyang \
&& abuild -r -P /pkgs/apk
# This stage builds a dist tarball from the source
-FROM alpine:3.13 as source-builder
+FROM alpine:3.15 as source-builder
RUN mkdir -p /src/alpine
COPY alpine/APKBUILD.in /src/alpine
@@ -33,6 +33,7 @@ RUN source /src/alpine/APKBUILD.in \
$makedepends \
gzip \
py-pip \
+ rtrlib \
&& pip install pytest
RUN mkdir -p /pkgs/apk
@@ -66,7 +67,7 @@ RUN cd /dist \
&& abuild -r -P /pkgs/apk
# This stage installs frr from the apk
-FROM alpine:3.13
+FROM alpine:3.15
RUN mkdir -p /pkgs/apk
COPY --from=frr-apk-builder /pkgs/apk/ /pkgs/apk/
RUN apk add \
diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile
index 73097df8fa..2d1ee9efa4 100644
--- a/docker/centos-7/Dockerfile
+++ b/docker/centos-7/Dockerfile
@@ -4,11 +4,11 @@ RUN yum install -y epel-release
RUN yum install -y rpm-build autoconf automake libtool make \
readline-devel texinfo net-snmp-devel groff pkgconfig \
json-c-devel pam-devel bison flex pytest c-ares-devel \
- python3-devel python3-sphinx libcap-devel \
+ python3-devel python3-sphinx libcap-devel systemd-devel \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.x86_64.rpm
COPY . /src
ARG PKGVER
@@ -33,7 +33,7 @@ RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >>
FROM centos:centos7
RUN mkdir -p /pkgs/rpm \
&& yum install -y https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm
COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/
diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile
index df095edcde..df772b8318 100644
--- a/docker/centos-8/Dockerfile
+++ b/docker/centos-8/Dockerfile
@@ -11,8 +11,8 @@ RUN dnf install --enablerepo=powertools -y rpm-build git autoconf pcre-devel \
c-ares-devel python3-devel python3-sphinx libcap-devel platform-python-devel \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.x86_64.rpm
COPY . /src
@@ -42,7 +42,7 @@ RUN sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-* \
RUN mkdir -p /pkgs/rpm \
&& yum install -y https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm
COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/
diff --git a/docker/ubi-8/Dockerfile b/docker/ubi-8/Dockerfile
index 7b2db66ede..1d1e8bdc6e 100644
--- a/docker/ubi-8/Dockerfile
+++ b/docker/ubi-8/Dockerfile
@@ -16,8 +16,8 @@ RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.
libcap-devel platform-python-devel \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.x86_64.rpm
COPY . /src
@@ -60,7 +60,7 @@ RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \
RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \
&& mkdir -p /pkgs/rpm \
&& dnf install --enablerepo=* -qy https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm
COPY --from=ubi-8-builder /rpmbuild/RPMS/ /pkgs/rpm/
diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c
index 491b2994b0..dd5ba8a164 100644
--- a/eigrpd/eigrp_packet.c
+++ b/eigrpd/eigrp_packet.c
@@ -370,7 +370,7 @@ void eigrp_write(struct thread *thread)
if (ep->dst.s_addr == htonl(EIGRP_MULTICAST_ADDRESS))
eigrp_if_ipmulticast(eigrp, &ei->address, ei->ifp->ifindex);
- memset(&iph, 0, sizeof(struct ip));
+ memset(&iph, 0, sizeof(iph));
memset(&sa_dst, 0, sizeof(sa_dst));
/*
@@ -713,7 +713,7 @@ static struct stream *eigrp_recv_packet(struct eigrp *eigrp,
char buff[CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())];
struct msghdr msgh;
- memset(&msgh, 0, sizeof(struct msghdr));
+ memset(&msgh, 0, sizeof(msgh));
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_control = (caddr_t)buff;
diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c
index 9ada292feb..5a930424c0 100644
--- a/eigrpd/eigrp_snmp.c
+++ b/eigrpd/eigrp_snmp.c
@@ -1034,7 +1034,7 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&nbr_addr, 0, sizeof(struct in_addr));
+ memset(&nbr_addr, 0, sizeof(nbr_addr));
ifindex = 0;
nbr = eigrpNbrLookup(v, name, length, &nbr_addr, &ifindex, exact);
diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c
index 84d4f6aeeb..7bc7be9706 100644
--- a/eigrpd/eigrpd.c
+++ b/eigrpd/eigrpd.c
@@ -123,7 +123,7 @@ void eigrp_master_init(void)
{
struct timeval tv;
- memset(&eigrp_master, 0, sizeof(struct eigrp_master));
+ memset(&eigrp_master, 0, sizeof(eigrp_master));
eigrp_om = &eigrp_master;
eigrp_om->eigrp = list_new();
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index e5cea27829..53892303ba 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -519,6 +519,31 @@ enum ipvlan_mode {
#define IPVLAN_F_PRIVATE 0x01
#define IPVLAN_F_VEPA 0x02
+/* Tunnel RTM header */
+struct tunnel_msg {
+ __u8 family;
+ __u8 reserved1;
+ __u16 reserved2;
+ __u32 ifindex;
+};
+
+enum {
+ VXLAN_VNIFILTER_ENTRY_UNSPEC,
+ VXLAN_VNIFILTER_ENTRY_START,
+ VXLAN_VNIFILTER_ENTRY_END,
+ VXLAN_VNIFILTER_ENTRY_GROUP,
+ VXLAN_VNIFILTER_ENTRY_GROUP6,
+ __VXLAN_VNIFILTER_ENTRY_MAX
+};
+#define VXLAN_VNIFILTER_ENTRY_MAX (__VXLAN_VNIFILTER_ENTRY_MAX - 1)
+
+enum {
+ VXLAN_VNIFILTER_UNSPEC,
+ VXLAN_VNIFILTER_ENTRY,
+ __VXLAN_VNIFILTER_MAX
+};
+#define VXLAN_VNIFILTER_MAX (__VXLAN_VNIFILTER_MAX - 1)
+
/* VXLAN section */
enum {
IFLA_VXLAN_UNSPEC,
@@ -550,6 +575,9 @@ enum {
IFLA_VXLAN_LABEL,
IFLA_VXLAN_GPE,
IFLA_VXLAN_TTL_INHERIT,
+ IFLA_VXLAN_DF,
+ IFLA_VXLAN_VNIFILTER, /* only applicable when COLLECT_METADATA mode is
+ on */
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
index 1fb90ec021..8c0640671a 100644
--- a/include/linux/mroute6.h
+++ b/include/linux/mroute6.h
@@ -63,7 +63,8 @@ typedef uint32_t if_mask;
#define NIFBITS (sizeof(if_mask) * 8) /* bits per mask */
typedef struct if_set {
- if_mask ifs_bits[__KERNEL_DIV_ROUND_UP(IF_SETSIZE, NIFBITS)];
+ /* __KERNEL_DIV_ROUND_UP() */
+ if_mask ifs_bits[(IF_SETSIZE + NIFBITS - 1) / NIFBITS];
} if_set;
#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS)))
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 5888492a52..b15b72a262 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -185,6 +185,16 @@ enum {
RTM_GETNEXTHOPBUCKET,
#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET
+ RTM_SETHWFLAGS = 119,
+#define RTM_SETHWFLAGS RTM_SETHWFLAGS
+
+ RTM_NEWTUNNEL = 120,
+#define RTM_NEWTUNNEL RTM_NEWTUNNEL
+ RTM_DELTUNNEL,
+#define RTM_DELTUNNEL RTM_DELTUNNEL
+ RTM_GETTUNNEL,
+#define RTM_GETTUNNEL RTM_GETTUNNEL
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@@ -217,6 +227,11 @@ struct rtattr {
#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
+#ifndef TUNNEL_RTA
+#define TUNNEL_RTA(r) \
+ ((struct rtattr *)(((char *)(r)) + \
+ NLMSG_ALIGN(sizeof(struct tunnel_msg))))
+#endif
@@ -754,6 +769,8 @@ enum rtnetlink_groups {
#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP
RTNLGRP_BRVLAN,
#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN
+ RTNLGRP_TUNNEL,
+#define RTNLGRP_TUNNEL RTNLGRP_TUNNEL
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
index 88c3bfa63c..5f3d70ed3a 100644
--- a/isisd/isis_bpf.c
+++ b/isisd/isis_bpf.c
@@ -161,7 +161,7 @@ static int open_bpf_dev(struct isis_circuit *circuit)
/*
* And set the filter
*/
- memset(&bpf_prog, 0, sizeof(struct bpf_program));
+ memset(&bpf_prog, 0, sizeof(bpf_prog));
bpf_prog.bf_len = 8;
bpf_prog.bf_insns = &(llcfilter[0]);
if (ioctl(fd, BIOCSETF, (caddr_t)&bpf_prog) < 0) {
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index c23b0f0dc1..2bf9eadaba 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -1348,26 +1348,30 @@ void cli_show_isis_redistribute_ipv6(struct vty *vty,
/*
* XPath: /frr-isisd:isis/instance/multi-topology
*/
-DEFPY_YANG(isis_topology, isis_topology_cmd,
- "[no] topology <ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology [overload]$overload",
- NO_STR
- "Configure IS-IS topologies\n"
- "IPv4 unicast topology\n"
- "IPv4 management topology\n"
- "IPv6 unicast topology\n"
- "IPv4 multicast topology\n"
- "IPv6 multicast topology\n"
- "IPv6 management topology\n"
- "IPv6 dst-src topology\n"
- "Set overload bit for topology\n")
+DEFPY_YANG(
+ isis_topology, isis_topology_cmd,
+ "[no] topology <standard|ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology [overload]$overload",
+ NO_STR
+ "Configure IS-IS topologies\n"
+ "standard topology\n"
+ "IPv4 unicast topology\n"
+ "IPv4 management topology\n"
+ "IPv6 unicast topology\n"
+ "IPv4 multicast topology\n"
+ "IPv6 multicast topology\n"
+ "IPv6 management topology\n"
+ "IPv6 dst-src topology\n"
+ "Set overload bit for topology\n")
{
char base_xpath[XPATH_MAXLEN];
- /* Since IPv4-unicast is not configurable it is not present in the
+ /* Since standard is not configurable it is not present in the
* YANG model, so we need to validate it here
*/
- if (strmatch(topology, "ipv4-unicast")) {
- vty_out(vty, "Cannot configure IPv4 unicast topology\n");
+ if (strmatch(topology, "standard") ||
+ strmatch(topology, "ipv4-unicast")) {
+ vty_out(vty,
+ "Cannot configure IPv4 unicast (Standard) topology\n");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -2333,10 +2337,11 @@ void cli_show_ip_isis_psnp_interval(struct vty *vty,
* XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology
*/
DEFPY_YANG(circuit_topology, circuit_topology_cmd,
- "[no] isis topology<ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology",
+ "[no] isis topology<standard|ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology",
NO_STR
"IS-IS routing protocol\n"
"Configure interface IS-IS topologies\n"
+ "Standard topology\n"
"IPv4 unicast topology\n"
"IPv4 management topology\n"
"IPv6 unicast topology\n"
@@ -2353,18 +2358,20 @@ DEFPY_YANG(circuit_topology, circuit_topology_cmd,
else if (strmatch(topology, "ipv6-mgmt"))
return nb_cli_apply_changes(
vty, "./frr-isisd:isis/multi-topology/ipv6-management");
+ if (strmatch(topology, "ipv4-unicast"))
+ return nb_cli_apply_changes(
+ vty, "./frr-isisd:isis/multi-topology/standard");
else
return nb_cli_apply_changes(
vty, "./frr-isisd:isis/multi-topology/%s", topology);
}
-void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty,
- const struct lyd_node *dnode,
- bool show_defaults)
+void cli_show_ip_isis_mt_standard(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
{
if (!yang_dnode_get_bool(dnode, NULL))
vty_out(vty, " no");
- vty_out(vty, " isis topology ipv4-unicast\n");
+ vty_out(vty, " isis topology standard\n");
}
void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty,
diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c
index f937bdf55c..6bbb01c307 100644
--- a/isisd/isis_mt.c
+++ b/isisd/isis_mt.c
@@ -56,8 +56,8 @@ const char *isis_mtid2str(uint16_t mtid)
static char buf[sizeof("65535")];
switch (mtid) {
- case ISIS_MT_IPV4_UNICAST:
- return "ipv4-unicast";
+ case ISIS_MT_STANDARD:
+ return "standard";
case ISIS_MT_IPV4_MGMT:
return "ipv4-mgmt";
case ISIS_MT_IPV6_UNICAST:
@@ -80,6 +80,8 @@ uint16_t isis_str2mtid(const char *name)
{
if (!strcmp(name, "ipv4-unicast"))
return ISIS_MT_IPV4_UNICAST;
+ if (!strcmp(name, "standard"))
+ return ISIS_MT_STANDARD;
if (!strcmp(name, "ipv4-mgmt"))
return ISIS_MT_IPV4_MGMT;
if (!strcmp(name, "ipv6-unicast"))
diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h
index 2952a2f171..13e508451b 100644
--- a/isisd/isis_mt.h
+++ b/isisd/isis_mt.h
@@ -27,6 +27,7 @@
#define ISIS_MT_AT_MASK 0x4000
#define ISIS_MT_IPV4_UNICAST 0
+#define ISIS_MT_STANDARD ISIS_MT_IPV4_UNICAST
#define ISIS_MT_IPV4_MGMT 1
#define ISIS_MT_IPV6_UNICAST 2
#define ISIS_MT_IPV4_MULTICAST 3
@@ -37,7 +38,7 @@
#define ISIS_MT_DISABLE 4096
#define ISIS_MT_NAMES \
- "<ipv4-unicast" \
+ "<standard" \
"|ipv4-mgmt" \
"|ipv6-unicast" \
"|ipv4-multicast" \
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 20e4806525..a2ba33d078 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -899,10 +899,10 @@ const struct frr_yang_module_info frr_isisd_info = {
},
},
{
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast",
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/standard",
.cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv4_unicast,
- .modify = lib_interface_isis_multi_topology_ipv4_unicast_modify,
+ .cli_show = cli_show_ip_isis_mt_standard,
+ .modify = lib_interface_isis_multi_topology_standard_modify,
},
},
{
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index 96de64a68b..00ca8be3b0 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -291,7 +291,7 @@ int lib_interface_isis_password_password_type_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_disable_three_way_handshake_modify(
struct nb_cb_modify_args *args);
-int lib_interface_isis_multi_topology_ipv4_unicast_modify(
+int lib_interface_isis_multi_topology_standard_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_multi_topology_ipv4_multicast_modify(
struct nb_cb_modify_args *args);
@@ -543,9 +543,8 @@ void cli_show_ip_isis_csnp_interval(struct vty *vty,
void cli_show_ip_isis_psnp_interval(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
-void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty,
- const struct lyd_node *dnode,
- bool show_defaults);
+void cli_show_ip_isis_mt_standard(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index cf4c2aea0a..79b167718b 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -3070,10 +3070,6 @@ int lib_interface_isis_disable_three_way_handshake_modify(
return NB_OK;
}
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast
- */
static int lib_interface_isis_multi_topology_common(
enum nb_event event, const struct lyd_node *dnode, char *errmsg,
size_t errmsg_len, uint16_t mtid)
@@ -3104,12 +3100,16 @@ static int lib_interface_isis_multi_topology_common(
return NB_OK;
}
-int lib_interface_isis_multi_topology_ipv4_unicast_modify(
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/standard
+ */
+int lib_interface_isis_multi_topology_standard_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
args->event, args->dnode, args->errmsg, args->errmsg_len,
- ISIS_MT_IPV4_UNICAST);
+ ISIS_MT_STANDARD);
}
/*
diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
index d58cd1c5bc..a448e0043c 100644
--- a/isisd/isis_pfpacket.c
+++ b/isisd/isis_pfpacket.c
@@ -144,7 +144,7 @@ static int open_packet_socket(struct isis_circuit *circuit)
/*
* Bind to the physical interface
*/
- memset(&s_addr, 0, sizeof(struct sockaddr_ll));
+ memset(&s_addr, 0, sizeof(s_addr));
s_addr.sll_family = AF_PACKET;
s_addr.sll_protocol = htons(ETH_P_ALL);
s_addr.sll_ifindex = circuit->interface->ifindex;
@@ -233,7 +233,7 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa)
addr_len = sizeof(s_addr);
- memset(&s_addr, 0, sizeof(struct sockaddr_ll));
+ memset(&s_addr, 0, sizeof(s_addr));
bytesread =
recvfrom(circuit->fd, (void *)&llc, LLC_LEN, MSG_PEEK,
@@ -306,7 +306,7 @@ int isis_recv_pdu_p2p(struct isis_circuit *circuit, uint8_t *ssnpa)
int bytesread, addr_len;
struct sockaddr_ll s_addr;
- memset(&s_addr, 0, sizeof(struct sockaddr_ll));
+ memset(&s_addr, 0, sizeof(s_addr));
addr_len = sizeof(s_addr);
/* we can read directly to the stream */
@@ -351,7 +351,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
struct sockaddr_ll sa;
stream_set_getp(circuit->snd_stream, 0);
- memset(&sa, 0, sizeof(struct sockaddr_ll));
+ memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN;
@@ -398,7 +398,7 @@ int isis_send_pdu_p2p(struct isis_circuit *circuit, int level)
ssize_t rv;
stream_set_getp(circuit->snd_stream, 0);
- memset(&sa, 0, sizeof(struct sockaddr_ll));
+ memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_ifindex = circuit->interface->ifindex;
sa.sll_halen = ETH_ALEN;
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 10ba6a35bb..09f92554c0 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -273,7 +273,7 @@ isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
vadj = XCALLOC(MTYPE_ISIS_VERTEX_ADJ, sizeof(*vadj));
vadj->sadj = sadj;
- if (psid) {
+ if (spftree->area->srdb.enabled && psid) {
if (vertex->N.ip.sr.present
&& vertex->N.ip.sr.sid.value != psid->value)
zlog_warn(
@@ -575,7 +575,7 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
vertex = isis_vertex_new(spftree, id, vtype);
vertex->d_N = cost;
vertex->depth = depth;
- if (VTYPE_IP(vtype) && psid) {
+ if (VTYPE_IP(vtype) && spftree->area->srdb.enabled && psid) {
struct isis_area *area = spftree->area;
struct isis_vertex *vertex_psid;
@@ -706,7 +706,7 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
if (vtype >= VTYPE_IPREACH_INTERNAL) {
memcpy(&p, id, sizeof(p));
apply_mask(&p.dest);
- apply_mask((struct prefix *)&p.src);
+ apply_mask(&p.src);
id = &p;
}
@@ -975,9 +975,9 @@ lspfragloop:
ip_info.dest.u.prefix4 = r->prefix.prefix;
ip_info.dest.prefixlen = r->prefix.prefixlen;
- /* Parse list of Prefix-SID subTLVs */
+ /* Parse list of Prefix-SID subTLVs if SR is enabled */
has_valid_psid = false;
- if (r->subtlvs) {
+ if (spftree->area->srdb.enabled && r->subtlvs) {
for (struct isis_item *i =
r->subtlvs->prefix_sids.head;
i; i = i->next) {
@@ -1026,9 +1026,9 @@ lspfragloop:
ip_info.dest.u.prefix6 = r->prefix.prefix;
ip_info.dest.prefixlen = r->prefix.prefixlen;
- if (r->subtlvs
- && r->subtlvs->source_prefix
- && r->subtlvs->source_prefix->prefixlen) {
+ if (spftree->area->srdb.enabled && r->subtlvs &&
+ r->subtlvs->source_prefix &&
+ r->subtlvs->source_prefix->prefixlen) {
if (spftree->tree_id != SPFTREE_DSTSRC) {
char buff[VID2STR_BUFFER];
zlog_warn("Ignoring dest-src route %s in non dest-src topology",
@@ -1169,8 +1169,8 @@ static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix,
else
vtype = VTYPE_IP6REACH_INTERNAL;
- /* Parse list of Prefix-SID subTLVs */
- if (subtlvs) {
+ /* Parse list of Prefix-SID subTLVs if SR is enabled */
+ if (spftree->area->srdb.enabled && subtlvs) {
for (struct isis_item *i = subtlvs->prefix_sids.head; i;
i = i->next) {
struct isis_prefix_sid *psid =
diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c
index 91886cbc37..107fa71d71 100644
--- a/isisd/isis_sr.c
+++ b/isisd/isis_sr.c
@@ -1070,7 +1070,10 @@ DEFUN(show_sr_node, show_sr_node_cmd,
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
vty_out(vty, "Area %s:\n",
area->area_tag ? area->area_tag : "null");
-
+ if (!area->srdb.enabled) {
+ vty_out(vty, " Segment Routing is disabled\n");
+ continue;
+ }
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
level++)
show_node(vty, area, level);
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index 3ba5c6ccfa..11be3c3a71 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -119,6 +119,14 @@ static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
static void append_item(struct isis_item_list *dest, struct isis_item *item);
static void init_item_list(struct isis_item_list *items);
+/* For tests/isisd, TLV text requires ipv4-unicast instead of standard */
+static const char *isis_mtid2str_fake(uint16_t mtid)
+{
+ if (mtid == ISIS_MT_STANDARD)
+ return "ipv4-unicast";
+ return isis_mtid2str(mtid);
+}
+
/* Functions for Extended IS Reachability SubTLVs a.k.a Traffic Engineering */
struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void)
{
@@ -2391,7 +2399,7 @@ static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i,
json_object_string_add(mt_json, "attached", info->attached?"true":"false");
} else
sbuf_push(buf, indent, "MT Router Info: %s%s%s\n",
- isis_mtid2str(info->mtid),
+ isis_mtid2str_fake(info->mtid),
info->overload ? " Overload" : "",
info->attached ? " Attached" : "");
}
@@ -4306,7 +4314,7 @@ static int unpack_tlv_with_items(enum isis_tlv_context context,
mtid = stream_getw(s) & ISIS_MT_MASK;
tlv_pos += 2;
sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
- isis_mtid2str(mtid));
+ isis_mtid2str_fake(mtid));
} else {
sbuf_push(log, indent, "Unpacking as item TLV...\n");
mtid = ISIS_MT_IPV4_UNICAST;
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 85307f448b..996a62f4d5 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -177,7 +177,7 @@ struct isis *isis_lookup_by_sysid(const uint8_t *sysid)
void isis_master_init(struct thread_master *master)
{
- memset(&isis_master, 0, sizeof(struct isis_master));
+ memset(&isis_master, 0, sizeof(isis_master));
im = &isis_master;
im->isis = list_new();
im->master = master;
diff --git a/lib/keychain.c b/lib/keychain.c
index c29c45a114..5dcb572a3c 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -542,7 +542,7 @@ static time_t key_str2time(const char *time_str, const char *day_str,
/* Check year_str. Year must be <1993-2035>. */
GET_LONG_RANGE(year, year_str, 1993, 2035);
- memset(&tm, 0, sizeof(struct tm));
+ memset(&tm, 0, sizeof(tm));
tm.tm_sec = sec;
tm.tm_min = min;
tm.tm_hour = hour;
diff --git a/lib/linklist.c b/lib/linklist.c
index 8137b68d84..8519482885 100644
--- a/lib/linklist.c
+++ b/lib/linklist.c
@@ -370,10 +370,15 @@ void list_sort(struct list *list, int (*cmp)(const void **, const void **))
int i = -1;
void *data;
size_t n = list->count;
- void **items = XCALLOC(MTYPE_TMP, (sizeof(void *)) * n);
+ void **items;
int (*realcmp)(const void *, const void *) =
(int (*)(const void *, const void *))cmp;
+ if (!n)
+ return;
+
+ items = XCALLOC(MTYPE_TMP, (sizeof(void *)) * n);
+
for (ALL_LIST_ELEMENTS(list, ln, nn, data)) {
items[++i] = data;
list_delete_node(list, ln);
diff --git a/lib/routemap.c b/lib/routemap.c
index 46161fd817..722693ea27 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -759,7 +759,7 @@ struct route_map *route_map_lookup_by_name(const char *name)
return NULL;
// map.deleted is false via memset
- memset(&tmp_map, 0, sizeof(struct route_map));
+ memset(&tmp_map, 0, sizeof(tmp_map));
tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
map = hash_lookup(route_map_master_hash, &tmp_map);
XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);
@@ -797,7 +797,7 @@ int route_map_mark_updated(const char *name)
* with deleted=true
*/
if (!map) {
- memset(&tmp_map, 0, sizeof(struct route_map));
+ memset(&tmp_map, 0, sizeof(tmp_map));
tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
tmp_map.deleted = true;
map = hash_lookup(route_map_master_hash, &tmp_map);
@@ -2481,7 +2481,7 @@ void route_map_notify_pentry_dependencies(const char *affected_name,
if (!dep->this_hash)
dep->this_hash = upd8_hash;
- memset(&pentry_dep, 0, sizeof(struct route_map_pentry_dep));
+ memset(&pentry_dep, 0, sizeof(pentry_dep));
pentry_dep.pentry = pentry;
pentry_dep.plist_name = affected_name;
pentry_dep.event = event;
@@ -2760,7 +2760,7 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
struct route_map_dep *dep = bucket->data;
struct route_map_dep_data *dep_data = NULL, tmp_dep_data;
- memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
+ memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
tmp_dep_data.rname = arg;
dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
if (dep_data) {
@@ -2873,7 +2873,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
if (!dep->this_hash)
dep->this_hash = dephash;
- memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
+ memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
tmp_dep_data.rname = rname;
dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
if (!dep_data)
@@ -2897,7 +2897,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
goto out;
}
- memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
+ memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
tmp_dep_data.rname = rname;
dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
/*
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 9763b38e28..97b198c018 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -107,7 +107,7 @@ static void sockunion_normalise_mapped(union sockunion *su)
if (su->sa.sa_family == AF_INET6
&& IN6_IS_ADDR_V4MAPPED(&su->sin6.sin6_addr)) {
- memset(&sin, 0, sizeof(struct sockaddr_in));
+ memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = su->sin6.sin6_port;
memcpy(&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
diff --git a/lib/table.c b/lib/table.c
index 523183bef2..6c4c6c7d36 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -228,7 +228,7 @@ struct route_node *route_node_match_ipv4(struct route_table *table,
{
struct prefix_ipv4 p;
- memset(&p, 0, sizeof(struct prefix_ipv4));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET;
p.prefixlen = IPV4_MAX_BITLEN;
p.prefix = *addr;
@@ -241,7 +241,7 @@ struct route_node *route_node_match_ipv6(struct route_table *table,
{
struct prefix_ipv6 p;
- memset(&p, 0, sizeof(struct prefix_ipv6));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET6;
p.prefixlen = IPV6_MAX_BITLEN;
p.prefix = *addr;
diff --git a/lib/thread.c b/lib/thread.c
index 44183257bb..fd79503cc6 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -2143,14 +2143,6 @@ void debug_signals(const sigset_t *sigs)
zlog_debug("%s: %s", __func__, buf);
}
-bool thread_is_scheduled(struct thread *thread)
-{
- if (thread == NULL)
- return false;
-
- return true;
-}
-
static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea,
const struct thread *thread)
{
diff --git a/lib/thread.h b/lib/thread.h
index a2049ae52a..acdcec51de 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -283,7 +283,14 @@ extern pthread_key_t thread_current;
extern char *thread_timer_to_hhmmss(char *buf, int buf_size,
struct thread *t_timer);
-extern bool thread_is_scheduled(struct thread *thread);
+static inline bool thread_is_scheduled(struct thread *thread)
+{
+ if (thread)
+ return true;
+
+ return false;
+}
+
/* Debug signal mask */
void debug_signals(const sigset_t *sigs);
diff --git a/lib/vty.c b/lib/vty.c
index 619d51e1ce..e0e2f39229 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1827,7 +1827,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
int sock;
char port_str[BUFSIZ];
- memset(&req, 0, sizeof(struct addrinfo));
+ memset(&req, 0, sizeof(req));
req.ai_flags = AI_PASSIVE;
req.ai_family = AF_UNSPEC;
req.ai_socktype = SOCK_STREAM;
@@ -1912,7 +1912,7 @@ static void vty_serv_un(const char *path)
}
/* Make server socket. */
- memset(&serv, 0, sizeof(struct sockaddr_un));
+ memset(&serv, 0, sizeof(serv));
serv.sun_family = AF_UNIX;
strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
@@ -1976,7 +1976,7 @@ static void vtysh_accept(struct thread *thread)
vty_event_serv(VTYSH_SERV, vtyserv);
- memset(&client, 0, sizeof(struct sockaddr_un));
+ memset(&client, 0, sizeof(client));
client_len = sizeof(struct sockaddr_un);
sock = accept(accept_sock, (struct sockaddr *)&client,
diff --git a/nhrpd/vici.c b/nhrpd/vici.c
index 56adde406e..6ba2399e05 100644
--- a/nhrpd/vici.c
+++ b/nhrpd/vici.c
@@ -600,7 +600,7 @@ int sock_open_unix(const char *path)
if (fd < 0)
return -1;
- memset(&addr, 0, sizeof(struct sockaddr_un));
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 0ca4eb3d5d..c210b4476c 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -628,7 +628,7 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
offset = sizeof(*external)
+ OSPF6_PREFIX_SPACE(external->prefix.prefix_length);
- memset(&fwd_addr, 0, sizeof(struct prefix));
+ memset(&fwd_addr, 0, sizeof(fwd_addr));
fwd_addr.family = AF_INET6;
fwd_addr.prefixlen = IPV6_MAX_BITLEN;
memcpy(&fwd_addr.u.prefix6, (caddr_t)external + offset,
@@ -1121,7 +1121,7 @@ void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6,
{
SET_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED);
- if (ospf6->t_distribute_update)
+ if (thread_is_scheduled(ospf6->t_distribute_update))
return;
if (IS_OSPF6_DEBUG_ASBR)
@@ -3360,7 +3360,7 @@ ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6,
{
aggr->action = operation;
- if (ospf6->t_external_aggr) {
+ if (thread_is_scheduled(ospf6->t_external_aggr)) {
if (ospf6->aggr_action == OSPF6_ROUTE_AGGR_ADD) {
if (IS_OSPF6_DEBUG_AGGR)
diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c
index 1095473f4a..77ac4a1877 100644
--- a/ospf6d/ospf6_auth_trailer.c
+++ b/ospf6d/ospf6_auth_trailer.c
@@ -144,8 +144,6 @@ unsigned char *ospf6_hash_message_xor(unsigned char *mes1,
uint32_t i;
result = XCALLOC(MTYPE_OSPF6_AUTH_HASH_XOR, len);
- if (!result)
- return NULL;
for (i = 0; i < len; i++)
result[i] = mes1[i] ^ mes2[i];
@@ -408,7 +406,7 @@ int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len,
return OSPF6_AUTH_VALIDATE_FAILURE;
}
- memset(&ospf6_auth_info, 0, sizeof(struct ospf6_auth_hdr));
+ memset(&ospf6_auth_info, 0, sizeof(ospf6_auth_info));
if ((*pkt_len - hdr_len - (*lls_block_len)) > sizeof(ospf6_auth_info)) {
if (IS_OSPF6_DEBUG_AUTH_RX)
zlog_err("RECV[%s] : Wrong auth data in %s packet",
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
index 87407245b3..d7de66c663 100644
--- a/ospf6d/ospf6_gr.c
+++ b/ospf6d/ospf6_gr.c
@@ -459,7 +459,6 @@ static void ospf6_gr_grace_period_expired(struct thread *thread)
{
struct ospf6 *ospf6 = THREAD_ARG(thread);
- ospf6->gr_info.t_grace_period = NULL;
ospf6_gr_restart_exit(ospf6, "grace period has expired");
}
diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c
index 493dc6f093..33a7a57c0f 100644
--- a/ospf6d/ospf6_gr_helper.c
+++ b/ospf6d/ospf6_gr_helper.c
@@ -154,6 +154,12 @@ static int ospf6_extract_grace_lsa_fields(struct ospf6_lsa *lsa,
int sum = 0;
lsah = (struct ospf6_lsa_header *)lsa->header;
+ if (ntohs(lsah->length) <= OSPF6_LSA_HEADER_SIZE) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("%s: undersized (%u B) lsa", __func__,
+ ntohs(lsah->length));
+ return OSPF6_FAILURE;
+ }
length = ntohs(lsah->length) - OSPF6_LSA_HEADER_SIZE;
@@ -394,8 +400,7 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
}
if (OSPF6_GR_IS_ACTIVE_HELPER(restarter)) {
- if (restarter->gr_helper_info.t_grace_timer)
- THREAD_OFF(restarter->gr_helper_info.t_grace_timer);
+ THREAD_OFF(restarter->gr_helper_info.t_grace_timer);
if (ospf6->ospf6_helper_cfg.active_restarter_cnt > 0)
ospf6->ospf6_helper_cfg.active_restarter_cnt--;
@@ -1245,6 +1250,12 @@ static int ospf6_grace_lsa_show_info(struct vty *vty, struct ospf6_lsa *lsa,
int sum = 0;
lsah = (struct ospf6_lsa_header *)lsa->header;
+ if (ntohs(lsah->length) <= OSPF6_LSA_HEADER_SIZE) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("%s: undersized (%u B) lsa", __func__,
+ ntohs(lsah->length));
+ return OSPF6_FAILURE;
+ }
length = ntohs(lsah->length) - OSPF6_LSA_HEADER_SIZE;
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 55f1a1c7b5..2a503c6233 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -827,7 +827,7 @@ void interface_up(struct thread *thread)
/* Schedule Hello */
if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)
&& !if_is_loopback(oi->interface)) {
- thread_add_event(master, ospf6_hello_send, oi, 0,
+ thread_add_timer(master, ospf6_hello_send, oi, 0,
&oi->thread_send_hello);
}
@@ -1130,9 +1130,9 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
ospf6_interface_state_str[oi->state], oi->transdelay,
oi->priority);
vty_out(vty, " Timer intervals configured:\n");
- vty_out(vty, " Hello %d, Dead %d, Retransmit %d\n",
- oi->hello_interval, oi->dead_interval,
- oi->rxmt_interval);
+ vty_out(vty, " Hello %d(%pTHd), Dead %d, Retransmit %d\n",
+ oi->hello_interval, oi->thread_send_hello,
+ oi->dead_interval, oi->rxmt_interval);
}
inet_ntop(AF_INET, &oi->drouter, drouter, sizeof(drouter));
@@ -1152,7 +1152,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
if (use_json) {
timerclear(&res);
- if (oi->thread_send_lsupdate)
+ if (thread_is_scheduled(oi->thread_send_lsupdate))
timersub(&oi->thread_send_lsupdate->u.sands, &now,
&res);
timerstring(&res, duration, sizeof(duration));
@@ -1162,7 +1162,9 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
duration);
json_object_string_add(
json_obj, "lsUpdateSendThread",
- (oi->thread_send_lsupdate ? "on" : "off"));
+ (thread_is_scheduled(oi->thread_send_lsupdate)
+ ? "on"
+ : "off"));
json_arr = json_object_new_array();
for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext))
@@ -1172,7 +1174,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
json_arr);
timerclear(&res);
- if (oi->thread_send_lsack)
+ if (thread_is_scheduled(oi->thread_send_lsack))
timersub(&oi->thread_send_lsack->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
@@ -1180,8 +1182,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
oi->lsack_list->count);
json_object_string_add(json_obj, "pendingLsaLsAckTime",
duration);
- json_object_string_add(json_obj, "lsAckSendThread",
- (oi->thread_send_lsack ? "on" : "off"));
+ json_object_string_add(
+ json_obj, "lsAckSendThread",
+ (thread_is_scheduled(oi->thread_send_lsack) ? "on"
+ : "off"));
json_arr = json_object_new_array();
for (ALL_LSDB(oi->lsack_list, lsa, lsanext))
@@ -1191,25 +1195,28 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
} else {
timerclear(&res);
- if (oi->thread_send_lsupdate)
+ if (thread_is_scheduled(oi->thread_send_lsupdate))
timersub(&oi->thread_send_lsupdate->u.sands, &now,
&res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for LSUpdate in Time %s [thread %s]\n",
oi->lsupdate_list->count, duration,
- (oi->thread_send_lsupdate ? "on" : "off"));
+ (thread_is_scheduled(oi->thread_send_lsupdate)
+ ? "on"
+ : "off"));
for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
timerclear(&res);
- if (oi->thread_send_lsack)
+ if (thread_is_scheduled(oi->thread_send_lsack))
timersub(&oi->thread_send_lsack->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for LSAck in Time %s [thread %s]\n",
oi->lsack_list->count, duration,
- (oi->thread_send_lsack ? "on" : "off"));
+ (thread_is_scheduled(oi->thread_send_lsack) ? "on"
+ : "off"));
for (ALL_LSDB(oi->lsack_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
}
@@ -2102,6 +2109,16 @@ DEFUN (ipv6_ospf6_hellointerval,
oi->hello_interval = strmatch(argv[0]->text, "no")
? OSPF_HELLO_INTERVAL_DEFAULT
: strtoul(argv[idx_number]->arg, NULL, 10);
+
+ /*
+ * If the thread is scheduled, send the new hello now.
+ */
+ if (thread_is_scheduled(oi->thread_send_hello)) {
+ THREAD_OFF(oi->thread_send_hello);
+
+ thread_add_timer(master, ospf6_hello_send, oi, 0,
+ &oi->thread_send_hello);
+ }
return CMD_SUCCESS;
}
@@ -2348,7 +2365,7 @@ DEFUN (no_ipv6_ospf6_passive,
/* don't send hellos over loopback interface */
if (!if_is_loopback(oi->interface))
- thread_add_event(master, ospf6_hello_send, oi, 0,
+ thread_add_timer(master, ospf6_hello_send, oi, 0,
&oi->thread_send_hello);
return CMD_SUCCESS;
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index f3fd9dab66..b5ea3ada36 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -2010,7 +2010,7 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
break;
prefix_num--;
- memset(&prefix, 0, sizeof(struct prefix));
+ memset(&prefix, 0, sizeof(prefix));
prefix.family = AF_INET6;
prefix.prefixlen = op->prefix_length;
ospf6_prefix_in6_addr(&prefix.u.prefix6, intra_prefix_lsa, op);
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index 6645f83b00..93a062b215 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -1733,19 +1733,22 @@ static unsigned int iobuflen = 0;
int ospf6_iobuf_size(unsigned int size)
{
- uint8_t *recvnew, *sendnew;
+ /* NB: there was previously code here that tried to dynamically size
+ * the buffer for whatever we see in MTU on interfaces. Which is
+ * _unconditionally wrong_ - we can always receive fragmented IPv6
+ * up to the regular 64k length limit. (No jumbograms, thankfully.)
+ */
- if (size <= iobuflen)
- return iobuflen;
+ if (!iobuflen) {
+ /* the + 128 is to have some runway at the end */
+ size_t alloc_size = 65536 + 128;
- recvnew = XMALLOC(MTYPE_OSPF6_MESSAGE, size);
- sendnew = XMALLOC(MTYPE_OSPF6_MESSAGE, size);
+ assert(!recvbuf && !sendbuf);
- XFREE(MTYPE_OSPF6_MESSAGE, recvbuf);
- XFREE(MTYPE_OSPF6_MESSAGE, sendbuf);
- recvbuf = recvnew;
- sendbuf = sendnew;
- iobuflen = size;
+ recvbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size);
+ sendbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size);
+ iobuflen = alloc_size;
+ }
return iobuflen;
}
@@ -1779,7 +1782,6 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
memset(&src, 0, sizeof(src));
memset(&dst, 0, sizeof(dst));
ifindex = 0;
- memset(recvbuf, 0, iobuflen);
iovector[0].iov_base = recvbuf;
iovector[0].iov_len = iobuflen;
iovector[1].iov_base = NULL;
@@ -1795,6 +1797,9 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
return OSPF6_READ_ERROR;
}
+ /* ensure some zeroes past the end, just as a security precaution */
+ memset(recvbuf + len, 0, MIN(128, iobuflen - len));
+
oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
if (oi == NULL || oi->area == NULL
|| CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) {
@@ -2242,7 +2247,6 @@ void ospf6_hello_send(struct thread *thread)
uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
- oi->thread_send_hello = (struct thread *)NULL;
if (oi->state <= OSPF6_INTERFACE_DOWN) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR))
@@ -2340,7 +2344,6 @@ void ospf6_dbdesc_send(struct thread *thread)
struct ospf6_packet *op;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
- on->thread_send_dbdesc = (struct thread *)NULL;
if (on->state < OSPF6_NEIGHBOR_EXSTART) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND))
@@ -2506,7 +2509,6 @@ void ospf6_lsreq_send(struct thread *thread)
uint16_t length = OSPF6_HEADER_SIZE;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
- on->thread_send_lsreq = (struct thread *)NULL;
/* LSReq will be sent only in ExStart or Loading */
if (on->state != OSPF6_NEIGHBOR_EXCHANGE
@@ -2686,7 +2688,6 @@ void ospf6_lsupdate_send_neighbor(struct thread *thread)
int lsa_cnt = 0;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
- on->thread_send_lsupdate = (struct thread *)NULL;
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR))
zlog_debug("LSUpdate to neighbor %s", on->name);
@@ -2822,7 +2823,6 @@ void ospf6_lsupdate_send_interface(struct thread *thread)
int lsa_cnt = 0;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
- oi->thread_send_lsupdate = (struct thread *)NULL;
if (oi->state <= OSPF6_INTERFACE_WAITING) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE,
@@ -2863,7 +2863,6 @@ void ospf6_lsack_send_neighbor(struct thread *thread)
uint16_t length = OSPF6_HEADER_SIZE;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
- on->thread_send_lsack = (struct thread *)NULL;
if (on->state < OSPF6_NEIGHBOR_EXCHANGE) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR))
@@ -2940,7 +2939,6 @@ void ospf6_lsack_send_interface(struct thread *thread)
uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
- oi->thread_send_lsack = (struct thread *)NULL;
if (oi->state <= OSPF6_INTERFACE_WAITING) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR))
diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c
index 8dca0913d3..439b94c9af 100644
--- a/ospf6d/ospf6_neighbor.c
+++ b/ospf6d/ospf6_neighbor.c
@@ -435,8 +435,7 @@ void ospf6_check_nbr_loading(struct ospf6_neighbor *on)
if (on->request_list->count == 0)
thread_add_event(master, loading_done, on, 0, NULL);
else if (on->last_ls_req == NULL) {
- if (on->thread_send_lsreq != NULL)
- THREAD_OFF(on->thread_send_lsreq);
+ THREAD_OFF(on->thread_send_lsreq);
thread_add_event(master, ospf6_lsreq_send, on, 0,
&on->thread_send_lsreq);
}
@@ -481,7 +480,6 @@ void adj_ok(struct thread *thread)
SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT);
THREAD_OFF(on->thread_send_dbdesc);
- on->thread_send_dbdesc = NULL;
thread_add_event(master, ospf6_dbdesc_send, on, 0,
&on->thread_send_dbdesc);
@@ -527,7 +525,6 @@ void seqnumber_mismatch(struct thread *thread)
THREAD_OFF(on->thread_send_dbdesc);
on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */
- on->thread_send_dbdesc = NULL;
thread_add_event(master, ospf6_dbdesc_send, on, 0,
&on->thread_send_dbdesc);
}
@@ -562,7 +559,6 @@ void bad_lsreq(struct thread *thread)
THREAD_OFF(on->thread_send_dbdesc);
on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */
- on->thread_send_dbdesc = NULL;
thread_add_event(master, ospf6_dbdesc_send, on, 0,
&on->thread_send_dbdesc);
@@ -850,15 +846,17 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
timerclear(&res);
- if (on->thread_send_dbdesc)
+ if (thread_is_scheduled(on->thread_send_dbdesc))
timersub(&on->thread_send_dbdesc->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
json_object_int_add(json_neighbor, "pendingLsaDbDescCount",
on->dbdesc_list->count);
json_object_string_add(json_neighbor, "pendingLsaDbDescTime",
duration);
- json_object_string_add(json_neighbor, "dbDescSendThread",
- (on->thread_send_dbdesc ? "on" : "off"));
+ json_object_string_add(
+ json_neighbor, "dbDescSendThread",
+ (thread_is_scheduled(on->thread_send_dbdesc) ? "on"
+ : "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->dbdesc_list, lsa, lsanext))
json_object_array_add(
@@ -867,15 +865,17 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
json_array);
timerclear(&res);
- if (on->thread_send_lsreq)
+ if (thread_is_scheduled(on->thread_send_lsreq))
timersub(&on->thread_send_lsreq->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
json_object_int_add(json_neighbor, "pendingLsaLsReqCount",
on->request_list->count);
json_object_string_add(json_neighbor, "pendingLsaLsReqTime",
duration);
- json_object_string_add(json_neighbor, "lsReqSendThread",
- (on->thread_send_lsreq ? "on" : "off"));
+ json_object_string_add(
+ json_neighbor, "lsReqSendThread",
+ (thread_is_scheduled(on->thread_send_lsreq) ? "on"
+ : "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->request_list, lsa, lsanext))
json_object_array_add(
@@ -885,7 +885,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
timerclear(&res);
- if (on->thread_send_lsupdate)
+ if (thread_is_scheduled(on->thread_send_lsupdate))
timersub(&on->thread_send_lsupdate->u.sands, &now,
&res);
timerstring(&res, duration, sizeof(duration));
@@ -895,7 +895,9 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
duration);
json_object_string_add(
json_neighbor, "lsUpdateSendThread",
- (on->thread_send_lsupdate ? "on" : "off"));
+ (thread_is_scheduled(on->thread_send_lsupdate)
+ ? "on"
+ : "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->lsupdate_list, lsa, lsanext))
json_object_array_add(
@@ -904,15 +906,17 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
json_array);
timerclear(&res);
- if (on->thread_send_lsack)
+ if (thread_is_scheduled(on->thread_send_lsack))
timersub(&on->thread_send_lsack->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
json_object_int_add(json_neighbor, "pendingLsaLsAckCount",
on->lsack_list->count);
json_object_string_add(json_neighbor, "pendingLsaLsAckTime",
duration);
- json_object_string_add(json_neighbor, "lsAckSendThread",
- (on->thread_send_lsack ? "on" : "off"));
+ json_object_string_add(
+ json_neighbor, "lsAckSendThread",
+ (thread_is_scheduled(on->thread_send_lsack) ? "on"
+ : "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->lsack_list, lsa, lsanext))
json_object_array_add(
@@ -1000,47 +1004,52 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
vty_out(vty, " %s\n", lsa->name);
timerclear(&res);
- if (on->thread_send_dbdesc)
+ if (thread_is_scheduled(on->thread_send_dbdesc))
timersub(&on->thread_send_dbdesc->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for DbDesc in Time %s [thread %s]\n",
on->dbdesc_list->count, duration,
- (on->thread_send_dbdesc ? "on" : "off"));
+ (thread_is_scheduled(on->thread_send_dbdesc) ? "on"
+ : "off"));
for (ALL_LSDB(on->dbdesc_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
timerclear(&res);
- if (on->thread_send_lsreq)
+ if (thread_is_scheduled(on->thread_send_lsreq))
timersub(&on->thread_send_lsreq->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for LSReq in Time %s [thread %s]\n",
on->request_list->count, duration,
- (on->thread_send_lsreq ? "on" : "off"));
+ (thread_is_scheduled(on->thread_send_lsreq) ? "on"
+ : "off"));
for (ALL_LSDB(on->request_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
timerclear(&res);
- if (on->thread_send_lsupdate)
+ if (thread_is_scheduled(on->thread_send_lsupdate))
timersub(&on->thread_send_lsupdate->u.sands, &now,
&res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for LSUpdate in Time %s [thread %s]\n",
on->lsupdate_list->count, duration,
- (on->thread_send_lsupdate ? "on" : "off"));
+ (thread_is_scheduled(on->thread_send_lsupdate)
+ ? "on"
+ : "off"));
for (ALL_LSDB(on->lsupdate_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
timerclear(&res);
- if (on->thread_send_lsack)
+ if (thread_is_scheduled(on->thread_send_lsack))
timersub(&on->thread_send_lsack->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for LSAck in Time %s [thread %s]\n",
on->lsack_list->count, duration,
- (on->thread_send_lsack ? "on" : "off"));
+ (thread_is_scheduled(on->thread_send_lsack) ? "on"
+ : "off"));
for (ALL_LSDB(on->lsack_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c
index 7501f49fe0..9a549e2fc7 100644
--- a/ospf6d/ospf6_network.c
+++ b/ospf6d/ospf6_network.c
@@ -173,7 +173,7 @@ int ospf6_sendmsg(struct in6_addr *src, struct in6_addr *dst,
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
scmsgp = (struct cmsghdr *)&cmsgbuf;
pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
- memset(&dst_sin6, 0, sizeof(struct sockaddr_in6));
+ memset(&dst_sin6, 0, sizeof(dst_sin6));
/* source address */
pktinfo->ipi6_ifindex = ifindex;
@@ -226,7 +226,7 @@ int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst,
rcmsgp = (struct cmsghdr *)cmsgbuf;
pktinfo = (struct in6_pktinfo *)(CMSG_DATA(rcmsgp));
- memset(&src_sin6, 0, sizeof(struct sockaddr_in6));
+ memset(&src_sin6, 0, sizeof(src_sin6));
/* receive control msg */
rcmsgp->cmsg_level = IPPROTO_IPV6;
diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c
index 53b45d6ca3..b1bff69f06 100644
--- a/ospf6d/ospf6_nssa.c
+++ b/ospf6d/ospf6_nssa.c
@@ -1007,7 +1007,7 @@ static void ospf6_abr_task_timer(struct thread *thread)
void ospf6_schedule_abr_task(struct ospf6 *ospf6)
{
- if (ospf6->t_abr_task) {
+ if (thread_is_scheduled(ospf6->t_abr_task)) {
if (IS_OSPF6_DEBUG_ABR)
zlog_debug("ABR task already scheduled");
return;
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 3c74ca55c1..8e964393f1 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -1550,7 +1550,7 @@ int ospf6_route_table_show(struct vty *vty, int argc_start, int argc,
int arg_end = use_json ? (argc - 1) : argc;
json_object *json = NULL;
- memset(&prefix, 0, sizeof(struct prefix));
+ memset(&prefix, 0, sizeof(prefix));
if (use_json)
json = json_object_new_object();
@@ -1723,9 +1723,9 @@ int ospf6_linkstate_table_show(struct vty *vty, int idx_ipv4, int argc,
int i, ret;
struct prefix router, id, prefix;
- memset(&router, 0, sizeof(struct prefix));
- memset(&id, 0, sizeof(struct prefix));
- memset(&prefix, 0, sizeof(struct prefix));
+ memset(&router, 0, sizeof(router));
+ memset(&id, 0, sizeof(id));
+ memset(&prefix, 0, sizeof(prefix));
for (i = idx_ipv4; i < argc; i++) {
if (strmatch(argv[i]->text, "detail")) {
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 2e8e9f2cdd..aac371ebbb 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -615,7 +615,6 @@ static void ospf6_spf_calculation_thread(struct thread *t)
char rbuf[32];
ospf6 = (struct ospf6 *)THREAD_ARG(t);
- ospf6->t_spf_calc = NULL;
/* execute SPF calculation */
monotime(&start);
@@ -703,7 +702,7 @@ void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason)
}
/* SPF calculation timer is already scheduled. */
- if (ospf6->t_spf_calc) {
+ if (thread_is_scheduled(ospf6->t_spf_calc)) {
if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME))
zlog_debug(
"SPF: calculation timer is already scheduled: %p",
@@ -740,7 +739,7 @@ void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason)
if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME))
zlog_debug("SPF: Rescheduling in %ld msec", delay);
- ospf6->t_spf_calc = NULL;
+ THREAD_OFF(ospf6->t_spf_calc);
thread_add_timer_msec(master, ospf6_spf_calculation_thread, ospf6,
delay, &ospf6->t_spf_calc);
}
@@ -1253,7 +1252,6 @@ static void ospf6_ase_calculate_timer(struct thread *t)
uint16_t type;
ospf6 = THREAD_ARG(t);
- ospf6->t_ase_calc = NULL;
/* Calculate external route for each AS-external-LSA */
type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index 2a6d7cd099..d48e85cedb 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -579,7 +579,7 @@ static void ospf6_disable(struct ospf6 *o)
void ospf6_master_init(struct thread_master *master)
{
- memset(&ospf6_master, 0, sizeof(struct ospf6_master));
+ memset(&ospf6_master, 0, sizeof(ospf6_master));
om6 = &ospf6_master;
om6->ospf6 = list_new();
@@ -1372,9 +1372,7 @@ static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json,
} else
json_object_boolean_false_add(json, "spfHasRun");
-
- threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf));
- if (o->t_spf_calc) {
+ if (thread_is_scheduled(o->t_spf_calc)) {
long time_store;
json_object_boolean_true_add(json, "spfTimerActive");
@@ -1467,7 +1465,9 @@ static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json,
threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf));
vty_out(vty, " SPF timer %s%s\n",
- (o->t_spf_calc ? "due in " : "is "), buf);
+ (thread_is_scheduled(o->t_spf_calc) ? "due in "
+ : "is "),
+ buf);
if (CHECK_FLAG(o->flag, OSPF6_STUB_ROUTER))
vty_out(vty, " Router Is Stub Router\n");
@@ -1756,14 +1756,10 @@ DEFUN(show_ipv6_ospf6_route_type_detail, show_ipv6_ospf6_route_type_detail_cmd,
bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p)
{
- struct in6_addr addr_zero;
-
- memset(&addr_zero, 0, sizeof(struct in6_addr));
-
- /* Default prefix validation*/
- if ((is_default_prefix((struct prefix *)p))
- || (!memcmp(&p->u.prefix6, &addr_zero, sizeof(struct in6_addr)))) {
- vty_out(vty, "Default address should not be configured as summary address.\n");
+ /* Default prefix validation*/
+ if (is_default_prefix(p)) {
+ vty_out(vty,
+ "Default address should not be configured as summary address.\n");
return false;
}
@@ -1802,7 +1798,7 @@ DEFPY (ospf6_external_route_aggregation,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!ospf6_is_valid_summary_addr(vty, &p))
return CMD_WARNING_CONFIG_FAILED;
@@ -1850,7 +1846,7 @@ DEFPY(no_ospf6_external_route_aggregation,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!ospf6_is_valid_summary_addr(vty, &p))
return CMD_WARNING_CONFIG_FAILED;
@@ -1881,7 +1877,7 @@ DEFPY (ospf6_external_route_aggregation_no_advertise,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!ospf6_is_valid_summary_addr(vty, &p))
return CMD_WARNING_CONFIG_FAILED;
@@ -1913,7 +1909,7 @@ DEFPY (no_ospf6_external_route_aggregation_no_advertise,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!ospf6_is_valid_summary_addr(vty, &p))
return CMD_WARNING_CONFIG_FAILED;
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 18b1959b9b..8366716585 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -241,7 +241,7 @@ static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command,
if (!zclient || zclient->sock < 0 || !ospf6)
return 1;
- memset(&api, 0, sizeof(struct zapi_cap));
+ memset(&api, 0, sizeof(api));
api.cap = command;
api.stale_removal_time = stale_time;
api.vrf_id = ospf6->vrf_id;
diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c
index 1908604bd9..e84c6f5b3c 100644
--- a/ospfclient/ospf_apiclient.c
+++ b/ospfclient/ospf_apiclient.c
@@ -124,7 +124,7 @@ struct ospf_apiclient *ospf_apiclient_connect(char *host, int syncport)
/* Prepare socket for asynchronous messages */
/* Initialize async address structure */
- memset(&myaddr_async, 0, sizeof(struct sockaddr_in));
+ memset(&myaddr_async, 0, sizeof(myaddr_async));
myaddr_async.sin_family = AF_INET;
myaddr_async.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr_async.sin_port = htons(syncport + 1);
@@ -219,7 +219,7 @@ struct ospf_apiclient *ospf_apiclient_connect(char *host, int syncport)
want the sync port number on a fixed port number. The reverse
async channel will be at this port+1 */
- memset(&myaddr_sync, 0, sizeof(struct sockaddr_in));
+ memset(&myaddr_sync, 0, sizeof(myaddr_sync));
myaddr_sync.sin_family = AF_INET;
myaddr_sync.sin_port = htons(syncport);
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
diff --git a/ospfclient/ospfclient.py b/ospfclient/ospfclient.py
new file mode 100755
index 0000000000..be8b51f007
--- /dev/null
+++ b/ospfclient/ospfclient.py
@@ -0,0 +1,1133 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# December 22 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+# 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
+#
+
+import argparse
+import asyncio
+import errno
+import logging
+import socket
+import struct
+import sys
+from asyncio import Event, Lock
+from ipaddress import ip_address as ip
+
+FMT_APIMSGHDR = ">BBHL"
+FMT_APIMSGHDR_SIZE = struct.calcsize(FMT_APIMSGHDR)
+
+FMT_LSA_FILTER = ">HBB" # + plus x"I" areas
+LSAF_ORIGIN_NON_SELF = 0
+LSAF_ORIGIN_SELF = 1
+LSAF_ORIGIN_ANY = 2
+
+FMT_LSA_HEADER = ">HBBIILHH"
+FMT_LSA_HEADER_SIZE = struct.calcsize(FMT_LSA_HEADER)
+
+# ------------------------
+# Messages to OSPF daemon.
+# ------------------------
+
+MSG_REGISTER_OPAQUETYPE = 1
+MSG_UNREGISTER_OPAQUETYPE = 2
+MSG_REGISTER_EVENT = 3
+MSG_SYNC_LSDB = 4
+MSG_ORIGINATE_REQUEST = 5
+MSG_DELETE_REQUEST = 6
+MSG_SYNC_REACHABLE = 7
+MSG_SYNC_ISM = 8
+MSG_SYNC_NSM = 9
+
+smsg_info = {
+ MSG_REGISTER_OPAQUETYPE: ("REGISTER_OPAQUETYPE", "BBxx"),
+ MSG_UNREGISTER_OPAQUETYPE: ("UNREGISTER_OPAQUETYPE", "BBxx"),
+ MSG_REGISTER_EVENT: ("REGISTER_EVENT", FMT_LSA_FILTER),
+ MSG_SYNC_LSDB: ("SYNC_LSDB", FMT_LSA_FILTER),
+ MSG_ORIGINATE_REQUEST: ("ORIGINATE_REQUEST", ">II" + FMT_LSA_HEADER[1:]),
+ MSG_DELETE_REQUEST: ("DELETE_REQUEST", ">IBBxxL"),
+ MSG_SYNC_REACHABLE: ("MSG_SYNC_REACHABLE", ""),
+ MSG_SYNC_ISM: ("MSG_SYNC_ISM", ""),
+ MSG_SYNC_NSM: ("MSG_SYNC_NSM", ""),
+}
+
+# --------------------------
+# Messages from OSPF daemon.
+# --------------------------
+
+MSG_REPLY = 10
+MSG_READY_NOTIFY = 11
+MSG_LSA_UPDATE_NOTIFY = 12
+MSG_LSA_DELETE_NOTIFY = 13
+MSG_NEW_IF = 14
+MSG_DEL_IF = 15
+MSG_ISM_CHANGE = 16
+MSG_NSM_CHANGE = 17
+MSG_REACHABLE_CHANGE = 18
+
+amsg_info = {
+ MSG_REPLY: ("REPLY", "bxxx"),
+ MSG_READY_NOTIFY: ("READY_NOTIFY", ">BBxxI"),
+ MSG_LSA_UPDATE_NOTIFY: ("LSA_UPDATE_NOTIFY", ">IIBxxx" + FMT_LSA_HEADER[1:]),
+ MSG_LSA_DELETE_NOTIFY: ("LSA_DELETE_NOTIFY", ">IIBxxx" + FMT_LSA_HEADER[1:]),
+ MSG_NEW_IF: ("NEW_IF", ">II"),
+ MSG_DEL_IF: ("DEL_IF", ">I"),
+ MSG_ISM_CHANGE: ("ISM_CHANGE", ">IIBxxx"),
+ MSG_NSM_CHANGE: ("NSM_CHANGE", ">IIIBxxx"),
+ MSG_REACHABLE_CHANGE: ("REACHABLE_CHANGE", ">HH"),
+}
+
+OSPF_API_OK = 0
+OSPF_API_NOSUCHINTERFACE = -1
+OSPF_API_NOSUCHAREA = -2
+OSPF_API_NOSUCHLSA = -3
+OSPF_API_ILLEGALLSATYPE = -4
+OSPF_API_OPAQUETYPEINUSE = -5
+OSPF_API_OPAQUETYPENOTREGISTERED = -6
+OSPF_API_NOTREADY = -7
+OSPF_API_NOMEMORY = -8
+OSPF_API_ERROR = -9
+OSPF_API_UNDEF = -10
+
+msg_errname = {
+ OSPF_API_OK: "OSPF_API_OK",
+ OSPF_API_NOSUCHINTERFACE: "OSPF_API_NOSUCHINTERFACE",
+ OSPF_API_NOSUCHAREA: "OSPF_API_NOSUCHAREA",
+ OSPF_API_NOSUCHLSA: "OSPF_API_NOSUCHLSA",
+ OSPF_API_ILLEGALLSATYPE: "OSPF_API_ILLEGALLSATYPE",
+ OSPF_API_OPAQUETYPEINUSE: "OSPF_API_OPAQUETYPEINUSE",
+ OSPF_API_OPAQUETYPENOTREGISTERED: "OSPF_API_OPAQUETYPENOTREGISTERED",
+ OSPF_API_NOTREADY: "OSPF_API_NOTREADY",
+ OSPF_API_NOMEMORY: "OSPF_API_NOMEMORY",
+ OSPF_API_ERROR: "OSPF_API_ERROR",
+ OSPF_API_UNDEF: "OSPF_API_UNDEF",
+}
+
+# msg_info = {**smsg_info, **amsg_info}
+msg_info = {}
+msg_info.update(smsg_info)
+msg_info.update(amsg_info)
+msg_name = {k: v[0] for k, v in msg_info.items()}
+msg_fmt = {k: v[1] for k, v in msg_info.items()}
+msg_size = {k: struct.calcsize(v) for k, v in msg_fmt.items()}
+
+
+def api_msgname(mt):
+ return msg_name.get(mt, str(mt))
+
+
+def api_errname(ecode):
+ return msg_errname.get(ecode, str(ecode))
+
+
+# -------------------
+# API Semantic Errors
+# -------------------
+
+
+class APIError(Exception):
+ pass
+
+
+class MsgTypeError(Exception):
+ pass
+
+
+class SeqNumError(Exception):
+ pass
+
+
+# ---------
+# LSA Types
+# ---------
+
+LSA_TYPE_UNKNOWN = 0
+LSA_TYPE_ROUTER = 1
+LSA_TYPE_NETWORK = 2
+LSA_TYPE_SUMMARY = 3
+LSA_TYPE_ASBR_SUMMARY = 4
+LSA_TYPE_AS_EXTERNAL = 5
+LSA_TYPE_GROUP_MEMBER = 6
+LSA_TYPE_AS_NSSA = 7
+LSA_TYPE_EXTERNAL_ATTRIBUTES = 8
+LSA_TYPE_OPAQUE_LINK = 9
+LSA_TYPE_OPAQUE_AREA = 10
+LSA_TYPE_OPAQUE_AS = 11
+
+
+def lsa_typename(lsa_type):
+ names = {
+ LSA_TYPE_ROUTER: "LSA:ROUTER",
+ LSA_TYPE_NETWORK: "LSA:NETWORK",
+ LSA_TYPE_SUMMARY: "LSA:SUMMARY",
+ LSA_TYPE_ASBR_SUMMARY: "LSA:ASBR_SUMMARY",
+ LSA_TYPE_AS_EXTERNAL: "LSA:AS_EXTERNAL",
+ LSA_TYPE_GROUP_MEMBER: "LSA:GROUP_MEMBER",
+ LSA_TYPE_AS_NSSA: "LSA:AS_NSSA",
+ LSA_TYPE_EXTERNAL_ATTRIBUTES: "LSA:EXTERNAL_ATTRIBUTES",
+ LSA_TYPE_OPAQUE_LINK: "LSA:OPAQUE_LINK",
+ LSA_TYPE_OPAQUE_AREA: "LSA:OPAQUE_AREA",
+ LSA_TYPE_OPAQUE_AS: "LSA:OPAQUE_AS",
+ }
+ return names.get(lsa_type, str(lsa_type))
+
+
+# ------------------------------
+# Interface State Machine States
+# ------------------------------
+
+ISM_DEPENDUPON = 0
+ISM_DOWN = 1
+ISM_LOOPBACK = 2
+ISM_WAITING = 3
+ISM_POINTTOPOINT = 4
+ISM_DROTHER = 5
+ISM_BACKUP = 6
+ISM_DR = 7
+
+
+def ism_name(state):
+ names = {
+ ISM_DEPENDUPON: "ISM_DEPENDUPON",
+ ISM_DOWN: "ISM_DOWN",
+ ISM_LOOPBACK: "ISM_LOOPBACK",
+ ISM_WAITING: "ISM_WAITING",
+ ISM_POINTTOPOINT: "ISM_POINTTOPOINT",
+ ISM_DROTHER: "ISM_DROTHER",
+ ISM_BACKUP: "ISM_BACKUP",
+ ISM_DR: "ISM_DR",
+ }
+ return names.get(state, str(state))
+
+
+# -----------------------------
+# Neighbor State Machine States
+# -----------------------------
+
+NSM_DEPENDUPON = 0
+NSM_DELETED = 1
+NSM_DOWN = 2
+NSM_ATTEMPT = 3
+NSM_INIT = 4
+NSM_TWOWAY = 5
+NSM_EXSTART = 6
+NSM_EXCHANGE = 7
+NSM_LOADING = 8
+NSM_FULL = 9
+
+
+def nsm_name(state):
+ names = {
+ NSM_DEPENDUPON: "NSM_DEPENDUPON",
+ NSM_DELETED: "NSM_DELETED",
+ NSM_DOWN: "NSM_DOWN",
+ NSM_ATTEMPT: "NSM_ATTEMPT",
+ NSM_INIT: "NSM_INIT",
+ NSM_TWOWAY: "NSM_TWOWAY",
+ NSM_EXSTART: "NSM_EXSTART",
+ NSM_EXCHANGE: "NSM_EXCHANGE",
+ NSM_LOADING: "NSM_LOADING",
+ NSM_FULL: "NSM_FULL",
+ }
+ return names.get(state, str(state))
+
+
+# --------------
+# Client Classes
+# --------------
+
+
+class OspfApiClient:
+ def __str__(self):
+ return "OspfApiClient({})".format(self.server)
+
+ @staticmethod
+ def _get_bound_sockets(port):
+ s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ try:
+ s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ # s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ s1.bind(("", port))
+ s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ try:
+ s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ # s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ s2.bind(("", port + 1))
+ return s1, s2
+ except Exception:
+ s2.close()
+ raise
+ except Exception:
+ s1.close()
+ raise
+
+ def __init__(self, server="localhost", handlers=None):
+ """A client connection to OSPF Daemon using the OSPF API
+
+ The client object is not created in a connected state. To connect to the server
+ the `connect` method should be called. If an error is encountered when sending
+ messages to the server an exception will be raised and the connection will be
+ closed. When this happens `connect` may be called again to restore the
+ connection.
+
+ Args:
+ server: hostname or IP address of server default is "localhost"
+ handlers: dict of message handlers, the key is the API message
+ type, the value is a function. The functions signature is:
+ `handler(msg_type, msg, msg_extra, *params)`, where `msg` is the
+ message data after the API header, `*params` will be the
+ unpacked message values, and msg_extra are any bytes beyond the
+ fixed parameters of the message.
+ Raises:
+ Will raise exceptions for failures with various `socket` modules
+ functions such as `socket.socket`, `socket.setsockopt`, `socket.bind`.
+ """
+ self._seq = 0
+ self._s = None
+ self._as = None
+ self._ls = None
+ self._ar = self._r = self._w = None
+ self.server = server
+ self.handlers = handlers if handlers is not None else dict()
+ self.write_lock = Lock()
+
+ # try and get consecutive 2 ports
+ PORTSTART = 49152
+ PORTEND = 65534
+ for port in range(PORTSTART, PORTEND + 2, 2):
+ try:
+ logging.debug("%s: binding to ports %s, %s", self, port, port + 1)
+ self._s, self._ls = self._get_bound_sockets(port)
+ break
+ except OSError as error:
+ if error.errno != errno.EADDRINUSE or port == PORTEND:
+ logging.warning("%s: binding port %s error %s", self, port, error)
+ raise
+ logging.debug("%s: ports %s, %s in use.", self, port, port + 1)
+ else:
+ assert False, "Should not reach this code execution point"
+
+ async def _connect_locked(self):
+ logging.debug("%s: connect to OSPF API", self)
+
+ loop = asyncio.get_event_loop()
+
+ self._ls.listen()
+ try:
+ logging.debug("%s: connecting sync socket to server", self)
+ await loop.sock_connect(self._s, (self.server, 2607))
+
+ logging.debug("%s: accepting connect from server", self)
+ self._as, _ = await loop.sock_accept(self._ls)
+ except Exception:
+ await self._close_locked()
+ raise
+
+ logging.debug("%s: success", self)
+ self._r, self._w = await asyncio.open_connection(sock=self._s)
+ self._ar, _ = await asyncio.open_connection(sock=self._as)
+ self._seq = 1
+
+ async def connect(self):
+ async with self.write_lock:
+ await self._connect_locked()
+
+ @property
+ def closed(self):
+ "True if the connection is closed."
+ return self._seq == 0
+
+ async def _close_locked(self):
+ logging.debug("%s: closing", self)
+ if self._s:
+ if self._w:
+ self._w.close()
+ await self._w.wait_closed()
+ self._w = None
+ else:
+ self._s.close()
+ self._s = None
+ self._r = None
+ assert self._w is None
+ if self._as:
+ self._as.close()
+ self._as = None
+ self._ar = None
+ if self._ls:
+ self._ls.close()
+ self._ls = None
+ self._seq = 0
+
+ async def close(self):
+ async with self.write_lock:
+ await self._close_locked()
+
+ @staticmethod
+ async def _msg_read(r, expseq=-1):
+ """Read an OSPF API message from the socket `r`
+
+ Args:
+ r: socket to read msg from
+ expseq: sequence number to expect or -1 for any.
+ Raises:
+ Will raise exceptions for failures with various `socket` modules,
+ Additionally may raise SeqNumError if unexpected seqnum is received.
+ """
+ try:
+ mh = await r.readexactly(FMT_APIMSGHDR_SIZE)
+ v, mt, l, seq = struct.unpack(FMT_APIMSGHDR, mh)
+ if v != 1:
+ raise Exception("received unexpected OSPF API version {}".format(v))
+ if expseq == -1:
+ logging.debug("_msg_read: got seq: 0x%x on async read", seq)
+ elif seq != expseq:
+ raise SeqNumError("rx {} != {}".format(seq, expseq))
+ msg = await r.readexactly(l) if l else b""
+ return mt, msg
+ except asyncio.IncompleteReadError:
+ raise EOFError
+
+ async def msg_read(self):
+ """Read a message from the async notify channel.
+
+ Raises:
+ May raise exceptions for failures with various `socket` modules.
+ """
+ return await OspfApiClient._msg_read(self._ar, -1)
+
+ async def msg_send(self, mt, mp):
+ """Send a message to OSPF API and wait for error code reply.
+
+ Args:
+ mt: the messaage type
+ mp: the message payload
+ Returns:
+ error: an OSPF_API_XXX error code, 0 for OK.
+ Raises:
+ Raises SeqNumError if the synchronous reply is the wrong sequence number;
+ MsgTypeError if the synchronous reply is not MSG_REPLY. Also,
+ may raise exceptions for failures with various `socket` modules,
+
+ The connection will be closed.
+ """
+ logging.debug("SEND: %s: sending %s seq 0x%x", self, api_msgname(mt), self._seq)
+ mh = struct.pack(FMT_APIMSGHDR, 1, mt, len(mp), self._seq)
+
+ seq = self._seq
+ self._seq = seq + 1
+
+ try:
+ async with self.write_lock:
+ self._w.write(mh + mp)
+ await self._w.drain()
+ mt, mp = await OspfApiClient._msg_read(self._r, seq)
+
+ if mt != MSG_REPLY:
+ raise MsgTypeError(
+ "rx {} != {}".format(api_msgname(mt), api_msgname(MSG_REPLY))
+ )
+
+ return struct.unpack(msg_fmt[MSG_REPLY], mp)[0]
+ except Exception:
+ # We've written data with a sequence number
+ await self.close()
+ raise
+
+ async def msg_send_raises(self, mt, mp=b"\x00" * 4):
+ """Send a message to OSPF API and wait for error code reply.
+
+ Args:
+ mt: the messaage type
+ mp: the message payload
+ Raises:
+ APIError if the server replies with an error.
+
+ Also may raise exceptions for failures with various `socket` modules,
+ as well as MsgTypeError if the synchronous reply is incorrect.
+ The connection will be closed for these non-API error exceptions.
+ """
+ ecode = await self.msg_send(mt, mp)
+ if ecode:
+ raise APIError("{} error {}".format(api_msgname(mt), api_errname(ecode)))
+
+ async def handle_async_msg(self, mt, msg):
+ if mt not in msg_fmt:
+ logging.debug("RECV: %s: unknown async msg type %s", self, mt)
+ return
+
+ fmt = msg_fmt[mt]
+ sz = msg_size[mt]
+ tup = struct.unpack(fmt, msg[:sz])
+ extra = msg[sz:]
+
+ if mt not in self.handlers:
+ logging.debug(
+ "RECV: %s: no handlers for msg type %s", self, api_msgname(mt)
+ )
+ return
+
+ logging.debug("RECV: %s: calling handler for %s", self, api_msgname(mt))
+ await self.handlers[mt](mt, msg, extra, *tup)
+
+ #
+ # Client to Server Messaging
+ #
+ @staticmethod
+ def lsa_type_mask(*lsa_types):
+ "Return a 16 bit mask for each LSA type passed."
+ if not lsa_types:
+ return 0xFFFF
+ mask = 0
+ for t in lsa_types:
+ assert 0 < t < 16, "LSA type {} out of range [1, 15]".format(t)
+ mask |= 1 << t
+ return mask
+
+ @staticmethod
+ def lsa_filter(origin, areas, lsa_types):
+ """Return an LSA filter.
+
+ Return the filter message bytes based on `origin` the `areas` list and the LSAs
+ types in the `lsa_types` list.
+ """
+ mask = OspfApiClient.lsa_type_mask(*lsa_types)
+ narea = len(areas)
+ fmt = FMT_LSA_FILTER + ("{}I".format(narea) if narea else "")
+ # lsa type mask, origin, number of areas, each area
+ return struct.pack(fmt, mask, origin, narea, *areas)
+
+ async def req_lsdb_sync(self):
+ "Register for all LSA notifications and request an LSDB synchronoization."
+ logging.debug("SEND: %s: request LSDB events", self)
+ mp = OspfApiClient.lsa_filter(LSAF_ORIGIN_ANY, [], [])
+ await self.msg_send_raises(MSG_REGISTER_EVENT, mp)
+
+ logging.debug("SEND: %s: request LSDB sync", self)
+ await self.msg_send_raises(MSG_SYNC_LSDB, mp)
+
+ async def req_reachable_routers(self):
+ "Request a dump of all reachable routers."
+ logging.debug("SEND: %s: request reachable changes", self)
+ await self.msg_send_raises(MSG_SYNC_REACHABLE)
+
+ async def req_ism_states(self):
+ "Request a dump of the current ISM states of all interfaces."
+ logging.debug("SEND: %s: request ISM changes", self)
+ await self.msg_send_raises(MSG_SYNC_ISM)
+
+ async def req_nsm_states(self):
+ "Request a dump of the current NSM states of all neighbors."
+ logging.debug("SEND: %s: request NSM changes", self)
+ await self.msg_send_raises(MSG_SYNC_NSM)
+
+
+class OspfOpaqueClient(OspfApiClient):
+ """A client connection to OSPF Daemon for manipulating Opaque LSA data.
+
+ The client object is not created in a connected state. To connect to the server
+ the `connect` method should be called. If an error is encountered when sending
+ messages to the server an exception will be raised and the connection will be
+ closed. When this happens `connect` may be called again to restore the
+ connection.
+
+ Args:
+ server: hostname or IP address of server default is "localhost"
+
+ Raises:
+ Will raise exceptions for failures with various `socket` modules
+ functions such as `socket.socket`, `socket.setsockopt`, `socket.bind`.
+ """
+
+ def __init__(self, server="localhost"):
+ handlers = {
+ MSG_READY_NOTIFY: self._ready_msg,
+ MSG_LSA_UPDATE_NOTIFY: self._lsa_change_msg,
+ MSG_LSA_DELETE_NOTIFY: self._lsa_change_msg,
+ MSG_NEW_IF: self._if_msg,
+ MSG_DEL_IF: self._if_msg,
+ MSG_ISM_CHANGE: self._if_change_msg,
+ MSG_NSM_CHANGE: self._nbr_change_msg,
+ MSG_REACHABLE_CHANGE: self._reachable_msg,
+ }
+ super().__init__(server, handlers)
+
+ self.ready_lock = Lock()
+ self.ready_cond = {
+ LSA_TYPE_OPAQUE_LINK: {},
+ LSA_TYPE_OPAQUE_AREA: {},
+ LSA_TYPE_OPAQUE_AS: {},
+ }
+ self.lsid_seq_num = {}
+
+ self.lsa_change_cb = None
+ self.opaque_change_cb = {}
+
+ self.reachable_routers = set()
+ self.reachable_change_cb = None
+
+ self.if_area = {}
+ self.ism_states = {}
+ self.ism_change_cb = None
+
+ self.nsm_states = {}
+ self.nsm_change_cb = None
+
+ async def _register_opaque_data(self, lsa_type, otype):
+ async with self.ready_lock:
+ cond = self.ready_cond[lsa_type].get(otype)
+ assert cond is None, "multiple registers for {} opaque-type {}".format(
+ lsa_typename(lsa_type), otype
+ )
+
+ logging.debug("register %s opaque-type %s", lsa_typename(lsa_type), otype)
+
+ mt = MSG_REGISTER_OPAQUETYPE
+ mp = struct.pack(msg_fmt[mt], lsa_type, otype)
+ await self.msg_send_raises(mt, mp)
+
+ async def _assure_opaque_ready(self, lsa_type, otype):
+ async with self.ready_lock:
+ if self.ready_cond[lsa_type].get(otype) is True:
+ return
+
+ await self._register_opaque_data(lsa_type, otype)
+ await self.wait_opaque_ready(lsa_type, otype)
+
+ async def _handle_msg_loop(self):
+ try:
+ logging.debug("entering async msg handling loop")
+ while True:
+ mt, msg = await self.msg_read()
+ if mt in amsg_info:
+ await self.handle_async_msg(mt, msg)
+ else:
+ mts = api_msgname(mt)
+ logging.warning(
+ "ignoring unexpected msg: %s len: %s", mts, len(msg)
+ )
+ except EOFError:
+ logging.info("Got EOF from OSPF API server on async notify socket")
+ return 2
+
+ @staticmethod
+ def _opaque_args(lsa_type, otype, oid, mp):
+ lsid = (otype << 24) | oid
+ return 0, 0, lsa_type, lsid, 0, 0, 0, FMT_LSA_HEADER_SIZE + len(mp)
+
+ @staticmethod
+ def _make_opaque_lsa(lsa_type, otype, oid, mp):
+ # /* Make a new LSA from parameters */
+ lsa = struct.pack(
+ FMT_LSA_HEADER, *OspfOpaqueClient._opaque_args(lsa_type, otype, oid, mp)
+ )
+ lsa += mp
+ return lsa
+
+ async def _ready_msg(self, mt, msg, extra, lsa_type, otype, addr):
+ if lsa_type == LSA_TYPE_OPAQUE_LINK:
+ e = "ifaddr {}".format(ip(addr))
+ elif lsa_type == LSA_TYPE_OPAQUE_AREA:
+ e = "area {}".format(ip(addr))
+ else:
+ e = ""
+ logging.info(
+ "RECV: %s ready notify for %s opaque-type %s%s",
+ self,
+ lsa_typename(lsa_type),
+ otype,
+ e,
+ )
+
+ # Signal all waiting senders they can send now.
+ async with self.ready_lock:
+ cond = self.ready_cond[lsa_type].get(otype)
+ self.ready_cond[lsa_type][otype] = True
+
+ if cond is True:
+ logging.warning(
+ "RECV: dup ready received for %s opaque-type %s",
+ lsa_typename(lsa_type),
+ otype,
+ )
+ elif cond:
+ for evt in cond:
+ evt.set()
+
+ async def _if_msg(self, mt, msg, extra, *args):
+ if mt == MSG_NEW_IF:
+ ifaddr, aid = args
+ else:
+ assert mt == MSG_DEL_IF
+ ifaddr, aid = args[0], 0
+ logging.info(
+ "RECV: %s ifaddr %s areaid %s", api_msgname(mt), ip(ifaddr), ip(aid)
+ )
+
+ async def _if_change_msg(self, mt, msg, extra, ifaddr, aid, state):
+ ifaddr = ip(ifaddr)
+ aid = ip(aid)
+
+ logging.info(
+ "RECV: %s ifaddr %s areaid %s state %s",
+ api_msgname(mt),
+ ifaddr,
+ aid,
+ ism_name(state),
+ )
+
+ self.if_area[ifaddr] = aid
+ self.ism_states[ifaddr] = state
+
+ if self.ism_change_cb:
+ self.ism_change_cb(ifaddr, aid, state)
+
+ async def _nbr_change_msg(self, mt, msg, extra, ifaddr, nbraddr, router_id, state):
+ ifaddr = ip(ifaddr)
+ nbraddr = ip(nbraddr)
+ router_id = ip(router_id)
+
+ logging.info(
+ "RECV: %s ifaddr %s nbraddr %s router_id %s state %s",
+ api_msgname(mt),
+ ifaddr,
+ nbraddr,
+ router_id,
+ nsm_name(state),
+ )
+
+ if ifaddr not in self.nsm_states:
+ self.nsm_states[ifaddr] = {}
+ self.nsm_states[ifaddr][(nbraddr, router_id)] = state
+
+ if self.nsm_change_cb:
+ self.nsm_change_cb(ifaddr, nbraddr, router_id, state)
+
+ async def _lsa_change_msg(self, mt, msg, extra, ifaddr, aid, is_self, *ls_header):
+ (
+ lsa_age, # ls_age,
+ _, # ls_options,
+ lsa_type,
+ ls_id,
+ _, # ls_adv_router,
+ ls_seq,
+ _, # ls_cksum,
+ ls_len,
+ ) = ls_header
+
+ otype = (ls_id >> 24) & 0xFF
+
+ if mt == MSG_LSA_UPDATE_NOTIFY:
+ ts = "update"
+ else:
+ assert mt == MSG_LSA_DELETE_NOTIFY
+ ts = "delete"
+
+ logging.info(
+ "RECV: LSA %s msg for LSA %s in area %s seq 0x%x len %s age %s",
+ ts,
+ ip(ls_id),
+ ip(aid),
+ ls_seq,
+ ls_len,
+ lsa_age,
+ )
+ idx = (lsa_type, otype)
+
+ pre_lsa_size = msg_size[mt] - FMT_LSA_HEADER_SIZE
+ lsa = msg[pre_lsa_size:]
+
+ if idx in self.opaque_change_cb:
+ self.opaque_change_cb[idx](mt, ifaddr, aid, ls_header, extra, lsa)
+
+ if self.lsa_change_cb:
+ self.lsa_change_cb(mt, ifaddr, aid, ls_header, extra, lsa)
+
+ async def _reachable_msg(self, mt, msg, extra, nadd, nremove):
+ router_ids = struct.unpack(">{}I".format(nadd + nremove), extra)
+ router_ids = [ip(x) for x in router_ids]
+ logging.info(
+ "RECV: %s added %s removed %s",
+ api_msgname(mt),
+ router_ids[:nadd],
+ router_ids[nadd:],
+ )
+ self.reachable_routers |= set(router_ids[:nadd])
+ self.reachable_routers -= set(router_ids[nadd:])
+ logging.info("RECV: %s new set %s", api_msgname(mt), self.reachable_routers)
+
+ if self.reachable_change_cb:
+ logging.info("RECV: %s calling callback", api_msgname(mt))
+ await self.reachable_change_cb(router_ids[:nadd], router_ids[nadd:])
+
+ async def add_opaque_data(self, addr, lsa_type, otype, oid, data):
+ """Add an instance of opaque data.
+
+ Add an instance of opaque data. This call will register for the given
+ LSA and opaque type if not already done.
+
+ Args:
+ addr: depends on lsa_type, LINK => ifaddr, AREA => area ID, AS => ignored
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type
+ oid: (3 octets) ID of this opaque data
+ data: the opaque data
+ Raises:
+ See `msg_send_raises`
+ """
+
+ if lsa_type == LSA_TYPE_OPAQUE_LINK:
+ ifaddr, aid = int(addr), 0
+ elif lsa_type == LSA_TYPE_OPAQUE_AREA:
+ ifaddr, aid = 0, int(addr)
+ else:
+ assert lsa_type == LSA_TYPE_OPAQUE_AS
+ ifaddr, aid = 0, 0
+
+ mt = MSG_ORIGINATE_REQUEST
+ msg = struct.pack(
+ msg_fmt[mt],
+ ifaddr,
+ aid,
+ *OspfOpaqueClient._opaque_args(lsa_type, otype, oid, data),
+ )
+ msg += data
+ await self._assure_opaque_ready(lsa_type, otype)
+ await self.msg_send_raises(mt, msg)
+
+ async def delete_opaque_data(self, addr, lsa_type, otype, oid):
+ """Delete an instance of opaque data.
+
+ Delete an instance of opaque data. This call will register for the given
+ LSA and opaque type if not already done.
+
+ Args:
+ addr: depends on lsa_type, LINK => ifaddr, AREA => area ID, AS => ignored
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ oid: (3 octets) ID of this opaque data
+ Raises:
+ See `msg_send_raises`
+ """
+ if (lsa_type, otype) in self.opaque_change_cb:
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ mt = MSG_DELETE_REQUEST
+ await self._assure_opaque_ready(lsa_type, otype)
+ mp = struct.pack(msg_fmt[mt], int(addr), lsa_type, otype, oid)
+ await self.msg_send_raises(mt, mp)
+
+ async def register_opaque_data(self, lsa_type, otype, callback=None):
+ """Register intent to advertise opaque data.
+
+ The application should wait for the async notificaiton that the server is
+ ready to advertise the given opaque data type. The API currently only allows
+ a single "owner" of each unique (lsa_type,otype). To wait call `wait_opaque_ready`
+
+ Args:
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ callback: if given, callback will be called when changes are received for
+ LSA of the given (lsa_type, otype). The callbacks signature is:
+
+ `callback(msg_type, ifaddr, area_id, lsa_header, data, lsa)`
+
+ Args:
+ msg_type: MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ lsa_header: the LSA header as an unpacked tuple (fmt: ">HBBIILHH")
+ data: the opaque data that follows the LSA header
+ lsa: the octets of the full lsa
+ Raises:
+ See `msg_send_raises`
+ """
+ if callback:
+ self.opaque_change_cb[(lsa_type, otype)] = callback
+ elif (lsa_type, otype) in self.opaque_change_cb:
+ logging.warning(
+ "OSPFCLIENT: register: removing callback for %s opaque-type %s",
+ lsa_typename(lsa_type),
+ otype,
+ )
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ await self._register_opaque_data(lsa_type, otype)
+
+ async def wait_opaque_ready(self, lsa_type, otype):
+ async with self.ready_lock:
+ cond = self.ready_cond[lsa_type].get(otype)
+ if cond is True:
+ return
+
+ logging.debug(
+ "waiting for ready %s opaque-type %s", lsa_typename(lsa_type), otype
+ )
+
+ if not cond:
+ cond = self.ready_cond[lsa_type][otype] = []
+
+ evt = Event()
+ cond.append(evt)
+
+ await evt.wait()
+ logging.debug("READY for %s opaque-type %s", lsa_typename(lsa_type), otype)
+
+ async def register_opaque_data_wait(self, lsa_type, otype, callback=None):
+ """Register intent to advertise opaque data and wait for ready.
+
+ The API currently only allows a single "owner" of each unique (lsa_type,otype).
+
+ Args:
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ callback: if given, callback will be called when changes are received for
+ LSA of the given (lsa_type, otype). The callbacks signature is:
+
+ `callback(msg_type, ifaddr, area_id, lsa_header, data, lsa)`
+
+ Args:
+ msg_type: MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ lsa_header: the LSA header as an unpacked tuple (fmt: ">HBBIILHH")
+ data: the opaque data that follows the LSA header
+ lsa: the octets of the full lsa
+ Raises:
+
+ See `msg_send_raises`
+ """
+ if callback:
+ self.opaque_change_cb[(lsa_type, otype)] = callback
+ elif (lsa_type, otype) in self.opaque_change_cb:
+ logging.warning(
+ "OSPFCLIENT: register: removing callback for %s opaque-type %s",
+ lsa_typename(lsa_type),
+ otype,
+ )
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ return await self._assure_opaque_ready(lsa_type, otype)
+
+ async def unregister_opaque_data(self, lsa_type, otype):
+ """Unregister intent to advertise opaque data.
+
+ This will also cause the server to flush/delete all opaque data of
+ the given (lsa_type,otype).
+
+ Args:
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ Raises:
+ See `msg_send_raises`
+ """
+
+ if (lsa_type, otype) in self.opaque_change_cb:
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ mt = MSG_UNREGISTER_OPAQUETYPE
+ mp = struct.pack(msg_fmt[mt], lsa_type, otype)
+ await self.msg_send_raises(mt, mp)
+
+ async def monitor_lsa(self, callback=None):
+ """Monitor changes to LSAs.
+
+ Args:
+ callback: if given, callback will be called when changes are received for
+ any LSA. The callback signature is:
+
+ `callback(msg_type, ifaddr, area_id, lsa_header, extra, lsa)`
+
+ Args:
+ msg_type: MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ lsa_header: the LSA header as an unpacked tuple (fmt: ">HBBIILHH")
+ extra: the octets that follow the LSA header
+ lsa: the octets of the full lsa
+ """
+ self.lsa_change_cb = callback
+ await self.req_lsdb_sync()
+
+ async def monitor_reachable(self, callback=None):
+ """Monitor the set of reachable routers.
+
+ The property `reachable_routers` contains the set() of reachable router IDs
+ as integers. This set is updated prior to calling the `callback`
+
+ Args:
+ callback: callback will be called when the set of reachable
+ routers changes. The callback signature is:
+
+ `callback(added, removed)`
+
+ Args:
+ added: list of integer router IDs being added
+ removed: list of integer router IDs being removed
+ """
+ self.reachable_change_cb = callback
+ await self.req_reachable_routers()
+
+ async def monitor_ism(self, callback=None):
+ """Monitor the state of OSPF enabled interfaces.
+
+ Args:
+ callback: callback will be called when an interface changes state.
+ The callback signature is:
+
+ `callback(ifaddr, area_id, state)`
+
+ Args:
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ state: ISM_*
+ """
+ self.ism_change_cb = callback
+ await self.req_ism_states()
+
+ async def monitor_nsm(self, callback=None):
+ """Monitor the state of OSPF neighbors.
+
+ Args:
+ callback: callback will be called when a neighbor changes state.
+ The callback signature is:
+
+ `callback(ifaddr, nbr_addr, router_id, state)`
+
+ Args:
+ ifaddr: integer identifying an interface (by IP address)
+ nbr_addr: integer identifying neighbor by IP address
+ router_id: integer identifying neighbor router ID
+ state: NSM_*
+ """
+ self.nsm_change_cb = callback
+ await self.req_nsm_states()
+
+
+# ================
+# CLI/Script Usage
+# ================
+
+
+async def async_main(args):
+ c = OspfOpaqueClient(args.server)
+ await c.connect()
+
+ try:
+ # Start handling async messages from server.
+ if sys.version_info[1] > 6:
+ asyncio.create_task(c._handle_msg_loop())
+ else:
+ asyncio.get_event_loop().create_task(c._handle_msg_loop())
+
+ await c.req_lsdb_sync()
+ await c.req_reachable_routers()
+ await c.req_ism_states()
+ await c.req_nsm_states()
+
+ if args.actions:
+ for action in args.actions:
+ _s = action.split(",")
+ what = _s.pop(False)
+ ltype = int(_s.pop(False))
+ if ltype == 11:
+ addr = ip(0)
+ else:
+ aval = _s.pop(False)
+ try:
+ addr = ip(int(aval))
+ except ValueError:
+ addr = ip(aval)
+ oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))]
+ assert len(_s) <= 1, "Bad format for action argument"
+ try:
+ b = bytes.fromhex(_s.pop(False))
+ except IndexError:
+ b = b""
+ logging.info("opaque data is %s octets", len(b))
+ # Needs to be multiple of 4 in length
+ mod = len(b) % 4
+ if mod:
+ b += b"\x00" * (4 - mod)
+ logging.info("opaque padding to %s octets", len(b))
+
+ if what.casefold() == "add":
+ await c.add_opaque_data(*oargs, b)
+ else:
+ assert what.casefold().startswith("del")
+ await c.delete_opaque_data(*oargs)
+ if args.exit:
+ return 0
+ except Exception as error:
+ logging.error("async_main: unexpected error: %s", error, exc_info=True)
+ return 2
+
+ try:
+ logging.info("Sleeping forever")
+ while True:
+ await asyncio.sleep(120)
+ except EOFError:
+ logging.info("Got EOF from OSPF API server on async notify socket")
+ return 2
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ ap.add_argument("--exit", action="store_true", help="Exit after commands")
+ ap.add_argument("--server", default="localhost", help="OSPF API server")
+ ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ ap.add_argument(
+ "actions", nargs="*", help="(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA]"
+ )
+ args = ap.parse_args()
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level, format="%(asctime)s %(levelname)s: CLIENT: %(name)s %(message)s"
+ )
+
+ logging.info("ospfclient: starting")
+
+ status = 3
+ try:
+ if sys.version_info[1] > 6:
+ # python >= 3.7
+ status = asyncio.run(async_main(args))
+ else:
+ loop = asyncio.get_event_loop()
+ try:
+ status = loop.run_until_complete(async_main(args))
+ finally:
+ loop.close()
+ except KeyboardInterrupt:
+ logging.info("Exiting, received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("Exiting, unexpected exception %s", error, exc_info=True)
+ else:
+ logging.info("ospfclient: clean exit")
+
+ return status
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am
index 1f9547ab87..b8c82c0bcf 100644
--- a/ospfclient/subdir.am
+++ b/ospfclient/subdir.am
@@ -6,6 +6,10 @@ if OSPFCLIENT
lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la
noinst_PROGRAMS += ospfclient/ospfclient
#man8 += $(MANBUILD)/frr-ospfclient.8
+
+sbin_SCRIPTS += \
+ ospfclient/ospfclient.py \
+ # end
endif
ospfclient_libfrrospfapiclient_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
@@ -41,3 +45,7 @@ endif
ospfclient_ospfclient_SOURCES = \
ospfclient/ospfclient.c \
# end
+
+EXTRA_DIST += \
+ ospfclient/ospfclient.py \
+ # end
diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c
index 81de882754..99bc6c0b03 100644
--- a/ospfd/ospf_api.c
+++ b/ospfd/ospf_api.c
@@ -177,6 +177,10 @@ const char *ospf_api_typename(int msgtype)
{
MSG_NSM_CHANGE, "NSM change",
},
+ {
+ MSG_REACHABLE_CHANGE,
+ "Reachable change",
+ },
};
int i, n = array_size(NameTab);
@@ -651,4 +655,31 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
return msg_new(msgtype, nmsg, seqnum, len);
}
+struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
+ struct in_addr *add, uint16_t nremove,
+ struct in_addr *remove)
+{
+ uint8_t buf[OSPF_API_MAX_MSG_SIZE];
+ struct msg_reachable_change *nmsg = (void *)buf;
+ const uint insz = sizeof(*nmsg->router_ids);
+ const uint nmax = (sizeof(buf) - sizeof(*nmsg)) / insz;
+ uint len;
+
+ if (nadd > nmax)
+ nadd = nmax;
+ if (nremove > (nmax - nadd))
+ nremove = (nmax - nadd);
+
+ if (nadd)
+ memcpy(nmsg->router_ids, add, nadd * insz);
+ if (nremove)
+ memcpy(&nmsg->router_ids[nadd], remove, nremove * insz);
+
+ nmsg->nadd = htons(nadd);
+ nmsg->nremove = htons(nremove);
+ len = sizeof(*nmsg) + insz * (nadd + nremove);
+
+ return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len);
+}
+
#endif /* SUPPORT_OSPF_API */
diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h
index c20284aed5..50b0c21c77 100644
--- a/ospfd/ospf_api.h
+++ b/ospfd/ospf_api.h
@@ -26,6 +26,9 @@
#ifndef _OSPF_API_H
#define _OSPF_API_H
+#include <zebra.h>
+#include "ospf_lsa.h"
+
#define OSPF_API_VERSION 1
/* MTYPE definition is not reflected to "memory.h". */
@@ -112,6 +115,9 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
#define MSG_SYNC_LSDB 4
#define MSG_ORIGINATE_REQUEST 5
#define MSG_DELETE_REQUEST 6
+#define MSG_SYNC_REACHABLE 7
+#define MSG_SYNC_ISM 8
+#define MSG_SYNC_NSM 9
/* Messages from OSPF daemon. */
#define MSG_REPLY 10
@@ -122,6 +128,7 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
#define MSG_DEL_IF 15
#define MSG_ISM_CHANGE 16
#define MSG_NSM_CHANGE 17
+#define MSG_REACHABLE_CHANGE 18
struct msg_register_opaque_type {
uint8_t lsatype;
@@ -247,6 +254,12 @@ struct msg_nsm_change {
uint8_t pad[3];
};
+struct msg_reachable_change {
+ uint16_t nadd;
+ uint16_t nremove;
+ struct in_addr router_ids[]; /* add followed by remove */
+};
+
/* We make use of a union to define a structure that covers all
possible API messages. This allows us to find out how much memory
needs to be reserved for the largest API message. */
@@ -265,6 +278,7 @@ struct apimsg {
struct msg_ism_change ism_change;
struct msg_nsm_change nsm_change;
struct msg_lsa_change_notify lsa_change_notify;
+ struct msg_reachable_change reachable_change;
} u;
};
@@ -320,6 +334,10 @@ extern struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
uint8_t is_self_originated,
struct lsa_header *data);
+extern struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
+ struct in_addr *add,
+ uint16_t nremove,
+ struct in_addr *remove);
/* string printing functions */
extern const char *ospf_api_errname(int errcode);
extern const char *ospf_api_typename(int msgtype);
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index 97bd125aee..7c3fef4536 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -615,7 +615,7 @@ void ospf_apiserver_accept(struct thread *thread)
/* Get port address and port number of peer to make reverse connection.
The reverse channel uses the port number of the peer port+1. */
- memset(&peer_sync, 0, sizeof(struct sockaddr_in));
+ memset(&peer_sync, 0, sizeof(peer_sync));
peerlen = sizeof(struct sockaddr_in);
ret = getpeername(new_sync_sock, (struct sockaddr *)&peer_sync,
@@ -726,6 +726,7 @@ static int ospf_apiserver_send_msg(struct ospf_apiserver *apiserv,
case MSG_DEL_IF:
case MSG_ISM_CHANGE:
case MSG_NSM_CHANGE:
+ case MSG_REACHABLE_CHANGE:
fifo = apiserv->out_async_fifo;
fd = apiserv->fd_async;
event = OSPF_APISERVER_ASYNC_WRITE;
@@ -799,6 +800,15 @@ int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, struct msg *msg)
case MSG_DELETE_REQUEST:
rc = ospf_apiserver_handle_delete_request(apiserv, msg);
break;
+ case MSG_SYNC_REACHABLE:
+ rc = ospf_apiserver_handle_sync_reachable(apiserv, msg);
+ break;
+ case MSG_SYNC_ISM:
+ rc = ospf_apiserver_handle_sync_ism(apiserv, msg);
+ break;
+ case MSG_SYNC_NSM:
+ rc = ospf_apiserver_handle_sync_nsm(apiserv, msg);
+ break;
default:
zlog_warn("ospf_apiserver_handle_msg: Unknown message type: %d",
msg->hdr.msgtype);
@@ -1343,6 +1353,131 @@ int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
return rc;
}
+/*
+ * -----------------------------------------------------------
+ * Followings are functions for synchronization.
+ * -----------------------------------------------------------
+ */
+
+int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
+ struct msg *msg)
+{
+ struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct route_table *rt = ospf->all_rtrs;
+ uint32_t seqnum = msg_get_seq(msg);
+ struct in_addr *a, *abuf;
+ struct msg_reachable_change *areach;
+ struct msg *amsg;
+ uint mcount, count;
+ int _rc, rc = 0;
+
+ if (!rt)
+ goto out;
+
+ /* send all adds based on current reachable routers */
+ a = abuf = XCALLOC(MTYPE_OSPF_APISERVER,
+ sizeof(struct in_addr) * rt->count);
+ for (struct route_node *rn = route_top(rt); rn; rn = route_next(rn))
+ if (listhead((struct list *)rn->info))
+ *a++ = rn->p.u.prefix4;
+
+ assert((a - abuf) <= (long)rt->count);
+ count = (a - abuf);
+
+ a = abuf;
+ while (count && !rc) {
+ amsg = new_msg_reachable_change(seqnum, count, a, 0, NULL);
+ areach = (struct msg_reachable_change *)STREAM_DATA(amsg->s);
+ mcount = ntohs(areach->nadd) + ntohs(areach->nremove);
+ assert(mcount <= count);
+ a = a + mcount;
+ count -= mcount;
+ rc = ospf_apiserver_send_msg(apiserv, amsg);
+ msg_free(amsg);
+ }
+ XFREE(MTYPE_OSPF_APISERVER, abuf);
+
+out:
+ /* Send a reply back to client with return code */
+ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+ rc = rc ? rc : _rc;
+ apiserv->reachable_sync = !rc;
+ return rc;
+}
+
+int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv,
+ struct msg *msg)
+{
+ struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct listnode *anode, *inode;
+ struct ospf_area *area;
+ struct ospf_interface *oi;
+ struct msg *m;
+ uint32_t seqnum = msg_get_seq(msg);
+ int _rc, rc = 0;
+
+ /* walk all areas */
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
+ /* walk all interfaces */
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
+ m = new_msg_ism_change(seqnum, oi->address->u.prefix4,
+ area->area_id, oi->state);
+ rc = ospf_apiserver_send_msg(apiserv, m);
+ msg_free(m);
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+ /* Send a reply back to client with return code */
+ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+ return rc ? rc : _rc;
+}
+
+
+int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv,
+ struct msg *msg)
+{
+ struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct listnode *anode, *inode;
+ struct ospf_area *area;
+ struct ospf_interface *oi;
+ struct ospf_neighbor *nbr;
+ struct route_node *rn;
+ struct msg *m;
+ uint32_t seqnum = msg_get_seq(msg);
+ int _rc, rc = 0;
+
+ /* walk all areas */
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
+ /* walk all interfaces */
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
+ /* walk all neighbors */
+ for (rn = route_top(oi->nbrs); rn;
+ rn = route_next(rn)) {
+ nbr = rn->info;
+ if (!nbr)
+ continue;
+ m = new_msg_nsm_change(
+ seqnum, oi->address->u.prefix4,
+ nbr->src, nbr->router_id, nbr->state);
+ rc = ospf_apiserver_send_msg(apiserv, m);
+ msg_free(m);
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+ /* Send a reply back to client with return code */
+ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+ return rc ? rc : _rc;
+}
+
/* -----------------------------------------------------------
* Following are functions to originate or update LSA
@@ -1427,45 +1562,20 @@ struct ospf_lsa *ospf_apiserver_opaque_lsa_new(struct ospf_area *area,
int ospf_apiserver_is_ready_type9(struct ospf_interface *oi)
{
- /* Type 9 opaque LSA can be originated if there is at least one
- active opaque-capable neighbor attached to the outgoing
- interface. */
-
- return (ospf_nbr_count_opaque_capable(oi) > 0);
+ /* We can always handle getting opaque's even if we can't flood them */
+ return 1;
}
int ospf_apiserver_is_ready_type10(struct ospf_area *area)
{
- /* Type 10 opaque LSA can be originated if there is at least one
- interface belonging to the area that has an active opaque-capable
- neighbor. */
- struct listnode *node, *nnode;
- struct ospf_interface *oi;
-
- for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi))
- /* Is there an active neighbor attached to this interface? */
- if (ospf_apiserver_is_ready_type9(oi))
- return 1;
-
- /* No active neighbor in area */
- return 0;
+ /* We can always handle getting opaque's even if we can't flood them */
+ return 1;
}
int ospf_apiserver_is_ready_type11(struct ospf *ospf)
{
- /* Type 11 opaque LSA can be originated if there is at least one
- interface
- that has an active opaque-capable neighbor. */
- struct listnode *node, *nnode;
- struct ospf_interface *oi;
-
- for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi))
- /* Is there an active neighbor attached to this interface? */
- if (ospf_apiserver_is_ready_type9(oi))
- return 1;
-
- /* No active neighbor at all */
- return 0;
+ /* We can always handle getting opaque's even if we can't flood them */
+ return 1;
}
@@ -1570,9 +1680,9 @@ int ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv,
/* Determine if LSA is new or an update for an existing one. */
old = ospf_lsdb_lookup(lsdb, new);
- if (!old) {
+ if (!old || !ospf_opaque_is_owned(old)) {
/* New LSA install in LSDB. */
- rc = ospf_apiserver_originate1(new);
+ rc = ospf_apiserver_originate1(new, old);
} else {
/*
* Keep the new LSA instance in the "waiting place" until the
@@ -1639,17 +1749,33 @@ void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa)
}
}
-int ospf_apiserver_originate1(struct ospf_lsa *lsa)
+int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old)
{
struct ospf *ospf;
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
assert(ospf);
+ if (old) {
+ /*
+ * An old LSA exists that we didn't originate it in this
+ * session. Dump it, but increment past it's seqnum.
+ */
+ assert(!ospf_opaque_is_owned(old));
+ if (IS_LSA_MAX_SEQ(old)) {
+ flog_warn(
+ EC_OSPF_LSA_INSTALL_FAILURE,
+ "ospf_apiserver_originate1: old LSA at maxseq");
+ return -1;
+ }
+ lsa->data->ls_seqnum = lsa_seqnum_increment(old);
+ ospf_discard_from_db(ospf, old->lsdb, old);
+ }
+
/* Install this LSA into LSDB. */
if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "ospf_apiserver_originate1: ospf_lsa_install failed");
+ "%s: ospf_lsa_install failed", __func__);
return -1;
}
@@ -2015,6 +2141,7 @@ int ospf_apiserver_del_if(struct interface *ifp)
if (!oi) {
/* This interface is known to Zebra but not to OSPF daemon
anymore. No need to tell clients about it */
+ zlog_warn("ifp name=%s not known to OSPFd", ifp->name);
return 0;
}
@@ -2061,9 +2188,6 @@ void ospf_apiserver_show_info(struct vty *vty, struct json_object *json,
struct opaque_lsa *olsa;
int opaquelen;
- if (json)
- return;
-
olsa = (struct opaque_lsa *)lsa->data;
if (VALID_OPAQUE_INFO_LEN(lsa->data))
@@ -2072,7 +2196,10 @@ void ospf_apiserver_show_info(struct vty *vty, struct json_object *json,
opaquelen = 0;
/* Output information about opaque LSAs */
- if (vty != NULL) {
+ if (json)
+ json_object_string_addf(json, "opaqueData", "%*pHXn",
+ (int)opaquelen, olsa->data);
+ else if (vty != NULL) {
int i;
vty_out(vty,
" Added using OSPF API: %u octets of opaque data %s\n",
@@ -2333,8 +2460,8 @@ void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr)
msg_free(msg);
}
-static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
- struct ospf_lsa *lsa)
+static int apiserver_clients_lsa_change_notify(uint8_t msgtype,
+ struct ospf_lsa *lsa)
{
struct msg *msg;
struct listnode *node, *nnode;
@@ -2362,7 +2489,7 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
if (!msg) {
zlog_warn(
"apiserver_clients_lsa_change_notify: msg_new failed");
- return;
+ return -1;
}
/* Now send message to all clients with a matching filter */
@@ -2413,6 +2540,8 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
}
/* Free message since it is not used anymore */
msg_free(msg);
+
+ return 0;
}
@@ -2422,53 +2551,132 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
*/
-static int apiserver_notify_clients_lsa(uint8_t msgtype, struct ospf_lsa *lsa)
+int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
{
- struct msg *msg;
- /* default area for AS-External and Opaque11 LSAs */
- struct in_addr area_id = {.s_addr = 0L};
-
- /* default interface for non Opaque9 LSAs */
- struct in_addr ifaddr = {.s_addr = 0L};
/* Only notify this update if the LSA's age is smaller than
MAXAGE. Otherwise clients would see LSA updates with max age just
before they are deleted from the LSDB. LSA delete messages have
MAXAGE too but should not be filtered. */
- if (IS_LSA_MAXAGE(lsa) && (msgtype == MSG_LSA_UPDATE_NOTIFY)) {
+ if (IS_LSA_MAXAGE(lsa))
return 0;
- }
-
- if (lsa->area) {
- area_id = lsa->area->area_id;
- }
- if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) {
- ifaddr = lsa->oi->address->u.prefix4;
- }
- msg = new_msg_lsa_change_notify(msgtype, 0L, /* no sequence number */
- ifaddr, area_id,
- lsa->flags & OSPF_LSA_SELF, lsa->data);
- if (!msg) {
- zlog_warn("notify_clients_lsa: msg_new failed");
- return -1;
- }
- /* Notify all clients that new LSA is added/updated */
- apiserver_clients_lsa_change_notify(msgtype, lsa);
-
- /* Clients made their own copies of msg so we can free msg here */
- msg_free(msg);
+ return apiserver_clients_lsa_change_notify(MSG_LSA_UPDATE_NOTIFY, lsa);
+}
- return 0;
+int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
+{
+ return apiserver_clients_lsa_change_notify(MSG_LSA_DELETE_NOTIFY, lsa);
}
-int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
+/* -------------------------------------------------------------
+ * Reachable functions
+ * -------------------------------------------------------------
+ */
+
+static inline int cmp_route_nodes(struct route_node *orn,
+ struct route_node *nrn)
{
- return apiserver_notify_clients_lsa(MSG_LSA_UPDATE_NOTIFY, lsa);
+ if (!orn)
+ return 1;
+ else if (!nrn)
+ return -1;
+ else if (orn->p.u.prefix4.s_addr < nrn->p.u.prefix4.s_addr)
+ return -1;
+ else if (orn->p.u.prefix4.s_addr > nrn->p.u.prefix4.s_addr)
+ return 1;
+ else
+ return 0;
}
-int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
+void ospf_apiserver_notify_reachable(struct route_table *ort,
+ struct route_table *nrt)
{
- return apiserver_notify_clients_lsa(MSG_LSA_DELETE_NOTIFY, lsa);
+ struct msg *msg;
+ struct msg_reachable_change *areach;
+ struct route_node *orn, *nrn;
+ const uint insz = sizeof(struct in_addr);
+ struct in_addr *abuf = NULL, *dbuf = NULL;
+ struct in_addr *a = NULL, *d = NULL;
+ uint nadd, nremove;
+ int cmp;
+
+ if (!ort && !nrt) {
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("%s: no routing tables", __func__);
+ return;
+ }
+ if (nrt && nrt->count)
+ a = abuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * nrt->count);
+ if (ort && ort->count)
+ d = dbuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * ort->count);
+
+ /* walk both tables */
+ orn = ort ? route_top(ort) : NULL;
+ nrn = nrt ? route_top(nrt) : NULL;
+ while (orn || nrn) {
+ if (orn && !listhead((struct list *)orn->info)) {
+ orn = route_next(orn);
+ continue;
+ }
+ if (nrn && !listhead((struct list *)nrn->info)) {
+ nrn = route_next(nrn);
+ continue;
+ }
+ cmp = cmp_route_nodes(orn, nrn);
+ if (!cmp) {
+ /* if old == new advance old and new */
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("keeping router id: %pI4",
+ &orn->p.u.prefix4);
+ orn = route_next(orn);
+ nrn = route_next(nrn);
+ } else if (cmp < 0) {
+ assert(d != NULL); /* Silence SA warning */
+
+ /* if old < new, delete old, advance old */
+ *d++ = orn->p.u.prefix4;
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("removing router id: %pI4",
+ &orn->p.u.prefix4);
+ orn = route_next(orn);
+ } else {
+ assert(a != NULL); /* Silence SA warning */
+
+ /* if new < old, add new, advance new */
+ *a++ = nrn->p.u.prefix4;
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("adding router id: %pI4",
+ &nrn->p.u.prefix4);
+ nrn = route_next(nrn);
+ }
+ }
+
+ nadd = abuf ? (a - abuf) : 0;
+ nremove = dbuf ? (d - dbuf) : 0;
+ a = abuf;
+ d = dbuf;
+
+ while (nadd + nremove) {
+ msg = new_msg_reachable_change(0, nadd, a, nremove, d);
+ areach = (struct msg_reachable_change *)STREAM_DATA(msg->s);
+
+ a += ntohs(areach->nadd);
+ nadd = nadd - ntohs(areach->nadd);
+
+ d += ntohs(areach->nremove);
+ nremove = nremove - ntohs(areach->nremove);
+
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("%s: adding %d removing %d", __func__,
+ ntohs(areach->nadd), ntohs(areach->nremove));
+ ospf_apiserver_clients_notify_all(msg);
+ msg_free(msg);
+ }
+ if (abuf)
+ XFREE(MTYPE_OSPF_APISERVER, abuf);
+ if (dbuf)
+ XFREE(MTYPE_OSPF_APISERVER, dbuf);
}
+
#endif /* SUPPORT_OSPF_API */
diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h
index b4d8bb2f52..7d728ead93 100644
--- a/ospfd/ospf_apiserver.h
+++ b/ospfd/ospf_apiserver.h
@@ -22,6 +22,10 @@
#ifndef _OSPF_APISERVER_H
#define _OSPF_APISERVER_H
+#include <zebra.h>
+#include "ospf_api.h"
+#include "ospf_lsdb.h"
+
/* MTYPE definition is not reflected to "memory.h". */
#define MTYPE_OSPF_APISERVER MTYPE_TMP
#define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP
@@ -52,6 +56,9 @@ struct ospf_apiserver {
/* Temporary storage for LSA instances to be refreshed. */
struct ospf_lsdb reserve;
+ /* Sync reachable routers */
+ bool reachable_sync;
+
/* filter for LSA update/delete notifies */
struct lsa_filter_type *filter;
@@ -144,7 +151,15 @@ extern int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
struct msg *msg);
extern int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
struct msg *msg);
+extern int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
+ struct msg *msg);
+extern int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv,
+ struct msg *msg);
+extern int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv,
+ struct msg *msg);
+extern void ospf_apiserver_notify_reachable(struct route_table *ort,
+ struct route_table *nrt);
/* -----------------------------------------------------------
* Following are functions for LSA origination/deletion
@@ -164,7 +179,8 @@ extern struct ospf_interface *
ospf_apiserver_if_lookup_by_addr(struct in_addr address);
extern struct ospf_interface *
ospf_apiserver_if_lookup_by_ifp(struct interface *ifp);
-extern int ospf_apiserver_originate1(struct ospf_lsa *lsa);
+extern int ospf_apiserver_originate1(struct ospf_lsa *lsa,
+ struct ospf_lsa *old);
extern void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa);
@@ -201,7 +217,4 @@ extern void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv,
extern int ospf_apiserver_lsa_update(struct ospf_lsa *lsa);
extern int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa);
-extern void ospf_apiserver_clients_lsa_change_notify(uint8_t msgtype,
- struct ospf_lsa *lsa);
-
#endif /* _OSPF_APISERVER_H */
diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c
index 9d4ce97fa5..2d746946a5 100644
--- a/ospfd/ospf_asbr.c
+++ b/ospfd/ospf_asbr.c
@@ -648,7 +648,7 @@ struct ospf_lsa *ospf_originate_summary_lsa(struct ospf *ospf,
}
/* Prepare the extrenal_info for aggregator */
- memset(&ei_aggr, 0, sizeof(struct external_info));
+ memset(&ei_aggr, 0, sizeof(ei_aggr));
ei_aggr.p = aggr->p;
ei_aggr.tag = aggr->tag;
ei_aggr.type = 0;
@@ -1011,7 +1011,7 @@ static void ospf_handle_external_aggr_update(struct ospf *ospf)
aggr->action = OSPF_ROUTE_AGGR_NONE;
/* Prepare the extrenal_info for aggregator */
- memset(&ei_aggr, 0, sizeof(struct external_info));
+ memset(&ei_aggr, 0, sizeof(ei_aggr));
ei_aggr.p = aggr->p;
ei_aggr.tag = aggr->tag;
ei_aggr.type = 0;
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index f40d056742..4d76181c6a 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -62,6 +62,7 @@ unsigned long conf_debug_ospf_defaultinfo = 0;
unsigned long conf_debug_ospf_ldp_sync = 0;
unsigned long conf_debug_ospf_gr = 0;
unsigned long conf_debug_ospf_bfd;
+unsigned long conf_debug_ospf_client_api;
/* Enable debug option variables -- valid only session. */
unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
@@ -79,6 +80,7 @@ unsigned long term_debug_ospf_defaultinfo;
unsigned long term_debug_ospf_ldp_sync;
unsigned long term_debug_ospf_gr = 0;
unsigned long term_debug_ospf_bfd;
+unsigned long term_debug_ospf_client_api;
const char *ospf_redist_string(unsigned int route_type)
{
@@ -1620,6 +1622,33 @@ DEFPY(debug_ospf_bfd, debug_ospf_bfd_cmd,
return CMD_SUCCESS;
}
+DEFUN(debug_ospf_client_api,
+ debug_ospf_client_api_cmd,
+ "debug ospf client-api",
+ DEBUG_STR OSPF_STR
+ "OSPF client API information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_ON(client_api, CLIENT_API);
+ TERM_DEBUG_ON(client_api, CLIENT_API);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_debug_ospf_client_api,
+ no_debug_ospf_client_api_cmd,
+ "no debug ospf client-api",
+ NO_STR
+ DEBUG_STR
+ OSPF_STR
+ "OSPF client API information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_OFF(client_api, CLIENT_API);
+ TERM_DEBUG_OFF(client_api, CLIENT_API);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_ospf,
no_debug_ospf_cmd,
"no debug ospf",
@@ -1654,6 +1683,7 @@ DEFUN (no_debug_ospf,
DEBUG_OFF(te, TE);
DEBUG_OFF(sr, SR);
DEBUG_OFF(ti_lfa, TI_LFA);
+ DEBUG_OFF(client_api, CLIENT_API);
/* BFD debugging is two parts: OSPF and library. */
DEBUG_OFF(bfd, BFD_LIB);
@@ -1690,6 +1720,7 @@ DEFUN (no_debug_ospf,
TERM_DEBUG_OFF(sr, SR);
TERM_DEBUG_OFF(ti_lfa, TI_LFA);
TERM_DEBUG_OFF(bfd, BFD_LIB);
+ TERM_DEBUG_OFF(client_api, CLIENT_API);
return CMD_SUCCESS;
}
@@ -1815,6 +1846,10 @@ static int show_debugging_ospf_common(struct vty *vty)
vty_out(vty,
" OSPF BFD integration library debugging is on\n");
+ /* Show debug status for LDP-SYNC. */
+ if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API)
+ vty_out(vty, " OSPF client-api debugging is on\n");
+
vty_out(vty, "\n");
return CMD_SUCCESS;
@@ -2007,6 +2042,13 @@ static int config_write_debug(struct vty *vty)
write = 1;
}
+ /* debug ospf client-api */
+ if (IS_CONF_DEBUG_OSPF(client_api, CLIENT_API) ==
+ OSPF_DEBUG_CLIENT_API) {
+ vty_out(vty, "debug ospf%s client-api\n", str);
+ write = 1;
+ }
+
return write;
}
@@ -2027,6 +2069,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &debug_ospf_ti_lfa_cmd);
install_element(ENABLE_NODE, &debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd);
+ install_element(ENABLE_NODE, &debug_ospf_client_api_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
@@ -2038,6 +2081,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &no_debug_ospf_ti_lfa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf_client_api_cmd);
install_element(ENABLE_NODE, &debug_ospf_gr_cmd);
install_element(ENABLE_NODE, &debug_ospf_bfd_cmd);
@@ -2072,6 +2116,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &debug_ospf_ti_lfa_cmd);
install_element(CONFIG_NODE, &debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd);
+ install_element(CONFIG_NODE, &debug_ospf_client_api_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
@@ -2082,6 +2127,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &no_debug_ospf_ti_lfa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf_client_api_cmd);
install_element(CONFIG_NODE, &debug_ospf_gr_cmd);
install_element(CONFIG_NODE, &debug_ospf_bfd_cmd);
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
index 58227d038e..251be7c8d1 100644
--- a/ospfd/ospf_dump.h
+++ b/ospfd/ospf_dump.h
@@ -68,6 +68,8 @@
#define OSPF_DEBUG_BFD_LIB 0x01
+#define OSPF_DEBUG_CLIENT_API 0x01
+
/* Macro for setting debug option. */
#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b)
#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b)
@@ -118,6 +120,7 @@
#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC)
#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR)
+#define IS_DEBUG_OSPF_CLIENT_API IS_DEBUG_OSPF(client_api, CLIENT_API)
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
@@ -142,6 +145,7 @@ extern unsigned long term_debug_ospf_defaultinfo;
extern unsigned long term_debug_ospf_ldp_sync;
extern unsigned long term_debug_ospf_gr;
extern unsigned long term_debug_ospf_bfd;
+extern unsigned long term_debug_ospf_client_api;
/* Message Strings. */
extern char *ospf_lsa_type_str[];
diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c
index 69847088e4..1288686a10 100644
--- a/ospfd/ospf_ext.c
+++ b/ospfd/ospf_ext.c
@@ -114,7 +114,7 @@ int ospf_ext_init(void)
{
int rc = 0;
- memset(&OspfEXT, 0, sizeof(struct ospf_ext_lp));
+ memset(&OspfEXT, 0, sizeof(OspfEXT));
OspfEXT.enabled = false;
/* Only Area flooding is supported yet */
OspfEXT.scope = OSPF_OPAQUE_AREA_LSA;
diff --git a/ospfd/ospf_ldp_sync.c b/ospfd/ospf_ldp_sync.c
index b4d770d48a..330a433627 100644
--- a/ospfd/ospf_ldp_sync.c
+++ b/ospfd/ospf_ldp_sync.c
@@ -326,7 +326,7 @@ void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove)
if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG))
ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT;
if (remove) {
- ldp_sync_info_free((struct ldp_sync_info **)&(ldp_sync_info));
+ ldp_sync_info_free(&ldp_sync_info);
params->ldp_sync_info = NULL;
}
}
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index 59e1b73d24..2be81042a6 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -3149,7 +3149,7 @@ void ospf_lsa_maxage_delete(struct ospf *ospf, struct ospf_lsa *lsa)
struct route_node *rn;
struct prefix lsa_prefix;
- memset(&lsa_prefix, 0, sizeof(struct prefix));
+ memset(&lsa_prefix, 0, sizeof(lsa_prefix));
lsa_prefix.family = AF_UNSPEC;
lsa_prefix.prefixlen = sizeof(lsa_prefix.u.ptr) * CHAR_BIT;
lsa_prefix.u.ptr = (uintptr_t)lsa;
@@ -3190,7 +3190,7 @@ void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa)
return;
}
- memset(&lsa_prefix, 0, sizeof(struct prefix));
+ memset(&lsa_prefix, 0, sizeof(lsa_prefix));
lsa_prefix.family = AF_UNSPEC;
lsa_prefix.prefixlen = sizeof(lsa_prefix.u.ptr) * CHAR_BIT;
lsa_prefix.u.ptr = (uintptr_t)lsa;
@@ -3865,8 +3865,7 @@ struct ospf_lsa *ospf_lsa_refresh(struct ospf *ospf, struct ospf_lsa *lsa)
if (aggr) {
struct external_info ei_aggr;
- memset(&ei_aggr, 0,
- sizeof(struct external_info));
+ memset(&ei_aggr, 0, sizeof(ei_aggr));
ei_aggr.p = aggr->p;
ei_aggr.tag = aggr->tag;
ei_aggr.instance = ospf->instance;
diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c
index b0881c098f..f4fb858a5f 100644
--- a/ospfd/ospf_lsdb.c
+++ b/ospfd/ospf_lsdb.c
@@ -198,7 +198,7 @@ struct ospf_lsa *ospf_lsdb_lookup_by_id(struct ospf_lsdb *lsdb, uint8_t type,
table = lsdb->type[type].db;
- memset(&lp, 0, sizeof(struct prefix_ls));
+ memset(&lp, 0, sizeof(lp));
lp.family = AF_UNSPEC;
lp.prefixlen = 64;
lp.id = id;
@@ -225,7 +225,7 @@ struct ospf_lsa *ospf_lsdb_lookup_by_id_next(struct ospf_lsdb *lsdb,
table = lsdb->type[type].db;
- memset(&lp, 0, sizeof(struct prefix_ls));
+ memset(&lp, 0, sizeof(lp));
lp.family = AF_UNSPEC;
lp.prefixlen = 64;
lp.id = id;
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 947454c0df..7e95cb591a 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -74,9 +74,8 @@ int ospf_apiserver_enable;
static void ospf_opaque_register_vty(void);
static void ospf_opaque_funclist_init(void);
static void ospf_opaque_funclist_term(void);
-static void free_opaque_info_per_type(void *val);
+static void free_opaque_info_per_type_del(void *val);
static void free_opaque_info_per_id(void *val);
-static void free_opaque_info_owner(void *val);
static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
@@ -141,7 +140,7 @@ int ospf_opaque_type9_lsa_init(struct ospf_interface *oi)
list_delete(&oi->opaque_lsa_self);
oi->opaque_lsa_self = list_new();
- oi->opaque_lsa_self->del = free_opaque_info_per_type;
+ oi->opaque_lsa_self->del = free_opaque_info_per_type_del;
oi->t_opaque_lsa_self = NULL;
return 0;
}
@@ -161,7 +160,7 @@ int ospf_opaque_type10_lsa_init(struct ospf_area *area)
list_delete(&area->opaque_lsa_self);
area->opaque_lsa_self = list_new();
- area->opaque_lsa_self->del = free_opaque_info_per_type;
+ area->opaque_lsa_self->del = free_opaque_info_per_type_del;
area->t_opaque_lsa_self = NULL;
#ifdef MONITOR_LSDB_CHANGE
@@ -189,7 +188,7 @@ int ospf_opaque_type11_lsa_init(struct ospf *top)
list_delete(&top->opaque_lsa_self);
top->opaque_lsa_self = list_new();
- top->opaque_lsa_self->del = free_opaque_info_per_type;
+ top->opaque_lsa_self->del = free_opaque_info_per_type_del;
top->t_opaque_lsa_self = NULL;
#ifdef MONITOR_LSDB_CHANGE
@@ -263,6 +262,9 @@ static const char *ospf_opaque_type_name(uint8_t opaque_type)
struct opaque_info_per_type; /* Forward declaration. */
+static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
+ bool cleanup_owner);
+
struct ospf_opaque_functab {
uint8_t opaque_type;
struct opaque_info_per_type *oipt;
@@ -433,12 +435,9 @@ void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type)
if (functab->opaque_type == opaque_type) {
/* Cleanup internal control information, if it
* still remains. */
- if (functab->oipt != NULL) {
- free_opaque_info_owner(functab->oipt);
- free_opaque_info_per_type(
- functab->oipt);
- }
-
+ if (functab->oipt != NULL)
+ free_opaque_info_per_type(functab->oipt,
+ true);
/* Dequeue listnode entry from the list. */
listnode_delete(funclist, functab);
@@ -558,8 +557,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
case OSPF_OPAQUE_AS_LSA:
top = ospf_lookup_by_vrf_id(new->vrf_id);
if (new->area != NULL && (top = new->area->ospf) == NULL) {
- free_opaque_info_owner(oipt);
- free_opaque_info_per_type(oipt);
+ free_opaque_info_per_type(oipt, true);
oipt = NULL;
goto out; /* This case may not exist. */
}
@@ -571,8 +569,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
EC_OSPF_LSA_UNEXPECTED,
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
new->data->type);
- free_opaque_info_owner(oipt);
- free_opaque_info_per_type(oipt);
+ free_opaque_info_per_type(oipt, true);
oipt = NULL;
goto out; /* This case may not exist. */
}
@@ -589,42 +586,13 @@ out:
return oipt;
}
-/* Remove "oipt" from its owner's self-originated LSA list. */
-static void free_opaque_info_owner(void *val)
-{
- struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
-
- switch (oipt->lsa_type) {
- case OSPF_OPAQUE_LINK_LSA: {
- struct ospf_interface *oi =
- (struct ospf_interface *)(oipt->owner);
- listnode_delete(oi->opaque_lsa_self, oipt);
- break;
- }
- case OSPF_OPAQUE_AREA_LSA: {
- struct ospf_area *area = (struct ospf_area *)(oipt->owner);
- listnode_delete(area->opaque_lsa_self, oipt);
- break;
- }
- case OSPF_OPAQUE_AS_LSA: {
- struct ospf *top = (struct ospf *)(oipt->owner);
- listnode_delete(top->opaque_lsa_self, oipt);
- break;
- }
- default:
- flog_warn(EC_OSPF_LSA_UNEXPECTED,
- "free_opaque_info_owner: Unexpected LSA-type(%u)",
- oipt->lsa_type);
- break; /* This case may not exist. */
- }
-}
-
-static void free_opaque_info_per_type(void *val)
+static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
+ bool cleanup_owner)
{
- struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
struct opaque_info_per_id *oipi;
struct ospf_lsa *lsa;
struct listnode *node, *nnode;
+ struct list *l;
/* Control information per opaque-id may still exist. */
for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) {
@@ -637,10 +605,37 @@ static void free_opaque_info_per_type(void *val)
OSPF_TIMER_OFF(oipt->t_opaque_lsa_self);
list_delete(&oipt->id_list);
+ if (cleanup_owner) {
+ /* Remove from its owner's self-originated LSA list. */
+ switch (oipt->lsa_type) {
+ case OSPF_OPAQUE_LINK_LSA:
+ l = ((struct ospf_interface *)oipt->owner)
+ ->opaque_lsa_self;
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ l = ((struct ospf_area *)oipt->owner)->opaque_lsa_self;
+ break;
+ case OSPF_OPAQUE_AS_LSA:
+ l = ((struct ospf *)oipt->owner)->opaque_lsa_self;
+ break;
+ default:
+ flog_warn(
+ EC_OSPF_LSA_UNEXPECTED,
+ "free_opaque_info_owner: Unexpected LSA-type(%u)",
+ oipt->lsa_type);
+ return;
+ }
+ listnode_delete(l, oipt);
+ }
XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt);
return;
}
+static void free_opaque_info_per_type_del(void *val)
+{
+ free_opaque_info_per_type((struct opaque_info_per_type *)val, false);
+}
+
static struct opaque_info_per_type *
lookup_opaque_info_by_type(struct ospf_lsa *lsa)
{
@@ -758,6 +753,13 @@ out:
return oipi;
}
+int ospf_opaque_is_owned(struct ospf_lsa *lsa)
+{
+ struct opaque_info_per_type *oipt = lookup_opaque_info_by_type(lsa);
+
+ return (oipt != NULL && lookup_opaque_info_by_id(oipt, lsa) != NULL);
+}
+
/*------------------------------------------------------------------------*
* Following are (vty) configuration functions for Opaque-LSAs handling.
*------------------------------------------------------------------------*/
diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h
index 59d4288bf2..9c76877908 100644
--- a/ospfd/ospf_opaque.h
+++ b/ospfd/ospf_opaque.h
@@ -173,4 +173,6 @@ extern void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
struct ospf_lsa *lsa);
extern struct ospf *oi_to_top(struct ospf_interface *oi);
+extern int ospf_opaque_is_owned(struct ospf_lsa *lsa);
+
#endif /* _ZEBRA_OSPF_OPAQUE_H */
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 55a010a293..c319f8068a 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -696,7 +696,7 @@ static void ospf_write(struct thread *thread)
/* reset get pointer */
stream_set_getp(op->s, 0);
- memset(&iph, 0, sizeof(struct ip));
+ memset(&iph, 0, sizeof(iph));
memset(&sa_dst, 0, sizeof(sa_dst));
sa_dst.sin_family = AF_INET;
@@ -2310,7 +2310,7 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd,
char buff[CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())];
struct msghdr msgh;
- memset(&msgh, 0, sizeof(struct msghdr));
+ memset(&msgh, 0, sizeof(msgh));
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_control = (caddr_t)buff;
diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c
index 3efdb53102..e227a3151f 100644
--- a/ospfd/ospf_ri.c
+++ b/ospfd/ospf_ri.c
@@ -90,7 +90,7 @@ int ospf_router_info_init(void)
zlog_info("RI (%s): Initialize Router Information", __func__);
- memset(&OspfRI, 0, sizeof(struct ospf_router_info));
+ memset(&OspfRI, 0, sizeof(OspfRI));
OspfRI.enabled = false;
OspfRI.registered = 0;
OspfRI.scope = OSPF_OPAQUE_AS_LSA;
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
index ec0c5524c9..c5b26bbd76 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -363,7 +363,7 @@ void ospf_route_install(struct ospf *ospf, struct route_table *rt)
/* RFC2328 16.1. (4). For "router". */
void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
- struct ospf_area *area)
+ struct ospf_area *area, bool add_all)
{
struct route_node *rn;
struct ospf_route * or ;
@@ -388,7 +388,8 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
/* If the newly added vertex is an area border router or AS boundary
router, a routing table entry is added whose destination type is
"router". */
- if (!IS_ROUTER_LSA_BORDER(lsa) && !IS_ROUTER_LSA_EXTERNAL(lsa)) {
+ if (!add_all && !IS_ROUTER_LSA_BORDER(lsa) &&
+ !IS_ROUTER_LSA_EXTERNAL(lsa)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"ospf_intra_add_router: this router is neither ASBR nor ABR, skipping it");
@@ -733,6 +734,24 @@ void ospf_route_table_dump(struct route_table *rt)
zlog_debug("========================================");
}
+void ospf_router_route_table_dump(struct route_table *rt)
+{
+ struct route_node *rn;
+ struct ospf_route *or;
+ struct listnode *node;
+
+ zlog_debug("========== OSPF routing table ==========");
+ for (rn = route_top(rt); rn; rn = route_next(rn)) {
+ for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) {
+ assert(or->type == OSPF_DESTINATION_ROUTER);
+ zlog_debug("R %-18pI4 %-15pI4 %s %d", &rn->p.u.prefix4,
+ &or->u.std.area_id,
+ ospf_path_type_str[or->path_type], or->cost);
+ }
+ }
+ zlog_debug("========================================");
+}
+
/* This is 16.4.1 implementation.
o Intra-area paths using non-backbone areas are always the most preferred.
o The other paths, intra-area backbone paths and inter-area paths,
diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h
index 5463e70ffb..fa9478fced 100644
--- a/ospfd/ospf_route.h
+++ b/ospfd/ospf_route.h
@@ -139,9 +139,10 @@ extern void ospf_route_table_free(struct route_table *);
extern void ospf_route_install(struct ospf *, struct route_table *);
extern void ospf_route_table_dump(struct route_table *);
+extern void ospf_router_route_table_dump(struct route_table *rt);
-extern void ospf_intra_add_router(struct route_table *, struct vertex *,
- struct ospf_area *);
+extern void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
+ struct ospf_area *area, bool add_all);
extern void ospf_intra_add_transit(struct route_table *, struct vertex *,
struct ospf_area *);
diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c
index bf37556863..9727c7039c 100644
--- a/ospfd/ospf_snmp.c
+++ b/ospfd/ospf_snmp.c
@@ -693,7 +693,7 @@ static uint8_t *ospfAreaEntry(struct variable *v, oid *name, size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
area = ospfAreaLookup(v, name, length, &addr, exact);
if (!area)
@@ -821,7 +821,7 @@ static uint8_t *ospfStubAreaEntry(struct variable *v, oid *name, size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
area = ospfStubAreaLookup(v, name, length, &addr, exact);
if (!area)
@@ -1044,10 +1044,10 @@ static uint8_t *ospfLsdbEntry(struct variable *v, oid *name, size_t *length,
/* INDEX { ospfLsdbAreaId, ospfLsdbType,
ospfLsdbLsid, ospfLsdbRouterId } */
- memset(&area_id, 0, sizeof(struct in_addr));
+ memset(&area_id, 0, sizeof(area_id));
type = 0;
- memset(&ls_id, 0, sizeof(struct in_addr));
- memset(&router_id, 0, sizeof(struct in_addr));
+ memset(&ls_id, 0, sizeof(ls_id));
+ memset(&router_id, 0, sizeof(router_id));
/* Check OSPF instance. */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
@@ -1279,7 +1279,7 @@ static uint8_t *ospfHostEntry(struct variable *v, oid *name, size_t *length,
if (ospf == NULL)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
nbr_nbma = ospfHostLookup(v, name, length, &addr, exact);
if (nbr_nbma == NULL)
@@ -1595,7 +1595,7 @@ static uint8_t *ospfIfEntry(struct variable *v, oid *name, size_t *length,
return NULL;
ifindex = 0;
- memset(&ifaddr, 0, sizeof(struct in_addr));
+ memset(&ifaddr, 0, sizeof(ifaddr));
/* Check OSPF instance. */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
@@ -1742,7 +1742,7 @@ static uint8_t *ospfIfMetricEntry(struct variable *v, oid *name, size_t *length,
return NULL;
ifindex = 0;
- memset(&ifaddr, 0, sizeof(struct in_addr));
+ memset(&ifaddr, 0, sizeof(ifaddr));
/* Check OSPF instance. */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
@@ -1778,7 +1778,7 @@ static int ospf_snmp_vl_add(struct ospf_vl_data *vl_data)
struct prefix_ls lp;
struct route_node *rn;
- memset(&lp, 0, sizeof(struct prefix_ls));
+ memset(&lp, 0, sizeof(lp));
lp.family = AF_UNSPEC;
lp.prefixlen = 64;
lp.id = vl_data->vl_area_id;
@@ -1797,7 +1797,7 @@ static int ospf_snmp_vl_delete(struct ospf_vl_data *vl_data)
struct prefix_ls lp;
struct route_node *rn;
- memset(&lp, 0, sizeof(struct prefix_ls));
+ memset(&lp, 0, sizeof(lp));
lp.family = AF_UNSPEC;
lp.prefixlen = 64;
lp.id = vl_data->vl_area_id;
@@ -1819,7 +1819,7 @@ static struct ospf_vl_data *ospf_snmp_vl_lookup(struct in_addr *area_id,
struct route_node *rn;
struct ospf_vl_data *vl_data;
- memset(&lp, 0, sizeof(struct prefix_ls));
+ memset(&lp, 0, sizeof(lp));
lp.family = AF_UNSPEC;
lp.prefixlen = 64;
lp.id = *area_id;
@@ -1842,7 +1842,7 @@ static struct ospf_vl_data *ospf_snmp_vl_lookup_next(struct in_addr *area_id,
struct route_node *rn;
struct ospf_vl_data *vl_data;
- memset(&lp, 0, sizeof(struct prefix_ls));
+ memset(&lp, 0, sizeof(lp));
lp.family = AF_UNSPEC;
lp.prefixlen = 64;
lp.id = *area_id;
@@ -1927,8 +1927,8 @@ static uint8_t *ospfVirtIfEntry(struct variable *v, oid *name, size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&area_id, 0, sizeof(struct in_addr));
- memset(&neighbor, 0, sizeof(struct in_addr));
+ memset(&area_id, 0, sizeof(area_id));
+ memset(&neighbor, 0, sizeof(neighbor));
vl_data = ospfVirtIfLookup(v, name, length, &area_id, &neighbor, exact);
if (!vl_data)
@@ -2139,7 +2139,7 @@ static uint8_t *ospfNbrEntry(struct variable *v, oid *name, size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&nbr_addr, 0, sizeof(struct in_addr));
+ memset(&nbr_addr, 0, sizeof(nbr_addr));
ifindex = 0;
nbr = ospfNbrLookup(v, name, length, &nbr_addr, &ifindex, exact);
@@ -2190,8 +2190,8 @@ static uint8_t *ospfVirtNbrEntry(struct variable *v, oid *name, size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&area_id, 0, sizeof(struct in_addr));
- memset(&neighbor, 0, sizeof(struct in_addr));
+ memset(&area_id, 0, sizeof(area_id));
+ memset(&neighbor, 0, sizeof(neighbor));
/* Check OSPF instance. */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
@@ -2331,8 +2331,8 @@ static uint8_t *ospfExtLsdbEntry(struct variable *v, oid *name, size_t *length,
return NULL;
type = OSPF_AS_EXTERNAL_LSA;
- memset(&ls_id, 0, sizeof(struct in_addr));
- memset(&router_id, 0, sizeof(struct in_addr));
+ memset(&ls_id, 0, sizeof(ls_id));
+ memset(&router_id, 0, sizeof(router_id));
/* Check OSPF instance. */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index baf02365a2..44549b980c 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -48,6 +48,7 @@
#include "ospfd/ospf_sr.h"
#include "ospfd/ospf_ti_lfa.h"
#include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_apiserver.h"
/* Variables to ensure a SPF scheduled log message is printed only once */
@@ -1669,6 +1670,7 @@ void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list)
/* Calculating the shortest-path tree for an area, see RFC2328 16.1. */
void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs, bool is_dry_run,
bool is_root_node)
{
@@ -1737,10 +1739,13 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
ospf_vertex_add_parent(v);
/* RFC2328 16.1. (4). */
- if (v->type == OSPF_VERTEX_ROUTER)
- ospf_intra_add_router(new_rtrs, v, area);
- else
+ if (v->type != OSPF_VERTEX_ROUTER)
ospf_intra_add_transit(new_table, v, area);
+ else {
+ ospf_intra_add_router(new_rtrs, v, area, false);
+ if (all_rtrs)
+ ospf_intra_add_router(all_rtrs, v, area, true);
+ }
/* Iterate back to (2), see RFC2328 16.1. (5). */
}
@@ -1748,6 +1753,8 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
if (IS_DEBUG_OSPF_EVENT) {
ospf_spf_dump(area->spf, 0);
ospf_route_table_dump(new_table);
+ if (all_rtrs)
+ ospf_router_route_table_dump(all_rtrs);
}
/*
@@ -1771,10 +1778,11 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs)
{
- ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
- false, true);
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
+ new_rtrs, false, true);
if (ospf->ti_lfa_enabled)
ospf_ti_lfa_compute(area, new_table,
@@ -1787,6 +1795,7 @@ void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
}
void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs)
{
struct ospf_area *area;
@@ -1799,13 +1808,14 @@ void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
if (ospf->backbone && ospf->backbone == area)
continue;
- ospf_spf_calculate_area(ospf, area, new_table, new_rtrs);
+ ospf_spf_calculate_area(ospf, area, new_table, all_rtrs,
+ new_rtrs);
}
/* SPF for backbone, if required */
if (ospf->backbone)
ospf_spf_calculate_area(ospf, ospf->backbone, new_table,
- new_rtrs);
+ all_rtrs, new_rtrs);
}
/* Worker for SPF calculation scheduler. */
@@ -1813,6 +1823,7 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
{
struct ospf *ospf = THREAD_ARG(thread);
struct route_table *new_table, *new_rtrs;
+ struct route_table *all_rtrs = NULL;
struct timeval start_time, spf_start_time;
unsigned long ia_time, prune_time, rt_time;
unsigned long abr_time, total_spf_time, spf_time;
@@ -1829,7 +1840,12 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
monotime(&spf_start_time);
new_table = route_table_init(); /* routing table */
new_rtrs = route_table_init(); /* ABR/ASBR routing table */
- ospf_spf_calculate_areas(ospf, new_table, new_rtrs);
+
+ /* If we have opaque enabled then track all router reachability */
+ if (CHECK_FLAG(ospf->opaque, OPAQUE_OPERATION_READY_BIT))
+ all_rtrs = route_table_init();
+
+ ospf_spf_calculate_areas(ospf, new_table, all_rtrs, new_rtrs);
spf_time = monotime_since(&spf_start_time, NULL);
ospf_vl_shut_unapproved(ospf);
@@ -1842,6 +1858,8 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
/* Get rid of transit networks and routers we cannot reach anyway. */
monotime(&start_time);
ospf_prune_unreachable_networks(new_table);
+ if (all_rtrs)
+ ospf_prune_unreachable_routers(all_rtrs);
ospf_prune_unreachable_routers(new_rtrs);
prune_time = monotime_since(&start_time, NULL);
@@ -1866,6 +1884,16 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
ospf_route_install(ospf, new_table);
rt_time = monotime_since(&start_time, NULL);
+ /* Free old all routers routing table */
+ if (ospf->oall_rtrs)
+ /* ospf_route_delete (ospf->old_rtrs); */
+ ospf_rtrs_free(ospf->oall_rtrs);
+
+ /* Update all routers routing table */
+ ospf->oall_rtrs = ospf->all_rtrs;
+ ospf->all_rtrs = all_rtrs;
+ ospf_apiserver_notify_reachable(ospf->oall_rtrs, ospf->all_rtrs);
+
/* Free old ABR/ASBR routing table */
if (ospf->old_rtrs)
/* ospf_route_delete (ospf->old_rtrs); */
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
index 20f38440aa..834bfd0bb0 100644
--- a/ospfd/ospf_spf.h
+++ b/ospfd/ospf_spf.h
@@ -76,13 +76,16 @@ extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t);
extern void ospf_spf_calculate(struct ospf_area *area,
struct ospf_lsa *root_lsa,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs, bool is_dry_run,
bool is_root_node);
extern void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs);
extern void ospf_spf_calculate_areas(struct ospf *ospf,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs);
extern void ospf_rtrs_free(struct route_table *);
extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list);
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index 090a655cee..8fa5ce77bb 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -608,7 +608,7 @@ int ospf_sr_init(void)
osr_debug("SR (%s): Initialize SR Data Base", __func__);
- memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
+ memset(&OspfSR, 0, sizeof(OspfSR));
OspfSR.status = SR_OFF;
/* Only AREA flooding is supported in this release */
OspfSR.scope = OSPF_OPAQUE_AREA_LSA;
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index ddc62982bd..75f4e0c9f0 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -159,7 +159,7 @@ int ospf_mpls_te_init(void)
return rc;
}
- memset(&OspfMplsTE, 0, sizeof(struct ospf_mpls_te));
+ memset(&OspfMplsTE, 0, sizeof(OspfMplsTE));
OspfMplsTE.enabled = false;
OspfMplsTE.export = false;
OspfMplsTE.inter_as = Off;
diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c
index 347128a4f4..28d24bcbe6 100644
--- a/ospfd/ospf_ti_lfa.c
+++ b/ospfd/ospf_ti_lfa.c
@@ -326,8 +326,8 @@ static void ospf_ti_lfa_generate_inner_label_stack(
XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head));
/* dry run true, root node false */
- ospf_spf_calculate(area, start_vertex->lsa_p, new_table, new_rtrs, true,
- false);
+ ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, new_rtrs,
+ true, false);
q_node = ospf_spf_vertex_find(end_vertex->id, area->spf_vertex_list);
@@ -676,6 +676,7 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
sizeof(struct ospf_ti_lfa_node_info));
new_table = route_table_init();
+ /* XXX do these get freed?? */
new_rtrs = route_table_init();
/*
@@ -683,7 +684,8 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
* dry run true, root node false
*/
area->spf_reversed = true;
- ospf_spf_calculate(area, dest->lsa_p, new_table, new_rtrs, true, false);
+ ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, new_rtrs, true,
+ false);
/* Reset the flag for reverse SPF */
area->spf_reversed = false;
@@ -750,6 +752,7 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
struct route_table *new_table, *new_rtrs;
new_table = route_table_init();
+ /* XXX do these get freed?? */
new_rtrs = route_table_init();
area->spf_protected_resource = p_space->protected_resource;
@@ -769,8 +772,8 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
* endeavour (because LSAs are stored as a 'raw' stream), so we go with
* this rather hacky way for now.
*/
- ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
- true, false);
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL,
+ new_rtrs, true, false);
p_space->pc_spf = area->spf;
p_space->pc_vertex_list = area->spf_vertex_list;
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 3bd4a9bb68..e5985a8660 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -10036,7 +10036,7 @@ DEFUN (ospf_external_route_aggregation,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!is_valid_summary_addr(&p)) {
vty_out(vty, "Not a valid summary address.\n");
@@ -10077,7 +10077,7 @@ DEFUN (no_ospf_external_route_aggregation,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!is_valid_summary_addr(&p)) {
vty_out(vty, "Not a valid summary address.\n");
@@ -10377,7 +10377,7 @@ DEFUN (ospf_external_route_aggregation_no_adrvertise,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!is_valid_summary_addr(&p)) {
vty_out(vty, "Not a valid summary address.\n");
@@ -10413,7 +10413,7 @@ DEFUN (no_ospf_external_route_aggregation_no_adrvertise,
}
/* Apply mask for given prefix. */
- apply_mask((struct prefix *)&p);
+ apply_mask(&p);
if (!is_valid_summary_addr(&p)) {
vty_out(vty, "Not a valid summary address.\n");
@@ -10741,8 +10741,9 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf,
*json_nexthop = NULL;
if (!json)
- vty_out(vty,
- "============ OSPF router routing table =============\n");
+ vty_out(vty, "============ OSPF %s table =============\n",
+ ospf->all_rtrs == rtrs ? "reachable routers"
+ : "router routing");
for (rn = route_top(rtrs); rn; rn = route_next(rn)) {
if (rn->info == NULL)
@@ -11004,6 +11005,114 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf,
vty_out(vty, "\n");
}
+static int show_ip_ospf_reachable_routers_common(struct vty *vty,
+ struct ospf *ospf,
+ uint8_t use_vrf)
+{
+ if (ospf->instance)
+ vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance);
+
+ ospf_show_vrf_name(ospf, vty, NULL, use_vrf);
+
+ if (ospf->all_rtrs == NULL) {
+ vty_out(vty, "No OSPF reachable router information exist\n");
+ return CMD_SUCCESS;
+ }
+
+ /* Show Router routes. */
+ show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, NULL);
+
+ vty_out(vty, "\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_reachable_routers,
+ show_ip_ospf_reachable_routers_cmd,
+ "show ip ospf [vrf <NAME|all>] reachable-routers",
+ SHOW_STR
+ IP_STR
+ "OSPF information\n"
+ VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "Show all the reachable OSPF routers\n")
+{
+ struct ospf *ospf = NULL;
+ struct listnode *node = NULL;
+ char *vrf_name = NULL;
+ bool all_vrf = false;
+ int ret = CMD_SUCCESS;
+ int inst = 0;
+ int idx_vrf = 0;
+ uint8_t use_vrf = 0;
+
+ OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+ if (vrf_name) {
+ bool ospf_output = false;
+
+ use_vrf = 1;
+
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
+ if (!ospf->oi_running)
+ continue;
+
+ ospf_output = true;
+ ret = show_ip_ospf_reachable_routers_common(
+ vty, ospf, use_vrf);
+ }
+
+ if (!ospf_output)
+ vty_out(vty, "%% OSPF instance not found\n");
+ } else {
+ ospf = ospf_lookup_by_inst_name(inst, vrf_name);
+ if (ospf == NULL || !ospf->oi_running) {
+ vty_out(vty, "%% OSPF instance not found\n");
+ return CMD_SUCCESS;
+ }
+
+ ret = show_ip_ospf_reachable_routers_common(vty, ospf,
+ use_vrf);
+ }
+ } else {
+ /* Display default ospf (instance 0) info */
+ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ if (ospf == NULL || !ospf->oi_running) {
+ vty_out(vty, "%% OSPF instance not found\n");
+ return CMD_SUCCESS;
+ }
+
+ ret = show_ip_ospf_reachable_routers_common(vty, ospf, use_vrf);
+ }
+
+ return ret;
+}
+
+DEFUN (show_ip_ospf_instance_reachable_routers,
+ show_ip_ospf_instance_reachable_routers_cmd,
+ "show ip ospf (1-65535) reachable-routers",
+ SHOW_STR
+ IP_STR
+ "OSPF information\n"
+ "Instance ID\n"
+ "Show all the reachable OSPF routers\n")
+{
+ int idx_number = 3;
+ struct ospf *ospf;
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ ospf = ospf_lookup_instance(instance);
+ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return show_ip_ospf_reachable_routers_common(vty, ospf, 0);
+}
+
static int show_ip_ospf_border_routers_common(struct vty *vty,
struct ospf *ospf,
uint8_t use_vrf)
@@ -11146,6 +11255,10 @@ static int show_ip_ospf_route_common(struct vty *vty, struct ospf *ospf,
/* Show Router routes. */
show_ip_ospf_route_router(vty, ospf, ospf->new_rtrs, json_vrf);
+ /* Show Router routes. */
+ if (ospf->all_rtrs)
+ show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, json_vrf);
+
/* Show AS External routes. */
show_ip_ospf_route_external(vty, ospf, ospf->old_external_route,
json_vrf);
@@ -12603,9 +12716,12 @@ void ospf_vty_show_init(void)
/* "show ip ospf route" commands. */
install_element(VIEW_NODE, &show_ip_ospf_route_cmd);
install_element(VIEW_NODE, &show_ip_ospf_border_routers_cmd);
+ install_element(VIEW_NODE, &show_ip_ospf_reachable_routers_cmd);
install_element(VIEW_NODE, &show_ip_ospf_instance_route_cmd);
install_element(VIEW_NODE, &show_ip_ospf_instance_border_routers_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_ospf_instance_reachable_routers_cmd);
/* "show ip ospf vrfs" commands. */
install_element(VIEW_NODE, &show_ip_ospf_vrfs_cmd);
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 496d85fd7b..46eb625383 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -1253,7 +1253,7 @@ static int ospf_zebra_gr_update(struct ospf *ospf, int command,
if (!zclient || zclient->sock < 0 || !ospf)
return 1;
- memset(&api, 0, sizeof(struct zapi_cap));
+ memset(&api, 0, sizeof(api));
api.cap = command;
api.stale_removal_time = stale_time;
api.vrf_id = ospf->vrf_id;
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 221f6e7e4e..337456ecd8 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -2114,7 +2114,7 @@ int ospf_nbr_nbma_poll_interval_unset(struct ospf *ospf, struct in_addr addr)
void ospf_master_init(struct thread_master *master)
{
- memset(&ospf_master, 0, sizeof(struct ospf_master));
+ memset(&ospf_master, 0, sizeof(ospf_master));
om = &ospf_master;
om->ospf = list_new();
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 268e4d6f8d..76501dd6bb 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -234,6 +234,9 @@ struct ospf {
struct route_table *old_table; /* Old routing table. */
struct route_table *new_table; /* Current routing table. */
+ struct route_table *oall_rtrs; /* Old router RT. */
+ struct route_table *all_rtrs; /* New routers RT. */
+
struct route_table *old_rtrs; /* Old ABR/ASBR RT. */
struct route_table *new_rtrs; /* New ABR/ASBR RT. */
diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c
index a6f253d3e3..d2b49a7d95 100644
--- a/pathd/path_pcep_cli.c
+++ b/pathd/path_pcep_cli.c
@@ -1027,7 +1027,7 @@ static int path_pcep_cli_pcc_pcc_peer(struct vty *vty, const char *peer_name,
/* Verify the PCE has the IP set */
struct in6_addr zero_v6_addr;
- memset(&zero_v6_addr, 0, sizeof(struct in6_addr));
+ memset(&zero_v6_addr, 0, sizeof(zero_v6_addr));
if (memcmp(&pce_opts->addr.ip, &zero_v6_addr, IPADDRSZ(&pce_opts->addr))
== 0) {
vty_out(vty,
diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c
index c46e859c49..b5a65d457b 100644
--- a/pceplib/pcep_msg_tlvs_encoding.c
+++ b/pceplib/pcep_msg_tlvs_encoding.c
@@ -1185,8 +1185,7 @@ pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr,
return (struct pcep_object_tlv_header *)ipv4;
} else {
ipv4->is_ipv4 = false;
- struct pcep_object_tlv_srpag_pol_id *ipv6 =
- (struct pcep_object_tlv_srpag_pol_id *)ipv4;
+ struct pcep_object_tlv_srpag_pol_id *ipv6 = ipv4;
ipv6->color = ntohl(uint32_ptr[0]);
decode_ipv6(&uint32_ptr[1], &ipv6->end_point.ipv6);
return (struct pcep_object_tlv_header *)ipv6;
diff --git a/pceplib/pcep_pcc.c b/pceplib/pcep_pcc.c
index 649704f55b..18ccf250ae 100644
--- a/pceplib/pcep_pcc.c
+++ b/pceplib/pcep_pcc.c
@@ -215,7 +215,7 @@ void handle_signal_action(int sig_number)
int setup_signals()
{
struct sigaction sa;
- memset(&sa, 0, sizeof(struct sigaction));
+ memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_signal_action;
if (sigaction(SIGINT, &sa, 0) != 0) {
perror("sigaction()");
diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c
index 289772260c..77844988ea 100644
--- a/pimd/pim6_cmd.c
+++ b/pimd/pim6_cmd.c
@@ -40,12 +40,22 @@
#include "pim_nb.h"
#include "pim_addr.h"
#include "pim_nht.h"
-
+#include "pim_bsm.h"
+#include "pim_iface.h"
+#include "pim_zebra.h"
+#include "pim_instance.h"
#ifndef VTYSH_EXTRACT_PL
#include "pimd/pim6_cmd_clippy.c"
#endif
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = pim_debug_config_write,
+};
+
DEFPY (ipv6_pim_joinprune_time,
ipv6_pim_joinprune_time_cmd,
"ipv6 pim join-prune-interval (1-65535)$jpi",
@@ -215,20 +225,35 @@ DEFPY (no_ipv6_pim_register_suppress,
DEFPY (interface_ipv6_pim,
interface_ipv6_pim_cmd,
- "ipv6 pim",
+ "ipv6 pim [passive$passive]",
IPV6_STR
- PIM_STR)
+ PIM_STR
+ "Disable exchange of protocol packets\n")
{
- return pim_process_ip_pim_cmd(vty);
+ int ret;
+
+ ret = pim_process_ip_pim_cmd(vty);
+
+ if (ret != NB_OK)
+ return ret;
+
+ if (passive)
+ return pim_process_ip_pim_passive_cmd(vty, true);
+
+ return CMD_SUCCESS;
}
DEFPY (interface_no_ipv6_pim,
interface_no_ipv6_pim_cmd,
- "no ipv6 pim",
+ "no ipv6 pim [passive$passive]",
NO_STR
IPV6_STR
- PIM_STR)
+ PIM_STR
+ "Disable exchange of protocol packets\n")
{
+ if (passive)
+ return pim_process_ip_pim_passive_cmd(vty, false);
+
return pim_process_no_ip_pim_cmd(vty);
}
@@ -691,7 +716,7 @@ DEFPY (interface_ipv6_mld_query_max_response_time,
IPV6_STR
IFACE_MLD_STR
IFACE_MLD_QUERY_MAX_RESPONSE_TIME_STR
- "Query response value in deci-seconds\n")
+ "Query response value in milliseconds\n")
{
return gm_process_query_max_response_time_cmd(vty, qmrt_str);
}
@@ -1919,10 +1944,235 @@ DEFPY (show_ipv6_mroute_summary_vrf_all,
return CMD_SUCCESS;
}
+DEFPY (clear_ipv6_pim_statistics,
+ clear_ipv6_pim_statistics_cmd,
+ "clear ipv6 pim statistics [vrf NAME]$name",
+ CLEAR_STR
+ IPV6_STR
+ CLEAR_IP_PIM_STR
+ VRF_CMD_HELP_STR
+ "Reset PIM statistics\n")
+{
+ struct vrf *v = pim_cmd_lookup(vty, name);
+
+ if (!v)
+ return CMD_WARNING;
+
+ clear_pim_statistics(v->info);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (clear_ipv6_mroute,
+ clear_ipv6_mroute_cmd,
+ "clear ipv6 mroute [vrf NAME]$name",
+ CLEAR_STR
+ IPV6_STR
+ "Reset multicast routes\n"
+ VRF_CMD_HELP_STR)
+{
+ struct vrf *v = pim_cmd_lookup(vty, name);
+
+ if (!v)
+ return CMD_WARNING;
+
+ clear_mroute(v->info);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (clear_ipv6_pim_oil,
+ clear_ipv6_pim_oil_cmd,
+ "clear ipv6 pim [vrf NAME]$name oil",
+ CLEAR_STR
+ IPV6_STR
+ CLEAR_IP_PIM_STR
+ VRF_CMD_HELP_STR
+ "Rescan PIMv6 OIL (output interface list)\n")
+{
+ struct vrf *v = pim_cmd_lookup(vty, name);
+
+ if (!v)
+ return CMD_WARNING;
+
+ pim_scan_oil(v->info);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (clear_ipv6_mroute_count,
+ clear_ipv6_mroute_count_cmd,
+ "clear ipv6 mroute [vrf NAME]$name count",
+ CLEAR_STR
+ IPV6_STR
+ MROUTE_STR
+ VRF_CMD_HELP_STR
+ "Route and packet count data\n")
+{
+ return clear_ip_mroute_count_command(vty, name);
+}
+
+DEFPY (debug_pimv6,
+ debug_pimv6_cmd,
+ "[no] debug pimv6",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR)
+{
+ if (!no)
+ return pim_debug_pim_cmd();
+ else
+ return pim_no_debug_pim_cmd();
+}
+
+DEFPY (debug_pimv6_nht,
+ debug_pimv6_nht_cmd,
+ "[no] debug pimv6 nht",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ "Nexthop Tracking\n")
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_NHT;
+ else
+ PIM_DONT_DEBUG_PIM_NHT;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_nht_det,
+ debug_pimv6_nht_det_cmd,
+ "[no] debug pimv6 nht detail",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ "Nexthop Tracking\n"
+ "Detailed Information\n")
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_NHT_DETAIL;
+ else
+ PIM_DONT_DEBUG_PIM_NHT_DETAIL;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_events,
+ debug_pimv6_events_cmd,
+ "[no] debug pimv6 events",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_EVENTS_STR)
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_EVENTS;
+ else
+ PIM_DONT_DEBUG_PIM_EVENTS;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_packets,
+ debug_pimv6_packets_cmd,
+ "[no] debug pimv6 packets [<hello$hello|joins$joins|register$registers>]",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_PACKETS_STR
+ DEBUG_PIMV6_HELLO_PACKETS_STR
+ DEBUG_PIMV6_J_P_PACKETS_STR
+ DEBUG_PIMV6_PIM_REG_PACKETS_STR)
+{
+ if (!no)
+ return pim_debug_pim_packets_cmd(hello, joins, registers, vty);
+ else
+ return pim_no_debug_pim_packets_cmd(hello, joins, registers,
+ vty);
+}
+
+DEFPY (debug_pimv6_packetdump_send,
+ debug_pimv6_packetdump_send_cmd,
+ "[no] debug pimv6 packet-dump send",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_PACKETDUMP_STR
+ DEBUG_PIMV6_PACKETDUMP_SEND_STR)
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_PACKETDUMP_SEND;
+ else
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_packetdump_recv,
+ debug_pimv6_packetdump_recv_cmd,
+ "[no] debug pimv6 packet-dump receive",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_PACKETDUMP_STR
+ DEBUG_PIMV6_PACKETDUMP_RECV_STR)
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_PACKETDUMP_RECV;
+ else
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_trace,
+ debug_pimv6_trace_cmd,
+ "[no] debug pimv6 trace",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_TRACE_STR)
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_TRACE;
+ else
+ PIM_DONT_DEBUG_PIM_TRACE;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_trace_detail,
+ debug_pimv6_trace_detail_cmd,
+ "[no] debug pimv6 trace detail",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_TRACE_STR
+ "Detailed Information\n")
+{
+ if (!no)
+ PIM_DO_DEBUG_PIM_TRACE_DETAIL;
+ else
+ PIM_DONT_DEBUG_PIM_TRACE_DETAIL;
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_pimv6_zebra,
+ debug_pimv6_zebra_cmd,
+ "[no] debug pimv6 zebra",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIMV6_STR
+ DEBUG_PIMV6_ZEBRA_STR)
+{
+ if (!no)
+ PIM_DO_DEBUG_ZEBRA;
+ else
+ PIM_DONT_DEBUG_ZEBRA;
+ return CMD_SUCCESS;
+}
+
void pim_cmd_init(void)
{
if_cmd_init(pim_interface_config_write);
+ install_node(&debug_node);
+
install_element(CONFIG_NODE, &ipv6_pim_joinprune_time_cmd);
install_element(CONFIG_NODE, &no_ipv6_pim_joinprune_time_cmd);
install_element(CONFIG_NODE, &ipv6_pim_spt_switchover_infinity_cmd);
@@ -2025,4 +2275,30 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ipv6_mroute_count_vrf_all_cmd);
install_element(VIEW_NODE, &show_ipv6_mroute_summary_cmd);
install_element(VIEW_NODE, &show_ipv6_mroute_summary_vrf_all_cmd);
+
+ install_element(ENABLE_NODE, &clear_ipv6_pim_statistics_cmd);
+ install_element(ENABLE_NODE, &clear_ipv6_mroute_cmd);
+ install_element(ENABLE_NODE, &clear_ipv6_pim_oil_cmd);
+ install_element(ENABLE_NODE, &clear_ipv6_mroute_count_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_nht_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_nht_det_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_events_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_packets_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_packetdump_send_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_packetdump_recv_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_trace_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_trace_detail_cmd);
+ install_element(ENABLE_NODE, &debug_pimv6_zebra_cmd);
+
+ install_element(CONFIG_NODE, &debug_pimv6_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_nht_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_nht_det_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_events_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_packets_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_packetdump_send_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_packetdump_recv_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_trace_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_trace_detail_cmd);
+ install_element(CONFIG_NODE, &debug_pimv6_zebra_cmd);
}
diff --git a/pimd/pim6_cmd.h b/pimd/pim6_cmd.h
index d6853a7410..8fb82d9f26 100644
--- a/pimd/pim6_cmd.h
+++ b/pimd/pim6_cmd.h
@@ -39,11 +39,24 @@
#define IFACE_PIM_HELLO_TIME_STR "Time in seconds for Hello Interval\n"
#define IFACE_PIM_HELLO_HOLD_STR "Time in seconds for Hold Interval\n"
#define MROUTE_STR "IP multicast routing table\n"
+#define CLEAR_IP_PIM_STR "PIM clear commands\n"
#define DEBUG_MLD_STR "MLD protocol activity\n"
#define DEBUG_MLD_EVENTS_STR "MLD protocol events\n"
#define DEBUG_MLD_PACKETS_STR "MLD protocol packets\n"
#define DEBUG_MLD_TRACE_STR "MLD internal daemon activity\n"
#define CONF_SSMPINGD_STR "Enable ssmpingd operation\n"
+#define DEBUG_PIMV6_STR "PIMv6 protocol activity\n"
+#define DEBUG_PIMV6_EVENTS_STR "PIMv6 protocol events\n"
+#define DEBUG_PIMV6_PACKETS_STR "PIMv6 protocol packets\n"
+#define DEBUG_PIMV6_HELLO_PACKETS_STR "PIMv6 Hello protocol packets\n"
+#define DEBUG_PIMV6_J_P_PACKETS_STR "PIMv6 Join/Prune protocol packets\n"
+#define DEBUG_PIMV6_PIM_REG_PACKETS_STR \
+ "PIMv6 Register/Reg-Stop protocol packets\n"
+#define DEBUG_PIMV6_PACKETDUMP_STR "PIMv6 packet dump\n"
+#define DEBUG_PIMV6_PACKETDUMP_SEND_STR "Dump sent packets\n"
+#define DEBUG_PIMV6_PACKETDUMP_RECV_STR "Dump received packets\n"
+#define DEBUG_PIMV6_TRACE_STR "PIMv6 internal daemon activity\n"
+#define DEBUG_PIMV6_ZEBRA_STR "ZEBRA protocol activity\n"
void pim_cmd_init(void);
diff --git a/pimd/pim6_main.c b/pimd/pim6_main.c
index ed53924616..b3f4e4256c 100644
--- a/pimd/pim6_main.c
+++ b/pimd/pim6_main.c
@@ -38,6 +38,7 @@
#include "pim_zebra.h"
#include "pim_nb.h"
#include "pim6_cmd.h"
+#include "pim6_mld.h"
zebra_capabilities_t _caps_p[] = {
ZCAP_SYS_ADMIN,
@@ -133,7 +134,6 @@ FRR_DAEMON_INFO(pim6d, PIM6,
);
/* clang-format on */
-
int main(int argc, char **argv, char **envp)
{
static struct option longopts[] = {
@@ -184,6 +184,8 @@ int main(int argc, char **argv, char **envp)
*/
pim_iface_init();
+ gm_cli_init();
+
pim_zebra_init();
#if 0
pim_bfd_init();
diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c
new file mode 100644
index 0000000000..a67d33ca97
--- /dev/null
+++ b/pimd/pim6_mld.c
@@ -0,0 +1,3011 @@
+/*
+ * PIMv6 MLD querier
+ * Copyright (C) 2021-2022 David Lamparter for NetDEF, 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
+ */
+
+/*
+ * keep pim6_mld.h open when working on this code. Most data structures are
+ * commented in the header.
+ *
+ * IPv4 support is pre-planned but hasn't been tackled yet. It is intended
+ * that this code will replace the old IGMP querier at some point.
+ */
+
+#include <zebra.h>
+#include <netinet/ip6.h>
+
+#include "lib/memory.h"
+#include "lib/jhash.h"
+#include "lib/prefix.h"
+#include "lib/checksum.h"
+#include "lib/thread.h"
+
+#include "pimd/pim6_mld.h"
+#include "pimd/pim6_mld_protocol.h"
+#include "pimd/pim_memory.h"
+#include "pimd/pim_instance.h"
+#include "pimd/pim_iface.h"
+#include "pimd/pim_util.h"
+#include "pimd/pim_tib.h"
+#include "pimd/pimd.h"
+
+#ifndef IPV6_MULTICAST_ALL
+#define IPV6_MULTICAST_ALL 29
+#endif
+
+DEFINE_MTYPE_STATIC(PIMD, GM_IFACE, "MLD interface");
+DEFINE_MTYPE_STATIC(PIMD, GM_PACKET, "MLD packet");
+DEFINE_MTYPE_STATIC(PIMD, GM_SUBSCRIBER, "MLD subscriber");
+DEFINE_MTYPE_STATIC(PIMD, GM_STATE, "MLD subscription state");
+DEFINE_MTYPE_STATIC(PIMD, GM_SG, "MLD (S,G)");
+DEFINE_MTYPE_STATIC(PIMD, GM_GRP_PENDING, "MLD group query state");
+DEFINE_MTYPE_STATIC(PIMD, GM_GSQ_PENDING, "MLD group/source query aggregate");
+
+static void gm_t_query(struct thread *t);
+static void gm_trigger_specific(struct gm_sg *sg);
+static void gm_sg_timer_start(struct gm_if *gm_ifp, struct gm_sg *sg,
+ struct timeval expire_wait);
+
+/* shorthand for log messages */
+#define log_ifp(msg) \
+ "[MLD %s:%s] " msg, gm_ifp->ifp->vrf->name, gm_ifp->ifp->name
+#define log_pkt_src(msg) \
+ "[MLD %s:%s %pI6] " msg, gm_ifp->ifp->vrf->name, gm_ifp->ifp->name, \
+ &pkt_src->sin6_addr
+#define log_sg(sg, msg) \
+ "[MLD %s:%s %pSG] " msg, sg->iface->ifp->vrf->name, \
+ sg->iface->ifp->name, &sg->sgaddr
+
+/* clang-format off */
+#if PIM_IPV == 6
+static const pim_addr gm_all_hosts = {
+ .s6_addr = {
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ },
+};
+static const pim_addr gm_all_routers = {
+ .s6_addr = {
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ },
+};
+/* MLDv1 does not allow subscriber tracking due to report suppression
+ * hence, the source address is replaced with ffff:...:ffff
+ */
+static const pim_addr gm_dummy_untracked = {
+ .s6_addr = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+};
+#else
+/* 224.0.0.1 */
+static const pim_addr gm_all_hosts = { .s_addr = htonl(0xe0000001), };
+/* 224.0.0.22 */
+static const pim_addr gm_all_routers = { .s_addr = htonl(0xe0000016), };
+static const pim_addr gm_dummy_untracked = { .s_addr = 0xffffffff, };
+#endif
+/* clang-format on */
+
+#define IPV6_MULTICAST_SCOPE_LINK 2
+
+static inline uint8_t in6_multicast_scope(const pim_addr *addr)
+{
+ return addr->s6_addr[1] & 0xf;
+}
+
+static inline bool in6_multicast_nofwd(const pim_addr *addr)
+{
+ return in6_multicast_scope(addr) <= IPV6_MULTICAST_SCOPE_LINK;
+}
+
+/*
+ * (S,G) -> subscriber,(S,G)
+ */
+
+static int gm_packet_sg_cmp(const struct gm_packet_sg *a,
+ const struct gm_packet_sg *b)
+{
+ const struct gm_packet_state *s_a, *s_b;
+
+ s_a = gm_packet_sg2state(a);
+ s_b = gm_packet_sg2state(b);
+ return IPV6_ADDR_CMP(&s_a->subscriber->addr, &s_b->subscriber->addr);
+}
+
+DECLARE_RBTREE_UNIQ(gm_packet_sg_subs, struct gm_packet_sg, subs_itm,
+ gm_packet_sg_cmp);
+
+static struct gm_packet_sg *gm_packet_sg_find(struct gm_sg *sg,
+ enum gm_sub_sense sense,
+ struct gm_subscriber *sub)
+{
+ struct {
+ struct gm_packet_state hdr;
+ struct gm_packet_sg item;
+ } ref = {
+ /* clang-format off */
+ .hdr = {
+ .subscriber = sub,
+ },
+ .item = {
+ .offset = 0,
+ },
+ /* clang-format on */
+ };
+
+ return gm_packet_sg_subs_find(&sg->subs[sense], &ref.item);
+}
+
+/*
+ * interface -> (*,G),pending
+ */
+
+static int gm_grp_pending_cmp(const struct gm_grp_pending *a,
+ const struct gm_grp_pending *b)
+{
+ return IPV6_ADDR_CMP(&a->grp, &b->grp);
+}
+
+DECLARE_RBTREE_UNIQ(gm_grp_pends, struct gm_grp_pending, itm,
+ gm_grp_pending_cmp);
+
+/*
+ * interface -> ([S1,S2,...],G),pending
+ */
+
+static int gm_gsq_pending_cmp(const struct gm_gsq_pending *a,
+ const struct gm_gsq_pending *b)
+{
+ if (a->s_bit != b->s_bit)
+ return numcmp(a->s_bit, b->s_bit);
+
+ return IPV6_ADDR_CMP(&a->grp, &b->grp);
+}
+
+static uint32_t gm_gsq_pending_hash(const struct gm_gsq_pending *a)
+{
+ uint32_t seed = a->s_bit ? 0x68f0eb5e : 0x156b7f19;
+
+ return jhash(&a->grp, sizeof(a->grp), seed);
+}
+
+DECLARE_HASH(gm_gsq_pends, struct gm_gsq_pending, itm, gm_gsq_pending_cmp,
+ gm_gsq_pending_hash);
+
+/*
+ * interface -> (S,G)
+ */
+
+static int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b)
+{
+ return pim_sgaddr_cmp(a->sgaddr, b->sgaddr);
+}
+
+DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp);
+
+static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp,
+ pim_addr src)
+{
+ struct gm_sg ref = {};
+
+ ref.sgaddr.grp = grp;
+ ref.sgaddr.src = src;
+ return gm_sgs_find(gm_ifp->sgs, &ref);
+}
+
+static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp,
+ pim_addr src)
+{
+ struct gm_sg *ret, *prev;
+
+ ret = XCALLOC(MTYPE_GM_SG, sizeof(*ret));
+ ret->sgaddr.grp = grp;
+ ret->sgaddr.src = src;
+ ret->iface = gm_ifp;
+ prev = gm_sgs_add(gm_ifp->sgs, ret);
+
+ if (prev) {
+ XFREE(MTYPE_GM_SG, ret);
+ ret = prev;
+ } else {
+ monotime(&ret->created);
+ gm_packet_sg_subs_init(ret->subs_positive);
+ gm_packet_sg_subs_init(ret->subs_negative);
+ }
+ return ret;
+}
+
+/*
+ * interface -> packets, sorted by expiry (because add_tail insert order)
+ */
+
+DECLARE_DLIST(gm_packet_expires, struct gm_packet_state, exp_itm);
+
+/*
+ * subscriber -> packets
+ */
+
+DECLARE_DLIST(gm_packets, struct gm_packet_state, pkt_itm);
+
+/*
+ * interface -> subscriber
+ */
+
+static int gm_subscriber_cmp(const struct gm_subscriber *a,
+ const struct gm_subscriber *b)
+{
+ return IPV6_ADDR_CMP(&a->addr, &b->addr);
+}
+
+static uint32_t gm_subscriber_hash(const struct gm_subscriber *a)
+{
+ return jhash(&a->addr, sizeof(a->addr), 0xd0e94ad4);
+}
+
+DECLARE_HASH(gm_subscribers, struct gm_subscriber, itm, gm_subscriber_cmp,
+ gm_subscriber_hash);
+
+static struct gm_subscriber *gm_subscriber_findref(struct gm_if *gm_ifp,
+ pim_addr addr)
+{
+ struct gm_subscriber ref = {}, *ret;
+
+ ref.addr = addr;
+ ret = gm_subscribers_find(gm_ifp->subscribers, &ref);
+ if (ret)
+ ret->refcount++;
+ return ret;
+}
+
+static struct gm_subscriber *gm_subscriber_get(struct gm_if *gm_ifp,
+ pim_addr addr)
+{
+ struct gm_subscriber ref = {}, *ret;
+
+ ref.addr = addr;
+ ret = gm_subscribers_find(gm_ifp->subscribers, &ref);
+
+ if (!ret) {
+ ret = XCALLOC(MTYPE_GM_SUBSCRIBER, sizeof(*ret));
+ ret->iface = gm_ifp;
+ ret->addr = addr;
+ ret->refcount = 1;
+ monotime(&ret->created);
+ gm_packets_init(ret->packets);
+
+ gm_subscribers_add(gm_ifp->subscribers, ret);
+ }
+ return ret;
+}
+
+static void gm_subscriber_drop(struct gm_subscriber **subp)
+{
+ struct gm_subscriber *sub = *subp;
+ struct gm_if *gm_ifp;
+
+ if (!sub)
+ return;
+ gm_ifp = sub->iface;
+
+ *subp = NULL;
+ sub->refcount--;
+
+ if (sub->refcount)
+ return;
+
+ gm_subscribers_del(gm_ifp->subscribers, sub);
+ XFREE(MTYPE_GM_SUBSCRIBER, sub);
+}
+
+/****************************************************************************/
+
+/* bundle query timer values for combined v1/v2 handling */
+struct gm_query_timers {
+ unsigned int qrv;
+ unsigned int max_resp_ms;
+ unsigned int qqic_ms;
+
+ struct timeval fuzz;
+ struct timeval expire_wait;
+};
+
+static void gm_expiry_calc(struct gm_query_timers *timers)
+{
+ unsigned int expire =
+ (timers->qrv - 1) * timers->qqic_ms + timers->max_resp_ms;
+ ldiv_t exp_div = ldiv(expire, 1000);
+
+ timers->expire_wait.tv_sec = exp_div.quot;
+ timers->expire_wait.tv_usec = exp_div.rem * 1000;
+ timeradd(&timers->expire_wait, &timers->fuzz, &timers->expire_wait);
+}
+
+static void gm_sg_free(struct gm_sg *sg)
+{
+ /* t_sg_expiry is handled before this is reached */
+ THREAD_OFF(sg->t_sg_query);
+ gm_packet_sg_subs_fini(sg->subs_negative);
+ gm_packet_sg_subs_fini(sg->subs_positive);
+ XFREE(MTYPE_GM_SG, sg);
+}
+
+/* clang-format off */
+static const char *const gm_states[] = {
+ [GM_SG_NOINFO] = "NOINFO",
+ [GM_SG_JOIN] = "JOIN",
+ [GM_SG_JOIN_EXPIRING] = "JOIN_EXPIRING",
+ [GM_SG_PRUNE] = "PRUNE",
+ [GM_SG_NOPRUNE] = "NOPRUNE",
+ [GM_SG_NOPRUNE_EXPIRING] = "NOPRUNE_EXPIRING",
+};
+/* clang-format on */
+
+CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported");
+/* tib_sg_gm_prune() below is an "un-join", it doesn't prune S,G when *,G is
+ * joined. Whether we actually want/need to support this is a separate
+ * question - it is almost never used. In fact this is exactly what RFC5790
+ * ("lightweight" MLDv2) does: it removes S,G EXCLUDE support.
+ */
+
+static void gm_sg_update(struct gm_sg *sg, bool has_expired)
+{
+ struct gm_if *gm_ifp = sg->iface;
+ enum gm_sg_state prev, desired;
+ bool new_join;
+ struct gm_sg *grp = NULL;
+
+ if (!pim_addr_is_any(sg->sgaddr.src))
+ grp = gm_sg_find(gm_ifp, sg->sgaddr.grp, PIMADDR_ANY);
+ else
+ assert(sg->state != GM_SG_PRUNE);
+
+ if (gm_packet_sg_subs_count(sg->subs_positive)) {
+ desired = GM_SG_JOIN;
+ assert(!sg->t_sg_expire);
+ } else if ((sg->state == GM_SG_JOIN ||
+ sg->state == GM_SG_JOIN_EXPIRING) &&
+ !has_expired)
+ desired = GM_SG_JOIN_EXPIRING;
+ else if (!grp || !gm_packet_sg_subs_count(grp->subs_positive))
+ desired = GM_SG_NOINFO;
+ else if (gm_packet_sg_subs_count(grp->subs_positive) ==
+ gm_packet_sg_subs_count(sg->subs_negative)) {
+ if ((sg->state == GM_SG_NOPRUNE ||
+ sg->state == GM_SG_NOPRUNE_EXPIRING) &&
+ !has_expired)
+ desired = GM_SG_NOPRUNE_EXPIRING;
+ else
+ desired = GM_SG_PRUNE;
+ } else if (gm_packet_sg_subs_count(sg->subs_negative))
+ desired = GM_SG_NOPRUNE;
+ else
+ desired = GM_SG_NOINFO;
+
+ if (desired != sg->state && !gm_ifp->stopping) {
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(log_sg(sg, "%s => %s"), gm_states[sg->state],
+ gm_states[desired]);
+
+ if (desired == GM_SG_JOIN_EXPIRING ||
+ desired == GM_SG_NOPRUNE_EXPIRING) {
+ struct gm_query_timers timers;
+
+ timers.qrv = gm_ifp->cur_qrv;
+ timers.max_resp_ms = gm_ifp->cur_max_resp;
+ timers.qqic_ms = gm_ifp->cur_query_intv_trig;
+ timers.fuzz = gm_ifp->cfg_timing_fuzz;
+
+ gm_expiry_calc(&timers);
+ gm_sg_timer_start(gm_ifp, sg, timers.expire_wait);
+
+ THREAD_OFF(sg->t_sg_query);
+ sg->n_query = gm_ifp->cur_qrv;
+ sg->query_sbit = false;
+ gm_trigger_specific(sg);
+ }
+ }
+ prev = sg->state;
+ sg->state = desired;
+
+ if (in6_multicast_nofwd(&sg->sgaddr.grp) || gm_ifp->stopping)
+ new_join = false;
+ else
+ new_join = gm_sg_state_want_join(desired);
+
+ if (new_join && !sg->tib_joined) {
+ /* this will retry if join previously failed */
+ sg->tib_joined = tib_sg_gm_join(gm_ifp->pim, sg->sgaddr,
+ gm_ifp->ifp, &sg->oil);
+ if (!sg->tib_joined)
+ zlog_warn(
+ "MLD join for %pSG%%%s not propagated into TIB",
+ &sg->sgaddr, gm_ifp->ifp->name);
+ else
+ zlog_info(log_ifp("%pSG%%%s TIB joined"), &sg->sgaddr,
+ gm_ifp->ifp->name);
+
+ } else if (sg->tib_joined && !new_join) {
+ tib_sg_gm_prune(gm_ifp->pim, sg->sgaddr, gm_ifp->ifp, &sg->oil);
+
+ sg->oil = NULL;
+ sg->tib_joined = false;
+ }
+
+ if (desired == GM_SG_NOINFO) {
+ assertf((!sg->t_sg_expire &&
+ !gm_packet_sg_subs_count(sg->subs_positive) &&
+ !gm_packet_sg_subs_count(sg->subs_negative)),
+ "%pSG%%%s hx=%u exp=%pTHD state=%s->%s pos=%zu neg=%zu grp=%p",
+ &sg->sgaddr, gm_ifp->ifp->name, has_expired,
+ sg->t_sg_expire, gm_states[prev], gm_states[desired],
+ gm_packet_sg_subs_count(sg->subs_positive),
+ gm_packet_sg_subs_count(sg->subs_negative), grp);
+
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_sg(sg, "dropping"));
+
+ gm_sgs_del(gm_ifp->sgs, sg);
+ gm_sg_free(sg);
+ }
+}
+
+/****************************************************************************/
+
+/* the following bunch of functions deals with transferring state from
+ * received packets into gm_packet_state. As a reminder, the querier is
+ * structured to keep all items received in one packet together, since they
+ * will share expiry timers and thus allows efficient handling.
+ */
+
+static void gm_packet_free(struct gm_packet_state *pkt)
+{
+ gm_packet_expires_del(pkt->iface->expires, pkt);
+ gm_packets_del(pkt->subscriber->packets, pkt);
+ gm_subscriber_drop(&pkt->subscriber);
+ XFREE(MTYPE_GM_STATE, pkt);
+}
+
+static struct gm_packet_sg *gm_packet_sg_setup(struct gm_packet_state *pkt,
+ struct gm_sg *sg, bool is_excl,
+ bool is_src)
+{
+ struct gm_packet_sg *item;
+
+ assert(pkt->n_active < pkt->n_sg);
+
+ item = &pkt->items[pkt->n_active];
+ item->sg = sg;
+ item->is_excl = is_excl;
+ item->is_src = is_src;
+ item->offset = pkt->n_active;
+
+ pkt->n_active++;
+ return item;
+}
+
+static bool gm_packet_sg_drop(struct gm_packet_sg *item)
+{
+ struct gm_packet_state *pkt;
+ size_t i;
+
+ assert(item->sg);
+
+ pkt = gm_packet_sg2state(item);
+ if (item->sg->most_recent == item)
+ item->sg->most_recent = NULL;
+
+ for (i = 0; i < item->n_exclude; i++) {
+ struct gm_packet_sg *excl_item;
+
+ excl_item = item + 1 + i;
+ if (!excl_item->sg)
+ continue;
+
+ gm_packet_sg_subs_del(excl_item->sg->subs_negative, excl_item);
+ excl_item->sg = NULL;
+ pkt->n_active--;
+
+ assert(pkt->n_active > 0);
+ }
+
+ if (item->is_excl && item->is_src)
+ gm_packet_sg_subs_del(item->sg->subs_negative, item);
+ else
+ gm_packet_sg_subs_del(item->sg->subs_positive, item);
+ item->sg = NULL;
+ pkt->n_active--;
+
+ if (!pkt->n_active) {
+ gm_packet_free(pkt);
+ return true;
+ }
+ return false;
+}
+
+static void gm_packet_drop(struct gm_packet_state *pkt, bool trace)
+{
+ for (size_t i = 0; i < pkt->n_sg; i++) {
+ struct gm_sg *sg = pkt->items[i].sg;
+ bool deleted;
+
+ if (!sg)
+ continue;
+
+ if (trace && PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_sg(sg, "general-dropping from %pPA"),
+ &pkt->subscriber->addr);
+ deleted = gm_packet_sg_drop(&pkt->items[i]);
+
+ gm_sg_update(sg, true);
+ if (deleted)
+ break;
+ }
+}
+
+static void gm_packet_sg_remove_sources(struct gm_if *gm_ifp,
+ struct gm_subscriber *subscriber,
+ pim_addr grp, pim_addr *srcs,
+ size_t n_src, enum gm_sub_sense sense)
+{
+ struct gm_sg *sg;
+ struct gm_packet_sg *old_src;
+ size_t i;
+
+ for (i = 0; i < n_src; i++) {
+ sg = gm_sg_find(gm_ifp, grp, srcs[i]);
+ if (!sg)
+ continue;
+
+ old_src = gm_packet_sg_find(sg, sense, subscriber);
+ if (!old_src)
+ continue;
+
+ gm_packet_sg_drop(old_src);
+ gm_sg_update(sg, false);
+ }
+}
+
+static void gm_sg_expiry_cancel(struct gm_sg *sg)
+{
+ if (sg->t_sg_expire && PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_sg(sg, "alive, cancelling expiry timer"));
+ THREAD_OFF(sg->t_sg_expire);
+ sg->query_sbit = true;
+}
+
+/* first pass: process all changes resulting in removal of state:
+ * - {TO,IS}_INCLUDE removes *,G EXCLUDE state (and S,G)
+ * - ALLOW_NEW_SOURCES, if *,G in EXCLUDE removes S,G state
+ * - BLOCK_OLD_SOURCES, if *,G in INCLUDE removes S,G state
+ * - {TO,IS}_EXCLUDE, if *,G in INCLUDE removes S,G state
+ * note *replacing* state is NOT considered *removing* state here
+ *
+ * everything else is thrown into pkt for creation of state in pass 2
+ */
+static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
+ struct mld_v2_rec_hdr *rechdr)
+{
+ /* NB: pkt->subscriber can be NULL here if the subscriber was not
+ * previously seen!
+ */
+ struct gm_subscriber *subscriber = pkt->subscriber;
+ struct gm_sg *grp;
+ struct gm_packet_sg *old_grp = NULL;
+ struct gm_packet_sg *item;
+ size_t n_src = ntohs(rechdr->n_src);
+ size_t j;
+ bool is_excl = false;
+
+ grp = gm_sg_find(pkt->iface, rechdr->grp, PIMADDR_ANY);
+ if (grp && subscriber)
+ old_grp = gm_packet_sg_find(grp, GM_SUB_POS, subscriber);
+
+ assert(old_grp == NULL || old_grp->is_excl);
+
+ switch (rechdr->type) {
+ case MLD_RECTYPE_IS_EXCLUDE:
+ case MLD_RECTYPE_CHANGE_TO_EXCLUDE:
+ /* this always replaces or creates state */
+ is_excl = true;
+ if (!grp)
+ grp = gm_sg_make(pkt->iface, rechdr->grp, PIMADDR_ANY);
+
+ item = gm_packet_sg_setup(pkt, grp, is_excl, false);
+ item->n_exclude = n_src;
+
+ /* [EXCL_INCL_SG_NOTE] referenced below
+ *
+ * in theory, we should drop any S,G that the host may have
+ * previously added in INCLUDE mode. In practice, this is both
+ * incredibly rare and entirely irrelevant. It only makes any
+ * difference if an S,G that the host previously had on the
+ * INCLUDE list is now on the blocked list for EXCLUDE, which
+ * we can cover in processing the S,G list in pass2_excl().
+ *
+ * Other S,G from the host are simply left to expire
+ * "naturally" through general expiry.
+ */
+ break;
+
+ case MLD_RECTYPE_IS_INCLUDE:
+ case MLD_RECTYPE_CHANGE_TO_INCLUDE:
+ if (old_grp) {
+ /* INCLUDE has no *,G state, so old_grp here refers to
+ * previous EXCLUDE => delete it
+ */
+ gm_packet_sg_drop(old_grp);
+ gm_sg_update(grp, false);
+ CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+ }
+ break;
+
+ case MLD_RECTYPE_ALLOW_NEW_SOURCES:
+ if (old_grp) {
+ /* remove S,Gs from EXCLUDE, and then we're done */
+ gm_packet_sg_remove_sources(pkt->iface, subscriber,
+ rechdr->grp, rechdr->srcs,
+ n_src, GM_SUB_NEG);
+ return;
+ }
+ /* in INCLUDE mode => ALLOW_NEW_SOURCES is functionally
+ * idential to IS_INCLUDE (because the list of sources in
+ * IS_INCLUDE is not exhaustive)
+ */
+ break;
+
+ case MLD_RECTYPE_BLOCK_OLD_SOURCES:
+ if (old_grp) {
+ /* this is intentionally not implemented because it
+ * would be complicated as hell. we only take the list
+ * of blocked sources from full group state records
+ */
+ return;
+ }
+
+ if (subscriber)
+ gm_packet_sg_remove_sources(pkt->iface, subscriber,
+ rechdr->grp, rechdr->srcs,
+ n_src, GM_SUB_POS);
+ return;
+ }
+
+ for (j = 0; j < n_src; j++) {
+ struct gm_sg *sg;
+
+ sg = gm_sg_find(pkt->iface, rechdr->grp, rechdr->srcs[j]);
+ if (!sg)
+ sg = gm_sg_make(pkt->iface, rechdr->grp,
+ rechdr->srcs[j]);
+
+ gm_packet_sg_setup(pkt, sg, is_excl, true);
+ }
+}
+
+/* second pass: creating/updating/refreshing state. All the items from the
+ * received packet have already been thrown into gm_packet_state.
+ */
+
+static void gm_handle_v2_pass2_incl(struct gm_packet_state *pkt, size_t i)
+{
+ struct gm_packet_sg *item = &pkt->items[i];
+ struct gm_packet_sg *old = NULL;
+ struct gm_sg *sg = item->sg;
+
+ /* EXCLUDE state was already dropped in pass1 */
+ assert(!gm_packet_sg_find(sg, GM_SUB_NEG, pkt->subscriber));
+
+ old = gm_packet_sg_find(sg, GM_SUB_POS, pkt->subscriber);
+ if (old)
+ gm_packet_sg_drop(old);
+
+ pkt->n_active++;
+ gm_packet_sg_subs_add(sg->subs_positive, item);
+
+ sg->most_recent = item;
+ gm_sg_expiry_cancel(sg);
+ gm_sg_update(sg, false);
+}
+
+static void gm_handle_v2_pass2_excl(struct gm_packet_state *pkt, size_t offs)
+{
+ struct gm_packet_sg *item = &pkt->items[offs];
+ struct gm_packet_sg *old_grp, *item_dup;
+ struct gm_sg *sg_grp = item->sg;
+ size_t i;
+
+ old_grp = gm_packet_sg_find(sg_grp, GM_SUB_POS, pkt->subscriber);
+ if (old_grp) {
+ for (i = 0; i < item->n_exclude; i++) {
+ struct gm_packet_sg *item_src, *old_src;
+
+ item_src = &pkt->items[offs + 1 + i];
+ old_src = gm_packet_sg_find(item_src->sg, GM_SUB_NEG,
+ pkt->subscriber);
+ if (old_src)
+ gm_packet_sg_drop(old_src);
+
+ /* See [EXCL_INCL_SG_NOTE] above - we can have old S,G
+ * items left over if the host previously had INCLUDE
+ * mode going. Remove them here if we find any.
+ */
+ old_src = gm_packet_sg_find(item_src->sg, GM_SUB_POS,
+ pkt->subscriber);
+ if (old_src)
+ gm_packet_sg_drop(old_src);
+ }
+
+ /* the previous loop has removed the S,G entries which are
+ * still excluded after this update. So anything left on the
+ * old item was previously excluded but is now included
+ * => need to trigger update on S,G
+ */
+ for (i = 0; i < old_grp->n_exclude; i++) {
+ struct gm_packet_sg *old_src;
+ struct gm_sg *old_sg_src;
+
+ old_src = old_grp + 1 + i;
+ old_sg_src = old_src->sg;
+ if (!old_sg_src)
+ continue;
+
+ gm_packet_sg_drop(old_src);
+ gm_sg_update(old_sg_src, false);
+ }
+
+ gm_packet_sg_drop(old_grp);
+ }
+
+ item_dup = gm_packet_sg_subs_add(sg_grp->subs_positive, item);
+ assert(!item_dup);
+ pkt->n_active++;
+
+ sg_grp->most_recent = item;
+ gm_sg_expiry_cancel(sg_grp);
+
+ for (i = 0; i < item->n_exclude; i++) {
+ struct gm_packet_sg *item_src;
+
+ item_src = &pkt->items[offs + 1 + i];
+ item_dup = gm_packet_sg_subs_add(item_src->sg->subs_negative,
+ item_src);
+
+ if (item_dup)
+ item_src->sg = NULL;
+ else {
+ pkt->n_active++;
+ gm_sg_update(item_src->sg, false);
+ }
+ }
+
+ /* TODO: determine best ordering between gm_sg_update(S,G) and (*,G)
+ * to get lower PIM churn/flapping
+ */
+ gm_sg_update(sg_grp, false);
+}
+
+CPP_NOTICE("TODO: QRV/QQIC are not copied from queries to local state");
+/* on receiving a query, we need to update our robustness/query interval to
+ * match, so we correctly process group/source specific queries after last
+ * member leaves
+ */
+
+static void gm_handle_v2_report(struct gm_if *gm_ifp,
+ const struct sockaddr_in6 *pkt_src, char *data,
+ size_t len)
+{
+ struct mld_v2_report_hdr *hdr;
+ size_t i, n_records, max_entries;
+ struct gm_packet_state *pkt;
+
+ if (len < sizeof(*hdr)) {
+ if (PIM_DEBUG_IGMP_PACKETS)
+ zlog_debug(log_pkt_src(
+ "malformed MLDv2 report (truncated header)"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ /* errors after this may at least partially process the packet */
+ gm_ifp->stats.rx_new_report++;
+
+ hdr = (struct mld_v2_report_hdr *)data;
+ data += sizeof(*hdr);
+ len -= sizeof(*hdr);
+
+ /* can't have more *,G and S,G items than there is space for ipv6
+ * addresses, so just use this to allocate temporary buffer
+ */
+ max_entries = len / sizeof(pim_addr);
+ pkt = XCALLOC(MTYPE_GM_STATE,
+ offsetof(struct gm_packet_state, items[max_entries]));
+ pkt->n_sg = max_entries;
+ pkt->iface = gm_ifp;
+ pkt->subscriber = gm_subscriber_findref(gm_ifp, pkt_src->sin6_addr);
+
+ n_records = ntohs(hdr->n_records);
+
+ /* validate & remove state in v2_pass1() */
+ for (i = 0; i < n_records; i++) {
+ struct mld_v2_rec_hdr *rechdr;
+ size_t n_src, record_size;
+
+ if (len < sizeof(*rechdr)) {
+ zlog_warn(log_pkt_src(
+ "malformed MLDv2 report (truncated record header)"));
+ gm_ifp->stats.rx_trunc_report++;
+ break;
+ }
+
+ rechdr = (struct mld_v2_rec_hdr *)data;
+ data += sizeof(*rechdr);
+ len -= sizeof(*rechdr);
+
+ n_src = ntohs(rechdr->n_src);
+ record_size = n_src * sizeof(pim_addr) + rechdr->aux_len * 4;
+
+ if (len < record_size) {
+ zlog_warn(log_pkt_src(
+ "malformed MLDv2 report (truncated source list)"));
+ gm_ifp->stats.rx_trunc_report++;
+ break;
+ }
+ if (!IN6_IS_ADDR_MULTICAST(&rechdr->grp)) {
+ zlog_warn(
+ log_pkt_src(
+ "malformed MLDv2 report (invalid group %pI6)"),
+ &rechdr->grp);
+ gm_ifp->stats.rx_trunc_report++;
+ break;
+ }
+
+ data += record_size;
+ len -= record_size;
+
+ gm_handle_v2_pass1(pkt, rechdr);
+ }
+
+ if (!pkt->n_active) {
+ gm_subscriber_drop(&pkt->subscriber);
+ XFREE(MTYPE_GM_STATE, pkt);
+ return;
+ }
+
+ pkt = XREALLOC(MTYPE_GM_STATE, pkt,
+ offsetof(struct gm_packet_state, items[pkt->n_active]));
+ pkt->n_sg = pkt->n_active;
+ pkt->n_active = 0;
+
+ monotime(&pkt->received);
+ if (!pkt->subscriber)
+ pkt->subscriber = gm_subscriber_get(gm_ifp, pkt_src->sin6_addr);
+ gm_packets_add_tail(pkt->subscriber->packets, pkt);
+ gm_packet_expires_add_tail(gm_ifp->expires, pkt);
+
+ for (i = 0; i < pkt->n_sg; i++)
+ if (!pkt->items[i].is_excl)
+ gm_handle_v2_pass2_incl(pkt, i);
+ else {
+ gm_handle_v2_pass2_excl(pkt, i);
+ i += pkt->items[i].n_exclude;
+ }
+
+ if (pkt->n_active == 0)
+ gm_packet_free(pkt);
+}
+
+static void gm_handle_v1_report(struct gm_if *gm_ifp,
+ const struct sockaddr_in6 *pkt_src, char *data,
+ size_t len)
+{
+ struct mld_v1_pkt *hdr;
+ struct gm_packet_state *pkt;
+ struct gm_sg *grp;
+ struct gm_packet_sg *item;
+ size_t max_entries;
+
+ if (len < sizeof(*hdr)) {
+ if (PIM_DEBUG_IGMP_PACKETS)
+ zlog_debug(log_pkt_src(
+ "malformed MLDv1 report (truncated)"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ gm_ifp->stats.rx_old_report++;
+
+ hdr = (struct mld_v1_pkt *)data;
+
+ max_entries = 1;
+ pkt = XCALLOC(MTYPE_GM_STATE,
+ offsetof(struct gm_packet_state, items[max_entries]));
+ pkt->n_sg = max_entries;
+ pkt->iface = gm_ifp;
+ pkt->subscriber = gm_subscriber_findref(gm_ifp, gm_dummy_untracked);
+
+ /* { equivalent of gm_handle_v2_pass1() with IS_EXCLUDE */
+
+ grp = gm_sg_find(pkt->iface, hdr->grp, PIMADDR_ANY);
+ if (!grp)
+ grp = gm_sg_make(pkt->iface, hdr->grp, PIMADDR_ANY);
+
+ item = gm_packet_sg_setup(pkt, grp, true, false);
+ item->n_exclude = 0;
+ CPP_NOTICE("set v1-seen timer on grp here");
+
+ /* } */
+
+ /* pass2 will count n_active back up to 1. Also since a v1 report
+ * has exactly 1 group, we can skip the realloc() that v2 needs here.
+ */
+ assert(pkt->n_active == 1);
+ pkt->n_sg = pkt->n_active;
+ pkt->n_active = 0;
+
+ monotime(&pkt->received);
+ if (!pkt->subscriber)
+ pkt->subscriber = gm_subscriber_get(gm_ifp, gm_dummy_untracked);
+ gm_packets_add_tail(pkt->subscriber->packets, pkt);
+ gm_packet_expires_add_tail(gm_ifp->expires, pkt);
+
+ /* pass2 covers installing state & removing old state; all the v1
+ * compat is handled at this point.
+ *
+ * Note that "old state" may be v2; subscribers will switch from v2
+ * reports to v1 reports when the querier changes from v2 to v1. So,
+ * limiting this to v1 would be wrong.
+ */
+ gm_handle_v2_pass2_excl(pkt, 0);
+
+ if (pkt->n_active == 0)
+ gm_packet_free(pkt);
+}
+
+static void gm_handle_v1_leave(struct gm_if *gm_ifp,
+ const struct sockaddr_in6 *pkt_src, char *data,
+ size_t len)
+{
+ struct mld_v1_pkt *hdr;
+ struct gm_subscriber *subscriber;
+ struct gm_sg *grp;
+ struct gm_packet_sg *old_grp;
+
+ if (len < sizeof(*hdr)) {
+ if (PIM_DEBUG_IGMP_PACKETS)
+ zlog_debug(log_pkt_src(
+ "malformed MLDv1 leave (truncated)"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ gm_ifp->stats.rx_old_leave++;
+
+ hdr = (struct mld_v1_pkt *)data;
+
+ subscriber = gm_subscriber_findref(gm_ifp, gm_dummy_untracked);
+ if (!subscriber)
+ return;
+
+ /* { equivalent of gm_handle_v2_pass1() with IS_INCLUDE */
+
+ grp = gm_sg_find(gm_ifp, hdr->grp, PIMADDR_ANY);
+ if (grp) {
+ old_grp = gm_packet_sg_find(grp, GM_SUB_POS, subscriber);
+ if (old_grp) {
+ gm_packet_sg_drop(old_grp);
+ gm_sg_update(grp, false);
+ CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+ }
+ }
+
+ /* } */
+
+ /* nothing more to do here, pass2 is no-op for leaves */
+ gm_subscriber_drop(&subscriber);
+}
+
+/* for each general query received (or sent), a timer is started to expire
+ * _everything_ at the appropriate time (including robustness multiplier).
+ *
+ * So when this timer hits, all packets - with all of their items - that were
+ * received *before* the query are aged out, and state updated accordingly.
+ * Note that when we receive a refresh/update, the previous/old packet is
+ * already dropped and replaced with a new one, so in normal steady-state
+ * operation, this timer won't be doing anything.
+ *
+ * Additionally, if a subscriber actively leaves a group, that goes through
+ * its own path too and won't hit this. This is really only triggered when a
+ * host straight up disappears.
+ */
+static void gm_t_expire(struct thread *t)
+{
+ struct gm_if *gm_ifp = THREAD_ARG(t);
+ struct gm_packet_state *pkt;
+
+ zlog_info(log_ifp("general expiry timer"));
+
+ while (gm_ifp->n_pending) {
+ struct gm_general_pending *pend = gm_ifp->pending;
+ struct timeval remain;
+ int64_t remain_ms;
+
+ remain_ms = monotime_until(&pend->expiry, &remain);
+ if (remain_ms > 0) {
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(
+ log_ifp("next general expiry in %" PRId64 "ms"),
+ remain_ms / 1000);
+
+ thread_add_timer_tv(router->master, gm_t_expire, gm_ifp,
+ &remain, &gm_ifp->t_expire);
+ return;
+ }
+
+ while ((pkt = gm_packet_expires_first(gm_ifp->expires))) {
+ if (timercmp(&pkt->received, &pend->query, >=))
+ break;
+
+ if (PIM_DEBUG_IGMP_PACKETS)
+ zlog_debug(log_ifp("expire packet %p"), pkt);
+ gm_packet_drop(pkt, true);
+ }
+
+ gm_ifp->n_pending--;
+ memmove(gm_ifp->pending, gm_ifp->pending + 1,
+ gm_ifp->n_pending * sizeof(gm_ifp->pending[0]));
+ }
+
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(log_ifp("next general expiry waiting for query"));
+}
+
+/* NB: the receive handlers will also run when sending packets, since we
+ * receive our own packets back in.
+ */
+static void gm_handle_q_general(struct gm_if *gm_ifp,
+ struct gm_query_timers *timers)
+{
+ struct timeval now, expiry;
+ struct gm_general_pending *pend;
+
+ monotime(&now);
+ timeradd(&now, &timers->expire_wait, &expiry);
+
+ while (gm_ifp->n_pending) {
+ pend = &gm_ifp->pending[gm_ifp->n_pending - 1];
+
+ if (timercmp(&pend->expiry, &expiry, <))
+ break;
+
+ /* if we end up here, the last item in pending[] has an expiry
+ * later than the expiry for this query. But our query time
+ * (now) is later than that of the item (because, well, that's
+ * how time works.) This makes this query meaningless since
+ * it's "supersetted" within the preexisting query
+ */
+
+ if (PIM_DEBUG_IGMP_TRACE_DETAIL)
+ zlog_debug(
+ log_ifp("zapping supersetted general timer %pTVMu"),
+ &pend->expiry);
+
+ gm_ifp->n_pending--;
+ if (!gm_ifp->n_pending)
+ THREAD_OFF(gm_ifp->t_expire);
+ }
+
+ /* people might be messing with their configs or something */
+ if (gm_ifp->n_pending == array_size(gm_ifp->pending))
+ return;
+
+ pend = &gm_ifp->pending[gm_ifp->n_pending];
+ pend->query = now;
+ pend->expiry = expiry;
+
+ if (!gm_ifp->n_pending++) {
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(
+ log_ifp("starting general timer @ 0: %pTVMu"),
+ &pend->expiry);
+ thread_add_timer_tv(router->master, gm_t_expire, gm_ifp,
+ &timers->expire_wait, &gm_ifp->t_expire);
+ } else if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_ifp("appending general timer @ %u: %pTVMu"),
+ gm_ifp->n_pending, &pend->expiry);
+}
+
+static void gm_t_sg_expire(struct thread *t)
+{
+ struct gm_sg *sg = THREAD_ARG(t);
+ struct gm_if *gm_ifp = sg->iface;
+ struct gm_packet_sg *item;
+
+ assertf(sg->state == GM_SG_JOIN_EXPIRING ||
+ sg->state == GM_SG_NOPRUNE_EXPIRING,
+ "%pSG%%%s %pTHD", &sg->sgaddr, gm_ifp->ifp->name, t);
+
+ frr_each_safe (gm_packet_sg_subs, sg->subs_positive, item)
+ /* this will also drop EXCLUDE mode S,G lists together with
+ * the *,G entry
+ */
+ gm_packet_sg_drop(item);
+
+ /* subs_negative items are only timed out together with the *,G entry
+ * since we won't get any reports for a group-and-source query
+ */
+ gm_sg_update(sg, true);
+}
+
+static bool gm_sg_check_recent(struct gm_if *gm_ifp, struct gm_sg *sg,
+ struct timeval ref)
+{
+ struct gm_packet_state *pkt;
+
+ if (!sg->most_recent) {
+ struct gm_packet_state *best_pkt = NULL;
+ struct gm_packet_sg *item;
+
+ frr_each (gm_packet_sg_subs, sg->subs_positive, item) {
+ pkt = gm_packet_sg2state(item);
+
+ if (!best_pkt ||
+ timercmp(&pkt->received, &best_pkt->received, >)) {
+ best_pkt = pkt;
+ sg->most_recent = item;
+ }
+ }
+ }
+ if (sg->most_recent) {
+ struct timeval fuzz;
+
+ pkt = gm_packet_sg2state(sg->most_recent);
+
+ /* this shouldn't happen on plain old real ethernet segment,
+ * but on something like a VXLAN or VPLS it is very possible
+ * that we get a report before the query that triggered it.
+ * (imagine a triangle scenario with 3 datacenters, it's very
+ * possible A->B + B->C is faster than A->C due to odd routing)
+ *
+ * This makes a little tolerance allowance to handle that case.
+ */
+ timeradd(&pkt->received, &gm_ifp->cfg_timing_fuzz, &fuzz);
+
+ if (timercmp(&fuzz, &ref, >))
+ return true;
+ }
+ return false;
+}
+
+static void gm_sg_timer_start(struct gm_if *gm_ifp, struct gm_sg *sg,
+ struct timeval expire_wait)
+{
+ struct timeval now;
+
+ if (!sg)
+ return;
+ if (sg->state == GM_SG_PRUNE)
+ return;
+
+ monotime(&now);
+ if (gm_sg_check_recent(gm_ifp, sg, now))
+ return;
+
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_sg(sg, "expiring in %pTVI"), &expire_wait);
+
+ if (sg->t_sg_expire) {
+ struct timeval remain;
+
+ remain = thread_timer_remain(sg->t_sg_expire);
+ if (timercmp(&remain, &expire_wait, <=))
+ return;
+
+ THREAD_OFF(sg->t_sg_expire);
+ }
+
+ thread_add_timer_tv(router->master, gm_t_sg_expire, sg, &expire_wait,
+ &sg->t_sg_expire);
+}
+
+static void gm_handle_q_groupsrc(struct gm_if *gm_ifp,
+ struct gm_query_timers *timers, pim_addr grp,
+ const pim_addr *srcs, size_t n_src)
+{
+ struct gm_sg *sg;
+ size_t i;
+
+ for (i = 0; i < n_src; i++) {
+ sg = gm_sg_find(gm_ifp, grp, srcs[i]);
+ gm_sg_timer_start(gm_ifp, sg, timers->expire_wait);
+ }
+}
+
+static void gm_t_grp_expire(struct thread *t)
+{
+ /* if we're here, that means when we received the group-specific query
+ * there was one or more active S,G for this group. For *,G the timer
+ * in sg->t_sg_expire is running separately and gets cancelled when we
+ * receive a report, so that work is left to gm_t_sg_expire and we
+ * shouldn't worry about it here.
+ */
+ struct gm_grp_pending *pend = THREAD_ARG(t);
+ struct gm_if *gm_ifp = pend->iface;
+ struct gm_sg *sg, *sg_start, sg_ref = {};
+
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(log_ifp("*,%pPAs S,G timer expired"), &pend->grp);
+
+ /* gteq lookup - try to find *,G or S,G (S,G is > *,G)
+ * could technically be gt to skip a possible *,G
+ */
+ sg_ref.sgaddr.grp = pend->grp;
+ sg_ref.sgaddr.src = PIMADDR_ANY;
+ sg_start = gm_sgs_find_gteq(gm_ifp->sgs, &sg_ref);
+
+ frr_each_from (gm_sgs, gm_ifp->sgs, sg, sg_start) {
+ struct gm_packet_sg *item;
+
+ if (pim_addr_cmp(sg->sgaddr.grp, pend->grp))
+ break;
+ if (pim_addr_is_any(sg->sgaddr.src))
+ /* handled by gm_t_sg_expire / sg->t_sg_expire */
+ continue;
+ if (gm_sg_check_recent(gm_ifp, sg, pend->query))
+ continue;
+
+ /* we may also have a group-source-specific query going on in
+ * parallel. But if we received nothing for the *,G query,
+ * the S,G query is kinda irrelevant.
+ */
+ THREAD_OFF(sg->t_sg_expire);
+
+ frr_each_safe (gm_packet_sg_subs, sg->subs_positive, item)
+ /* this will also drop the EXCLUDE S,G lists */
+ gm_packet_sg_drop(item);
+
+ gm_sg_update(sg, true);
+ }
+
+ gm_grp_pends_del(gm_ifp->grp_pends, pend);
+ XFREE(MTYPE_GM_GRP_PENDING, pend);
+}
+
+static void gm_handle_q_group(struct gm_if *gm_ifp,
+ struct gm_query_timers *timers, pim_addr grp)
+{
+ struct gm_sg *sg, sg_ref = {};
+ struct gm_grp_pending *pend, pend_ref = {};
+
+ sg_ref.sgaddr.grp = grp;
+ sg_ref.sgaddr.src = PIMADDR_ANY;
+ /* gteq lookup - try to find *,G or S,G (S,G is > *,G) */
+ sg = gm_sgs_find_gteq(gm_ifp->sgs, &sg_ref);
+
+ if (!sg || pim_addr_cmp(sg->sgaddr.grp, grp))
+ /* we have nothing at all for this group - don't waste RAM */
+ return;
+
+ if (pim_addr_is_any(sg->sgaddr.src)) {
+ /* actually found *,G entry here */
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_ifp("*,%pPAs expiry timer starting"),
+ &grp);
+ gm_sg_timer_start(gm_ifp, sg, timers->expire_wait);
+
+ sg = gm_sgs_next(gm_ifp->sgs, sg);
+ if (!sg || pim_addr_cmp(sg->sgaddr.grp, grp))
+ /* no S,G for this group */
+ return;
+ }
+
+ pend_ref.grp = grp;
+ pend = gm_grp_pends_find(gm_ifp->grp_pends, &pend_ref);
+
+ if (pend) {
+ struct timeval remain;
+
+ remain = thread_timer_remain(pend->t_expire);
+ if (timercmp(&remain, &timers->expire_wait, <=))
+ return;
+
+ THREAD_OFF(pend->t_expire);
+ } else {
+ pend = XCALLOC(MTYPE_GM_GRP_PENDING, sizeof(*pend));
+ pend->grp = grp;
+ pend->iface = gm_ifp;
+ gm_grp_pends_add(gm_ifp->grp_pends, pend);
+ }
+
+ monotime(&pend->query);
+ thread_add_timer_tv(router->master, gm_t_grp_expire, pend,
+ &timers->expire_wait, &pend->t_expire);
+
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_ifp("*,%pPAs S,G timer started: %pTHD"), &grp,
+ pend->t_expire);
+}
+
+static void gm_bump_querier(struct gm_if *gm_ifp)
+{
+ struct pim_interface *pim_ifp = gm_ifp->ifp->info;
+
+ THREAD_OFF(gm_ifp->t_query);
+
+ if (pim_addr_is_any(pim_ifp->ll_lowest))
+ return;
+ if (!IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest))
+ return;
+
+ gm_ifp->n_startup = gm_ifp->cur_qrv;
+
+ thread_execute(router->master, gm_t_query, gm_ifp, 0);
+}
+
+static void gm_t_other_querier(struct thread *t)
+{
+ struct gm_if *gm_ifp = THREAD_ARG(t);
+ struct pim_interface *pim_ifp = gm_ifp->ifp->info;
+
+ zlog_info(log_ifp("other querier timer expired"));
+
+ gm_ifp->querier = pim_ifp->ll_lowest;
+ gm_ifp->n_startup = gm_ifp->cur_qrv;
+
+ thread_execute(router->master, gm_t_query, gm_ifp, 0);
+}
+
+static void gm_handle_query(struct gm_if *gm_ifp,
+ const struct sockaddr_in6 *pkt_src,
+ pim_addr *pkt_dst, char *data, size_t len)
+{
+ struct mld_v2_query_hdr *hdr;
+ struct pim_interface *pim_ifp = gm_ifp->ifp->info;
+ struct gm_query_timers timers;
+ bool general_query;
+
+ if (len < sizeof(struct mld_v2_query_hdr) &&
+ len != sizeof(struct mld_v1_pkt)) {
+ zlog_warn(log_pkt_src("invalid query size"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ hdr = (struct mld_v2_query_hdr *)data;
+ general_query = pim_addr_is_any(hdr->grp);
+
+ if (!general_query && !IN6_IS_ADDR_MULTICAST(&hdr->grp)) {
+ zlog_warn(log_pkt_src(
+ "malformed MLDv2 query (invalid group %pI6)"),
+ &hdr->grp);
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ if (len >= sizeof(struct mld_v2_query_hdr)) {
+ size_t src_space = ntohs(hdr->n_src) * sizeof(pim_addr);
+
+ if (len < sizeof(struct mld_v2_query_hdr) + src_space) {
+ zlog_warn(log_pkt_src(
+ "malformed MLDv2 query (truncated source list)"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ if (general_query && src_space) {
+ zlog_warn(log_pkt_src(
+ "malformed MLDv2 query (general query with non-empty source list)"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+ }
+
+ /* accepting queries unicast to us (or addressed to a wrong group)
+ * can mess up querier election as well as cause us to terminate
+ * traffic (since after a unicast query no reports will be coming in)
+ */
+ if (!IPV6_ADDR_SAME(pkt_dst, &gm_all_hosts)) {
+ if (pim_addr_is_any(hdr->grp)) {
+ zlog_warn(
+ log_pkt_src(
+ "wrong destination %pPA for general query"),
+ pkt_dst);
+ gm_ifp->stats.rx_drop_dstaddr++;
+ return;
+ }
+
+ if (!IPV6_ADDR_SAME(&hdr->grp, pkt_dst)) {
+ gm_ifp->stats.rx_drop_dstaddr++;
+ zlog_warn(
+ log_pkt_src(
+ "wrong destination %pPA for group specific query"),
+ pkt_dst);
+ return;
+ }
+ }
+
+ if (IPV6_ADDR_CMP(&pkt_src->sin6_addr, &gm_ifp->querier) < 0) {
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(
+ log_pkt_src("replacing elected querier %pPA"),
+ &gm_ifp->querier);
+
+ gm_ifp->querier = pkt_src->sin6_addr;
+ }
+
+ if (len == sizeof(struct mld_v1_pkt)) {
+ timers.qrv = gm_ifp->cur_qrv;
+ timers.max_resp_ms = hdr->max_resp_code;
+ timers.qqic_ms = gm_ifp->cur_query_intv;
+ } else {
+ timers.qrv = (hdr->flags & 0x7) ?: 8;
+ timers.max_resp_ms = mld_max_resp_decode(hdr->max_resp_code);
+ timers.qqic_ms = igmp_msg_decode8to16(hdr->qqic) * 1000;
+ }
+ timers.fuzz = gm_ifp->cfg_timing_fuzz;
+
+ gm_expiry_calc(&timers);
+
+ if (PIM_DEBUG_IGMP_TRACE_DETAIL)
+ zlog_debug(
+ log_ifp("query timers: QRV=%u max_resp=%ums qqic=%ums expire_wait=%pTVI"),
+ timers.qrv, timers.max_resp_ms, timers.qqic_ms,
+ &timers.expire_wait);
+
+ if (IPV6_ADDR_CMP(&pkt_src->sin6_addr, &pim_ifp->ll_lowest) < 0) {
+ unsigned int other_ms;
+
+ THREAD_OFF(gm_ifp->t_query);
+ THREAD_OFF(gm_ifp->t_other_querier);
+
+ other_ms = timers.qrv * timers.qqic_ms + timers.max_resp_ms / 2;
+ thread_add_timer_msec(router->master, gm_t_other_querier,
+ gm_ifp, other_ms,
+ &gm_ifp->t_other_querier);
+ }
+
+ if (len == sizeof(struct mld_v1_pkt)) {
+ if (general_query) {
+ gm_handle_q_general(gm_ifp, &timers);
+ gm_ifp->stats.rx_query_old_general++;
+ } else {
+ gm_handle_q_group(gm_ifp, &timers, hdr->grp);
+ gm_ifp->stats.rx_query_old_group++;
+ }
+ return;
+ }
+
+ /* v2 query - [S]uppress bit */
+ if (hdr->flags & 0x8) {
+ gm_ifp->stats.rx_query_new_sbit++;
+ return;
+ }
+
+ if (general_query) {
+ gm_handle_q_general(gm_ifp, &timers);
+ gm_ifp->stats.rx_query_new_general++;
+ } else if (!ntohs(hdr->n_src)) {
+ gm_handle_q_group(gm_ifp, &timers, hdr->grp);
+ gm_ifp->stats.rx_query_new_group++;
+ } else {
+ gm_handle_q_groupsrc(gm_ifp, &timers, hdr->grp, hdr->srcs,
+ ntohs(hdr->n_src));
+ gm_ifp->stats.rx_query_new_groupsrc++;
+ }
+}
+
+static void gm_rx_process(struct gm_if *gm_ifp,
+ const struct sockaddr_in6 *pkt_src, pim_addr *pkt_dst,
+ void *data, size_t pktlen)
+{
+ struct icmp6_plain_hdr *icmp6 = data;
+ uint16_t pkt_csum, ref_csum;
+ struct ipv6_ph ph6 = {
+ .src = pkt_src->sin6_addr,
+ .dst = *pkt_dst,
+ .ulpl = htons(pktlen),
+ .next_hdr = IPPROTO_ICMPV6,
+ };
+
+ pkt_csum = icmp6->icmp6_cksum;
+ icmp6->icmp6_cksum = 0;
+ ref_csum = in_cksum_with_ph6(&ph6, data, pktlen);
+
+ if (pkt_csum != ref_csum) {
+ zlog_warn(
+ log_pkt_src(
+ "(dst %pPA) packet RX checksum failure, expected %04hx, got %04hx"),
+ pkt_dst, pkt_csum, ref_csum);
+ gm_ifp->stats.rx_drop_csum++;
+ return;
+ }
+
+ data = (icmp6 + 1);
+ pktlen -= sizeof(*icmp6);
+
+ switch (icmp6->icmp6_type) {
+ case ICMP6_MLD_QUERY:
+ gm_handle_query(gm_ifp, pkt_src, pkt_dst, data, pktlen);
+ break;
+ case ICMP6_MLD_V1_REPORT:
+ gm_handle_v1_report(gm_ifp, pkt_src, data, pktlen);
+ break;
+ case ICMP6_MLD_V1_DONE:
+ gm_handle_v1_leave(gm_ifp, pkt_src, data, pktlen);
+ break;
+ case ICMP6_MLD_V2_REPORT:
+ gm_handle_v2_report(gm_ifp, pkt_src, data, pktlen);
+ break;
+ }
+}
+
+static bool ip6_check_hopopts_ra(uint8_t *hopopts, size_t hopopt_len,
+ uint16_t alert_type)
+{
+ uint8_t *hopopt_end;
+
+ if (hopopt_len < 8)
+ return false;
+ if (hopopt_len < (hopopts[1] + 1U) * 8U)
+ return false;
+
+ hopopt_end = hopopts + (hopopts[1] + 1) * 8;
+ hopopts += 2;
+
+ while (hopopts < hopopt_end) {
+ if (hopopts[0] == IP6OPT_PAD1) {
+ hopopts++;
+ continue;
+ }
+
+ if (hopopts > hopopt_end - 2)
+ break;
+ if (hopopts > hopopt_end - 2 - hopopts[1])
+ break;
+
+ if (hopopts[0] == IP6OPT_ROUTER_ALERT && hopopts[1] == 2) {
+ uint16_t have_type = (hopopts[2] << 8) | hopopts[3];
+
+ if (have_type == alert_type)
+ return true;
+ }
+
+ hopopts += 2 + hopopts[1];
+ }
+ return false;
+}
+
+static void gm_t_recv(struct thread *t)
+{
+ struct pim_instance *pim = THREAD_ARG(t);
+ union {
+ char buf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(256) /* hop options */ +
+ CMSG_SPACE(sizeof(int)) /* hopcount */];
+ struct cmsghdr align;
+ } cmsgbuf;
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pktinfo = NULL;
+ uint8_t *hopopts = NULL;
+ size_t hopopt_len = 0;
+ int *hoplimit = NULL;
+ char rxbuf[2048];
+ struct msghdr mh[1] = {};
+ struct iovec iov[1];
+ struct sockaddr_in6 pkt_src[1];
+ ssize_t nread;
+ size_t pktlen;
+
+ thread_add_read(router->master, gm_t_recv, pim, pim->gm_socket,
+ &pim->t_gm_recv);
+
+ iov->iov_base = rxbuf;
+ iov->iov_len = sizeof(rxbuf);
+
+ mh->msg_name = pkt_src;
+ mh->msg_namelen = sizeof(pkt_src);
+ mh->msg_control = cmsgbuf.buf;
+ mh->msg_controllen = sizeof(cmsgbuf.buf);
+ mh->msg_iov = iov;
+ mh->msg_iovlen = array_size(iov);
+ mh->msg_flags = 0;
+
+ nread = recvmsg(pim->gm_socket, mh, MSG_PEEK | MSG_TRUNC);
+ if (nread <= 0) {
+ zlog_err("(VRF %s) RX error: %m", pim->vrf->name);
+ pim->gm_rx_drop_sys++;
+ return;
+ }
+
+ if ((size_t)nread > sizeof(rxbuf)) {
+ iov->iov_base = XMALLOC(MTYPE_GM_PACKET, nread);
+ iov->iov_len = nread;
+ }
+ nread = recvmsg(pim->gm_socket, mh, 0);
+ if (nread <= 0) {
+ zlog_err("(VRF %s) RX error: %m", pim->vrf->name);
+ pim->gm_rx_drop_sys++;
+ goto out_free;
+ }
+
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(pkt_src->sin6_scope_id, pim->vrf->vrf_id);
+ if (!ifp || !ifp->info)
+ goto out_free;
+
+ struct pim_interface *pim_ifp = ifp->info;
+ struct gm_if *gm_ifp = pim_ifp->mld;
+
+ if (!gm_ifp)
+ goto out_free;
+
+ for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg)) {
+ if (cmsg->cmsg_level != SOL_IPV6)
+ continue;
+
+ switch (cmsg->cmsg_type) {
+ case IPV6_PKTINFO:
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ break;
+ case IPV6_HOPOPTS:
+ hopopts = CMSG_DATA(cmsg);
+ hopopt_len = cmsg->cmsg_len - sizeof(*cmsg);
+ break;
+ case IPV6_HOPLIMIT:
+ hoplimit = (int *)CMSG_DATA(cmsg);
+ break;
+ }
+ }
+
+ if (!pktinfo || !hoplimit) {
+ zlog_err(log_ifp(
+ "BUG: packet without IPV6_PKTINFO or IPV6_HOPLIMIT"));
+ pim->gm_rx_drop_sys++;
+ goto out_free;
+ }
+
+ if (*hoplimit != 1) {
+ zlog_err(log_pkt_src("packet with hop limit != 1"));
+ /* spoofing attempt => count on srcaddr counter */
+ gm_ifp->stats.rx_drop_srcaddr++;
+ goto out_free;
+ }
+
+ if (!ip6_check_hopopts_ra(hopopts, hopopt_len, IP6_ALERT_MLD)) {
+ zlog_err(log_pkt_src(
+ "packet without IPv6 Router Alert MLD option"));
+ gm_ifp->stats.rx_drop_ra++;
+ goto out_free;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&pkt_src->sin6_addr))
+ /* reports from :: happen in normal operation for DAD, so
+ * don't spam log messages about this
+ */
+ goto out_free;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&pkt_src->sin6_addr)) {
+ zlog_warn(log_pkt_src("packet from invalid source address"));
+ gm_ifp->stats.rx_drop_srcaddr++;
+ goto out_free;
+ }
+
+ pktlen = nread;
+ if (pktlen < sizeof(struct icmp6_plain_hdr)) {
+ zlog_warn(log_pkt_src("truncated packet"));
+ gm_ifp->stats.rx_drop_malformed++;
+ goto out_free;
+ }
+
+ gm_rx_process(gm_ifp, pkt_src, &pktinfo->ipi6_addr, iov->iov_base,
+ pktlen);
+
+out_free:
+ if (iov->iov_base != rxbuf)
+ XFREE(MTYPE_GM_PACKET, iov->iov_base);
+}
+
+static void gm_send_query(struct gm_if *gm_ifp, pim_addr grp,
+ const pim_addr *srcs, size_t n_srcs, bool s_bit)
+{
+ struct pim_interface *pim_ifp = gm_ifp->ifp->info;
+ struct sockaddr_in6 dstaddr = {
+ .sin6_family = AF_INET6,
+ .sin6_scope_id = gm_ifp->ifp->ifindex,
+ };
+ struct {
+ struct icmp6_plain_hdr hdr;
+ struct mld_v2_query_hdr v2_query;
+ } query = {
+ /* clang-format off */
+ .hdr = {
+ .icmp6_type = ICMP6_MLD_QUERY,
+ .icmp6_code = 0,
+ },
+ .v2_query = {
+ .grp = grp,
+ },
+ /* clang-format on */
+ };
+ struct ipv6_ph ph6 = {
+ .src = pim_ifp->ll_lowest,
+ .ulpl = htons(sizeof(query)),
+ .next_hdr = IPPROTO_ICMPV6,
+ };
+ union {
+ char buf[CMSG_SPACE(8) /* hop options */ +
+ CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ struct cmsghdr align;
+ } cmsg = {};
+ struct cmsghdr *cmh;
+ struct msghdr mh[1] = {};
+ struct iovec iov[3];
+ size_t iov_len;
+ ssize_t ret, expect_ret;
+ uint8_t *dp;
+ struct in6_pktinfo *pktinfo;
+
+ if (if_is_loopback(gm_ifp->ifp)) {
+ /* Linux is a bit odd with multicast on loopback */
+ ph6.src = in6addr_loopback;
+ dstaddr.sin6_addr = in6addr_loopback;
+ } else if (pim_addr_is_any(grp))
+ dstaddr.sin6_addr = gm_all_hosts;
+ else
+ dstaddr.sin6_addr = grp;
+
+ query.v2_query.max_resp_code =
+ mld_max_resp_encode(gm_ifp->cur_max_resp);
+ query.v2_query.flags = (gm_ifp->cur_qrv < 8) ? gm_ifp->cur_qrv : 0;
+ if (s_bit)
+ query.v2_query.flags |= 0x08;
+ query.v2_query.qqic =
+ igmp_msg_encode16to8(gm_ifp->cur_query_intv / 1000);
+ query.v2_query.n_src = htons(n_srcs);
+
+ ph6.dst = dstaddr.sin6_addr;
+
+ /* ph6 not included in sendmsg */
+ iov[0].iov_base = &ph6;
+ iov[0].iov_len = sizeof(ph6);
+ iov[1].iov_base = &query;
+ if (gm_ifp->cur_version == GM_MLDV1) {
+ iov_len = 2;
+ iov[1].iov_len = sizeof(query.hdr) + sizeof(struct mld_v1_pkt);
+ } else if (!n_srcs) {
+ iov_len = 2;
+ iov[1].iov_len = sizeof(query);
+ } else {
+ iov[1].iov_len = sizeof(query);
+ iov[2].iov_base = (void *)srcs;
+ iov[2].iov_len = n_srcs * sizeof(srcs[0]);
+ iov_len = 3;
+ }
+
+ query.hdr.icmp6_cksum = in_cksumv(iov, iov_len);
+
+ if (PIM_DEBUG_IGMP_PACKETS)
+ zlog_debug(
+ log_ifp("MLD query %pPA -> %pI6 (grp=%pPA, %zu srcs)"),
+ &pim_ifp->ll_lowest, &dstaddr.sin6_addr, &grp, n_srcs);
+
+ mh->msg_name = &dstaddr;
+ mh->msg_namelen = sizeof(dstaddr);
+ mh->msg_iov = iov + 1;
+ mh->msg_iovlen = iov_len - 1;
+ mh->msg_control = &cmsg;
+ mh->msg_controllen = sizeof(cmsg.buf);
+
+ cmh = CMSG_FIRSTHDR(mh);
+ cmh->cmsg_level = IPPROTO_IPV6;
+ cmh->cmsg_type = IPV6_HOPOPTS;
+ cmh->cmsg_len = CMSG_LEN(8);
+ dp = CMSG_DATA(cmh);
+ *dp++ = 0; /* next header */
+ *dp++ = 0; /* length (8-byte blocks, minus 1) */
+ *dp++ = IP6OPT_ROUTER_ALERT; /* router alert */
+ *dp++ = 2; /* length */
+ *dp++ = 0; /* value (2 bytes) */
+ *dp++ = 0; /* value (2 bytes) (0 = MLD) */
+ *dp++ = 0; /* pad0 */
+ *dp++ = 0; /* pad0 */
+
+ cmh = CMSG_NXTHDR(mh, cmh);
+ cmh->cmsg_level = IPPROTO_IPV6;
+ cmh->cmsg_type = IPV6_PKTINFO;
+ cmh->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmh);
+ pktinfo->ipi6_ifindex = gm_ifp->ifp->ifindex;
+ pktinfo->ipi6_addr = gm_ifp->cur_ll_lowest;
+
+ expect_ret = iov[1].iov_len;
+ if (iov_len == 3)
+ expect_ret += iov[2].iov_len;
+
+ frr_with_privs (&pimd_privs) {
+ ret = sendmsg(gm_ifp->pim->gm_socket, mh, 0);
+ }
+
+ if (ret != expect_ret) {
+ zlog_warn(log_ifp("failed to send query: %m"));
+ gm_ifp->stats.tx_query_fail++;
+ } else {
+ if (gm_ifp->cur_version == GM_MLDV1) {
+ if (pim_addr_is_any(grp))
+ gm_ifp->stats.tx_query_old_general++;
+ else
+ gm_ifp->stats.tx_query_old_group++;
+ } else {
+ if (pim_addr_is_any(grp))
+ gm_ifp->stats.tx_query_new_general++;
+ else if (!n_srcs)
+ gm_ifp->stats.tx_query_new_group++;
+ else
+ gm_ifp->stats.tx_query_new_groupsrc++;
+ }
+ }
+}
+
+static void gm_t_query(struct thread *t)
+{
+ struct gm_if *gm_ifp = THREAD_ARG(t);
+ unsigned int timer_ms = gm_ifp->cur_query_intv;
+
+ if (gm_ifp->n_startup) {
+ timer_ms /= 4;
+ gm_ifp->n_startup--;
+ }
+
+ thread_add_timer_msec(router->master, gm_t_query, gm_ifp, timer_ms,
+ &gm_ifp->t_query);
+
+ gm_send_query(gm_ifp, PIMADDR_ANY, NULL, 0, false);
+}
+
+static void gm_t_sg_query(struct thread *t)
+{
+ struct gm_sg *sg = THREAD_ARG(t);
+
+ gm_trigger_specific(sg);
+}
+
+/* S,G specific queries (triggered by a member leaving) get a little slack
+ * time so we can bundle queries for [S1,S2,S3,...],G into the same query
+ */
+static void gm_send_specific(struct gm_gsq_pending *pend_gsq)
+{
+ struct gm_if *gm_ifp = pend_gsq->iface;
+
+ gm_send_query(gm_ifp, pend_gsq->grp, pend_gsq->srcs, pend_gsq->n_src,
+ pend_gsq->s_bit);
+
+ gm_gsq_pends_del(gm_ifp->gsq_pends, pend_gsq);
+ XFREE(MTYPE_GM_GSQ_PENDING, pend_gsq);
+}
+
+static void gm_t_gsq_pend(struct thread *t)
+{
+ struct gm_gsq_pending *pend_gsq = THREAD_ARG(t);
+
+ gm_send_specific(pend_gsq);
+}
+
+static void gm_trigger_specific(struct gm_sg *sg)
+{
+ struct gm_if *gm_ifp = sg->iface;
+ struct pim_interface *pim_ifp = gm_ifp->ifp->info;
+ struct gm_gsq_pending *pend_gsq, ref = {};
+
+ sg->n_query--;
+ if (sg->n_query)
+ thread_add_timer_msec(router->master, gm_t_sg_query, sg,
+ gm_ifp->cur_query_intv_trig,
+ &sg->t_sg_query);
+
+ if (!IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest))
+ return;
+ if (gm_ifp->pim->gm_socket == -1)
+ return;
+
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_sg(sg, "triggered query"));
+
+ if (pim_addr_is_any(sg->sgaddr.src)) {
+ gm_send_query(gm_ifp, sg->sgaddr.grp, NULL, 0, sg->query_sbit);
+ return;
+ }
+
+ ref.grp = sg->sgaddr.grp;
+ ref.s_bit = sg->query_sbit;
+
+ pend_gsq = gm_gsq_pends_find(gm_ifp->gsq_pends, &ref);
+ if (!pend_gsq) {
+ pend_gsq = XCALLOC(MTYPE_GM_GSQ_PENDING, sizeof(*pend_gsq));
+ pend_gsq->grp = sg->sgaddr.grp;
+ pend_gsq->s_bit = sg->query_sbit;
+ pend_gsq->iface = gm_ifp;
+ gm_gsq_pends_add(gm_ifp->gsq_pends, pend_gsq);
+
+ thread_add_timer_tv(router->master, gm_t_gsq_pend, pend_gsq,
+ &gm_ifp->cfg_timing_fuzz,
+ &pend_gsq->t_send);
+ }
+
+ assert(pend_gsq->n_src < array_size(pend_gsq->srcs));
+
+ pend_gsq->srcs[pend_gsq->n_src] = sg->sgaddr.src;
+ pend_gsq->n_src++;
+
+ if (pend_gsq->n_src == array_size(pend_gsq->srcs)) {
+ THREAD_OFF(pend_gsq->t_send);
+ gm_send_specific(pend_gsq);
+ pend_gsq = NULL;
+ }
+}
+
+static void gm_vrf_socket_incref(struct pim_instance *pim)
+{
+ struct vrf *vrf = pim->vrf;
+ int ret, intval;
+ struct icmp6_filter filter[1];
+
+ if (pim->gm_socket_if_count++ && pim->gm_socket != -1)
+ return;
+
+ ICMP6_FILTER_SETBLOCKALL(filter);
+ ICMP6_FILTER_SETPASS(ICMP6_MLD_QUERY, filter);
+ ICMP6_FILTER_SETPASS(ICMP6_MLD_V1_REPORT, filter);
+ ICMP6_FILTER_SETPASS(ICMP6_MLD_V1_DONE, filter);
+ ICMP6_FILTER_SETPASS(ICMP6_MLD_V2_REPORT, filter);
+
+ frr_with_privs (&pimd_privs) {
+ pim->gm_socket = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6,
+ vrf->vrf_id, vrf->name);
+ if (pim->gm_socket < 0) {
+ zlog_err("(VRF %s) could not create MLD socket: %m",
+ vrf->name);
+ return;
+ }
+
+ ret = setsockopt(pim->gm_socket, SOL_ICMPV6, ICMP6_FILTER,
+ filter, sizeof(filter));
+ if (ret)
+ zlog_err("(VRF %s) failed to set ICMP6_FILTER: %m",
+ vrf->name);
+
+ intval = 1;
+ ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_RECVPKTINFO,
+ &intval, sizeof(intval));
+ if (ret)
+ zlog_err("(VRF %s) failed to set IPV6_RECVPKTINFO: %m",
+ vrf->name);
+
+ intval = 1;
+ ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_RECVHOPOPTS,
+ &intval, sizeof(intval));
+ if (ret)
+ zlog_err("(VRF %s) failed to set IPV6_HOPOPTS: %m",
+ vrf->name);
+
+ intval = 1;
+ ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_RECVHOPLIMIT,
+ &intval, sizeof(intval));
+ if (ret)
+ zlog_err("(VRF %s) failed to set IPV6_HOPLIMIT: %m",
+ vrf->name);
+
+ intval = 1;
+ ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_MULTICAST_LOOP,
+ &intval, sizeof(intval));
+ if (ret)
+ zlog_err(
+ "(VRF %s) failed to disable IPV6_MULTICAST_LOOP: %m",
+ vrf->name);
+
+ intval = 1;
+ ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_MULTICAST_HOPS,
+ &intval, sizeof(intval));
+ if (ret)
+ zlog_err(
+ "(VRF %s) failed to set IPV6_MULTICAST_HOPS: %m",
+ vrf->name);
+
+ /* NB: IPV6_MULTICAST_ALL does not completely bypass multicast
+ * RX filtering in Linux. It only means "receive all groups
+ * that something on the system has joined". To actually
+ * receive *all* MLD packets - which is what we need -
+ * multicast routing must be enabled on the interface. And
+ * this only works for MLD packets specifically.
+ *
+ * For reference, check ip6_mc_input() in net/ipv6/ip6_input.c
+ * and in particular the #ifdef CONFIG_IPV6_MROUTE block there.
+ *
+ * Also note that the code there explicitly checks for the IPv6
+ * router alert MLD option (which is required by the RFC to be
+ * on MLD packets.) That implies trying to support hosts which
+ * erroneously don't add that option is just not possible.
+ */
+ intval = 1;
+ ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_MULTICAST_ALL,
+ &intval, sizeof(intval));
+ if (ret)
+ zlog_info(
+ "(VRF %s) failed to set IPV6_MULTICAST_ALL: %m (OK on old kernels)",
+ vrf->name);
+ }
+
+ thread_add_read(router->master, gm_t_recv, pim, pim->gm_socket,
+ &pim->t_gm_recv);
+}
+
+static void gm_vrf_socket_decref(struct pim_instance *pim)
+{
+ if (--pim->gm_socket_if_count)
+ return;
+
+ THREAD_OFF(pim->t_gm_recv);
+ close(pim->gm_socket);
+ pim->gm_socket = -1;
+}
+
+static void gm_start(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct gm_if *gm_ifp;
+
+ assert(pim_ifp);
+ assert(pim_ifp->pim);
+ assert(pim_ifp->mroute_vif_index >= 0);
+ assert(!pim_ifp->mld);
+
+ gm_vrf_socket_incref(pim_ifp->pim);
+
+ gm_ifp = XCALLOC(MTYPE_GM_IFACE, sizeof(*gm_ifp));
+ gm_ifp->ifp = ifp;
+ pim_ifp->mld = gm_ifp;
+ gm_ifp->pim = pim_ifp->pim;
+ monotime(&gm_ifp->started);
+
+ zlog_info(log_ifp("starting MLD"));
+
+ if (pim_ifp->mld_version == 1)
+ gm_ifp->cur_version = GM_MLDV1;
+ else
+ gm_ifp->cur_version = GM_MLDV2;
+
+ /* hardcoded for dev without CLI */
+ gm_ifp->cur_qrv = 2;
+ gm_ifp->cur_query_intv = pim_ifp->gm_default_query_interval * 1000;
+ gm_ifp->cur_query_intv_trig = gm_ifp->cur_query_intv;
+ gm_ifp->cur_max_resp = 250;
+
+ gm_ifp->cfg_timing_fuzz.tv_sec = 0;
+ gm_ifp->cfg_timing_fuzz.tv_usec = 10 * 1000;
+
+ gm_sgs_init(gm_ifp->sgs);
+ gm_subscribers_init(gm_ifp->subscribers);
+ gm_packet_expires_init(gm_ifp->expires);
+ gm_grp_pends_init(gm_ifp->grp_pends);
+ gm_gsq_pends_init(gm_ifp->gsq_pends);
+
+ frr_with_privs (&pimd_privs) {
+ struct ipv6_mreq mreq;
+ int ret;
+
+ /* all-MLDv2 group */
+ mreq.ipv6mr_multiaddr = gm_all_routers;
+ mreq.ipv6mr_interface = ifp->ifindex;
+ ret = setsockopt(gm_ifp->pim->gm_socket, SOL_IPV6,
+ IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
+ if (ret)
+ zlog_err("(%s) failed to join ff02::16 (all-MLDv2): %m",
+ ifp->name);
+ }
+}
+
+void gm_ifp_teardown(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct gm_if *gm_ifp;
+ struct gm_packet_state *pkt;
+ struct gm_grp_pending *pend_grp;
+ struct gm_gsq_pending *pend_gsq;
+ struct gm_subscriber *subscriber;
+ struct gm_sg *sg;
+
+ if (!pim_ifp || !pim_ifp->mld)
+ return;
+
+ gm_ifp = pim_ifp->mld;
+ gm_ifp->stopping = true;
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(log_ifp("MLD stop"));
+
+ THREAD_OFF(gm_ifp->t_query);
+ THREAD_OFF(gm_ifp->t_other_querier);
+ THREAD_OFF(gm_ifp->t_expire);
+
+ frr_with_privs (&pimd_privs) {
+ struct ipv6_mreq mreq;
+ int ret;
+
+ /* all-MLDv2 group */
+ mreq.ipv6mr_multiaddr = gm_all_routers;
+ mreq.ipv6mr_interface = ifp->ifindex;
+ ret = setsockopt(gm_ifp->pim->gm_socket, SOL_IPV6,
+ IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
+ if (ret)
+ zlog_err(
+ "(%s) failed to leave ff02::16 (all-MLDv2): %m",
+ ifp->name);
+ }
+
+ gm_vrf_socket_decref(gm_ifp->pim);
+
+ while ((pkt = gm_packet_expires_first(gm_ifp->expires)))
+ gm_packet_drop(pkt, false);
+
+ while ((pend_grp = gm_grp_pends_pop(gm_ifp->grp_pends))) {
+ THREAD_OFF(pend_grp->t_expire);
+ XFREE(MTYPE_GM_GRP_PENDING, pend_grp);
+ }
+
+ while ((pend_gsq = gm_gsq_pends_pop(gm_ifp->gsq_pends))) {
+ THREAD_OFF(pend_gsq->t_send);
+ XFREE(MTYPE_GM_GSQ_PENDING, pend_gsq);
+ }
+
+ while ((sg = gm_sgs_pop(gm_ifp->sgs))) {
+ THREAD_OFF(sg->t_sg_expire);
+ assertf(!gm_packet_sg_subs_count(sg->subs_negative), "%pSG",
+ &sg->sgaddr);
+ assertf(!gm_packet_sg_subs_count(sg->subs_positive), "%pSG",
+ &sg->sgaddr);
+
+ gm_sg_free(sg);
+ }
+
+ while ((subscriber = gm_subscribers_pop(gm_ifp->subscribers))) {
+ assertf(!gm_packets_count(subscriber->packets), "%pPA",
+ &subscriber->addr);
+ XFREE(MTYPE_GM_SUBSCRIBER, subscriber);
+ }
+
+ gm_grp_pends_fini(gm_ifp->grp_pends);
+ gm_packet_expires_fini(gm_ifp->expires);
+ gm_subscribers_fini(gm_ifp->subscribers);
+ gm_sgs_fini(gm_ifp->sgs);
+
+ XFREE(MTYPE_GM_IFACE, gm_ifp);
+ pim_ifp->mld = NULL;
+}
+
+static void gm_update_ll(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct gm_if *gm_ifp = pim_ifp ? pim_ifp->mld : NULL;
+ bool was_querier;
+
+ was_querier =
+ !IPV6_ADDR_CMP(&gm_ifp->cur_ll_lowest, &gm_ifp->querier) &&
+ !pim_addr_is_any(gm_ifp->querier);
+
+ gm_ifp->cur_ll_lowest = pim_ifp->ll_lowest;
+ if (was_querier)
+ gm_ifp->querier = pim_ifp->ll_lowest;
+ THREAD_OFF(gm_ifp->t_query);
+
+ if (pim_addr_is_any(gm_ifp->cur_ll_lowest)) {
+ if (was_querier)
+ zlog_info(log_ifp(
+ "lost link-local address, stopping querier"));
+ return;
+ }
+
+ if (was_querier)
+ zlog_info(log_ifp("new link-local %pPA while querier"),
+ &gm_ifp->cur_ll_lowest);
+ else if (IPV6_ADDR_CMP(&gm_ifp->cur_ll_lowest, &gm_ifp->querier) < 0 ||
+ pim_addr_is_any(gm_ifp->querier)) {
+ zlog_info(log_ifp("new link-local %pPA, becoming querier"),
+ &gm_ifp->cur_ll_lowest);
+ gm_ifp->querier = gm_ifp->cur_ll_lowest;
+ } else
+ return;
+
+ gm_ifp->n_startup = gm_ifp->cur_qrv;
+ thread_execute(router->master, gm_t_query, gm_ifp, 0);
+}
+
+void gm_ifp_update(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct gm_if *gm_ifp;
+ bool changed = false;
+
+ if (!pim_ifp)
+ return;
+ if (!if_is_operative(ifp) || !pim_ifp->pim ||
+ pim_ifp->mroute_vif_index < 0) {
+ gm_ifp_teardown(ifp);
+ return;
+ }
+
+ if (!pim_ifp->mld)
+ gm_start(ifp);
+
+ gm_ifp = pim_ifp->mld;
+ if (IPV6_ADDR_CMP(&pim_ifp->ll_lowest, &gm_ifp->cur_ll_lowest))
+ gm_update_ll(ifp);
+
+ unsigned int cfg_query_intv = pim_ifp->gm_default_query_interval * 1000;
+
+ if (gm_ifp->cur_query_intv != cfg_query_intv) {
+ gm_ifp->cur_query_intv = cfg_query_intv;
+ gm_ifp->cur_query_intv_trig = cfg_query_intv;
+ changed = true;
+ }
+
+ enum gm_version cfg_version;
+
+ if (pim_ifp->mld_version == 1)
+ cfg_version = GM_MLDV1;
+ else
+ cfg_version = GM_MLDV2;
+ if (gm_ifp->cur_version != cfg_version) {
+ gm_ifp->cur_version = cfg_version;
+ changed = true;
+ }
+
+ if (changed) {
+ if (PIM_DEBUG_IGMP_TRACE)
+ zlog_debug(log_ifp(
+ "MLD querier config changed, querying"));
+ gm_bump_querier(gm_ifp);
+ }
+}
+
+/*
+ * CLI (show commands only)
+ */
+
+#include "lib/command.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "pimd/pim6_mld_clippy.c"
+#endif
+
+#define MLD_STR "Multicast Listener Discovery\n"
+
+static struct vrf *gm_cmd_vrf_lookup(struct vty *vty, const char *vrf_str,
+ int *err)
+{
+ struct vrf *ret;
+
+ if (!vrf_str)
+ return vrf_lookup_by_id(VRF_DEFAULT);
+ if (!strcmp(vrf_str, "all"))
+ return NULL;
+ ret = vrf_lookup_by_name(vrf_str);
+ if (ret)
+ return ret;
+
+ vty_out(vty, "%% VRF %pSQq does not exist\n", vrf_str);
+ *err = CMD_WARNING;
+ return NULL;
+}
+
+static void gm_show_if_one_detail(struct vty *vty, struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = (struct pim_interface *)ifp->info;
+ struct gm_if *gm_ifp;
+ bool querier;
+ size_t i;
+
+ if (!pim_ifp) {
+ vty_out(vty, "Interface %s: no PIM/MLD config\n\n", ifp->name);
+ return;
+ }
+
+ gm_ifp = pim_ifp->mld;
+ if (!gm_ifp) {
+ vty_out(vty, "Interface %s: MLD not running\n\n", ifp->name);
+ return;
+ }
+
+ querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest);
+
+ vty_out(vty, "Interface %s: MLD running\n", ifp->name);
+ vty_out(vty, " Uptime: %pTVMs\n", &gm_ifp->started);
+ vty_out(vty, " MLD version: %d\n", gm_ifp->cur_version);
+ vty_out(vty, " Querier: %pPA%s\n", &gm_ifp->querier,
+ querier ? " (this system)" : "");
+ vty_out(vty, " Query timer: %pTH\n", gm_ifp->t_query);
+ vty_out(vty, " Other querier timer: %pTH\n",
+ gm_ifp->t_other_querier);
+ vty_out(vty, " Robustness value: %u\n", gm_ifp->cur_qrv);
+ vty_out(vty, " Query interval: %ums\n",
+ gm_ifp->cur_query_intv);
+ vty_out(vty, " Query response timer: %ums\n", gm_ifp->cur_max_resp);
+ vty_out(vty, " Last member query intv.: %ums\n",
+ gm_ifp->cur_query_intv_trig);
+ vty_out(vty, " %u expiry timers from general queries:\n",
+ gm_ifp->n_pending);
+ for (i = 0; i < gm_ifp->n_pending; i++) {
+ struct gm_general_pending *p = &gm_ifp->pending[i];
+
+ vty_out(vty, " %9pTVMs ago (query) -> %9pTVMu (expiry)\n",
+ &p->query, &p->expiry);
+ }
+ vty_out(vty, " %zu expiry timers from *,G queries\n",
+ gm_grp_pends_count(gm_ifp->grp_pends));
+ vty_out(vty, " %zu expiry timers from S,G queries\n",
+ gm_gsq_pends_count(gm_ifp->gsq_pends));
+ vty_out(vty, " %zu total *,G/S,G from %zu hosts in %zu bundles\n",
+ gm_sgs_count(gm_ifp->sgs),
+ gm_subscribers_count(gm_ifp->subscribers),
+ gm_packet_expires_count(gm_ifp->expires));
+ vty_out(vty, "\n");
+}
+
+static void gm_show_if_one(struct vty *vty, struct interface *ifp,
+ json_object *js_if)
+{
+ struct pim_interface *pim_ifp = (struct pim_interface *)ifp->info;
+ struct gm_if *gm_ifp = pim_ifp->mld;
+ bool querier;
+
+ if (!gm_ifp) {
+ if (js_if)
+ json_object_string_add(js_if, "state", "down");
+ else
+ vty_out(vty, "%-16s %5s\n", ifp->name, "down");
+ return;
+ }
+
+ querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest);
+
+ if (js_if) {
+ json_object_string_add(js_if, "state", "up");
+ json_object_string_addf(js_if, "version", "%d",
+ gm_ifp->cur_version);
+ json_object_string_addf(js_if, "upTime", "%pTVMs",
+ &gm_ifp->started);
+ json_object_boolean_add(js_if, "querier", querier);
+ json_object_string_addf(js_if, "querierIp", "%pPA",
+ &gm_ifp->querier);
+ if (querier)
+ json_object_string_addf(js_if, "queryTimer", "%pTH",
+ gm_ifp->t_query);
+ else
+ json_object_string_addf(js_if, "otherQuerierTimer",
+ "%pTH",
+ gm_ifp->t_other_querier);
+ } else {
+ vty_out(vty, "%-16s %-5s %d %-25pPA %-5s %11pTH %pTVMs\n",
+ ifp->name, "up", gm_ifp->cur_version, &gm_ifp->querier,
+ querier ? "query" : "other",
+ querier ? gm_ifp->t_query : gm_ifp->t_other_querier,
+ &gm_ifp->started);
+ }
+}
+
+static void gm_show_if_vrf(struct vty *vty, struct vrf *vrf, const char *ifname,
+ bool detail, json_object *js)
+{
+ struct interface *ifp;
+ json_object *js_vrf;
+
+ if (js) {
+ js_vrf = json_object_new_object();
+ json_object_object_add(js, vrf->name, js_vrf);
+ }
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ json_object *js_if = NULL;
+
+ if (ifname && strcmp(ifp->name, ifname))
+ continue;
+ if (detail && !js) {
+ gm_show_if_one_detail(vty, ifp);
+ continue;
+ }
+
+ if (!ifp->info)
+ continue;
+ if (js) {
+ js_if = json_object_new_object();
+ json_object_object_add(js_vrf, ifp->name, js_if);
+ }
+
+ gm_show_if_one(vty, ifp, js_if);
+ }
+}
+
+static void gm_show_if(struct vty *vty, struct vrf *vrf, const char *ifname,
+ bool detail, json_object *js)
+{
+ if (!js && !detail)
+ vty_out(vty, "%-16s %-5s V %-25s %-18s %s\n", "Interface",
+ "State", "Querier", "Timer", "Uptime");
+
+ if (vrf)
+ gm_show_if_vrf(vty, vrf, ifname, detail, js);
+ else
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ gm_show_if_vrf(vty, vrf, ifname, detail, js);
+}
+
+DEFPY(gm_show_interface,
+ gm_show_interface_cmd,
+ "show ipv6 mld [vrf <VRF|all>$vrf_str] interface [IFNAME] [detail$detail|json$json]",
+ DEBUG_STR
+ SHOW_STR
+ IPV6_STR
+ MLD_STR
+ VRF_FULL_CMD_HELP_STR
+ "MLD interface information\n"
+ "Detailed output\n"
+ JSON_STR)
+{
+ int ret = CMD_SUCCESS;
+ struct vrf *vrf;
+ json_object *js = NULL;
+
+ vrf = gm_cmd_vrf_lookup(vty, vrf_str, &ret);
+ if (ret != CMD_SUCCESS)
+ return ret;
+
+ if (json)
+ js = json_object_new_object();
+ gm_show_if(vty, vrf, ifname, !!detail, js);
+ return vty_json(vty, js);
+}
+
+static void gm_show_stats_one(struct vty *vty, struct gm_if *gm_ifp,
+ json_object *js_if)
+{
+ struct gm_if_stats *stats = &gm_ifp->stats;
+ /* clang-format off */
+ struct {
+ const char *text;
+ const char *js_key;
+ uint64_t *val;
+ } *item, items[] = {
+ { "v2 reports received", "rxV2Reports", &stats->rx_new_report },
+ { "v1 reports received", "rxV1Reports", &stats->rx_old_report },
+ { "v1 done received", "rxV1Done", &stats->rx_old_leave },
+
+ { "v2 *,* queries received", "rxV2QueryGeneral", &stats->rx_query_new_general },
+ { "v2 *,G queries received", "rxV2QueryGroup", &stats->rx_query_new_group },
+ { "v2 S,G queries received", "rxV2QueryGroupSource", &stats->rx_query_new_groupsrc },
+ { "v2 S-bit queries received", "rxV2QuerySBit", &stats->rx_query_new_sbit },
+ { "v1 *,* queries received", "rxV1QueryGeneral", &stats->rx_query_old_general },
+ { "v1 *,G queries received", "rxV1QueryGroup", &stats->rx_query_old_group },
+
+ { "v2 *,* queries sent", "txV2QueryGeneral", &stats->tx_query_new_general },
+ { "v2 *,G queries sent", "txV2QueryGroup", &stats->tx_query_new_group },
+ { "v2 S,G queries sent", "txV2QueryGroupSource", &stats->tx_query_new_groupsrc },
+ { "v1 *,* queries sent", "txV1QueryGeneral", &stats->tx_query_old_general },
+ { "v1 *,G queries sent", "txV1QueryGroup", &stats->tx_query_old_group },
+ { "TX errors", "txErrors", &stats->tx_query_fail },
+
+ { "RX dropped (checksum error)", "rxDropChecksum", &stats->rx_drop_csum },
+ { "RX dropped (invalid source)", "rxDropSrcAddr", &stats->rx_drop_srcaddr },
+ { "RX dropped (invalid dest.)", "rxDropDstAddr", &stats->rx_drop_dstaddr },
+ { "RX dropped (missing alert)", "rxDropRtrAlert", &stats->rx_drop_ra },
+ { "RX dropped (malformed pkt.)", "rxDropMalformed", &stats->rx_drop_malformed },
+ { "RX truncated reports", "rxTruncatedRep", &stats->rx_trunc_report },
+ };
+ /* clang-format on */
+
+ for (item = items; item < items + array_size(items); item++) {
+ if (js_if)
+ json_object_int_add(js_if, item->js_key, *item->val);
+ else
+ vty_out(vty, " %-30s %" PRIu64 "\n", item->text,
+ *item->val);
+ }
+}
+
+static void gm_show_stats_vrf(struct vty *vty, struct vrf *vrf,
+ const char *ifname, json_object *js)
+{
+ struct interface *ifp;
+ json_object *js_vrf;
+
+ if (js) {
+ js_vrf = json_object_new_object();
+ json_object_object_add(js, vrf->name, js_vrf);
+ }
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct pim_interface *pim_ifp;
+ struct gm_if *gm_ifp;
+ json_object *js_if = NULL;
+
+ if (ifname && strcmp(ifp->name, ifname))
+ continue;
+
+ if (!ifp->info)
+ continue;
+ pim_ifp = ifp->info;
+ if (!pim_ifp->mld)
+ continue;
+ gm_ifp = pim_ifp->mld;
+
+ if (js) {
+ js_if = json_object_new_object();
+ json_object_object_add(js_vrf, ifp->name, js_if);
+ } else {
+ vty_out(vty, "Interface: %s\n", ifp->name);
+ }
+ gm_show_stats_one(vty, gm_ifp, js_if);
+ if (!js)
+ vty_out(vty, "\n");
+ }
+}
+
+DEFPY(gm_show_interface_stats,
+ gm_show_interface_stats_cmd,
+ "show ipv6 mld [vrf <VRF|all>$vrf_str] statistics [interface IFNAME] [json$json]",
+ SHOW_STR
+ IPV6_STR
+ MLD_STR
+ VRF_FULL_CMD_HELP_STR
+ "MLD statistics\n"
+ INTERFACE_STR
+ "Interface name\n"
+ JSON_STR)
+{
+ int ret = CMD_SUCCESS;
+ struct vrf *vrf;
+ json_object *js = NULL;
+
+ vrf = gm_cmd_vrf_lookup(vty, vrf_str, &ret);
+ if (ret != CMD_SUCCESS)
+ return ret;
+
+ if (json)
+ js = json_object_new_object();
+
+ if (vrf)
+ gm_show_stats_vrf(vty, vrf, ifname, js);
+ else
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ gm_show_stats_vrf(vty, vrf, ifname, js);
+ return vty_json(vty, js);
+}
+
+static void gm_show_joins_one(struct vty *vty, struct gm_if *gm_ifp,
+ const struct prefix_ipv6 *groups,
+ const struct prefix_ipv6 *sources, bool detail,
+ json_object *js_if)
+{
+ struct gm_sg *sg, *sg_start;
+ json_object *js_group = NULL;
+ pim_addr js_grpaddr = PIMADDR_ANY;
+ struct gm_subscriber sub_ref = {}, *sub_untracked;
+
+ if (groups) {
+ struct gm_sg sg_ref = {};
+
+ sg_ref.sgaddr.grp = pim_addr_from_prefix(groups);
+ sg_start = gm_sgs_find_gteq(gm_ifp->sgs, &sg_ref);
+ } else
+ sg_start = gm_sgs_first(gm_ifp->sgs);
+
+ sub_ref.addr = gm_dummy_untracked;
+ sub_untracked = gm_subscribers_find(gm_ifp->subscribers, &sub_ref);
+ /* NB: sub_untracked may be NULL if no untracked joins exist */
+
+ frr_each_from (gm_sgs, gm_ifp->sgs, sg, sg_start) {
+ struct timeval *recent = NULL, *untracked = NULL;
+ json_object *js_src;
+
+ if (groups) {
+ struct prefix grp_p;
+
+ pim_addr_to_prefix(&grp_p, sg->sgaddr.grp);
+ if (!prefix_match(groups, &grp_p))
+ break;
+ }
+
+ if (sources) {
+ struct prefix src_p;
+
+ pim_addr_to_prefix(&src_p, sg->sgaddr.src);
+ if (!prefix_match(sources, &src_p))
+ continue;
+ }
+
+ if (sg->most_recent) {
+ struct gm_packet_state *packet;
+
+ packet = gm_packet_sg2state(sg->most_recent);
+ recent = &packet->received;
+ }
+
+ if (sub_untracked) {
+ struct gm_packet_state *packet;
+ struct gm_packet_sg *item;
+
+ item = gm_packet_sg_find(sg, GM_SUB_POS, sub_untracked);
+ if (item) {
+ packet = gm_packet_sg2state(item);
+ untracked = &packet->received;
+ }
+ }
+
+ if (!js_if) {
+ FMT_NSTD_BEGIN; /* %.0p */
+ vty_out(vty,
+ "%-30pPA %-30pPAs %-16s %10.0pTVMs %10.0pTVMs %10.0pTVMs\n",
+ &sg->sgaddr.grp, &sg->sgaddr.src,
+ gm_states[sg->state], recent, untracked,
+ &sg->created);
+
+ if (!detail)
+ continue;
+
+ struct gm_packet_sg *item;
+ struct gm_packet_state *packet;
+
+ frr_each (gm_packet_sg_subs, sg->subs_positive, item) {
+ packet = gm_packet_sg2state(item);
+
+ if (packet->subscriber == sub_untracked)
+ continue;
+ vty_out(vty, " %-58pPA %-16s %10.0pTVMs\n",
+ &packet->subscriber->addr, "(JOIN)",
+ &packet->received);
+ }
+ frr_each (gm_packet_sg_subs, sg->subs_negative, item) {
+ packet = gm_packet_sg2state(item);
+
+ if (packet->subscriber == sub_untracked)
+ continue;
+ vty_out(vty, " %-58pPA %-16s %10.0pTVMs\n",
+ &packet->subscriber->addr, "(PRUNE)",
+ &packet->received);
+ }
+ FMT_NSTD_END; /* %.0p */
+ continue;
+ }
+ /* if (js_if) */
+
+ if (!js_group || pim_addr_cmp(js_grpaddr, sg->sgaddr.grp)) {
+ js_group = json_object_new_object();
+ json_object_object_addf(js_if, js_group, "%pPA",
+ &sg->sgaddr.grp);
+ js_grpaddr = sg->sgaddr.grp;
+ }
+
+ js_src = json_object_new_object();
+ json_object_object_addf(js_group, js_src, "%pPA",
+ &sg->sgaddr.src);
+
+ json_object_string_add(js_src, "state", gm_states[sg->state]);
+ json_object_string_addf(js_src, "created", "%pTVMs",
+ &sg->created);
+ json_object_string_addf(js_src, "lastSeen", "%pTVMs", recent);
+
+ if (untracked)
+ json_object_string_addf(js_src, "untrackedLastSeen",
+ "%pTVMs", untracked);
+ if (!detail)
+ continue;
+
+ json_object *js_subs;
+ struct gm_packet_sg *item;
+ struct gm_packet_state *packet;
+
+ js_subs = json_object_new_object();
+ json_object_object_add(js_src, "joinedBy", js_subs);
+ frr_each (gm_packet_sg_subs, sg->subs_positive, item) {
+ packet = gm_packet_sg2state(item);
+ if (packet->subscriber == sub_untracked)
+ continue;
+
+ json_object *js_sub;
+
+ js_sub = json_object_new_object();
+ json_object_object_addf(js_subs, js_sub, "%pPA",
+ &packet->subscriber->addr);
+ json_object_string_addf(js_sub, "lastSeen", "%pTVMs",
+ &packet->received);
+ }
+
+ js_subs = json_object_new_object();
+ json_object_object_add(js_src, "prunedBy", js_subs);
+ frr_each (gm_packet_sg_subs, sg->subs_negative, item) {
+ packet = gm_packet_sg2state(item);
+ if (packet->subscriber == sub_untracked)
+ continue;
+
+ json_object *js_sub;
+
+ js_sub = json_object_new_object();
+ json_object_object_addf(js_subs, js_sub, "%pPA",
+ &packet->subscriber->addr);
+ json_object_string_addf(js_sub, "lastSeen", "%pTVMs",
+ &packet->received);
+ }
+ }
+}
+
+static void gm_show_joins_vrf(struct vty *vty, struct vrf *vrf,
+ const char *ifname,
+ const struct prefix_ipv6 *groups,
+ const struct prefix_ipv6 *sources, bool detail,
+ json_object *js)
+{
+ struct interface *ifp;
+ json_object *js_vrf;
+
+ if (js) {
+ js_vrf = json_object_new_object();
+ json_object_object_add(js, vrf->name, js_vrf);
+ }
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct pim_interface *pim_ifp;
+ struct gm_if *gm_ifp;
+ json_object *js_if = NULL;
+
+ if (ifname && strcmp(ifp->name, ifname))
+ continue;
+
+ if (!ifp->info)
+ continue;
+ pim_ifp = ifp->info;
+ if (!pim_ifp->mld)
+ continue;
+ gm_ifp = pim_ifp->mld;
+
+ if (js) {
+ js_if = json_object_new_object();
+ json_object_object_add(js_vrf, ifp->name, js_if);
+ }
+
+ if (!js && !ifname)
+ vty_out(vty, "\nOn interface %s:\n", ifp->name);
+
+ gm_show_joins_one(vty, gm_ifp, groups, sources, detail, js_if);
+ }
+}
+
+DEFPY(gm_show_interface_joins,
+ gm_show_interface_joins_cmd,
+ "show ipv6 mld [vrf <VRF|all>$vrf_str] joins [{interface IFNAME|groups X:X::X:X/M|sources X:X::X:X/M|detail$detail}] [json$json]",
+ SHOW_STR
+ IPV6_STR
+ MLD_STR
+ VRF_FULL_CMD_HELP_STR
+ "MLD joined groups & sources\n"
+ INTERFACE_STR
+ "Interface name\n"
+ "Limit output to group range\n"
+ "Show groups covered by this prefix\n"
+ "Limit output to source range\n"
+ "Show sources covered by this prefix\n"
+ "Show details, including tracked receivers\n"
+ JSON_STR)
+{
+ int ret = CMD_SUCCESS;
+ struct vrf *vrf;
+ json_object *js = NULL;
+
+ vrf = gm_cmd_vrf_lookup(vty, vrf_str, &ret);
+ if (ret != CMD_SUCCESS)
+ return ret;
+
+ if (json)
+ js = json_object_new_object();
+ else
+ vty_out(vty, "%-30s %-30s %-16s %10s %10s %10s\n", "Group",
+ "Source", "State", "LastSeen", "NonTrkSeen", "Created");
+
+ if (vrf)
+ gm_show_joins_vrf(vty, vrf, ifname, groups, sources, !!detail,
+ js);
+ else
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ gm_show_joins_vrf(vty, vrf, ifname, groups, sources,
+ !!detail, js);
+ return vty_json(vty, js);
+}
+
+DEFPY(gm_debug_show,
+ gm_debug_show_cmd,
+ "debug show mld interface IFNAME",
+ DEBUG_STR
+ SHOW_STR
+ "MLD"
+ INTERFACE_STR
+ "interface name")
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct gm_if *gm_ifp;
+
+ ifp = if_lookup_by_name(ifname, VRF_DEFAULT);
+ if (!ifp) {
+ vty_out(vty, "%% no such interface: %pSQq\n", ifname);
+ return CMD_WARNING;
+ }
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ vty_out(vty, "%% no PIM state for interface %pSQq\n", ifname);
+ return CMD_WARNING;
+ }
+
+ gm_ifp = pim_ifp->mld;
+ if (!gm_ifp) {
+ vty_out(vty, "%% no MLD state for interface %pSQq\n", ifname);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "querier: %pPA\n", &gm_ifp->querier);
+ vty_out(vty, "ll_lowest: %pPA\n\n", &pim_ifp->ll_lowest);
+ vty_out(vty, "t_query: %pTHD\n", gm_ifp->t_query);
+ vty_out(vty, "t_other_querier: %pTHD\n", gm_ifp->t_other_querier);
+ vty_out(vty, "t_expire: %pTHD\n", gm_ifp->t_expire);
+
+ vty_out(vty, "\nn_pending: %u\n", gm_ifp->n_pending);
+ for (size_t i = 0; i < gm_ifp->n_pending; i++) {
+ int64_t query, expiry;
+
+ query = monotime_since(&gm_ifp->pending[i].query, NULL);
+ expiry = monotime_until(&gm_ifp->pending[i].expiry, NULL);
+
+ vty_out(vty, "[%zu]: query %"PRId64"ms ago, expiry in %"PRId64"ms\n",
+ i, query / 1000, expiry / 1000);
+ }
+
+ struct gm_sg *sg;
+ struct gm_packet_state *pkt;
+ struct gm_packet_sg *item;
+ struct gm_subscriber *subscriber;
+
+ vty_out(vty, "\n%zu S,G entries:\n", gm_sgs_count(gm_ifp->sgs));
+ frr_each (gm_sgs, gm_ifp->sgs, sg) {
+ vty_out(vty, "\t%pSG t_expire=%pTHD\n", &sg->sgaddr,
+ sg->t_sg_expire);
+
+ vty_out(vty, "\t @pos:%zu\n",
+ gm_packet_sg_subs_count(sg->subs_positive));
+ frr_each (gm_packet_sg_subs, sg->subs_positive, item) {
+ pkt = gm_packet_sg2state(item);
+
+ vty_out(vty, "\t\t+%s%s [%pPAs %p] %p+%u\n",
+ item->is_src ? "S" : "",
+ item->is_excl ? "E" : "",
+ &pkt->subscriber->addr, pkt->subscriber, pkt,
+ item->offset);
+
+ assert(item->sg == sg);
+ }
+ vty_out(vty, "\t @neg:%zu\n",
+ gm_packet_sg_subs_count(sg->subs_negative));
+ frr_each (gm_packet_sg_subs, sg->subs_negative, item) {
+ pkt = gm_packet_sg2state(item);
+
+ vty_out(vty, "\t\t-%s%s [%pPAs %p] %p+%u\n",
+ item->is_src ? "S" : "",
+ item->is_excl ? "E" : "",
+ &pkt->subscriber->addr, pkt->subscriber, pkt,
+ item->offset);
+
+ assert(item->sg == sg);
+ }
+ }
+
+ vty_out(vty, "\n%zu subscribers:\n",
+ gm_subscribers_count(gm_ifp->subscribers));
+ frr_each (gm_subscribers, gm_ifp->subscribers, subscriber) {
+ vty_out(vty, "\t%pPA %p %zu packets\n", &subscriber->addr,
+ subscriber, gm_packets_count(subscriber->packets));
+
+ frr_each (gm_packets, subscriber->packets, pkt) {
+ vty_out(vty, "\t\t%p %.3fs ago %u of %u items active\n",
+ pkt,
+ monotime_since(&pkt->received, NULL) *
+ 0.000001f,
+ pkt->n_active, pkt->n_sg);
+
+ for (size_t i = 0; i < pkt->n_sg; i++) {
+ item = pkt->items + i;
+
+ vty_out(vty, "\t\t[%zu]", i);
+
+ if (!item->sg) {
+ vty_out(vty, " inactive\n");
+ continue;
+ }
+
+ vty_out(vty, " %s%s %pSG nE=%u\n",
+ item->is_src ? "S" : "",
+ item->is_excl ? "E" : "",
+ &item->sg->sgaddr, item->n_exclude);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(gm_debug_iface_cfg,
+ gm_debug_iface_cfg_cmd,
+ "debug ipv6 mld {"
+ "robustness (0-7)|"
+ "query-max-response-time (1-8387584)"
+ "}",
+ DEBUG_STR
+ IPV6_STR
+ "Multicast Listener Discovery\n"
+ "QRV\nQRV\n"
+ "maxresp\nmaxresp\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pim_interface *pim_ifp;
+ struct gm_if *gm_ifp;
+ bool changed = false;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ vty_out(vty, "%% no PIM state for interface %pSQq\n",
+ ifp->name);
+ return CMD_WARNING;
+ }
+ gm_ifp = pim_ifp->mld;
+ if (!gm_ifp) {
+ vty_out(vty, "%% no MLD state for interface %pSQq\n",
+ ifp->name);
+ return CMD_WARNING;
+ }
+
+ if (robustness_str && gm_ifp->cur_qrv != robustness) {
+ gm_ifp->cur_qrv = robustness;
+ changed = true;
+ }
+ if (query_max_response_time_str &&
+ gm_ifp->cur_max_resp != (unsigned int)query_max_response_time) {
+ gm_ifp->cur_max_resp = query_max_response_time;
+ changed = true;
+ }
+
+ if (changed) {
+ vty_out(vty, "%% MLD querier config changed, bumping\n");
+ gm_bump_querier(gm_ifp);
+ }
+ return CMD_SUCCESS;
+}
+
+void gm_cli_init(void);
+
+void gm_cli_init(void)
+{
+ install_element(VIEW_NODE, &gm_show_interface_cmd);
+ install_element(VIEW_NODE, &gm_show_interface_stats_cmd);
+ install_element(VIEW_NODE, &gm_show_interface_joins_cmd);
+
+ install_element(VIEW_NODE, &gm_debug_show_cmd);
+ install_element(INTERFACE_NODE, &gm_debug_iface_cfg_cmd);
+}
diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h
new file mode 100644
index 0000000000..9c7a637007
--- /dev/null
+++ b/pimd/pim6_mld.h
@@ -0,0 +1,367 @@
+/*
+ * PIMv6 MLD querier
+ * Copyright (C) 2021-2022 David Lamparter for NetDEF, 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 PIM6_MLD_H
+#define PIM6_MLD_H
+
+#include "typesafe.h"
+#include "pim_addr.h"
+
+struct thread;
+struct pim_instance;
+struct gm_packet_sg;
+struct gm_if;
+struct channel_oil;
+
+#define MLD_DEFAULT_VERSION 2
+
+/* see comment below on subs_negative/subs_positive */
+enum gm_sub_sense {
+ /* negative/pruning: S,G in EXCLUDE */
+ GM_SUB_NEG = 0,
+ /* positive/joining: *,G in EXCLUDE and S,G in INCLUDE */
+ GM_SUB_POS = 1,
+};
+
+enum gm_sg_state {
+ GM_SG_NOINFO = 0,
+ GM_SG_JOIN,
+ GM_SG_JOIN_EXPIRING,
+ /* remaining 3 only valid for S,G when *,G in EXCLUDE */
+ GM_SG_PRUNE,
+ GM_SG_NOPRUNE,
+ GM_SG_NOPRUNE_EXPIRING,
+};
+
+static inline bool gm_sg_state_want_join(enum gm_sg_state state)
+{
+ return state != GM_SG_NOINFO && state != GM_SG_PRUNE;
+}
+
+/* MLD (S,G) state (on an interface)
+ *
+ * group is always != ::, src is :: for (*,G) joins. sort order in RB tree is
+ * such that sources for a particular group can be iterated by starting at the
+ * group. For INCLUDE, no (*,G) entry exists, only (S,G).
+ */
+
+PREDECL_RBTREE_UNIQ(gm_packet_sg_subs);
+PREDECL_RBTREE_UNIQ(gm_sgs);
+struct gm_sg {
+ pim_sgaddr sgaddr;
+ struct gm_if *iface;
+ struct gm_sgs_item itm;
+
+ enum gm_sg_state state;
+ struct channel_oil *oil;
+ bool tib_joined;
+
+ struct timeval created;
+
+ /* if a group- or group-and-source specific query is running
+ * (implies we haven't received any report yet, since it's cancelled
+ * by that)
+ */
+ struct thread *t_sg_expire;
+
+ /* last-member-left triggered queries (group/group-source specific)
+ *
+ * this timer will be running even if we aren't the elected querier,
+ * in case the election result changes midway through.
+ */
+ struct thread *t_sg_query;
+
+ /* we must keep sending (QRV) queries even if we get a positive
+ * response, to make sure other routers are updated. query_sbit
+ * will be set in that case, since other routers need the *response*,
+ * not the *query*
+ */
+ uint8_t n_query;
+ bool query_sbit;
+
+ /* subs_positive tracks gm_packet_sg resulting in a JOIN, i.e. for
+ * (*,G) it has *EXCLUDE* items, for (S,G) it has *INCLUDE* items.
+ *
+ * subs_negative is always empty for (*,G) and tracks EXCLUDE items
+ * for (S,G). This means that an (S,G) entry is active as a PRUNE if
+ * len(src->subs_negative) == len(grp->subs_positive)
+ * && len(src->subs_positive) == 0
+ * (i.e. all receivers for the group opted to exclude this S,G and
+ * noone did an SSM join for the S,G)
+ */
+ union {
+ struct {
+ struct gm_packet_sg_subs_head subs_negative[1];
+ struct gm_packet_sg_subs_head subs_positive[1];
+ };
+ struct gm_packet_sg_subs_head subs[2];
+ };
+
+ /* If the elected querier is not ourselves, queries and reports might
+ * get reordered in rare circumstances, i.e. the report could arrive
+ * just a microsecond before the query kicks off the timer. This can
+ * then result in us thinking there are no more receivers since no
+ * report might be received during the query period.
+ *
+ * To avoid this, keep track of the most recent report for this (S,G)
+ * so we can do a quick check to add just a little bit of slack.
+ *
+ * EXCLUDE S,Gs are never in most_recent.
+ */
+ struct gm_packet_sg *most_recent;
+};
+
+/* host tracking entry. addr will be one of:
+ *
+ * :: - used by hosts during address acquisition
+ * ::1 - may show up on some OS for joins by the router itself
+ * link-local - regular operation by MLDv2 hosts
+ * ffff:..:ffff - MLDv1 entry (cannot be tracked due to report suppression)
+ *
+ * global scope IPv6 addresses can never show up here
+ */
+PREDECL_HASH(gm_subscribers);
+PREDECL_DLIST(gm_packets);
+struct gm_subscriber {
+ pim_addr addr;
+ struct gm_subscribers_item itm;
+
+ struct gm_if *iface;
+ size_t refcount;
+
+ struct gm_packets_head packets[1];
+
+ struct timeval created;
+};
+
+/*
+ * MLD join state is kept batched by packet. Since the timers for all items
+ * in a packet are the same, this reduces the number of timers we're keeping
+ * track of. It also eases tracking for EXCLUDE state groups because the
+ * excluded sources are in the same packet. (MLD does not support splitting
+ * that if it exceeds MTU, it's always a full replace for exclude.)
+ *
+ * Since packets may be partially superseded by newer packets, the "active"
+ * field is used to track this.
+ */
+
+/* gm_packet_sg is allocated as part of gm_packet_state, note the items[0]
+ * array at the end of that. gm_packet_sg is NEVER directly allocated with
+ * XMALLOC/XFREE.
+ */
+struct gm_packet_sg {
+ /* non-NULL as long as this gm_packet_sg is the most recent entry
+ * for (subscriber,S,G). Cleared to NULL when a newer packet by the
+ * subscriber replaces this item.
+ *
+ * (Old items are kept around so we don't need to realloc/resize
+ * gm_packet_state, which would mess up a whole lot of pointers)
+ */
+ struct gm_sg *sg;
+
+ /* gm_sg -> (subscriber, gm_packet_sg)
+ * only on RB-tree while sg != NULL, i.e. not superseded by newer.
+ */
+ struct gm_packet_sg_subs_item subs_itm;
+
+ bool is_src : 1; /* := (src != ::) */
+ bool is_excl : 1;
+
+ /* for getting back to struct gm_packet_state, cf.
+ * gm_packet_sg2state() below
+ */
+ uint16_t offset;
+
+ /* if this is a group entry in EXCLUDE state, n_exclude counts how
+ * many sources are on the exclude list here. They follow immediately
+ * after.
+ */
+ uint16_t n_exclude;
+};
+
+#define gm_packet_sg2state(sg) \
+ container_of(sg, struct gm_packet_state, items[sg->offset])
+
+PREDECL_DLIST(gm_packet_expires);
+struct gm_packet_state {
+ struct gm_if *iface;
+ struct gm_subscriber *subscriber;
+ struct gm_packets_item pkt_itm;
+
+ struct timeval received;
+ struct gm_packet_expires_item exp_itm;
+
+ /* n_active starts equal to n_sg; whenever active is set to false on
+ * an item it is decremented. When n_active == 0, the packet can be
+ * freed.
+ */
+ uint16_t n_sg, n_active;
+ struct gm_packet_sg items[0];
+};
+
+/* general queries are rather different from group/S,G specific queries; it's
+ * not particularly efficient or useful to try to shoehorn them into the S,G
+ * timers. Instead, we keep a history of recent queries and their implied
+ * expiries.
+ */
+struct gm_general_pending {
+ struct timeval query, expiry;
+};
+
+/* similarly, group queries also age out S,G entries for the group, but in
+ * this case we only keep one query for each group
+ *
+ * why is this not in the *,G gm_sg? There may not be one (for INCLUDE mode
+ * groups, or groups we don't know about.) Also, malicious clients could spam
+ * random group-specific queries to trigger resource exhaustion, so it makes
+ * sense to limit these.
+ */
+PREDECL_RBTREE_UNIQ(gm_grp_pends);
+struct gm_grp_pending {
+ struct gm_grp_pends_item itm;
+ struct gm_if *iface;
+ pim_addr grp;
+
+ struct timeval query;
+ struct thread *t_expire;
+};
+
+/* guaranteed MTU for IPv6 is 1280 bytes. IPv6 header is 40 bytes, MLDv2
+ * query header is 24 bytes, RA option is 8 bytes - leaves 1208 bytes for the
+ * source list, which is 151 IPv6 addresses. But we may have some more IPv6
+ * extension headers (e.g. IPsec AH), so just cap to 128
+ */
+#define MLD_V2Q_MTU_MAX_SOURCES 128
+
+/* group-and-source-specific queries are bundled together, if some host joins
+ * multiple sources it's likely to drop all at the same time.
+ *
+ * Unlike gm_grp_pending, this is only used for aggregation since the S,G
+ * state is kept directly in the gm_sg structure.
+ */
+PREDECL_HASH(gm_gsq_pends);
+struct gm_gsq_pending {
+ struct gm_gsq_pends_item itm;
+
+ struct gm_if *iface;
+ struct thread *t_send;
+
+ pim_addr grp;
+ bool s_bit;
+
+ size_t n_src;
+ pim_addr srcs[MLD_V2Q_MTU_MAX_SOURCES];
+};
+
+
+/* The size of this history is limited by QRV, i.e. there can't be more than
+ * 8 items here.
+ */
+#define GM_MAX_PENDING 8
+
+enum gm_version {
+ GM_NONE,
+ GM_MLDV1,
+ GM_MLDV2,
+};
+
+struct gm_if_stats {
+ uint64_t rx_drop_csum;
+ uint64_t rx_drop_srcaddr;
+ uint64_t rx_drop_dstaddr;
+ uint64_t rx_drop_ra;
+ uint64_t rx_drop_malformed;
+ uint64_t rx_trunc_report;
+
+ /* since the types are different, this is rx_old_* not of rx_*_old */
+ uint64_t rx_old_report;
+ uint64_t rx_old_leave;
+ uint64_t rx_new_report;
+
+ uint64_t rx_query_new_general;
+ uint64_t rx_query_new_group;
+ uint64_t rx_query_new_groupsrc;
+ uint64_t rx_query_new_sbit;
+ uint64_t rx_query_old_general;
+ uint64_t rx_query_old_group;
+
+ uint64_t tx_query_new_general;
+ uint64_t tx_query_new_group;
+ uint64_t tx_query_new_groupsrc;
+ uint64_t tx_query_old_general;
+ uint64_t tx_query_old_group;
+
+ uint64_t tx_query_fail;
+};
+
+struct gm_if {
+ struct interface *ifp;
+ struct pim_instance *pim;
+ struct thread *t_query, *t_other_querier, *t_expire;
+
+ bool stopping;
+
+ uint8_t n_startup;
+
+ uint8_t cur_qrv;
+ unsigned int cur_query_intv; /* ms */
+ unsigned int cur_query_intv_trig; /* ms */
+ unsigned int cur_max_resp; /* ms */
+ enum gm_version cur_version;
+
+ /* this value (positive, default 10ms) defines our "timing tolerance":
+ * - added to deadlines for expiring joins
+ * - used to look backwards in time for queries, in case a report was
+ * reordered before the query
+ */
+ struct timeval cfg_timing_fuzz;
+
+ /* items in pending[] are sorted by expiry, pending[0] is earliest */
+ struct gm_general_pending pending[GM_MAX_PENDING];
+ uint8_t n_pending;
+ struct gm_grp_pends_head grp_pends[1];
+ struct gm_gsq_pends_head gsq_pends[1];
+
+ pim_addr querier;
+ pim_addr cur_ll_lowest;
+
+ struct gm_sgs_head sgs[1];
+ struct gm_subscribers_head subscribers[1];
+ struct gm_packet_expires_head expires[1];
+
+ struct timeval started;
+ struct gm_if_stats stats;
+};
+
+#if PIM_IPV == 6
+extern void gm_ifp_update(struct interface *ifp);
+extern void gm_ifp_teardown(struct interface *ifp);
+#else
+static inline void gm_ifp_update(struct interface *ifp)
+{
+}
+
+static inline void gm_ifp_teardown(struct interface *ifp)
+{
+}
+#endif
+
+extern void gm_cli_init(void);
+
+#endif /* PIM6_MLD_H */
diff --git a/pimd/pim6_mld_protocol.h b/pimd/pim6_mld_protocol.h
new file mode 100644
index 0000000000..8e602d3396
--- /dev/null
+++ b/pimd/pim6_mld_protocol.h
@@ -0,0 +1,125 @@
+/*
+ * MLD protocol definitions
+ * Copyright (C) 2022 David Lamparter for NetDEF, 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 _PIM6_MLD_PROTOCOL_H
+#define _PIM6_MLD_PROTOCOL_H
+
+#include <stdalign.h>
+#include <stdint.h>
+
+/* There is a struct icmp6_hdr provided by OS, but it includes 4 bytes of data.
+ * Not helpful for us if we want to put the MLD struct after it.
+ */
+
+struct icmp6_plain_hdr {
+ uint8_t icmp6_type;
+ uint8_t icmp6_code;
+ uint16_t icmp6_cksum;
+};
+static_assert(sizeof(struct icmp6_plain_hdr) == 4, "struct mismatch");
+static_assert(alignof(struct icmp6_plain_hdr) <= 4, "struct mismatch");
+
+/* for MLDv1 query, report and leave all use the same packet format */
+struct mld_v1_pkt {
+ uint16_t max_resp_code;
+ uint16_t rsvd0;
+ struct in6_addr grp;
+};
+static_assert(sizeof(struct mld_v1_pkt) == 20, "struct mismatch");
+static_assert(alignof(struct mld_v1_pkt) <= 4, "struct mismatch");
+
+
+struct mld_v2_query_hdr {
+ uint16_t max_resp_code;
+ uint16_t rsvd0;
+ struct in6_addr grp;
+ uint8_t flags;
+ uint8_t qqic;
+ uint16_t n_src;
+ struct in6_addr srcs[0];
+};
+static_assert(sizeof(struct mld_v2_query_hdr) == 24, "struct mismatch");
+static_assert(alignof(struct mld_v2_query_hdr) <= 4, "struct mismatch");
+
+
+struct mld_v2_report_hdr {
+ uint16_t rsvd;
+ uint16_t n_records;
+};
+static_assert(sizeof(struct mld_v2_report_hdr) == 4, "struct mismatch");
+static_assert(alignof(struct mld_v2_report_hdr) <= 4, "struct mismatch");
+
+
+struct mld_v2_rec_hdr {
+ uint8_t type;
+ uint8_t aux_len;
+ uint16_t n_src;
+ struct in6_addr grp;
+ struct in6_addr srcs[0];
+};
+static_assert(sizeof(struct mld_v2_rec_hdr) == 20, "struct mismatch");
+static_assert(alignof(struct mld_v2_rec_hdr) <= 4, "struct mismatch");
+
+/* clang-format off */
+enum icmp6_mld_type {
+ ICMP6_MLD_QUERY = 130,
+ ICMP6_MLD_V1_REPORT = 131,
+ ICMP6_MLD_V1_DONE = 132,
+ ICMP6_MLD_V2_REPORT = 143,
+};
+
+enum mld_v2_rec_type {
+ MLD_RECTYPE_IS_INCLUDE = 1,
+ MLD_RECTYPE_IS_EXCLUDE = 2,
+ MLD_RECTYPE_CHANGE_TO_INCLUDE = 3,
+ MLD_RECTYPE_CHANGE_TO_EXCLUDE = 4,
+ MLD_RECTYPE_ALLOW_NEW_SOURCES = 5,
+ MLD_RECTYPE_BLOCK_OLD_SOURCES = 6,
+};
+/* clang-format on */
+
+/* helper functions */
+
+static inline unsigned int mld_max_resp_decode(uint16_t wire)
+{
+ uint16_t code = ntohs(wire);
+ uint8_t exp;
+
+ if (code < 0x8000)
+ return code;
+ exp = (code >> 12) & 0x7;
+ return ((code & 0xfff) | 0x1000) << (exp + 3);
+}
+
+static inline uint16_t mld_max_resp_encode(uint32_t value)
+{
+ uint16_t code;
+ uint8_t exp;
+
+ if (value < 0x8000)
+ code = value;
+ else {
+ exp = 16 - __builtin_clz(value);
+ code = (value >> (exp + 3)) & 0xfff;
+ code |= 0x8000 | (exp << 12);
+ }
+ return htons(code);
+}
+
+#endif /* _PIM6_MLD_PROTOCOL_H */
diff --git a/pimd/pim6_mroute_msg.c b/pimd/pim6_mroute_msg.c
index 37d67ad048..0b4b31fefa 100644
--- a/pimd/pim6_mroute_msg.c
+++ b/pimd/pim6_mroute_msg.c
@@ -29,6 +29,7 @@
#include "lib/network.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_mroute.h"
#include "pim_oil.h"
#include "pim_str.h"
@@ -167,10 +168,12 @@ int pim_mroute_msg(struct pim_instance *pim, const char *buf,
msg);
case MRT6MSG_WHOLEPKT:
return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
- (const char *)msg);
+ (const char *)msg,
+ buf_size);
case MRT6MSG_WRMIFWHOLE:
- return pim_mroute_msg_wrvifwhole(
- pim->mroute_socket, ifp, (const char *)msg);
+ return pim_mroute_msg_wrvifwhole(pim->mroute_socket,
+ ifp, (const char *)msg,
+ buf_size);
default:
break;
}
diff --git a/pimd/pim6_stubs.c b/pimd/pim6_stubs.c
index f799f04f33..8213b9e97f 100644
--- a/pimd/pim6_stubs.c
+++ b/pimd/pim6_stubs.c
@@ -25,6 +25,7 @@
#include "pim_pim.h"
#include "pim_register.h"
#include "pim_cmd.h"
+#include "pim_bsm.h"
/*
* NH lookup / NHT
diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c
index 7d924d6505..2cc98f7329 100644
--- a/pimd/pim_assert.c
+++ b/pimd/pim_assert.c
@@ -24,6 +24,7 @@
#include "if.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_str.h"
#include "pim_tlv.h"
#include "pim_msg.h"
@@ -302,6 +303,15 @@ int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh,
pim_ifp = ifp->info;
assert(pim_ifp);
+
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip receiving PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
+
++pim_ifp->pim_ifstat_assert_recv;
return dispatch_assert(ifp, msg_source_addr, sg.grp, msg_metric);
@@ -459,11 +469,12 @@ static int pim_assert_do(struct pim_ifchannel *ch,
metric.metric_preference, metric.route_metric,
PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
}
- ++pim_ifp->pim_ifstat_assert_send;
+ if (!pim_ifp->pim_passive_enable)
+ ++pim_ifp->pim_ifstat_assert_send;
if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
- ifp->name)) {
+ ifp)) {
zlog_warn("%s: could not send PIM message on interface %s",
__func__, ifp->name);
return -3;
diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c
index 003ce47432..8ef3c43a99 100644
--- a/pimd/pim_bsm.c
+++ b/pimd/pim_bsm.c
@@ -704,13 +704,15 @@ static bool pim_bsm_send_intf(uint8_t *buf, int len, struct interface *ifp,
}
if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
- dst_addr, buf, len, ifp->name)) {
+ dst_addr, buf, len, ifp)) {
zlog_warn("%s: Could not send BSM message on interface: %s",
__func__, ifp->name);
return false;
}
- pim_ifp->pim_ifstat_bsm_tx++;
+ if (!pim_ifp->pim_passive_enable)
+ pim_ifp->pim_ifstat_bsm_tx++;
+
pim_ifp->pim->bsm_sent++;
return true;
}
@@ -1298,6 +1300,14 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
return -1;
}
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip receiving PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
+
pim_ifp->pim_ifstat_bsm_rx++;
pim = pim_ifp->pim;
pim->bsm_rcvd++;
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 55e58f2d9a..2d6ce24381 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1303,27 +1303,6 @@ static void pim_show_group_rp_mappings_info(struct pim_instance *pim,
vty_json(vty, json);
}
-static void clear_pim_statistics(struct pim_instance *pim)
-{
- struct interface *ifp;
-
- pim->bsm_rcvd = 0;
- pim->bsm_sent = 0;
- pim->bsm_dropped = 0;
-
- /* scan interfaces */
- FOR_ALL_INTERFACES (pim->vrf, ifp) {
- struct pim_interface *pim_ifp = ifp->info;
-
- if (!pim_ifp)
- continue;
-
- pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
- pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
- pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
- }
-}
-
static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj)
{
struct interface *ifp;
@@ -1842,79 +1821,39 @@ DEFUN (clear_ip_igmp_interfaces,
return CMD_SUCCESS;
}
-DEFUN (clear_ip_pim_statistics,
+DEFPY (clear_ip_pim_statistics,
clear_ip_pim_statistics_cmd,
- "clear ip pim statistics [vrf NAME]",
+ "clear ip pim statistics [vrf NAME]$name",
CLEAR_STR
IP_STR
CLEAR_IP_PIM_STR
VRF_CMD_HELP_STR
"Reset PIM statistics\n")
{
- int idx = 2;
- struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+ struct vrf *v = pim_cmd_lookup(vty, name);
- if (!vrf)
+ if (!v)
return CMD_WARNING;
- clear_pim_statistics(vrf->info);
- return CMD_SUCCESS;
-}
-
-static void clear_mroute(struct pim_instance *pim)
-{
- struct pim_upstream *up;
- struct interface *ifp;
-
- /* scan interfaces */
- FOR_ALL_INTERFACES (pim->vrf, ifp) {
- struct pim_interface *pim_ifp = ifp->info;
- struct pim_ifchannel *ch;
-
- if (!pim_ifp)
- continue;
-
- /* deleting all ifchannels */
- while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
- ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
-
- pim_ifchannel_delete(ch);
- }
-
-#if PIM_IPV == 4
- /* clean up all igmp groups */
- struct gm_group *grp;
-
- if (pim_ifp->gm_group_list) {
- while (pim_ifp->gm_group_list->count) {
- grp = listnode_head(pim_ifp->gm_group_list);
- igmp_group_delete(grp);
- }
- }
-#endif
- }
-
- /* clean up all upstreams*/
- while ((up = rb_pim_upstream_first(&pim->upstream_head)))
- pim_upstream_del(pim, up, __func__);
+ clear_pim_statistics(v->info);
+ return CMD_SUCCESS;
}
-DEFUN (clear_ip_mroute,
+DEFPY (clear_ip_mroute,
clear_ip_mroute_cmd,
- "clear ip mroute [vrf NAME]",
+ "clear ip mroute [vrf NAME]$name",
CLEAR_STR
IP_STR
"Reset multicast routes\n"
VRF_CMD_HELP_STR)
{
- int idx = 2;
- struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+ struct vrf *v = pim_cmd_lookup(vty, name);
- if (!vrf)
+ if (!v)
return CMD_WARNING;
- clear_mroute(vrf->info);
+ clear_mroute(v->info);
return CMD_SUCCESS;
}
@@ -1985,22 +1924,21 @@ DEFUN (clear_ip_pim_interface_traffic,
return CMD_SUCCESS;
}
-DEFUN (clear_ip_pim_oil,
+DEFPY (clear_ip_pim_oil,
clear_ip_pim_oil_cmd,
- "clear ip pim [vrf NAME] oil",
+ "clear ip pim [vrf NAME]$name oil",
CLEAR_STR
IP_STR
CLEAR_IP_PIM_STR
VRF_CMD_HELP_STR
"Rescan PIM OIL (output interface list)\n")
{
- int idx = 2;
- struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+ struct vrf *v = pim_cmd_lookup(vty, name);
- if (!vrf)
+ if (!v)
return CMD_WARNING;
- pim_scan_oil(vrf->info);
+ pim_scan_oil(v->info);
return CMD_SUCCESS;
}
@@ -3848,47 +3786,16 @@ DEFPY (show_ip_mroute_vrf_all,
return CMD_SUCCESS;
}
-DEFUN (clear_ip_mroute_count,
+DEFPY (clear_ip_mroute_count,
clear_ip_mroute_count_cmd,
- "clear ip mroute [vrf NAME] count",
+ "clear ip mroute [vrf NAME]$name count",
CLEAR_STR
IP_STR
MROUTE_STR
VRF_CMD_HELP_STR
"Route and packet count data\n")
{
- int idx = 2;
- struct listnode *node;
- struct channel_oil *c_oil;
- struct static_route *sr;
- struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
- struct pim_instance *pim;
-
- if (!vrf)
- return CMD_WARNING;
-
- pim = vrf->info;
- frr_each(rb_pim_oil, &pim->channel_oil_head, c_oil) {
- if (!c_oil->installed)
- continue;
-
- pim_mroute_update_counters(c_oil);
- c_oil->cc.origpktcnt = c_oil->cc.pktcnt;
- c_oil->cc.origbytecnt = c_oil->cc.bytecnt;
- c_oil->cc.origwrong_if = c_oil->cc.wrong_if;
- }
-
- for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) {
- if (!sr->c_oil.installed)
- continue;
-
- pim_mroute_update_counters(&sr->c_oil);
-
- sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt;
- sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt;
- sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if;
- }
- return CMD_SUCCESS;
+ return clear_ip_mroute_count_command(vty, name);
}
DEFPY (show_ip_mroute_count,
@@ -5262,13 +5169,24 @@ DEFUN_HIDDEN (interface_ip_pim_sm,
return pim_process_ip_pim_cmd(vty);
}
-DEFUN (interface_ip_pim,
+DEFPY (interface_ip_pim,
interface_ip_pim_cmd,
- "ip pim",
+ "ip pim [passive$passive]",
IP_STR
- PIM_STR)
+ PIM_STR
+ "Disable exchange of protocol packets\n")
{
- return pim_process_ip_pim_cmd(vty);
+ int ret;
+
+ ret = pim_process_ip_pim_cmd(vty);
+
+ if (ret != NB_OK)
+ return ret;
+
+ if (passive)
+ return pim_process_ip_pim_passive_cmd(vty, true);
+
+ return CMD_SUCCESS;
}
DEFUN_HIDDEN (interface_no_ip_pim_ssm,
@@ -5293,13 +5211,17 @@ DEFUN_HIDDEN (interface_no_ip_pim_sm,
return pim_process_no_ip_pim_cmd(vty);
}
-DEFUN (interface_no_ip_pim,
+DEFPY (interface_no_ip_pim,
interface_no_ip_pim_cmd,
- "no ip pim",
+ "no ip pim [passive$passive]",
NO_STR
IP_STR
- PIM_STR)
+ PIM_STR
+ "Disable exchange of protocol packets\n")
{
+ if (passive)
+ return pim_process_ip_pim_passive_cmd(vty, false);
+
return pim_process_no_ip_pim_cmd(vty);
}
@@ -5600,88 +5522,47 @@ DEFUN (no_debug_pim_static,
}
-DEFUN (debug_pim,
+DEFPY (debug_pim,
debug_pim_cmd,
- "debug pim",
- DEBUG_STR
- DEBUG_PIM_STR)
-{
- 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;
- PIM_DO_DEBUG_BSM;
- PIM_DO_DEBUG_VXLAN;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim,
- no_debug_pim_cmd,
- "no debug pim",
+ "[no] debug pim",
NO_STR
DEBUG_STR
DEBUG_PIM_STR)
{
- 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;
- PIM_DONT_DEBUG_BSM;
- PIM_DONT_DEBUG_VXLAN;
-
- return CMD_SUCCESS;
+ if (!no)
+ return pim_debug_pim_cmd();
+ else
+ return pim_no_debug_pim_cmd();
}
-DEFUN (debug_pim_nht,
+DEFPY (debug_pim_nht,
debug_pim_nht_cmd,
- "debug pim nht",
- DEBUG_STR
- DEBUG_PIM_STR
- "Nexthop Tracking\n")
-{
- PIM_DO_DEBUG_PIM_NHT;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_nht,
- no_debug_pim_nht_cmd,
- "no debug pim nht",
+ "[no] debug pim nht",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
"Nexthop Tracking\n")
{
- PIM_DONT_DEBUG_PIM_NHT;
+ if (!no)
+ PIM_DO_DEBUG_PIM_NHT;
+ else
+ PIM_DONT_DEBUG_PIM_NHT;
return CMD_SUCCESS;
}
-DEFUN (debug_pim_nht_det,
+DEFPY (debug_pim_nht_det,
debug_pim_nht_det_cmd,
- "debug pim nht detail",
- DEBUG_STR
- DEBUG_PIM_STR
- "Nexthop Tracking\n"
- "Detailed Information\n")
-{
- PIM_DO_DEBUG_PIM_NHT_DETAIL;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_nht_det,
- no_debug_pim_nht_det_cmd,
- "no debug pim nht detail",
+ "[no] debug pim nht detail",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
"Nexthop Tracking\n"
"Detailed Information\n")
{
- PIM_DONT_DEBUG_PIM_NHT_DETAIL;
+ if (!no)
+ PIM_DO_DEBUG_PIM_NHT_DETAIL;
+ else
+ PIM_DONT_DEBUG_PIM_NHT_DETAIL;
return CMD_SUCCESS;
}
@@ -5710,179 +5591,98 @@ DEFUN (no_debug_pim_nht_rp,
return CMD_SUCCESS;
}
-DEFUN (debug_pim_events,
+DEFPY (debug_pim_events,
debug_pim_events_cmd,
- "debug pim events",
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_EVENTS_STR)
-{
- PIM_DO_DEBUG_PIM_EVENTS;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_events,
- no_debug_pim_events_cmd,
- "no debug pim events",
+ "[no] debug pim events",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_EVENTS_STR)
{
- PIM_DONT_DEBUG_PIM_EVENTS;
+ if (!no)
+ PIM_DO_DEBUG_PIM_EVENTS;
+ else
+ PIM_DONT_DEBUG_PIM_EVENTS;
return CMD_SUCCESS;
}
-DEFUN (debug_pim_packets,
+DEFPY (debug_pim_packets,
debug_pim_packets_cmd,
- "debug pim packets [<hello|joins|register>]",
- DEBUG_STR
+ "[no] debug pim packets [<hello$hello|joins$joins|register$registers>]",
+ NO_STR DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_PACKETS_STR
DEBUG_PIM_HELLO_PACKETS_STR
DEBUG_PIM_J_P_PACKETS_STR
DEBUG_PIM_PIM_REG_PACKETS_STR)
{
- int idx = 0;
- if (argv_find(argv, argc, "hello", &idx)) {
- PIM_DO_DEBUG_PIM_HELLO;
- vty_out(vty, "PIM Hello debugging is on\n");
- } else if (argv_find(argv, argc, "joins", &idx)) {
- PIM_DO_DEBUG_PIM_J_P;
- vty_out(vty, "PIM Join/Prune debugging is on\n");
- } else if (argv_find(argv, argc, "register", &idx)) {
- PIM_DO_DEBUG_PIM_REG;
- vty_out(vty, "PIM Register debugging is on\n");
- } else {
- PIM_DO_DEBUG_PIM_PACKETS;
- vty_out(vty, "PIM Packet debugging is on \n");
- }
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_packets,
- no_debug_pim_packets_cmd,
- "no debug pim packets [<hello|joins|register>]",
- NO_STR
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_PACKETS_STR
- DEBUG_PIM_HELLO_PACKETS_STR
- DEBUG_PIM_J_P_PACKETS_STR
- DEBUG_PIM_PIM_REG_PACKETS_STR)
-{
- int idx = 0;
- if (argv_find(argv, argc, "hello", &idx)) {
- PIM_DONT_DEBUG_PIM_HELLO;
- vty_out(vty, "PIM Hello debugging is off \n");
- } else if (argv_find(argv, argc, "joins", &idx)) {
- PIM_DONT_DEBUG_PIM_J_P;
- vty_out(vty, "PIM Join/Prune debugging is off \n");
- } else if (argv_find(argv, argc, "register", &idx)) {
- PIM_DONT_DEBUG_PIM_REG;
- vty_out(vty, "PIM Register debugging is off\n");
- } else
- PIM_DONT_DEBUG_PIM_PACKETS;
-
- return CMD_SUCCESS;
+ if (!no)
+ return pim_debug_pim_packets_cmd(hello, joins, registers, vty);
+ else
+ return pim_no_debug_pim_packets_cmd(hello, joins, registers,
+ vty);
}
-
-DEFUN (debug_pim_packetdump_send,
+DEFPY (debug_pim_packetdump_send,
debug_pim_packetdump_send_cmd,
- "debug pim packet-dump send",
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_PACKETDUMP_STR
- DEBUG_PIM_PACKETDUMP_SEND_STR)
-{
- PIM_DO_DEBUG_PIM_PACKETDUMP_SEND;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_packetdump_send,
- no_debug_pim_packetdump_send_cmd,
- "no debug pim packet-dump send",
+ "[no] debug pim packet-dump send",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_PACKETDUMP_STR
DEBUG_PIM_PACKETDUMP_SEND_STR)
{
- PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
+ if (!no)
+ PIM_DO_DEBUG_PIM_PACKETDUMP_SEND;
+ else
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
return CMD_SUCCESS;
}
-DEFUN (debug_pim_packetdump_recv,
+DEFPY (debug_pim_packetdump_recv,
debug_pim_packetdump_recv_cmd,
- "debug pim packet-dump receive",
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_PACKETDUMP_STR
- DEBUG_PIM_PACKETDUMP_RECV_STR)
-{
- PIM_DO_DEBUG_PIM_PACKETDUMP_RECV;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_packetdump_recv,
- no_debug_pim_packetdump_recv_cmd,
- "no debug pim packet-dump receive",
+ "[no] debug pim packet-dump receive",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_PACKETDUMP_STR
DEBUG_PIM_PACKETDUMP_RECV_STR)
{
- PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
+ if (!no)
+ PIM_DO_DEBUG_PIM_PACKETDUMP_RECV;
+ else
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
return CMD_SUCCESS;
}
-DEFUN (debug_pim_trace,
+DEFPY (debug_pim_trace,
debug_pim_trace_cmd,
- "debug pim trace",
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_TRACE_STR)
-{
- PIM_DO_DEBUG_PIM_TRACE;
- return CMD_SUCCESS;
-}
-
-DEFUN (debug_pim_trace_detail,
- debug_pim_trace_detail_cmd,
- "debug pim trace detail",
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_TRACE_STR
- "Detailed Information\n")
-{
- PIM_DO_DEBUG_PIM_TRACE_DETAIL;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_trace,
- no_debug_pim_trace_cmd,
- "no debug pim trace",
+ "[no] debug pim trace",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_TRACE_STR)
{
- PIM_DONT_DEBUG_PIM_TRACE;
+ if (!no)
+ PIM_DO_DEBUG_PIM_TRACE;
+ else
+ PIM_DONT_DEBUG_PIM_TRACE;
return CMD_SUCCESS;
}
-DEFUN (no_debug_pim_trace_detail,
- no_debug_pim_trace_detail_cmd,
- "no debug pim trace detail",
+DEFPY (debug_pim_trace_detail,
+ debug_pim_trace_detail_cmd,
+ "[no] debug pim trace detail",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_TRACE_STR
"Detailed Information\n")
{
- PIM_DONT_DEBUG_PIM_TRACE_DETAIL;
+ if (!no)
+ PIM_DO_DEBUG_PIM_TRACE_DETAIL;
+ else
+ PIM_DONT_DEBUG_PIM_TRACE_DETAIL;
return CMD_SUCCESS;
}
@@ -5907,26 +5707,18 @@ DEFUN (no_debug_ssmpingd,
return CMD_SUCCESS;
}
-DEFUN (debug_pim_zebra,
+DEFPY (debug_pim_zebra,
debug_pim_zebra_cmd,
- "debug pim zebra",
- DEBUG_STR
- DEBUG_PIM_STR
- DEBUG_PIM_ZEBRA_STR)
-{
- PIM_DO_DEBUG_ZEBRA;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_pim_zebra,
- no_debug_pim_zebra_cmd,
- "no debug pim zebra",
+ "[no] debug pim zebra",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_ZEBRA_STR)
{
- PIM_DONT_DEBUG_ZEBRA;
+ if (!no)
+ PIM_DO_DEBUG_ZEBRA;
+ else
+ PIM_DONT_DEBUG_ZEBRA;
return CMD_SUCCESS;
}
@@ -7950,29 +7742,19 @@ void pim_cmd_init(void)
install_element(ENABLE_NODE, &debug_pim_static_cmd);
install_element(ENABLE_NODE, &no_debug_pim_static_cmd);
install_element(ENABLE_NODE, &debug_pim_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_cmd);
install_element(ENABLE_NODE, &debug_pim_nht_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_nht_cmd);
install_element(ENABLE_NODE, &debug_pim_nht_det_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_nht_det_cmd);
install_element(ENABLE_NODE, &debug_pim_nht_rp_cmd);
install_element(ENABLE_NODE, &no_debug_pim_nht_rp_cmd);
install_element(ENABLE_NODE, &debug_pim_events_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_events_cmd);
install_element(ENABLE_NODE, &debug_pim_packets_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_packets_cmd);
install_element(ENABLE_NODE, &debug_pim_packetdump_send_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_packetdump_send_cmd);
install_element(ENABLE_NODE, &debug_pim_packetdump_recv_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_packetdump_recv_cmd);
install_element(ENABLE_NODE, &debug_pim_trace_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_trace_cmd);
install_element(ENABLE_NODE, &debug_pim_trace_detail_cmd);
- install_element(ENABLE_NODE, &no_debug_pim_trace_detail_cmd);
install_element(ENABLE_NODE, &debug_ssmpingd_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_pim_mlag_cmd);
install_element(ENABLE_NODE, &no_debug_pim_mlag_cmd);
install_element(ENABLE_NODE, &debug_pim_vxlan_cmd);
@@ -8005,29 +7787,19 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_pim_static_cmd);
install_element(CONFIG_NODE, &no_debug_pim_static_cmd);
install_element(CONFIG_NODE, &debug_pim_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_cmd);
install_element(CONFIG_NODE, &debug_pim_nht_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_nht_cmd);
install_element(CONFIG_NODE, &debug_pim_nht_det_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_nht_det_cmd);
install_element(CONFIG_NODE, &debug_pim_nht_rp_cmd);
install_element(CONFIG_NODE, &no_debug_pim_nht_rp_cmd);
install_element(CONFIG_NODE, &debug_pim_events_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_events_cmd);
install_element(CONFIG_NODE, &debug_pim_packets_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_packets_cmd);
install_element(CONFIG_NODE, &debug_pim_packetdump_send_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_packetdump_send_cmd);
install_element(CONFIG_NODE, &debug_pim_packetdump_recv_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_packetdump_recv_cmd);
install_element(CONFIG_NODE, &debug_pim_trace_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_trace_cmd);
install_element(CONFIG_NODE, &debug_pim_trace_detail_cmd);
- install_element(CONFIG_NODE, &no_debug_pim_trace_detail_cmd);
install_element(CONFIG_NODE, &debug_ssmpingd_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_pim_mlag_cmd);
install_element(CONFIG_NODE, &no_debug_pim_mlag_cmd);
install_element(CONFIG_NODE, &debug_pim_vxlan_cmd);
diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c
index d69b94ab12..d5eb763df3 100644
--- a/pimd/pim_cmd_common.c
+++ b/pimd/pim_cmd_common.c
@@ -34,6 +34,7 @@
#include "lib/linklist.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_vty.h"
#include "lib/northbound_cli.h"
#include "pim_errors.h"
@@ -51,6 +52,7 @@
#include "pim_nht.h"
#include "pim_sock.h"
#include "pim_ssm.h"
+#include "pim_static.h"
#include "pim_addr.h"
#include "pim_static.h"
@@ -351,6 +353,19 @@ int pim_process_ip_pim_cmd(struct vty *vty)
FRR_PIM_AF_XPATH_VAL);
}
+int pim_process_ip_pim_passive_cmd(struct vty *vty, bool enable)
+{
+ if (enable)
+ nb_cli_enqueue_change(vty, "./pim-passive-enable", NB_OP_MODIFY,
+ "true");
+ else
+ nb_cli_enqueue_change(vty, "./pim-passive-enable", NB_OP_MODIFY,
+ "false");
+
+ return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
int pim_process_no_ip_pim_cmd(struct vty *vty)
{
const struct lyd_node *mld_enable_dnode;
@@ -2109,6 +2124,10 @@ void pim_show_interfaces_single(struct pim_instance *pim, struct vty *vty,
sec_list);
}
+ if (pim_ifp->pim_passive_enable)
+ json_object_boolean_true_add(json_row,
+ "passive");
+
/* PIM neighbors */
if (pim_ifp->pim_neighbor_list->count) {
json_pim_neighbors = json_object_new_object();
@@ -2275,6 +2294,12 @@ void pim_show_interfaces_single(struct pim_instance *pim, struct vty *vty,
} else {
vty_out(vty, "Address : %pPAs\n", &ifaddr);
}
+
+ if (pim_ifp->pim_passive_enable)
+ vty_out(vty, "Passive : %s\n",
+ (pim_ifp->pim_passive_enable) ? "yes"
+ : "no");
+
vty_out(vty, "\n");
/* PIM neighbors */
@@ -3539,3 +3564,176 @@ void show_mroute_summary(struct pim_instance *pim, struct vty *vty,
sg_hw_mroute_cnt);
}
}
+
+int clear_ip_mroute_count_command(struct vty *vty, const char *name)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ struct static_route *sr;
+ struct vrf *v = pim_cmd_lookup(vty, name);
+ struct pim_instance *pim;
+
+ if (!v)
+ return CMD_WARNING;
+
+ pim = v->info;
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
+ if (!c_oil->installed)
+ continue;
+
+ pim_mroute_update_counters(c_oil);
+ c_oil->cc.origpktcnt = c_oil->cc.pktcnt;
+ c_oil->cc.origbytecnt = c_oil->cc.bytecnt;
+ c_oil->cc.origwrong_if = c_oil->cc.wrong_if;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) {
+ if (!sr->c_oil.installed)
+ continue;
+
+ pim_mroute_update_counters(&sr->c_oil);
+
+ sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt;
+ sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt;
+ sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if;
+ }
+ return CMD_SUCCESS;
+}
+
+struct vrf *pim_cmd_lookup(struct vty *vty, const char *name)
+{
+ struct vrf *vrf;
+
+ if (name)
+ vrf = vrf_lookup_by_name(name);
+ else
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+
+ if (!vrf)
+ vty_out(vty, "Specified VRF: %s does not exist\n", name);
+
+ return vrf;
+}
+
+void clear_mroute(struct pim_instance *pim)
+{
+ struct pim_upstream *up;
+ struct interface *ifp;
+
+ /* scan interfaces */
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct pim_ifchannel *ch;
+
+ if (!pim_ifp)
+ continue;
+
+ /* deleting all ifchannels */
+ while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+ ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
+ pim_ifchannel_delete(ch);
+ }
+
+#if PIM_IPV == 4
+ /* clean up all igmp groups */
+ struct gm_group *grp;
+
+ if (pim_ifp->gm_group_list) {
+ while (pim_ifp->gm_group_list->count) {
+ grp = listnode_head(pim_ifp->gm_group_list);
+ igmp_group_delete(grp);
+ }
+ }
+#endif
+ }
+
+ /* clean up all upstreams*/
+ while ((up = rb_pim_upstream_first(&pim->upstream_head)))
+ pim_upstream_del(pim, up, __func__);
+}
+
+void clear_pim_statistics(struct pim_instance *pim)
+{
+ struct interface *ifp;
+
+ pim->bsm_rcvd = 0;
+ pim->bsm_sent = 0;
+ pim->bsm_dropped = 0;
+
+ /* scan interfaces */
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
+ pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
+ pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
+ }
+}
+
+int pim_debug_pim_cmd(void)
+{
+ 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;
+ PIM_DO_DEBUG_BSM;
+ PIM_DO_DEBUG_VXLAN;
+ return CMD_SUCCESS;
+}
+
+int pim_no_debug_pim_cmd(void)
+{
+ 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;
+ return CMD_SUCCESS;
+}
+
+int pim_debug_pim_packets_cmd(const char *hello, const char *joins,
+ const char *registers, struct vty *vty)
+{
+ if (hello) {
+ PIM_DO_DEBUG_PIM_HELLO;
+ vty_out(vty, "PIM Hello debugging is on\n");
+ } else if (joins) {
+ PIM_DO_DEBUG_PIM_J_P;
+ vty_out(vty, "PIM Join/Prune debugging is on\n");
+ } else if (registers) {
+ PIM_DO_DEBUG_PIM_REG;
+ vty_out(vty, "PIM Register debugging is on\n");
+ } else {
+ PIM_DO_DEBUG_PIM_PACKETS;
+ vty_out(vty, "PIM Packet debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+int pim_no_debug_pim_packets_cmd(const char *hello, const char *joins,
+ const char *registers, struct vty *vty)
+{
+ if (hello) {
+ PIM_DONT_DEBUG_PIM_HELLO;
+ vty_out(vty, "PIM Hello debugging is off\n");
+ } else if (joins) {
+ PIM_DONT_DEBUG_PIM_J_P;
+ vty_out(vty, "PIM Join/Prune debugging is off\n");
+ } else if (registers) {
+ PIM_DONT_DEBUG_PIM_REG;
+ vty_out(vty, "PIM Register debugging is off\n");
+ } else {
+ PIM_DONT_DEBUG_PIM_PACKETS;
+ vty_out(vty, "PIM Packet debugging is off\n");
+ }
+
+ return CMD_SUCCESS;
+}
diff --git a/pimd/pim_cmd_common.h b/pimd/pim_cmd_common.h
index 8753d1444e..9644f84e0c 100644
--- a/pimd/pim_cmd_common.h
+++ b/pimd/pim_cmd_common.h
@@ -20,6 +20,12 @@
#ifndef PIM_CMD_COMMON_H
#define PIM_CMD_COMMON_H
+struct pim_upstream;
+struct pim_instance;
+
+/* duplicated from pim_instance.h - needed to avoid dependency mess */
+struct pim_instance *pim_get_pim_instance(vrf_id_t vrf_id);
+
const char *pim_cli_get_vrf_name(struct vty *vty);
int pim_process_join_prune_cmd(struct vty *vty, const char *jpi_str);
int pim_process_no_join_prune_cmd(struct vty *vty);
@@ -46,6 +52,7 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str,
int pim_process_ip_pim_cmd(struct vty *vty);
int pim_process_no_ip_pim_cmd(struct vty *vty);
+int pim_process_ip_pim_passive_cmd(struct vty *vty, bool enable);
int pim_process_ip_pim_drprio_cmd(struct vty *vty, const char *drpriority_str);
int pim_process_no_ip_pim_drprio_cmd(struct vty *vty);
int pim_process_ip_pim_hello_cmd(struct vty *vty, const char *hello_str,
@@ -114,6 +121,16 @@ void show_mroute_count(struct pim_instance *pim, struct vty *vty,
json_object *json);
void show_mroute_summary(struct pim_instance *pim, struct vty *vty,
json_object *json);
+int clear_ip_mroute_count_command(struct vty *vty, const char *name);
+struct vrf *pim_cmd_lookup(struct vty *vty, const char *name);
+void clear_mroute(struct pim_instance *pim);
+void clear_pim_statistics(struct pim_instance *pim);
+int pim_debug_pim_cmd(void);
+int pim_no_debug_pim_cmd(void);
+int pim_debug_pim_packets_cmd(const char *hello, const char *joins,
+ const char *registers, struct vty *vty);
+int pim_no_debug_pim_packets_cmd(const char *hello, const char *joins,
+ const char *registers, struct vty *vty);
/*
* Special Macro to allow us to get the correct pim_instance;
diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c
index fe4f10aa6a..833103c27f 100644
--- a/pimd/pim_hello.c
+++ b/pimd/pim_hello.c
@@ -23,6 +23,7 @@
#include "if.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_tlv.h"
@@ -124,6 +125,14 @@ int pim_hello_recv(struct interface *ifp, pim_addr src_addr, uint8_t *tlv_buf,
pim_ifp = ifp->info;
assert(pim_ifp);
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip receiving PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
+
++pim_ifp->pim_ifstat_hello_recv;
/*
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index b98e64adfe..65889fd57b 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -50,6 +50,8 @@
#include "pim_igmp_join.h"
#include "pim_vxlan.h"
+#include "pim6_mld.h"
+
#if PIM_IPV == 4
static void pim_if_igmp_join_del_all(struct interface *ifp);
static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
@@ -127,6 +129,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim,
pim_ifp->mroute_vif_index = -1;
pim_ifp->igmp_version = IGMP_DEFAULT_VERSION;
+ pim_ifp->mld_version = MLD_DEFAULT_VERSION;
pim_ifp->gm_default_robustness_variable =
IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
pim_ifp->gm_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL;
@@ -150,6 +153,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim,
pim_ifp->gm_default_query_interval);
pim_ifp->pim_enable = pim;
+ pim_ifp->pim_passive_enable = false;
#if PIM_IPV == 4
pim_ifp->igmp_enable = igmp;
#endif
@@ -650,6 +654,7 @@ void pim_if_addr_add(struct connected *ifc)
vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp);
pim_if_add_vif(ifp, false, vxlan_term);
}
+ gm_ifp_update(ifp);
pim_ifchannel_scan_forward_start(ifp);
}
@@ -762,6 +767,8 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any)
"%s: removed link-local %pI6, lowest now %pI6, highest %pI6",
ifc->ifp->name, &ifc->address->u.prefix6,
&pim_ifp->ll_lowest, &pim_ifp->ll_highest);
+
+ gm_ifp_update(ifp);
}
#endif
@@ -821,6 +828,7 @@ void pim_if_addr_add_all(struct interface *ifp)
vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp);
pim_if_add_vif(ifp, false, vxlan_term);
}
+ gm_ifp_update(ifp);
pim_ifchannel_scan_forward_start(ifp);
pim_rp_setup(pim_ifp->pim);
@@ -999,12 +1007,15 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term)
}
ifaddr = pim_ifp->primary_address;
+#if PIM_IPV != 6
+ /* IPv6 API is always by interface index */
if (!ispimreg && !is_vxlan_term && pim_addr_is_any(ifaddr)) {
zlog_warn(
"%s: could not get address for interface %s ifindex=%d",
__func__, ifp->name, ifp->ifindex);
return -4;
}
+#endif
pim_ifp->mroute_vif_index = pim_iface_next_vif_index(ifp);
@@ -1029,9 +1040,10 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term)
pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 1;
+ gm_ifp_update(ifp);
+
/* if the device qualifies as pim_vxlan iif/oif update vxlan entries */
pim_vxlan_add_vif(ifp);
-
return 0;
}
@@ -1049,6 +1061,8 @@ int pim_if_del_vif(struct interface *ifp)
/* if the device was a pim_vxlan iif/oif update vxlan mroute entries */
pim_vxlan_del_vif(ifp);
+ gm_ifp_teardown(ifp);
+
pim_mroute_del_vif(ifp);
/*
@@ -1057,7 +1071,6 @@ int pim_if_del_vif(struct interface *ifp)
pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 0;
pim_ifp->mroute_vif_index = -1;
-
return 0;
}
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
index 3535db70a8..05be4e9c2c 100644
--- a/pimd/pim_iface.h
+++ b/pimd/pim_iface.h
@@ -30,7 +30,6 @@
#include "pim_igmp.h"
#include "pim_upstream.h"
-#include "pim_instance.h"
#include "bfd.h"
#include "pim_str.h"
@@ -69,9 +68,12 @@ struct pim_secondary_addr {
enum pim_secondary_addr_flags flags;
};
+struct gm_if;
+
struct pim_interface {
bool pim_enable : 1;
bool pim_can_disable_join_suppression : 1;
+ bool pim_passive_enable : 1;
bool igmp_enable : 1;
@@ -90,6 +92,7 @@ struct pim_interface {
* address of the interface */
int igmp_version; /* IGMP version */
+ int mld_version;
int gm_default_robustness_variable; /* IGMP or MLD QRV */
int gm_default_query_interval; /* IGMP or MLD secs between general
queries */
@@ -111,6 +114,8 @@ struct pim_interface {
struct list *gm_group_list; /* list of struct IGMP or MLD group */
struct hash *gm_group_hash;
+ struct gm_if *mld;
+
int pim_sock_fd; /* PIM socket file descriptor */
struct thread *t_pim_sock_read; /* thread for reading PIM socket */
int64_t pim_sock_creation; /* timestamp of PIM socket creation */
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index 40aaf1877c..e2d2ab97c9 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -29,6 +29,7 @@
#include "prefix.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_ifchannel.h"
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 0dce8faa90..749e259a7e 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -27,6 +27,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
index 9148d1050a..f6c23c8e89 100644
--- a/pimd/pim_igmp_mtrace.c
+++ b/pimd/pim_igmp_mtrace.c
@@ -22,6 +22,7 @@
#include <zebra.h>
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_util.h"
#include "pim_sock.h"
#include "pim_rp.h"
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
index a949d2b126..8d0925cb56 100644
--- a/pimd/pim_igmpv2.c
+++ b/pimd/pim_igmpv2.c
@@ -21,6 +21,7 @@
#include "zebra.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
index 5d46383a8e..6ed5c501b2 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -24,6 +24,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_iface.h"
#include "pim_igmp.h"
#include "pim_igmpv3.h"
@@ -1889,6 +1890,7 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
uint8_t *group_record;
uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
struct interface *ifp = igmp->interface;
+ struct pim_interface *pim_ifp = ifp->info;
int i;
if (igmp->mtrace_only)
@@ -1912,6 +1914,13 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
/* Collecting IGMP Rx stats */
igmp->igmp_stats.report_v3++;
+ if (pim_ifp->igmp_version == 2) {
+ zlog_warn(
+ "Received Version 3 packet but interface: %s is configured for version 2",
+ ifp->name);
+ return -1;
+ }
+
num_groups = ntohs(
*(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
if (num_groups < 1) {
diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c
index 14f984508d..8f117033e4 100644
--- a/pimd/pim_instance.c
+++ b/pimd/pim_instance.c
@@ -25,6 +25,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_ssm.h"
#include "pim_rpf.h"
#include "pim_rp.h"
@@ -114,6 +115,8 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf)
pim->send_v6_secondary = 1;
+ pim->gm_socket = -1;
+
pim_rp_init(pim);
pim_bsm_proc_init(pim);
diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h
index f8323deda0..b19e8208ba 100644
--- a/pimd/pim_instance.h
+++ b/pimd/pim_instance.h
@@ -167,6 +167,10 @@ struct pim_instance {
struct list *ssmpingd_list;
pim_addr ssmpingd_group_addr;
+ unsigned int gm_socket_if_count;
+ int gm_socket;
+ struct thread *t_gm_recv;
+
unsigned int igmp_group_count;
unsigned int igmp_watermark_limit;
unsigned int keep_alive_time;
@@ -194,6 +198,8 @@ struct pim_instance {
int64_t nexthop_lookups;
int64_t nexthop_lookups_avoided;
int64_t last_route_change_time;
+
+ uint64_t gm_rx_drop_sys;
};
void pim_vrf_init(void);
diff --git a/pimd/pim_join.c b/pimd/pim_join.c
index 88078dd366..1b722382b9 100644
--- a/pimd/pim_join.c
+++ b/pimd/pim_join.c
@@ -28,6 +28,7 @@
#include "plist.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_str.h"
#include "pim_tlv.h"
#include "pim_msg.h"
@@ -163,6 +164,14 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
pastend = tlv_buf + tlv_buf_size;
pim_ifp = ifp->info;
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip receiving PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
+
/*
Parse ucast addr
*/
@@ -496,7 +505,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
pim_ifp->primary_address,
qpim_all_pim_routers_addr, pim_msg,
packet_size,
- rpf->source_nexthop.interface->name)) {
+ rpf->source_nexthop.interface)) {
zlog_warn(
"%s: could not send PIM message on interface %s",
__func__,
@@ -534,8 +543,10 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
packet_size += group_size;
pim_msg_build_jp_groups(grp, group, group_size);
- pim_ifp->pim_ifstat_join_send += ntohs(grp->joins);
- pim_ifp->pim_ifstat_prune_send += ntohs(grp->prunes);
+ if (!pim_ifp->pim_passive_enable) {
+ pim_ifp->pim_ifstat_join_send += ntohs(grp->joins);
+ pim_ifp->pim_ifstat_prune_send += ntohs(grp->prunes);
+ }
if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
@@ -554,7 +565,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
pim_ifp->primary_address,
qpim_all_pim_routers_addr, pim_msg,
packet_size,
- rpf->source_nexthop.interface->name)) {
+ rpf->source_nexthop.interface)) {
zlog_warn(
"%s: could not send PIM message on interface %s",
__func__,
@@ -573,8 +584,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
pim_msg, packet_size, PIM_MSG_TYPE_JOIN_PRUNE, false);
if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
qpim_all_pim_routers_addr, pim_msg,
- packet_size,
- rpf->source_nexthop.interface->name)) {
+ packet_size, rpf->source_nexthop.interface)) {
zlog_warn(
"%s: could not send PIM message on interface %s",
__func__, rpf->source_nexthop.interface->name);
diff --git a/pimd/pim_jp_agg.c b/pimd/pim_jp_agg.c
index 7cbd0304ae..16774a03f5 100644
--- a/pimd/pim_jp_agg.c
+++ b/pimd/pim_jp_agg.c
@@ -25,6 +25,7 @@
#include "if.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_msg.h"
#include "pim_jp_agg.h"
#include "pim_join.h"
diff --git a/pimd/pim_jp_agg.h b/pimd/pim_jp_agg.h
index 506bf2d5ac..ee1ca6ac70 100644
--- a/pimd/pim_jp_agg.h
+++ b/pimd/pim_jp_agg.h
@@ -20,6 +20,8 @@
#ifndef __PIM_JP_AGG_H__
#define __PIM_JP_AGG_H__
+#include "pim_rpf.h"
+
struct pim_jp_sources {
struct pim_upstream *up;
int is_join;
diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c
index 0896c52a75..e4d463b70a 100644
--- a/pimd/pim_macro.c
+++ b/pimd/pim_macro.c
@@ -25,6 +25,7 @@
#include "plist.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_macro.h"
#include "pim_iface.h"
#include "pim_ifchannel.h"
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
index 5f951b4dfc..1978afa7c0 100644
--- a/pimd/pim_mroute.c
+++ b/pimd/pim_mroute.c
@@ -137,7 +137,8 @@ int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg)
return 0;
}
-int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf)
+int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf,
+ size_t len)
{
struct pim_interface *pim_ifp;
pim_sgaddr sg;
@@ -229,7 +230,7 @@ int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf)
}
pim_register_send((uint8_t *)buf + sizeof(ipv_hdr),
- ntohs(IPV_LEN(ip_hdr)) - sizeof(ipv_hdr),
+ len - sizeof(ipv_hdr),
pim_ifp->primary_address, rpg, 0, up);
}
return 0;
@@ -336,7 +337,8 @@ int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg)
return 0;
}
-int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf)
+int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
+ size_t len)
{
const ipv_hdr *ip_hdr = (const ipv_hdr *)buf;
struct pim_interface *pim_ifp;
@@ -463,7 +465,7 @@ int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf)
pim_upstream_keep_alive_timer_start(
up, pim_ifp->pim->keep_alive_time);
pim_upstream_inherited_olist(pim_ifp->pim, up);
- pim_mroute_msg_wholepkt(fd, ifp, buf);
+ pim_mroute_msg_wholepkt(fd, ifp, buf, len);
}
return 0;
}
@@ -490,7 +492,7 @@ int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf)
pim_upstream_mroute_add(up->channel_oil, __func__);
// Send the packet to the RP
- pim_mroute_msg_wholepkt(fd, ifp, buf);
+ pim_mroute_msg_wholepkt(fd, ifp, buf, len);
} else {
up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h
index 35ba60bf35..712c916994 100644
--- a/pimd/pim_mroute.h
+++ b/pimd/pim_mroute.h
@@ -136,6 +136,7 @@ typedef struct sioc_sg_req6 pim_sioc_sg_req;
*/
struct channel_oil;
+struct pim_instance;
int pim_mroute_socket_enable(struct pim_instance *pim);
int pim_mroute_socket_disable(struct pim_instance *pim);
@@ -158,8 +159,10 @@ bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size,
ifindex_t ifindex);
int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg);
-int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf);
+int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf,
+ size_t len);
int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg);
-int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf);
+int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
+ size_t len);
int pim_mroute_set(struct pim_instance *pim, int enable);
#endif /* PIM_MROUTE_H */
diff --git a/pimd/pim_mroute_msg.c b/pimd/pim_mroute_msg.c
index 7d80488c68..ef12ec7a49 100644
--- a/pimd/pim_mroute_msg.c
+++ b/pimd/pim_mroute_msg.c
@@ -29,6 +29,7 @@
#include "lib/network.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_mroute.h"
#include "pim_oil.h"
#include "pim_str.h"
@@ -226,10 +227,12 @@ int pim_mroute_msg(struct pim_instance *pim, const char *buf,
msg);
case IGMPMSG_WHOLEPKT:
return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
- (const char *)msg);
+ (const char *)msg,
+ buf_size);
case IGMPMSG_WRVIFWHOLE:
- return pim_mroute_msg_wrvifwhole(
- pim->mroute_socket, ifp, (const char *)msg);
+ return pim_mroute_msg_wrvifwhole(pim->mroute_socket,
+ ifp, (const char *)msg,
+ buf_size);
default:
break;
}
diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c
index 10e515cb56..0711a63f63 100644
--- a/pimd/pim_msdp.c
+++ b/pimd/pim_msdp.c
@@ -32,6 +32,7 @@
#include "pimd.h"
#include "pim_memory.h"
+#include "pim_instance.h"
#include "pim_iface.h"
#include "pim_rp.h"
#include "pim_str.h"
diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c
index 4adaca4e78..1152dd6f67 100644
--- a/pimd/pim_msdp_packet.c
+++ b/pimd/pim_msdp_packet.c
@@ -26,6 +26,7 @@
#include <lib/lib_errors.h>
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_str.h"
#include "pim_errors.h"
diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c
index facb771c25..819a747122 100644
--- a/pimd/pim_msdp_socket.c
+++ b/pimd/pim_msdp_socket.c
@@ -29,6 +29,7 @@
#include <lib/lib_errors.h>
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_sock.h"
#include "pim_errors.h"
diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c
index 1eda51417f..b67849fd8f 100644
--- a/pimd/pim_msg.c
+++ b/pimd/pim_msg.c
@@ -26,6 +26,7 @@
#include "plist.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_vty.h"
#include "pim_pim.h"
#include "pim_msg.h"
diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c
index 86dd8c490c..94696bb4c7 100644
--- a/pimd/pim_nb.c
+++ b/pimd/pim_nb.c
@@ -231,6 +231,12 @@ const struct frr_yang_module_info frr_pim_info = {
}
},
{
+ .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/pim-passive-enable",
+ .cbs = {
+ .modify = lib_interface_pim_address_family_pim_passive_enable_modify,
+ }
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/dr-priority",
.cbs = {
.modify = lib_interface_pim_address_family_dr_priority_modify,
diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h
index 273c7e8a61..be3cab66ce 100644
--- a/pimd/pim_nb.h
+++ b/pimd/pim_nb.h
@@ -106,6 +106,8 @@ int lib_interface_pim_address_family_create(struct nb_cb_create_args *args);
int lib_interface_pim_address_family_destroy(struct nb_cb_destroy_args *args);
int lib_interface_pim_address_family_pim_enable_modify(
struct nb_cb_modify_args *args);
+int lib_interface_pim_address_family_pim_passive_enable_modify(
+ struct nb_cb_modify_args *args);
int lib_interface_pim_address_family_hello_interval_modify(
struct nb_cb_modify_args *args);
int lib_interface_pim_address_family_hello_holdtime_modify(
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index d174b8a0af..c855ad7e01 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -35,6 +35,7 @@
#include "log.h"
#include "lib_errors.h"
#include "pim_util.h"
+#include "pim6_mld.h"
#if PIM_IPV == 6
#define pim6_msdp_err(funcname, argtype) \
@@ -1624,6 +1625,32 @@ int lib_interface_pim_address_family_pim_enable_modify(struct nb_cb_modify_args
}
/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-pim:pim/address-family/pim-passive-enable
+ */
+int lib_interface_pim_address_family_pim_passive_enable_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ pim_ifp->pim_passive_enable =
+ yang_dnode_get_bool(args->dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/hello-interval
*/
int lib_interface_pim_address_family_hello_interval_modify(
@@ -2676,12 +2703,22 @@ int lib_interface_gmp_address_family_igmp_version_destroy(
int lib_interface_gmp_address_family_mld_version_modify(
struct nb_cb_modify_args *args)
{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
+ break;
case NB_EV_APPLY:
- /* TBD depends on MLD data structure changes */
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return NB_ERR_INCONSISTENCY;
+
+ pim_ifp->mld_version = yang_dnode_get_uint8(args->dnode, NULL);
+ gm_ifp_update(ifp);
break;
}
@@ -2691,11 +2728,22 @@ int lib_interface_gmp_address_family_mld_version_modify(
int lib_interface_gmp_address_family_mld_version_destroy(
struct nb_cb_destroy_args *args)
{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
+ break;
case NB_EV_APPLY:
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return NB_ERR_INCONSISTENCY;
+
+ pim_ifp->mld_version = 2;
+ gm_ifp_update(ifp);
break;
}
@@ -2708,10 +2756,10 @@ int lib_interface_gmp_address_family_mld_version_destroy(
int lib_interface_gmp_address_family_query_interval_modify(
struct nb_cb_modify_args *args)
{
-#if PIM_IPV == 4
struct interface *ifp;
int query_interval;
+#if PIM_IPV == 4
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
@@ -2723,7 +2771,23 @@ int lib_interface_gmp_address_family_query_interval_modify(
change_query_interval(ifp->info, query_interval);
}
#else
- /* TBD Depends on MLD data structure changes */
+ struct pim_interface *pim_ifp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return NB_ERR_INCONSISTENCY;
+
+ query_interval = yang_dnode_get_uint16(args->dnode, NULL);
+ pim_ifp->gm_default_query_interval = query_interval;
+ gm_ifp_update(ifp);
+ }
#endif
return NB_OK;
}
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
index 84b532d345..0bbed1f99f 100644
--- a/pimd/pim_neighbor.c
+++ b/pimd/pim_neighbor.c
@@ -28,6 +28,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_neighbor.h"
#include "pim_time.h"
#include "pim_str.h"
@@ -362,19 +363,16 @@ static void delete_prefix_list(struct pim_neighbor *neigh)
#ifdef DUMP_PREFIX_LIST
struct listnode *p_node;
struct prefix *p;
- char addr_str[10];
int list_size = neigh->prefix_list
? (int)listcount(neigh->prefix_list)
: -1;
int i = 0;
for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) {
- pim_inet4_dump("<addr?>", p->u.prefix4, addr_str,
- sizeof(addr_str));
zlog_debug(
- "%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]",
+ "%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%pFXh [%d/%d]",
__func__, (unsigned)neigh,
- (unsigned)neigh->prefix_list, (unsigned)p,
- addr_str, i, list_size);
+ (unsigned)neigh->prefix_list, (unsigned)p, p, i,
+ list_size);
++i;
}
#endif
@@ -715,9 +713,8 @@ static void delete_from_neigh_addr(struct interface *ifp,
struct listnode *neigh_node;
struct pim_neighbor *neigh;
- if (addr->family != AF_INET)
+ if (addr->family != PIM_AF)
continue;
-
/*
Scan neighbors
*/
@@ -727,15 +724,9 @@ static void delete_from_neigh_addr(struct interface *ifp,
struct prefix *p = pim_neighbor_find_secondary(
neigh, addr);
if (p) {
- char addr_str[INET_ADDRSTRLEN];
-
- pim_inet4_dump(
- "<addr?>", addr->u.prefix4,
- addr_str, sizeof(addr_str));
-
zlog_info(
- "secondary addr %s recvd from neigh %pPA deleted from neigh %pPA on %s",
- addr_str, &neigh_addr,
+ "secondary addr %pFXh recvd from neigh %pPA deleted from neigh %pPA on %s",
+ addr, &neigh_addr,
&neigh->source_addr, ifp->name);
listnode_delete(neigh->prefix_list, p);
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c
index c1242e1509..22716c2a92 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -30,6 +30,7 @@
#include "pimd.h"
#include "pimd/pim_nht.h"
+#include "pim_instance.h"
#include "log.h"
#include "pim_time.h"
#include "pim_oil.h"
@@ -113,7 +114,7 @@ static struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim,
struct zclient *zclient = NULL;
zclient = pim_zebra_zclient_get();
- memset(&rpf, 0, sizeof(struct pim_rpf));
+ memset(&rpf, 0, sizeof(rpf));
rpf.rpf_addr = *addr;
pnc = pim_nexthop_cache_find(pim, &rpf);
diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c
index 1baa5c38ca..f0f4a7139b 100644
--- a/pimd/pim_pim.c
+++ b/pimd/pim_pim.c
@@ -26,6 +26,7 @@
#include "network.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_pim.h"
#include "pim_time.h"
#include "pim_iface.h"
@@ -593,13 +594,24 @@ static int pim_msg_send_frame(int fd, char *buf, size_t len,
}
int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
- int pim_msg_size, const char *ifname)
+ int pim_msg_size, struct interface *ifp)
{
socklen_t tolen;
unsigned char buffer[10000];
unsigned char *msg_start;
uint8_t ttl;
struct pim_msg_header *header;
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip sending PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
memset(buffer, 0, 10000);
@@ -672,7 +684,7 @@ int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
if (PIM_DEBUG_PIM_PACKETS)
zlog_debug("%s: to %pPA on %s: msg_size=%d checksum=%x",
- __func__, &dst, ifname, pim_msg_size,
+ __func__, &dst, ifp->name, pim_msg_size,
header->checksum);
if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
@@ -680,7 +692,7 @@ int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
}
pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to,
- tolen, ifname);
+ tolen, ifp->name);
return 0;
}
@@ -725,7 +737,7 @@ static int hello_send(struct interface *ifp, uint16_t holdtime)
if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
- ifp->name)) {
+ ifp)) {
if (PIM_DEBUG_PIM_HELLO) {
zlog_debug(
"%s: could not send PIM message on interface %s",
@@ -754,8 +766,10 @@ int pim_hello_send(struct interface *ifp, uint16_t holdtime)
return -1;
}
- ++pim_ifp->pim_ifstat_hello_sent;
- PIM_IF_FLAG_SET_HELLO_SENT(pim_ifp->flags);
+ if (!pim_ifp->pim_passive_enable) {
+ ++pim_ifp->pim_ifstat_hello_sent;
+ PIM_IF_FLAG_SET_HELLO_SENT(pim_ifp->flags);
+ }
return 0;
}
diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h
index 822d8a18fa..e2555eab8c 100644
--- a/pimd/pim_pim.h
+++ b/pimd/pim_pim.h
@@ -58,7 +58,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
pim_sgaddr sg);
int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
- int pim_msg_size, const char *ifname);
+ int pim_msg_size, struct interface *ifp);
int pim_hello_send(struct interface *ifp, uint16_t holdtime);
#endif /* PIM_PIM_H */
diff --git a/pimd/pim_register.c b/pimd/pim_register.c
index fef5339749..6b76f4c49b 100644
--- a/pimd/pim_register.c
+++ b/pimd/pim_register.c
@@ -100,14 +100,16 @@ void pim_register_stop_send(struct interface *ifp, pim_sgaddr *sg, pim_addr src,
return;
}
if (pim_msg_send(pinfo->pim_sock_fd, src, originator, buffer,
- b1length + PIM_MSG_REGISTER_STOP_LEN, ifp->name)) {
+ b1length + PIM_MSG_REGISTER_STOP_LEN, ifp)) {
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s: could not send PIM register stop message on interface %s",
__func__, ifp->name);
}
}
- ++pinfo->pim_ifstat_reg_stop_send;
+
+ if (!pinfo->pim_passive_enable)
+ ++pinfo->pim_ifstat_reg_stop_send;
}
static void pim_reg_stop_upstream(struct pim_instance *pim,
@@ -145,6 +147,14 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size)
bool handling_star = false;
int l;
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip receiving PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
+
++pim_ifp->pim_ifstat_reg_stop_recv;
memset(&sg, 0, sizeof(sg));
@@ -266,10 +276,11 @@ void pim_register_send(const uint8_t *buf, int buf_size, pim_addr src,
pim_msg_build_header(src, dst, buffer, buf_size + PIM_MSG_REGISTER_LEN,
PIM_MSG_TYPE_REGISTER, false);
- ++pinfo->pim_ifstat_reg_send;
+ if (!pinfo->pim_passive_enable)
+ ++pinfo->pim_ifstat_reg_send;
if (pim_msg_send(pinfo->pim_sock_fd, src, dst, buffer,
- buf_size + PIM_MSG_REGISTER_LEN, ifp->name)) {
+ buf_size + PIM_MSG_REGISTER_LEN, ifp)) {
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s: could not send PIM register message on interface %s",
@@ -305,7 +316,7 @@ void pim_null_register_send(struct pim_upstream *up)
return;
}
- memset(&ip_hdr, 0, sizeof(struct ip));
+ memset(&ip_hdr, 0, sizeof(ip_hdr));
ip_hdr.ip_p = PIM_IP_PROTO_PIM;
ip_hdr.ip_hl = 5;
ip_hdr.ip_v = 4;
@@ -446,6 +457,14 @@ int pim_register_recv(struct interface *ifp, pim_addr dest_addr,
struct pim_instance *pim = pim_ifp->pim;
pim_addr rp_addr;
+ if (pim_ifp->pim_passive_enable) {
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
+ "skip receiving PIM message on passive interface %s",
+ ifp->name);
+ return 0;
+ }
+
#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4
ip_hdr = (tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN);
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index 730870fb33..3da0a35303 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -34,6 +34,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_vty.h"
#include "pim_str.h"
#include "pim_iface.h"
@@ -273,10 +274,29 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
route_unlock_node(rn);
+ /*
+ * rp's with prefix lists have the group as 224.0.0.0/4 which will
+ * match anything. So if we have a rp_info that should match a prefix
+ * list then if we do match then best should be the answer( even
+ * if it is NULL )
+ */
+ if (!rp_info || (rp_info && rp_info->plist))
+ return best;
+
+ /*
+ * So we have a non plist rp_info found in the lookup and no plists
+ * at all to be choosen, return it!
+ */
if (!best)
return rp_info;
- if (rp_info->group.prefixlen < best->group.prefixlen)
+ /*
+ * If we have a matching non prefix list and a matching prefix
+ * list we should return the actual rp_info that has the LPM
+ * If they are equal, use the prefix-list( but let's hope
+ * the end-operator doesn't do this )
+ */
+ if (rp_info->group.prefixlen > bp->prefixlen)
best = rp_info;
return best;
@@ -422,7 +442,7 @@ int pim_rp_new(struct pim_instance *pim, pim_addr rp_addr, struct prefix group,
struct rp_info *tmp_rp_info;
char buffer[BUFSIZ];
struct prefix nht_p;
- struct route_node *rn;
+ struct route_node *rn = NULL;
struct pim_upstream *up;
bool upstream_updated = false;
@@ -600,13 +620,16 @@ int pim_rp_new(struct pim_instance *pim, pim_addr rp_addr, struct prefix group,
}
listnode_add_sort(pim->rp_list, rp_info);
- rn = route_node_get(pim->rp_table, &rp_info->group);
- rn->info = rp_info;
+
+ if (!rp_info->plist) {
+ rn = route_node_get(pim->rp_table, &rp_info->group);
+ rn->info = rp_info;
+ }
if (PIM_DEBUG_PIM_TRACE)
zlog_debug("Allocated: %p for rp_info: %p(%pFX) Lock: %d", rn,
rp_info, &rp_info->group,
- route_node_get_lock_count(rn));
+ rn ? route_node_get_lock_count(rn) : 0);
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
if (pim_addr_is_any(up->sg.src)) {
diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c
index 4e812ae3f0..c470bcd4d7 100644
--- a/pimd/pim_rpf.c
+++ b/pimd/pim_rpf.c
@@ -27,6 +27,7 @@
#include "jhash.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_rpf.h"
#include "pim_pim.h"
#include "pim_str.h"
diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h
index 40cd066bd1..a2289b4cc5 100644
--- a/pimd/pim_rpf.h
+++ b/pimd/pim_rpf.h
@@ -23,6 +23,8 @@
#include <zebra.h>
#include "pim_str.h"
+struct pim_instance;
+
/*
RFC 4601:
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
index 8619cc3f83..321775cce3 100644
--- a/pimd/pim_sock.c
+++ b/pimd/pim_sock.c
@@ -37,6 +37,7 @@
#include "network.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_mroute.h"
#include "pim_iface.h"
#include "pim_sock.h"
@@ -359,7 +360,7 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
*tolen = sizeof(*to);
}
- memset(&msgh, 0, sizeof(struct msghdr));
+ memset(&msgh, 0, sizeof(msgh));
iov.iov_base = buf;
iov.iov_len = len;
msgh.msg_control = cbuf;
diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h
index 2e9c043e84..ea9f7009bc 100644
--- a/pimd/pim_sock.h
+++ b/pimd/pim_sock.h
@@ -35,6 +35,8 @@
#define PIM_SOCK_ERR_NAME (-10) /* Socket name (getsockname) */
#define PIM_SOCK_ERR_BIND (-11) /* Can't bind to interface */
+struct pim_instance;
+
int pim_socket_bind(int fd, struct interface *ifp);
void pim_socket_ip_hdr(int fd);
int pim_socket_raw(int protocol);
diff --git a/pimd/pim_ssm.c b/pimd/pim_ssm.c
index 62d6eb8308..1ff60f9b64 100644
--- a/pimd/pim_ssm.c
+++ b/pimd/pim_ssm.c
@@ -27,6 +27,7 @@
#include <lib/lib_errors.h>
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_ssm.h"
#include "pim_igmp.h"
diff --git a/pimd/pim_ssm.h b/pimd/pim_ssm.h
index c6b6978218..adc0cfdea0 100644
--- a/pimd/pim_ssm.h
+++ b/pimd/pim_ssm.h
@@ -21,6 +21,8 @@
#define PIM_SSM_STANDARD_RANGE "232.0.0.0/8"
+struct pim_instance;
+
/* SSM error codes */
enum pim_ssm_err {
PIM_SSM_ERR_NONE = 0,
diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c
index d86be85bd8..c8d40371e7 100644
--- a/pimd/pim_ssmpingd.c
+++ b/pimd/pim_ssmpingd.c
@@ -27,6 +27,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_ssmpingd.h"
#include "pim_time.h"
#include "pim_sock.h"
diff --git a/pimd/pim_static.c b/pimd/pim_static.c
index d3b31771a0..581b855f92 100644
--- a/pimd/pim_static.c
+++ b/pimd/pim_static.c
@@ -26,6 +26,7 @@
#include "linklist.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_oil.h"
#include "pim_static.h"
#include "pim_time.h"
diff --git a/pimd/pim_static.h b/pimd/pim_static.h
index 56bfbd4e4f..dcbc790d7a 100644
--- a/pimd/pim_static.h
+++ b/pimd/pim_static.h
@@ -22,6 +22,7 @@
#include <zebra.h>
#include "pim_mroute.h"
+#include "pim_oil.h"
#include "if.h"
struct static_route {
diff --git a/pimd/pim_tib.c b/pimd/pim_tib.c
index 80e75f1b09..a69177a6e5 100644
--- a/pimd/pim_tib.c
+++ b/pimd/pim_tib.c
@@ -22,6 +22,7 @@
#include "pim_tib.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_iface.h"
#include "pim_upstream.h"
#include "pim_oil.h"
diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c
index 028514659b..90bf799093 100644
--- a/pimd/pim_tlv.c
+++ b/pimd/pim_tlv.c
@@ -24,6 +24,7 @@
#include "if.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_int.h"
#include "pim_tlv.h"
#include "pim_str.h"
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index d044aec510..8507749522 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -40,6 +40,7 @@
#include "pim_bfd.h"
#include "pim_bsm.h"
#include "pim_vxlan.h"
+#include "pim6_mld.h"
int pim_debug_config_write(struct vty *vty)
{
@@ -291,8 +292,8 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
}
#if PIM_IPV == 4
-static int pim_igmp_config_write(struct vty *vty, int writes,
- struct pim_interface *pim_ifp)
+static int gm_config_write(struct vty *vty, int writes,
+ struct pim_interface *pim_ifp)
{
/* IF ip igmp */
if (pim_ifp->igmp_enable) {
@@ -360,6 +361,17 @@ static int pim_igmp_config_write(struct vty *vty, int writes,
return writes;
}
+#else
+static int gm_config_write(struct vty *vty, int writes,
+ struct pim_interface *pim_ifp)
+{
+ if (pim_ifp->mld_version != MLD_DEFAULT_VERSION)
+ vty_out(vty, " ipv6 mld version %d\n", pim_ifp->mld_version);
+ if (pim_ifp->gm_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL)
+ vty_out(vty, " ipv6 mld query-interval %d\n",
+ pim_ifp->gm_default_query_interval);
+ return 0;
+}
#endif
int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
@@ -388,9 +400,7 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
++writes;
}
-#if PIM_IPV == 4
- writes += pim_igmp_config_write(vty, writes, pim_ifp);
-#endif
+ writes += gm_config_write(vty, writes, pim_ifp);
/* update source */
if (!pim_addr_is_any(pim_ifp->update_source)) {
@@ -409,6 +419,11 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
++writes;
}
+ if (pim_ifp->pim_passive_enable) {
+ vty_out(vty, " " PIM_AF_NAME " pim passive\n");
+ ++writes;
+ }
+
writes += pim_static_write_mroute(pim, vty, ifp);
pim_bsm_write_config(vty, ifp);
++writes;
diff --git a/pimd/pim_vty.h b/pimd/pim_vty.h
index c192ba3bbd..0f11d6ec4a 100644
--- a/pimd/pim_vty.h
+++ b/pimd/pim_vty.h
@@ -22,6 +22,8 @@
#include "vty.h"
+struct pim_instance;
+
int pim_debug_config_write(struct vty *vty);
int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty);
int pim_interface_config_write(struct vty *vty);
diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h
index 96882918ac..1a7e9dcf1b 100644
--- a/pimd/pim_vxlan.h
+++ b/pimd/pim_vxlan.h
@@ -22,6 +22,8 @@
#ifndef PIM_VXLAN_H
#define PIM_VXLAN_H
+#include "pim_instance.h"
+
/* global timer used for miscellaneous staggered processing */
#define PIM_VXLAN_WORK_TIME 1
/* number of SG entries processed at one shot */
diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c
index a9553089c9..a3978bd0d2 100644
--- a/pimd/pim_zlookup.c
+++ b/pimd/pim_zlookup.c
@@ -30,6 +30,7 @@
#include "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_iface.h"
#include "pim_neighbor.h"
#include "pim_pim.h"
diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h
index 4ea865a7e7..85d18d1d68 100644
--- a/pimd/pim_zlookup.h
+++ b/pimd/pim_zlookup.h
@@ -26,6 +26,8 @@
#define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */
+struct channel_oil;
+
struct pim_zlookup_nexthop {
vrf_id_t vrf_id;
pim_addr nexthop_addr;
diff --git a/pimd/pim_zpthread.c b/pimd/pim_zpthread.c
index a342931085..e6fb3b3a17 100644
--- a/pimd/pim_zpthread.c
+++ b/pimd/pim_zpthread.c
@@ -22,6 +22,7 @@
#include <lib/lib_errors.h>
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_mlag.h"
#include "pim_zebra.h"
diff --git a/pimd/pimd.h b/pimd/pimd.h
index aeb4859952..9ffa075d2a 100644
--- a/pimd/pimd.h
+++ b/pimd/pimd.h
@@ -28,7 +28,6 @@
#include "plist.h"
#include "pim_addr.h"
-#include "pim_instance.h"
#include "pim_str.h"
#include "pim_memory.h"
#include "pim_assert.h"
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 9dd6961d92..4dd63f6b88 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -9,6 +9,7 @@ noinst_PROGRAMS += pimd/test_igmpv3_join
vtysh_scan += \
pimd/pim_cmd.c \
pimd/pim6_cmd.c \
+ pimd/pim6_mld.c \
#end
vtysh_daemons += pimd
vtysh_daemons += pim6d
@@ -89,6 +90,7 @@ nodist_pimd_pimd_SOURCES = \
pimd_pim6d_SOURCES = \
$(pim_common) \
pimd/pim6_main.c \
+ pimd/pim6_mld.c \
pimd/pim6_stubs.c \
pimd/pim6_cmd.c \
pimd/pim6_mroute_msg.c \
@@ -155,6 +157,8 @@ noinst_HEADERS += \
pimd/pim_vxlan.h \
pimd/pim_vxlan_instance.h \
pimd/pimd.h \
+ pimd/pim6_mld.h \
+ pimd/pim6_mld_protocol.h \
pimd/mtracebis_netlink.h \
pimd/mtracebis_routeget.h \
pimd/pim6_cmd.h \
@@ -163,6 +167,7 @@ noinst_HEADERS += \
clippy_scan += \
pimd/pim_cmd.c \
pimd/pim6_cmd.c \
+ pimd/pim6_mld.c \
# end
pimd_pimd_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4
diff --git a/python/makefile.py b/python/makefile.py
index 44658013b3..afc993b5b9 100644
--- a/python/makefile.py
+++ b/python/makefile.py
@@ -32,7 +32,12 @@ for clippy_file in clippy_scan:
assert clippy_file.endswith(".c")
xref_targets = []
-for varname in ["bin_PROGRAMS", "sbin_PROGRAMS", "lib_LTLIBRARIES", "module_LTLIBRARIES"]:
+for varname in [
+ "bin_PROGRAMS",
+ "sbin_PROGRAMS",
+ "lib_LTLIBRARIES",
+ "module_LTLIBRARIES",
+]:
xref_targets.extend(mv[varname].strip().split())
# check for files using clippy but not listed in clippy_scan
@@ -63,6 +68,9 @@ if args.dev_build:
)
sys.exit(1)
+# this additional-dependency rule is stuck onto all compile targets that
+# compile a file which uses clippy-generated input, so it has a dependency to
+# make that first.
clippydep = Template(
"""
${clippybase}.$$(OBJEXT): ${clippybase}_clippy.c
@@ -70,6 +78,8 @@ ${clippybase}.lo: ${clippybase}_clippy.c
${clippybase}_clippy.c: $$(CLIPPY_DEPS)"""
)
+# this one is used when one .c file is built multiple times with different
+# CFLAGS
clippyauxdep = Template(
"""# clippy{
# auxiliary clippy target
@@ -88,6 +98,7 @@ while lines:
if line.startswith(autoderp):
line = line[len(autoderp) :]
+ # allow rerunning on already-clippified Makefile
if line == "# clippy{":
while lines:
line = lines.pop(0)
@@ -113,29 +124,53 @@ while lines:
target, dep = m.group(1), m.group(2)
+ filename = os.path.basename(target)
+ if "-" in filename:
+ # dashes in output filename = building same .c with different CFLAGS
+ am_name, _ = filename.split("-", 1)
+ am_name = os.path.join(os.path.dirname(target), am_name)
+ am_name = am_name.replace("/", "_")
+ extraflags = " $(%s_CFLAGS)" % (am_name,)
+ else:
+ # this path isn't really triggered because automake is using a generic
+ # .c => .o rule unless CFLAGS are customized for a target
+ extraflags = ""
+
if target.endswith(".lo") or target.endswith(".o"):
if not dep.endswith(".h"):
+ # LLVM bitcode targets for analysis tools
bcdeps.append("%s.bc: %s" % (target, target))
- bcdeps.append("\t$(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ %s" % (dep))
+ bcdeps.append(
+ "\t$(AM_V_LLVM_BC)$(COMPILE)%s -emit-llvm -c -o $@ %s"
+ % (extraflags, dep)
+ )
if m.group(2) in clippy_scan:
+ # again - this is only hit for targets with custom CFLAGS, because
+ # automake uses a generic .c -> .o rule for standard CFLAGS
out_lines.append(
clippyauxdep.substitute(target=m.group(1), clippybase=m.group(2)[:-2])
)
out_lines.append(line)
+# now, cover all the .c files that don't have special build rules
out_lines.append("# clippy{\n# main clippy targets")
for clippy_file in clippy_scan:
out_lines.append(clippydep.substitute(clippybase=clippy_file[:-2]))
+# combine daemon .xref files into frr.xref
out_lines.append("")
-out_lines.append("xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets])))
+out_lines.append(
+ "xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets]))
+)
out_lines.append("frr.xref: $(xrefs)")
out_lines.append("")
-#frr.xref: $(bin_PROGRAMS) $(sbin_PROGRAMS) $(lib_LTLIBRARIES) $(module_LTLIBRARIES)
-# $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ $^
+# analog but slower way to get the same frr.xref
+# frr.xref: $(bin_PROGRAMS) $(sbin_PROGRAMS) $(lib_LTLIBRARIES) $(module_LTLIBRARIES)
+# $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ $^
+# LLVM bitcode link targets creating a .bc file for whole daemon or lib
out_lines.append("")
out_lines.extend(bcdeps)
out_lines.append("")
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 73c214ea2e..91829122ee 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -267,7 +267,7 @@ developing OSPF-API and frr applications.
%package rpki-rtrlib
Summary: BGP RPKI support (rtrlib)
Group: System Environment/Daemons
-BuildRequires: librtr-devel >= 0.5
+BuildRequires: librtr-devel >= 0.8
Requires: %{name} = %{version}-%{release}
%description rpki-rtrlib
@@ -466,6 +466,9 @@ install -d -m750 %{buildroot}%{rundir}
# avoid `ERROR: ambiguous python shebang in` errors
pathfix.py -pni "%{__python3} %{py3_shbang_opts}" %{buildroot}/usr/lib/frr/*.py
%py_byte_compile %{__python3} %{buildroot}/usr/lib/frr/*.py
+%else
+# remove ospfclient.py (if present) as it requires > python36
+rm -f %{buildroot}%{_sbindir}/ospfclient.py
%endif
%pre
@@ -719,11 +722,13 @@ fi
%files contrib
%doc tools
-
%files pythontools
%{_sbindir}/generate_support_bundle.py
%{_sbindir}/frr-reload.py
%{_sbindir}/frr_babeltrace.py
+%if %{with_ospfclient} && (0%{?rhel} > 7 || 0%{?fedora} > 29)
+%{_sbindir}/ospfclient.py
+%endif
%if 0%{?rhel} > 7 || 0%{?fedora} > 29
%{_sbindir}/__pycache__/*
%else
@@ -774,9 +779,95 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons
%changelog
-* Tue Nov 4 2021 Martin Winter <mwinter@opensourcerouting.org> - %{version}
-
-* Tue Feb 1 2022 Donatas Abraitis <donatas.abraitis@gmail.com> - 8.2
+* Sun May 29 2022 Christian Hopps <chopps@labn.net> - %{version}
+- ospfclient:
+- Add OSPF API python client ospfclient.py
+
+* Tue Mar 1 2022 Martin Winter <mwinter@opensourcerouting.org> - %{version}
+
+* Tue Mar 1 2022 Jafar Al-Gharaibeh <jafar@atcorp.com> - 8.2
+- The FRRouting community would like to announce FRR Release 8.2.
+- This release consists of just over 800 commits from 62 authors.
+- Selected features and bug fixes are listed below.
+- babeld:
+- Fix the checks for truncated packets
+- bfdd:
+- Correct one spelling error of comment
+- Fix detection timeout update
+- Fix possibly wrong counter of control packets
+- bgpd:
+- Add "json" option to a few more show commands
+- Add 'show bgp <afi> <safi> json detail' header data
+- Add a 6 hour warning to missing policy
+- Add an ability to match ipv6 next-hop by prefix-list
+- Add autocomplete for access-list under bmp node
+- Add autocomplete for as-path filters
+- Add autocomplete for set/match community/large/ext lists
+- Add long-lived graceful restart capability
+- Add peer-groups to neighbor autocomplete
+- Adjust symbolic names for cease notifications according to rfc4486
+- Deprecate dpa, advertiser and rcid_path path attributes
+- Extended bgp administrative shutdown communication
+- Fix crash when using "show bgp vrf all"
+- Fix inconsistency of match ip/ipv6 next-hop commands
+- Fix missing name of default vrf
+- Handle TCP connection errors with connection callbacks for RPKI
+- Implement llgr helper mode
+- Implement rfc9072
+- Support redirect import more than one route-target ipv6
+- docker:
+- Update alpine build enable set own version
+- isisd:
+- Add link state traffic engineering support
+- Fix router capability tlv parsing issues
+- Fix running-config for fast-reroute
+- Make isis work with default vrf name different than 'default'
+- ospf6d
+- Add missing vrf parameter to "clear ipv6 ospf6 interface"
+- Add prompt for commands with non-exist vrf
+- Add support for nssa type-7 address ranges
+- Add the ability of specifying router-id/area-id in no debug ospf6
+- Do not originate type-4 lsa when nssa
+- Do not send type-5 into stub area
+- Fix ecmp inter-area route nexthop update
+- Fix memory leak for `show ipv6 ospf6 zebra json`
+- ospfd
+- Fix wrong comparison of routemap name
+- Fix crash on "ospf send-extra-data zebra"
+- Fix incorrect detection of topology changes in helper mode
+- Fix loss of mixed form in "range" command
+- Fix no-form of "graceful-restart" command
+- Fix summary-address deletion
+- Fix wrong parsing of te subtlv
+- pbrd
+- Add vlan actions to vty
+- Pbr route maps get addr family of nhgs
+- Protect from a possible null dereference
+- pimd
+- Do not allow 224.0.0.0/24 range in igmp join
+- Fix igmp user config
+- Fix msdp mesh grp with wildcard member addr
+- Fix stale forwarding entries left around after join goes away
+- Fix FRR drops IGMP packets for TOS value other than 0XC0
+- redhat
+- Check if frr.conf already exists
+- Logrotate file has typo for staticd
+- ripd
+- Fix packet send for non primary addresses
+- vtysh
+- Add missing rpki node when showing config
+- Improve startup time by ca. ×6
+- remove `address-family evpn`
+- watchfrr
+- Allow an integrated config to work within a namespace
+- zebra
+- Add optional nhg id output to `show ip ro`
+- Add resolver flag for nexthop in json
+- Add support for json output in srv6 locator detail command
+- Don't lose next hop weights while exporting via fpm
+- Fix buffer overflow
+- Fix netns deletion
+- Fix route-map application when when using vrfs
* Tue Nov 2 2021 Jafar Al-Gharaibeh <jafar@atcorp.com> - 8.1
- FRR 8.1 brings a long list of enhancements and fixes with 1200 commits from
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index 042c9713b2..7ac37b7ee2 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -929,7 +929,7 @@ int rip_neighbor_lookup(struct rip *rip, struct sockaddr_in *from)
struct prefix_ipv4 p;
struct route_node *node;
- memset(&p, 0, sizeof(struct prefix_ipv4));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET;
p.prefix = from->sin_addr;
p.prefixlen = IPV4_MAX_BITLEN;
diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c
index c640ca27af..5b1917e17a 100644
--- a/ripd/rip_nb_config.c
+++ b/ripd/rip_nb_config.c
@@ -132,7 +132,7 @@ int ripd_instance_default_information_originate_modify(
rip = nb_running_get_entry(args->dnode, NULL, true);
default_information = yang_dnode_get_bool(args->dnode, NULL);
- memset(&p, 0, sizeof(struct prefix_ipv4));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET;
if (default_information) {
struct nexthop nh;
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
index 436dc4de0e..0ce3804069 100644
--- a/ripd/rip_snmp.c
+++ b/ripd/rip_snmp.c
@@ -356,7 +356,7 @@ static uint8_t *rip2IfStatEntry(struct variable *v, oid name[], size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
/* Lookup interface. */
ifp = rip2IfLookup(v, name, length, &addr, exact);
@@ -457,7 +457,7 @@ static uint8_t *rip2IfConfAddress(struct variable *v, oid name[],
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
/* Lookup interface. */
ifp = rip2IfLookup(v, name, length, &addr, exact);
@@ -529,7 +529,7 @@ static uint8_t *rip2PeerTable(struct variable *v, oid name[], size_t *length,
== MATCH_FAILED)
return NULL;
- memset(&addr, 0, sizeof(struct in_addr));
+ memset(&addr, 0, sizeof(addr));
/* Lookup interface. */
peer = rip2PeerLookup(v, name, length, &addr, exact);
diff --git a/ripd/ripd.c b/ripd/ripd.c
index 2a05f30bc8..cc21c0bd69 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -1287,10 +1287,9 @@ static void rip_response_process(struct rip_packet *packet, int size,
uint32_t destination;
if (subnetted == -1) {
- memcpy(&ifaddr, ifc->address,
- sizeof(struct prefix_ipv4));
+ memcpy(&ifaddr, ifc->address, sizeof(ifaddr));
memcpy(&ifaddrclass, &ifaddr,
- sizeof(struct prefix_ipv4));
+ sizeof(ifaddrclass));
apply_classful_mask_ipv4(&ifaddrclass);
subnetted = 0;
if (ifaddr.prefixlen > ifaddrclass.prefixlen)
@@ -1476,7 +1475,7 @@ static int rip_send_packet(uint8_t *buf, int size, struct sockaddr_in *to,
}
/* Make destination address. */
- memset(&sin, 0, sizeof(struct sockaddr_in));
+ memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
sin.sin_len = sizeof(struct sockaddr_in);
@@ -1544,7 +1543,7 @@ void rip_redistribute_add(struct rip *rip, int type, int sub_type,
rp = route_node_get(rip->table, (struct prefix *)p);
- memset(&newinfo, 0, sizeof(struct rip_info));
+ memset(&newinfo, 0, sizeof(newinfo));
newinfo.type = type;
newinfo.sub_type = sub_type;
newinfo.metric = 1;
@@ -1738,7 +1737,7 @@ static void rip_read(struct thread *t)
rip_event(rip, RIP_READ, sock);
/* RIPd manages only IPv4. */
- memset(&from, 0, sizeof(struct sockaddr_in));
+ memset(&from, 0, sizeof(from));
fromlen = sizeof(struct sockaddr_in);
len = recvfrom(sock, (char *)&rip_buf.buf, sizeof(rip_buf.buf), 0,
@@ -2103,7 +2102,7 @@ void rip_output_process(struct connected *ifc, struct sockaddr_in *to,
}
if (version == RIPv1) {
- memcpy(&ifaddrclass, ifc->address, sizeof(struct prefix_ipv4));
+ memcpy(&ifaddrclass, ifc->address, sizeof(ifaddrclass));
apply_classful_mask_ipv4(&ifaddrclass);
subnetted = 0;
if (ifc->address->prefixlen > ifaddrclass.prefixlen)
@@ -2385,7 +2384,7 @@ static void rip_update_interface(struct connected *ifc, uint8_t version,
if (if_is_broadcast(ifp) || if_is_pointopoint(ifp)) {
if (ifc->address->family == AF_INET) {
/* Destination address and port setting. */
- memset(&to, 0, sizeof(struct sockaddr_in));
+ memset(&to, 0, sizeof(to));
if (ifc->destination)
/* use specified broadcast or peer destination
* addr */
@@ -2834,7 +2833,7 @@ uint8_t rip_distance_apply(struct rip *rip, struct rip_info *rinfo)
struct rip_distance *rdistance;
struct access_list *alist;
- memset(&p, 0, sizeof(struct prefix_ipv4));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET;
p.prefix = rinfo->from;
p.prefixlen = IPV4_MAX_BITLEN;
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index e103cdc3a8..e7d2928697 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -196,7 +196,7 @@ int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to,
zlog_debug(" send packet size %d", bufsize);
}
- memset(&addr, 0, sizeof(struct sockaddr_in6));
+ memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
#ifdef SIN6_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
@@ -217,7 +217,7 @@ int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to,
msg.msg_namelen = sizeof(struct sockaddr_in6);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
- msg.msg_control = (void *)adata;
+ msg.msg_control = adata;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
iov.iov_base = buf;
@@ -259,7 +259,7 @@ static int ripng_recv_packet(int sock, uint8_t *buf, int bufsize,
struct cmsghdr *cmsgptr;
struct in6_addr dst = {.s6_addr = {0}};
- memset(&dst, 0, sizeof(struct in6_addr));
+ memset(&dst, 0, sizeof(dst));
/* Ancillary data. This store cmsghdr and in6_pktinfo. But at this
point I can't determine size of cmsghdr */
@@ -940,7 +940,7 @@ void ripng_redistribute_add(struct ripng *ripng, int type, int sub_type,
rp = agg_node_get(ripng->table, (struct prefix *)p);
- memset(&newinfo, 0, sizeof(struct ripng_info));
+ memset(&newinfo, 0, sizeof(newinfo));
newinfo.type = type;
newinfo.sub_type = sub_type;
newinfo.ifindex = ifindex;
@@ -1145,7 +1145,7 @@ static void ripng_response_process(struct ripng_packet *packet, int size,
ripng_peer_update(ripng, from, packet->version);
/* Reset nexthop. */
- memset(&nexthop, 0, sizeof(struct ripng_nexthop));
+ memset(&nexthop, 0, sizeof(nexthop));
nexthop.flag = RIPNG_NEXTHOP_UNSPEC;
/* Set RTE pointer. */
@@ -1272,7 +1272,7 @@ static void ripng_request_process(struct ripng_packet *packet, int size,
field. Once all the entries have been filled in, change the
command from Request to Response and send the datagram back
to the requestor. */
- memset(&p, 0, sizeof(struct prefix_ipv6));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET6;
for (; ((caddr_t)rte) < lim; rte++) {
diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in
index 01033d0413..bf3902d9fa 100644
--- a/snapcraft/snapcraft.yaml.in
+++ b/snapcraft/snapcraft.yaml.in
@@ -2,8 +2,8 @@ name: frr
version: @VERSION@
summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing daemon
description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing daemon
- FRRouting (FRR) is free software which manages TCP/IP based routing
- protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2,
+ FRRouting (FRR) is free software which manages TCP/IP based routing
+ protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2,
RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing), PATHD (Segment
routing), BFD and OpenFabric as well as the IPv6 versions of these.
FRRouting (frr) is a fork of Quagga.
@@ -275,7 +275,7 @@ parts:
- usr/lib/x86_64-linux-gnu/libssh.so*
source: https://github.com/rtrlib/rtrlib.git
source-type: git
- source-tag: v0.7.0
+ source-tag: v0.8.0
plugin: cmake
configflags:
- -DCMAKE_BUILD_TYPE=Release
@@ -296,7 +296,7 @@ parts:
- -DENABLE_LYD_PRIV=ON
- -DENABLE_CACHE=ON
- -DCMAKE_BUILD_TYPE:String="Release"
- frr:
+ frr:
after: [rtrlib,libyang]
build-packages:
- gcc
@@ -366,7 +366,7 @@ parts:
- --enable-rpki
- --enable-vrrpd
- --enable-configfile-mask=0640
- - --enable-logfile-mask=0640
+ - --enable-logfile-mask=0640
- --localstatedir=/var/run
- --sbindir=/sbin
- --bindir=/bin
diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c
index 3c3129b9ce..2d8f024d16 100644
--- a/tests/lib/test_srcdest_table.c
+++ b/tests/lib/test_srcdest_table.c
@@ -328,7 +328,7 @@ static void get_rand_prefix(struct prng *prng, struct prefix_ipv6 *p)
p->prefixlen = prng_rand(prng) % 129;
p->family = AF_INET6;
- apply_mask((struct prefix *)p);
+ apply_mask(p);
}
static void get_rand_prefix_pair(struct prng *prng, struct prefix_ipv6 *dst_p,
diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c
index 3562b5f2f8..73f2e29834 100644
--- a/tests/ospfd/test_ospf_spf.c
+++ b/tests/ospfd/test_ospf_spf.c
@@ -52,6 +52,7 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf,
enum protection_type protection_type, bool verbose)
{
struct route_table *new_table, *new_rtrs;
+ struct route_table *all_rtrs = NULL;
struct ospf_area *area;
struct p_space *p_space;
struct q_space *q_space;
@@ -63,10 +64,11 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf,
new_table = route_table_init();
new_rtrs = route_table_init();
+ all_rtrs = route_table_init();
/* dryrun true, root_node false */
- ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
- true, false);
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
+ new_rtrs, true, false);
if (verbose) {
vty_out(vty, "SPF Tree without TI-LFA backup paths:\n\n");
diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.py b/tests/topotests/bfd_topo1/test_bfd_topo1.py
index adf02b02d4..c9020f16d3 100644
--- a/tests/topotests/bfd_topo1/test_bfd_topo1.py
+++ b/tests/topotests/bfd_topo1/test_bfd_topo1.py
@@ -100,7 +100,7 @@ def test_bfd_connection():
test_func = partial(
topotest.router_json_cmp, router, "show bfd peers json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assertmsg = '"{}" JSON output mismatches'.format(router.name)
assert result is None, assertmsg
diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.py b/tests/topotests/bfd_topo2/test_bfd_topo2.py
index 57ce0cdf09..a9b9358ef0 100644
--- a/tests/topotests/bfd_topo2/test_bfd_topo2.py
+++ b/tests/topotests/bfd_topo2/test_bfd_topo2.py
@@ -144,7 +144,7 @@ def test_bfd_connection():
test_func = partial(
topotest.router_json_cmp, router, "show bfd peers json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assertmsg = '"{}" JSON output mismatches'.format(router.name)
assert result is None, assertmsg
diff --git a/tests/topotests/bgp_auth/bgp_auth_common.py b/tests/topotests/bgp_auth/bgp_auth_common.py
new file mode 100644
index 0000000000..14e4d05c08
--- /dev/null
+++ b/tests/topotests/bgp_auth/bgp_auth_common.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def vrf_str(vrf):
+ if vrf == "":
+ vrf_str = ""
+ else:
+ vrf_str = "vrf {}".format(vrf)
+
+ return vrf_str
+
+
+def peer_name(rtr, prefix, vrf):
+ "generate VRF string for CLI"
+ if vrf == "":
+ vrf_str = ""
+ else:
+ vrf_str = "_" + vrf
+
+ if prefix == "yes":
+ if rtr == "R2":
+ return "TWO_GROUP" + vrf_str
+ else:
+ return "THREE_GROUP" + vrf_str
+ else:
+ if rtr == "R2":
+ return "2.2.2.2"
+ else:
+ return "3.3.3.3"
+
+
+def print_diag(vrf):
+ "print failure disagnostics"
+
+ tgen = get_topogen()
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ print(rname + ":")
+ print(router.vtysh_cmd("show run"))
+ print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf))))
+ print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf))))
+
+
+@common_config.retry(retry_timeout=190)
+def _check_neigh_state(router, peer, state, vrf=""):
+ "check BGP neighbor state on a router"
+
+ neigh_output = router.vtysh_cmd(
+ "show bgp {} neighbors {} json".format(vrf_str(vrf), peer)
+ )
+
+ peer_state = "Unknown"
+ neigh_output_json = json.loads(neigh_output)
+ if peer in neigh_output_json:
+ peer_state = neigh_output_json[peer]["bgpState"]
+ if peer_state == state:
+ return True
+ return "{} peer with {} expected state {} got {} ".format(
+ router.name, peer, state, peer_state
+ )
+
+
+def check_neigh_state(router, peer, state, vrf=""):
+ "check BGP neighbor state on a router"
+
+ assertmsg = _check_neigh_state(router, peer, state, vrf)
+ assert assertmsg is True, assertmsg
+
+
+def check_all_peers_established(vrf=""):
+ "standard check for extablished peers per vrf"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+ # do r1 last as he might be the dynamic one
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+
+def check_vrf_peer_remove_passwords(vrf="", prefix="no"):
+ "selectively remove passwords checking state"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format(
+ vrf_str(vrf), peer_name("R2", prefix, vrf)
+ )
+ )
+
+ check_neigh_state(r2, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf))
+ )
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format(
+ vrf_str(vrf), peer_name("R3", prefix, vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "3.3.3.3", "Connect", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf))
+ )
+ check_all_peers_established(vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nno neighbor 3.3.3.3 password".format(vrf_str(vrf))
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Connect", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nno neighbor 2.2.2.2 password".format(vrf_str(vrf))
+ )
+ check_all_peers_established(vrf)
+
+
+def check_vrf_peer_change_passwords(vrf="", prefix="no"):
+ "selectively change passwords checking state"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nneighbor {} password change1".format(
+ vrf_str(vrf), peer_name("R2", prefix, vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nneighbor 1.1.1.1 password change1".format(
+ vrf_str(vrf)
+ )
+ )
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nneighbor {} password change2".format(
+ vrf_str(vrf), peer_name("R3", prefix, vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "3.3.3.3", "Connect", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nneighbor 1.1.1.1 password change2".format(
+ vrf_str(vrf)
+ )
+ )
+ check_all_peers_established(vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nneighbor 3.3.3.3 password change3".format(
+ vrf_str(vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Connect", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nneighbor 2.2.2.2 password change3".format(
+ vrf_str(vrf)
+ )
+ )
+ check_all_peers_established(vrf)
diff --git a/tests/topotests/bgp_auth/test_bgp_auth.py b/tests/topotests/bgp_auth/test_bgp_auth.py
deleted file mode 100644
index 9e8136c17b..0000000000
--- a/tests/topotests/bgp_auth/test_bgp_auth.py
+++ /dev/null
@@ -1,587 +0,0 @@
-#!/usr/bin/env python
-
-#
-# test_bgp_auth.py
-# Part of NetDEF Topology Tests
-#
-# Copyright (c) 2020 by Volta Networks
-#
-# Permission to use, copy, modify, and/or distribute this software
-# for any purpose with or without fee is hereby granted, provided
-# that the above copyright notice and this permission notice appear
-# in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
-# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
-# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
-# OF THIS SOFTWARE.
-#
-
-"""
-test_bgp_auth.py: Test BGP Md5 Authentication
-
- +------+
- +--------| |--------+
- | +------| R1 |------+ |
- | | -----| |----+ | |
- | | | +------+ | | |
- | | | | | |
- +------+ +------+
- | |------------| |
- | R2 |------------| R3 |
- | |------------| |
- +------+ +------+
-
-
-setup is 3 routers with 3 links between each each link in a different vrf
-Default, blue and red respectively
-Tests check various fiddling with passwords and checking that the peer
-establishment is as expected and passwords are not leaked across sockets
-for bgp instances
-"""
-# pylint: disable=C0413
-
-import json
-import os
-import platform
-import sys
-from time import sleep
-
-import pytest
-from lib import common_config, topotest
-from lib.common_config import (
- save_initial_config_on_routers,
- reset_with_new_configs,
-)
-from lib.topogen import Topogen, TopoRouter, get_topogen
-
-pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
-
-CWD = os.path.dirname(os.path.realpath(__file__))
-
-
-def build_topo(tgen):
- tgen.add_router("R1")
- tgen.add_router("R2")
- tgen.add_router("R3")
-
- tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
- tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
- tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
- tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
- tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
- tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
- tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
- tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
- tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
-
-
-def setup_module(mod):
- "Sets up the pytest environment"
- # This function initiates the topology build with Topogen...
- tgen = Topogen(build_topo, mod.__name__)
- # ... and here it calls Mininet initialization functions.
- tgen.start_topology()
-
- r1 = tgen.gears["R1"]
- r2 = tgen.gears["R2"]
- r3 = tgen.gears["R3"]
-
- # blue vrf
- r1.cmd_raises("ip link add blue type vrf table 1001")
- r1.cmd_raises("ip link set up dev blue")
- r2.cmd_raises("ip link add blue type vrf table 1001")
- r2.cmd_raises("ip link set up dev blue")
- r3.cmd_raises("ip link add blue type vrf table 1001")
- r3.cmd_raises("ip link set up dev blue")
-
- r1.cmd_raises("ip link add lo1 type dummy")
- r1.cmd_raises("ip link set lo1 master blue")
- r1.cmd_raises("ip link set up dev lo1")
- r2.cmd_raises("ip link add lo1 type dummy")
- r2.cmd_raises("ip link set up dev lo1")
- r2.cmd_raises("ip link set lo1 master blue")
- r3.cmd_raises("ip link add lo1 type dummy")
- r3.cmd_raises("ip link set up dev lo1")
- r3.cmd_raises("ip link set lo1 master blue")
-
- r1.cmd_raises("ip link set R1-eth2 master blue")
- r1.cmd_raises("ip link set R1-eth3 master blue")
- r2.cmd_raises("ip link set R2-eth2 master blue")
- r2.cmd_raises("ip link set R2-eth3 master blue")
- r3.cmd_raises("ip link set R3-eth2 master blue")
- r3.cmd_raises("ip link set R3-eth3 master blue")
-
- r1.cmd_raises("ip link set up dev R1-eth2")
- r1.cmd_raises("ip link set up dev R1-eth3")
- r2.cmd_raises("ip link set up dev R2-eth2")
- r2.cmd_raises("ip link set up dev R2-eth3")
- r3.cmd_raises("ip link set up dev R3-eth2")
- r3.cmd_raises("ip link set up dev R3-eth3")
-
- # red vrf
- r1.cmd_raises("ip link add red type vrf table 1002")
- r1.cmd_raises("ip link set up dev red")
- r2.cmd_raises("ip link add red type vrf table 1002")
- r2.cmd_raises("ip link set up dev red")
- r3.cmd_raises("ip link add red type vrf table 1002")
- r3.cmd_raises("ip link set up dev red")
-
- r1.cmd_raises("ip link add lo2 type dummy")
- r1.cmd_raises("ip link set lo2 master red")
- r1.cmd_raises("ip link set up dev lo2")
- r2.cmd_raises("ip link add lo2 type dummy")
- r2.cmd_raises("ip link set up dev lo2")
- r2.cmd_raises("ip link set lo2 master red")
- r3.cmd_raises("ip link add lo2 type dummy")
- r3.cmd_raises("ip link set up dev lo2")
- r3.cmd_raises("ip link set lo2 master red")
-
- r1.cmd_raises("ip link set R1-eth4 master red")
- r1.cmd_raises("ip link set R1-eth5 master red")
- r2.cmd_raises("ip link set R2-eth4 master red")
- r2.cmd_raises("ip link set R2-eth5 master red")
- r3.cmd_raises("ip link set R3-eth4 master red")
- r3.cmd_raises("ip link set R3-eth5 master red")
-
- r1.cmd_raises("ip link set up dev R1-eth4")
- r1.cmd_raises("ip link set up dev R1-eth5")
- r2.cmd_raises("ip link set up dev R2-eth4")
- r2.cmd_raises("ip link set up dev R2-eth5")
- r3.cmd_raises("ip link set up dev R3-eth4")
- r3.cmd_raises("ip link set up dev R3-eth5")
-
- # This is a sample of configuration loading.
- router_list = tgen.routers()
-
- # For all registered routers, load the zebra configuration file
- for rname, router in router_list.items():
- router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
- router.load_config(TopoRouter.RD_OSPF)
- router.load_config(TopoRouter.RD_BGP)
-
- # After copying the configurations, this function loads configured daemons.
- tgen.start_router()
-
- # Save the initial router config. reset_config_on_routers will return to this config.
- save_initial_config_on_routers(tgen)
-
-
-def teardown_module(mod):
- "Teardown the pytest environment"
- tgen = get_topogen()
-
- # This function tears down the whole topology.
- tgen.stop_topology()
-
-
-def vrf_str(vrf):
- if vrf == "":
- vrf_str = ""
- else:
- vrf_str = "vrf {}".format(vrf)
-
- return vrf_str
-
-
-def peer_name(rtr, prefix, vrf):
- "generate VRF string for CLI"
- if vrf == "":
- vrf_str = ""
- else:
- vrf_str = "_" + vrf
-
- if prefix == "yes":
- if rtr == "R2":
- return "TWO_GROUP" + vrf_str
- else:
- return "THREE_GROUP" + vrf_str
- else:
- if rtr == "R2":
- return "2.2.2.2"
- else:
- return "3.3.3.3"
-
-
-def print_diag(vrf):
- "print failure disagnostics"
-
- tgen = get_topogen()
- router_list = tgen.routers()
- for rname, router in router_list.items():
- print(rname + ":")
- print(router.vtysh_cmd("show run"))
- print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf))))
- print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf))))
-
-
-@common_config.retry(retry_timeout=190)
-def _check_neigh_state(router, peer, state, vrf=""):
- "check BGP neighbor state on a router"
-
- neigh_output = router.vtysh_cmd(
- "show bgp {} neighbors {} json".format(vrf_str(vrf), peer)
- )
-
- peer_state = "Unknown"
- neigh_output_json = json.loads(neigh_output)
- if peer in neigh_output_json:
- peer_state = neigh_output_json[peer]["bgpState"]
- if peer_state == state:
- return True
- return "{} peer with {} expected state {} got {} ".format(
- router.name, peer, state, peer_state
- )
-
-
-def check_neigh_state(router, peer, state, vrf=""):
- "check BGP neighbor state on a router"
-
- assertmsg = _check_neigh_state(router, peer, state, vrf)
- assert assertmsg is True, assertmsg
-
-
-def check_all_peers_established(vrf=""):
- "standard check for extablished peers per vrf"
-
- tgen = get_topogen()
- r1 = tgen.gears["R1"]
- r2 = tgen.gears["R2"]
- r3 = tgen.gears["R3"]
- # do r1 last as he might be the dynamic one
- check_neigh_state(r2, "1.1.1.1", "Established", vrf)
- check_neigh_state(r2, "3.3.3.3", "Established", vrf)
- check_neigh_state(r3, "1.1.1.1", "Established", vrf)
- check_neigh_state(r3, "2.2.2.2", "Established", vrf)
- check_neigh_state(r1, "2.2.2.2", "Established", vrf)
- check_neigh_state(r1, "3.3.3.3", "Established", vrf)
-
-
-def check_vrf_peer_remove_passwords(vrf="", prefix="no"):
- "selectively remove passwords checking state"
-
- tgen = get_topogen()
- r1 = tgen.gears["R1"]
- r2 = tgen.gears["R2"]
- r3 = tgen.gears["R3"]
-
- check_all_peers_established(vrf)
-
- r1.vtysh_cmd(
- "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format(
- vrf_str(vrf), peer_name("R2", prefix, vrf)
- )
- )
-
- check_neigh_state(r2, "1.1.1.1", "Connect", vrf)
- check_neigh_state(r2, "3.3.3.3", "Established", vrf)
- check_neigh_state(r3, "1.1.1.1", "Established", vrf)
- check_neigh_state(r3, "2.2.2.2", "Established", vrf)
- # don't check dynamic downed peers - they are removed
- if prefix == "no":
- check_neigh_state(r1, "2.2.2.2", "Connect", vrf)
- check_neigh_state(r1, "3.3.3.3", "Established", vrf)
-
- r2.vtysh_cmd(
- "conf t\nrouter bgp 65002 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf))
- )
- check_all_peers_established(vrf)
-
- r1.vtysh_cmd(
- "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format(
- vrf_str(vrf), peer_name("R3", prefix, vrf)
- )
- )
- check_neigh_state(r2, "1.1.1.1", "Established", vrf)
- check_neigh_state(r2, "3.3.3.3", "Established", vrf)
- check_neigh_state(r3, "1.1.1.1", "Connect", vrf)
- check_neigh_state(r3, "2.2.2.2", "Established", vrf)
- check_neigh_state(r1, "2.2.2.2", "Established", vrf)
- # don't check dynamic downed peers - they are removed
- if prefix == "no":
- check_neigh_state(r1, "3.3.3.3", "Connect", vrf)
-
- r3.vtysh_cmd(
- "conf t\nrouter bgp 65003 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf))
- )
- check_all_peers_established(vrf)
-
- r2.vtysh_cmd(
- "conf t\nrouter bgp 65002 {}\nno neighbor 3.3.3.3 password".format(vrf_str(vrf))
- )
- check_neigh_state(r2, "1.1.1.1", "Established", vrf)
- check_neigh_state(r2, "3.3.3.3", "Connect", vrf)
- check_neigh_state(r3, "1.1.1.1", "Established", vrf)
- check_neigh_state(r3, "2.2.2.2", "Connect", vrf)
- check_neigh_state(r1, "2.2.2.2", "Established", vrf)
- check_neigh_state(r1, "3.3.3.3", "Established", vrf)
-
- r3.vtysh_cmd(
- "conf t\nrouter bgp 65003 {}\nno neighbor 2.2.2.2 password".format(vrf_str(vrf))
- )
- check_all_peers_established(vrf)
-
-
-def check_vrf_peer_change_passwords(vrf="", prefix="no"):
- "selectively change passwords checking state"
-
- tgen = get_topogen()
- r1 = tgen.gears["R1"]
- r2 = tgen.gears["R2"]
- r3 = tgen.gears["R3"]
- check_all_peers_established(vrf)
-
- r1.vtysh_cmd(
- "conf t\nrouter bgp 65001 {}\nneighbor {} password change1".format(
- vrf_str(vrf), peer_name("R2", prefix, vrf)
- )
- )
- check_neigh_state(r2, "1.1.1.1", "Connect", vrf)
- check_neigh_state(r2, "3.3.3.3", "Established", vrf)
- check_neigh_state(r3, "1.1.1.1", "Established", vrf)
- check_neigh_state(r3, "2.2.2.2", "Established", vrf)
- # don't check dynamic downed peers - they are removed
- if prefix == "no":
- check_neigh_state(r1, "2.2.2.2", "Connect", vrf)
- check_neigh_state(r1, "3.3.3.3", "Established", vrf)
-
- r2.vtysh_cmd(
- "conf t\nrouter bgp 65002 {}\nneighbor 1.1.1.1 password change1".format(
- vrf_str(vrf)
- )
- )
- check_all_peers_established(vrf)
-
- r1.vtysh_cmd(
- "conf t\nrouter bgp 65001 {}\nneighbor {} password change2".format(
- vrf_str(vrf), peer_name("R3", prefix, vrf)
- )
- )
- check_neigh_state(r2, "1.1.1.1", "Established", vrf)
- check_neigh_state(r2, "3.3.3.3", "Established", vrf)
- check_neigh_state(r3, "1.1.1.1", "Connect", vrf)
- check_neigh_state(r3, "2.2.2.2", "Established", vrf)
- check_neigh_state(r1, "2.2.2.2", "Established", vrf)
- # don't check dynamic downed peers - they are removed
- if prefix == "no":
- check_neigh_state(r1, "3.3.3.3", "Connect", vrf)
-
- r3.vtysh_cmd(
- "conf t\nrouter bgp 65003 {}\nneighbor 1.1.1.1 password change2".format(
- vrf_str(vrf)
- )
- )
- check_all_peers_established(vrf)
-
- r2.vtysh_cmd(
- "conf t\nrouter bgp 65002 {}\nneighbor 3.3.3.3 password change3".format(
- vrf_str(vrf)
- )
- )
- check_neigh_state(r2, "1.1.1.1", "Established", vrf)
- check_neigh_state(r2, "3.3.3.3", "Connect", vrf)
- check_neigh_state(r3, "1.1.1.1", "Established", vrf)
- check_neigh_state(r3, "2.2.2.2", "Connect", vrf)
- check_neigh_state(r1, "2.2.2.2", "Established", vrf)
- check_neigh_state(r1, "3.3.3.3", "Established", vrf)
-
- r3.vtysh_cmd(
- "conf t\nrouter bgp 65003 {}\nneighbor 2.2.2.2 password change3".format(
- vrf_str(vrf)
- )
- )
- check_all_peers_established(vrf)
-
-
-def test_default_peer_established(tgen):
- "default vrf 3 peers same password"
-
- reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
- check_all_peers_established()
-
-
-def test_default_peer_remove_passwords(tgen):
- "selectively remove passwords checking state"
-
- reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
- check_vrf_peer_remove_passwords()
-
-
-def test_default_peer_change_passwords(tgen):
- "selectively change passwords checking state"
-
- reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
- check_vrf_peer_change_passwords()
-
-
-def test_default_prefix_peer_established(tgen):
- "default vrf 3 peers same password with prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
- check_all_peers_established()
-
-
-def test_prefix_peer_remove_passwords(tgen):
- "selectively remove passwords checking state with prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
- check_vrf_peer_remove_passwords(prefix="yes")
-
-
-def test_prefix_peer_change_passwords(tgen):
- "selecively change passwords checkig state with prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
- check_vrf_peer_change_passwords(prefix="yes")
-
-
-def test_vrf_peer_established(tgen):
- "default vrf 3 peers same password with VRF config"
-
- # clean routers and load vrf config
- reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
- check_all_peers_established("blue")
-
-
-def test_vrf_peer_remove_passwords(tgen):
- "selectively remove passwords checking state with VRF config"
-
- reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
- check_vrf_peer_remove_passwords(vrf="blue")
-
-
-def test_vrf_peer_change_passwords(tgen):
- "selectively change passwords checking state with VRF config"
-
- reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
- check_vrf_peer_change_passwords(vrf="blue")
-
-
-def test_vrf_prefix_peer_established(tgen):
- "default vrf 3 peers same password with VRF prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
- check_all_peers_established("blue")
-
-
-def test_vrf_prefix_peer_remove_passwords(tgen):
- "selectively remove passwords checking state with VRF prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
- check_vrf_peer_remove_passwords(vrf="blue", prefix="yes")
-
-
-def test_vrf_prefix_peer_change_passwords(tgen):
- "selectively change passwords checking state with VRF prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
- check_vrf_peer_change_passwords(vrf="blue", prefix="yes")
-
-
-def test_multiple_vrf_peer_established(tgen):
- "default vrf 3 peers same password with multiple VRFs"
-
- reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
- check_all_peers_established("blue")
- check_all_peers_established("red")
-
-
-def test_multiple_vrf_peer_remove_passwords(tgen):
- "selectively remove passwords checking state with multiple VRFs"
-
- reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
- check_vrf_peer_remove_passwords("blue")
- check_all_peers_established("red")
- check_vrf_peer_remove_passwords("red")
- check_all_peers_established("blue")
-
-
-def test_multiple_vrf_peer_change_passwords(tgen):
- "selectively change passwords checking state with multiple VRFs"
-
- reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
- check_vrf_peer_change_passwords("blue")
- check_all_peers_established("red")
- check_vrf_peer_change_passwords("red")
- check_all_peers_established("blue")
-
-
-def test_multiple_vrf_prefix_peer_established(tgen):
- "default vrf 3 peers same password with multilpe VRFs and prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
- check_all_peers_established("blue")
- check_all_peers_established("red")
-
-
-def test_multiple_vrf_prefix_peer_remove_passwords(tgen):
- "selectively remove passwords checking state with multiple vrfs and prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
- check_vrf_peer_remove_passwords(vrf="blue", prefix="yes")
- check_all_peers_established("red")
- check_vrf_peer_remove_passwords(vrf="red", prefix="yes")
- check_all_peers_established("blue")
-
-
-def test_multiple_vrf_prefix_peer_change_passwords(tgen):
- "selectively change passwords checking state with multiple vrfs and prefix config"
-
- # only supported in kernel > 5.3
- if topotest.version_cmp(platform.release(), "5.3") < 0:
- return
-
- reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
- check_vrf_peer_change_passwords(vrf="blue", prefix="yes")
- check_all_peers_established("red")
- check_vrf_peer_change_passwords(vrf="red", prefix="yes")
- check_all_peers_established("blue")
-
-
-def test_memory_leak(tgen):
- "Run the memory leak test and report results."
- if not tgen.is_memleak_enabled():
- pytest.skip("Memory leak test/report is disabled")
-
- tgen.report_memory_leaks()
-
-
-if __name__ == "__main__":
- args = ["-s"] + sys.argv[1:]
- sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_auth/test_bgp_auth1.py b/tests/topotests/bgp_auth/test_bgp_auth1.py
new file mode 100644
index 0000000000..5be07cf8a9
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth1.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+
+from bgp_auth_common import (
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+ check_vrf_peer_change_passwords,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_default_peer_established(tgen):
+ "default vrf 3 peers same password"
+
+ reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
+ check_all_peers_established()
+
+
+def test_default_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state"
+
+ reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
+ check_vrf_peer_remove_passwords()
+
+
+def test_default_peer_change_passwords(tgen):
+ "selectively change passwords checking state"
+
+ reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
+ check_vrf_peer_change_passwords()
+
+
+def test_default_prefix_peer_established(tgen):
+ "default vrf 3 peers same password with prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
+ check_all_peers_established()
+
+
+def test_prefix_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
+ check_vrf_peer_remove_passwords(prefix="yes")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_auth/test_bgp_auth2.py b/tests/topotests/bgp_auth/test_bgp_auth2.py
new file mode 100644
index 0000000000..2b37e80869
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth2.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from bgp_auth_common import (
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+ check_vrf_peer_change_passwords,
+ check_all_peers_established,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_vrf_prefix_peer_established(tgen):
+ "default vrf 3 peers same password with VRF prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
+ check_all_peers_established("blue")
+
+
+def test_vrf_prefix_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with VRF prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
+ check_vrf_peer_remove_passwords(vrf="blue", prefix="yes")
+
+
+def test_vrf_prefix_peer_change_passwords(tgen):
+ "selectively change passwords checking state with VRF prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
+ check_vrf_peer_change_passwords(vrf="blue", prefix="yes")
+
+
+def test_multiple_vrf_peer_established(tgen):
+ "default vrf 3 peers same password with multiple VRFs"
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
+ check_all_peers_established("blue")
+ check_all_peers_established("red")
+
+
+def test_multiple_vrf_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with multiple VRFs"
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_remove_passwords("blue")
+ check_all_peers_established("red")
+ check_vrf_peer_remove_passwords("red")
+ check_all_peers_established("blue")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_auth/test_bgp_auth3.py b/tests/topotests/bgp_auth/test_bgp_auth3.py
new file mode 100644
index 0000000000..220f350d80
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth3.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from bgp_auth_common import (
+ check_vrf_peer_change_passwords,
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_prefix_peer_change_passwords(tgen):
+ "selecively change passwords checkig state with prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
+ check_vrf_peer_change_passwords(prefix="yes")
+
+
+def test_vrf_peer_established(tgen):
+ "default vrf 3 peers same password with VRF config"
+
+ # clean routers and load vrf config
+ reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
+ check_all_peers_established("blue")
+
+
+def test_vrf_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with VRF config"
+
+ reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
+ check_vrf_peer_remove_passwords(vrf="blue")
+
+
+def test_vrf_peer_change_passwords(tgen):
+ "selectively change passwords checking state with VRF config"
+
+ reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
+ check_vrf_peer_change_passwords(vrf="blue")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_auth/test_bgp_auth4.py b/tests/topotests/bgp_auth/test_bgp_auth4.py
new file mode 100644
index 0000000000..2b7a355483
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth4.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from bgp_auth_common import (
+ check_vrf_peer_change_passwords,
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_multiple_vrf_peer_change_passwords(tgen):
+ "selectively change passwords checking state with multiple VRFs"
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_change_passwords("blue")
+ check_all_peers_established("red")
+ check_vrf_peer_change_passwords("red")
+ check_all_peers_established("blue")
+
+
+def test_multiple_vrf_prefix_peer_established(tgen):
+ "default vrf 3 peers same password with multilpe VRFs and prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
+ check_all_peers_established("blue")
+ check_all_peers_established("red")
+
+
+def test_multiple_vrf_prefix_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with multiple vrfs and prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_remove_passwords(vrf="blue", prefix="yes")
+ check_all_peers_established("red")
+ check_vrf_peer_remove_passwords(vrf="red", prefix="yes")
+ check_all_peers_established("blue")
+
+
+def test_multiple_vrf_prefix_peer_change_passwords(tgen):
+ "selectively change passwords checking state with multiple vrfs and prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_change_passwords(vrf="blue", prefix="yes")
+ check_all_peers_established("red")
+ check_vrf_peer_change_passwords(vrf="red", prefix="yes")
+ check_all_peers_established("blue")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gr_notification/__init__.py b/tests/topotests/bgp_gr_notification/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/__init__.py
diff --git a/tests/topotests/bgp_gr_notification/r1/bgpd.conf b/tests/topotests/bgp_gr_notification/r1/bgpd.conf
new file mode 100644
index 0000000000..6119f67436
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r1/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ neighbor 192.168.255.2 remote-as external
+ neighbor 192.168.255.2 timers 1 3
+ neighbor 192.168.255.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gr_notification/r1/zebra.conf b/tests/topotests/bgp_gr_notification/r1/zebra.conf
new file mode 100644
index 0000000000..091794f475
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gr_notification/r2/bgpd.conf b/tests/topotests/bgp_gr_notification/r2/bgpd.conf
new file mode 100644
index 0000000000..05e17f0564
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r2/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp hard-administrative-reset
+ bgp graceful-restart
+ neighbor 192.168.255.1 remote-as external
+ neighbor 192.168.255.1 timers 1 3
+ neighbor 192.168.255.1 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gr_notification/r2/zebra.conf b/tests/topotests/bgp_gr_notification/r2/zebra.conf
new file mode 100644
index 0000000000..1fcccbe161
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.2/32
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
new file mode 100644
index 0000000000..3519e5cc7b
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+
+#
+# bgp_gr_notification.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+TC1: Disable the link between R1-R2 and wait for HoldTimerExpire notification:
+ 1) Check if R2 sent HoldTimerExpired notification
+ 2) Check if the routes are retained at R2
+TC2: Trigger `clear bgp` (Administrative Reset):
+ `bgp hard-administrative-reset` disabled:
+ a) Check if Administrative Reset notification was sent from R2
+ b) Routes should be retained on R1
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_hold_timer_expired_gr():
+ # TC1
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _disable_link_r1_r2():
+ r1.cmd_raises("ip link set down dev r1-eth0")
+
+ def _enable_link_r1_r2():
+ r1.cmd_raises("ip link set up dev r1-eth0")
+
+ def _bgp_check_hold_timer_expire_reason():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "lastNotificationReason": "Hold Timer Expired",
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_hold_timer_expire_stale():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "stale": True,
+ "valid": True,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Disable the link between R1-R2")
+ _disable_link_r1_r2()
+
+ step("Check if R2 sent HoldTimerExpire notification to R1")
+ test_func = functools.partial(_bgp_check_hold_timer_expire_reason)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see Hold Timer Expired notification from R2 on R1"
+
+ step("Check if the routes are retained at R2")
+ test_func = functools.partial(_bgp_check_hold_timer_expire_stale)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see retained stale routes on R2"
+
+ step("Enable the link between R1-R2")
+ _enable_link_r1_r2()
+
+
+def test_bgp_administrative_reset_gr():
+ # TC2
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_hard_reset():
+ output = json.loads(r1.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "lastNotificationReason": "Cease/Administrative Reset",
+ "lastNotificationHardReset": False,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_gr_notification_stale():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 172.16.255.2/32 json"))
+ expected = {
+ "paths": [
+ {
+ "stale": True,
+ "valid": True,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_clear_r1_and_shutdown():
+ r2.vtysh_cmd(
+ """
+ clear ip bgp 192.168.255.1
+ configure terminal
+ router bgp
+ neighbor 192.168.255.1 shutdown
+ """
+ )
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Reset and shutdown R1")
+ _bgp_clear_r1_and_shutdown()
+
+ step("Check if Hard Reset notification wasn't sent from R2")
+ test_func = functools.partial(_bgp_check_hard_reset)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to send Administrative Reset notification from R2"
+
+ step("Check if stale routes are retained on R1")
+ test_func = functools.partial(_bgp_check_gr_notification_stale)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see retained stale routes on R1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
index 16d6b1993d..fc2d2364c6 100644
--- a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
@@ -436,7 +436,6 @@ def test_restart_frr_p2(request):
step("Activate same IPv6 nbr from IPv4 unicast family")
step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
- reset_config_on_routers(tgen)
bgp_convergence = verify_bgp_convergence(tgen, topo)
assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, bgp_convergence
diff --git a/tests/topotests/cspf_topo1/reference/sharp-ted.json b/tests/topotests/cspf_topo1/reference/sharp-ted.json
index db50260ac7..d3d1f9e406 100644
--- a/tests/topotests/cspf_topo1/reference/sharp-ted.json
+++ b/tests/topotests/cspf_topo1/reference/sharp-ted.json
@@ -269,7 +269,6 @@
},
"segment-routing":[
{
- "adj-sid":5001,
"flags":"0xb0",
"weight":0
}
@@ -666,7 +665,6 @@
},
"segment-routing":[
{
- "adj-sid":5000,
"flags":"0x30",
"weight":0
}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref
index 4b204dbc4c..3ccd57a6fd 100644
--- a/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref
@@ -14,20 +14,14 @@
"ip":"10.0.7.4",
"afi":"ipv4",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16010
- ]
+ "active":true
},
{
"fib":true,
"ip":"10.0.8.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16010
- ]
+ "active":true
}
]
}
@@ -47,10 +41,7 @@
"ip":"10.0.7.4",
"afi":"ipv4",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16020
- ]
+ "active":true
}
]
}
@@ -70,10 +61,7 @@
"ip":"10.0.8.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16030
- ]
+ "active":true
}
]
}
@@ -93,10 +81,7 @@
"ip":"10.0.7.4",
"afi":"ipv4",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16040
- ]
+ "active":true
}
]
}
@@ -116,10 +101,7 @@
"ip":"10.0.8.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16050
- ]
+ "active":true
}
]
}
@@ -296,20 +278,14 @@
"ip":"10.0.7.4",
"afi":"ipv4",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16100
- ]
+ "active":true
},
{
"fib":true,
"ip":"10.0.8.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16100
- ]
+ "active":true
}
]
}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref
index 834cdfe6ca..22d3f2a246 100644
--- a/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref
@@ -13,19 +13,13 @@
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16011
- ]
+ "active":true
},
{
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16011
- ]
+ "active":true
}
]
}
@@ -44,10 +38,7 @@
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16021
- ]
+ "active":true
}
]
}
@@ -66,10 +57,7 @@
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16031
- ]
+ "active":true
}
]
}
@@ -88,10 +76,7 @@
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16041
- ]
+ "active":true
}
]
}
@@ -110,10 +95,7 @@
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16051
- ]
+ "active":true
}
]
}
@@ -132,19 +114,13 @@
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt4",
- "active":true,
- "labels":[
- 16101
- ]
+ "active":true
},
{
"fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16101
- ]
+ "active":true
}
]
}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref
index be87ed90a0..2c63c08510 100644
--- a/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref
@@ -1,170 +1,2 @@
{
- "18010":{
- "inLabel":18010,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "installed":true,
- "nexthop":"10.0.8.5"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "installed":true,
- "nexthop":"10.0.7.4"
- }
- ]
- },
- "18011":{
- "inLabel":18011,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "installed":true,
- "interface":"eth-rt5"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "installed":true,
- "interface":"eth-rt4"
- }
- ]
- },
- "18020":{
- "inLabel":18020,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16020,
- "installed":true,
- "nexthop":"10.0.7.4"
- }
- ]
- },
- "18021":{
- "inLabel":18021,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16021,
- "installed":true,
- "interface":"eth-rt4"
- }
- ]
- },
- "18030":{
- "inLabel":18030,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16030,
- "installed":true,
- "nexthop":"10.0.8.5"
- }
- ]
- },
- "18031":{
- "inLabel":18031,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16031,
- "installed":true,
- "interface":"eth-rt5"
- }
- ]
- },
- "18040":{
- "inLabel":18040,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16040,
- "installed":true,
- "nexthop":"10.0.7.4"
- }
- ]
- },
- "18041":{
- "inLabel":18041,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16041,
- "installed":true,
- "interface":"eth-rt4"
- }
- ]
- },
- "18050":{
- "inLabel":18050,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16050,
- "installed":true,
- "nexthop":"10.0.8.5"
- }
- ]
- },
- "18051":{
- "inLabel":18051,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16051,
- "installed":true,
- "interface":"eth-rt5"
- }
- ]
- },
- "18100":{
- "inLabel":18100,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16100,
- "installed":true,
- "nexthop":"10.0.8.5"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16100,
- "installed":true,
- "nexthop":"10.0.7.4"
- }
- ]
- },
- "18101":{
- "inLabel":18101,
- "installed":true,
- "nexthops":[
- {
- "type":"SR (IS-IS)",
- "outLabel":16101,
- "installed":true,
- "interface":"eth-rt5"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16101,
- "installed":true,
- "interface":"eth-rt4"
- }
- ]
- }
}
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff
index 8b115c2058..a9418473c7 100644
--- a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff
@@ -1,19 +1,24 @@
--- a/rt4/step3/show_ip_route.ref
+++ b/rt4/step4/show_ip_route.ref
-@@ -15,9 +15,6 @@
+@@ -14,37 +14,14 @@
+ "ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 16010
- ]
-@@ -28,20 +25,6 @@
+- "labels":[
+- 16010
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
@@ -28,30 +33,39 @@
- "afi":"ipv4",
- "interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16010
- ]
-@@ -65,9 +48,6 @@
+- "labels":[
+- 16010
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -64,38 +41,14 @@
+ "ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
-@@ -78,25 +58,10 @@
+- "labels":[
+- 3
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
- }
+- "labels":[
+- 3
+- ]
+- }
- ],
- "backupNexthops":[
- {
@@ -63,21 +77,56 @@
- 16030,
- 16020
- ]
-- }
++ "active":true
+ }
+ ]
+ }
+@@ -115,30 +68,21 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ }
]
}
- ],
-@@ -159,24 +124,10 @@
+@@ -158,24 +102,7 @@
+ "ip":"10.0.6.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
- }
+- "labels":[
+- 3
+- ]
+- }
- ],
- "backupNexthops":[
- {
@@ -88,21 +137,22 @@
- "labels":[
- 16050
- ]
-- }
++ "active":true
+ }
]
}
- ],
-@@ -196,24 +147,10 @@
+@@ -195,24 +122,7 @@
+ "ip":"10.0.7.6",
"afi":"ipv4",
"interfaceName":"eth-rt6",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
- }
+- "labels":[
+- 3
+- ]
+- }
- ],
- "backupNexthops":[
- {
@@ -113,11 +163,11 @@
- "labels":[
- 16060
- ]
-- }
++ "active":true
+ }
]
}
- ],
-@@ -232,27 +169,13 @@
+@@ -232,27 +142,13 @@
"ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
@@ -146,7 +196,7 @@
"active":true
}
]
-@@ -268,30 +191,13 @@
+@@ -268,30 +164,13 @@
{
"ip":"10.0.2.2",
"afi":"ipv4",
@@ -179,7 +229,7 @@
}
]
}
-@@ -307,29 +213,12 @@
+@@ -307,29 +186,12 @@
"ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
@@ -211,7 +261,7 @@
}
]
}
-@@ -349,31 +238,6 @@
+@@ -349,31 +211,6 @@
"ip":"10.0.6.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
@@ -243,7 +293,7 @@
"active":true
}
]
-@@ -394,31 +258,6 @@
+@@ -394,31 +231,6 @@
"ip":"10.0.6.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
@@ -275,7 +325,7 @@
"active":true
}
]
-@@ -434,18 +273,7 @@
+@@ -434,18 +246,7 @@
{
"ip":"10.0.6.5",
"afi":"ipv4",
@@ -295,7 +345,7 @@
}
]
}
-@@ -460,18 +288,7 @@
+@@ -460,18 +261,7 @@
{
"ip":"10.0.7.6",
"afi":"ipv4",
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff
index 7f39285089..991562ab99 100644
--- a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff
@@ -1,19 +1,23 @@
--- a/rt4/step3/show_ipv6_route.ref
+++ b/rt4/step4/show_ipv6_route.ref
-@@ -14,9 +14,6 @@
+@@ -13,35 +13,13 @@
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 16011
- ]
-@@ -26,19 +23,6 @@
+- "labels":[
+- 16011
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
@@ -27,30 +31,38 @@
- "afi":"ipv6",
- "interfaceName":"eth-rt5",
- "active":true,
- "labels":[
- 16011
- ]
-@@ -61,9 +45,6 @@
+- "labels":[
+- 16011
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -60,36 +38,13 @@
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
-@@ -73,24 +54,10 @@
+- "labels":[
+- 3
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
- }
+- "labels":[
+- 3
+- ]
+- }
- ],
- "backupNexthops":[
- {
@@ -61,21 +73,54 @@
- 16031,
- 16021
- ]
-- }
++ "active":true
+ }
]
}
- ],
-@@ -149,23 +116,10 @@
+@@ -108,28 +63,19 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt5",
- "active":true,
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -148,23 +94,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
- }
+- "labels":[
+- 3
+- ]
+- }
- ],
- "backupNexthops":[
- {
@@ -85,21 +130,22 @@
- "labels":[
- 16051
- ]
-- }
++ "active":true
+ }
]
}
- ],
-@@ -184,23 +138,10 @@
+@@ -183,23 +113,7 @@
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt6",
- "active":true,
+- "active":true,
- "backupIndex":[
- 0
- ],
- "labels":[
- 3
- ]
- }
+- "labels":[
+- 3
+- ]
+- }
- ],
- "backupNexthops":[
- {
@@ -109,7 +155,7 @@
- "labels":[
- 16061
- ]
-- }
++ "active":true
+ }
]
}
- ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff
index 3dcd36c176..660d2fbde2 100644
--- a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff
@@ -1,19 +1,24 @@
--- a/rt4/step3/show_mpls_table.ref
+++ b/rt4/step4/show_mpls_table.ref
-@@ -7,26 +7,13 @@
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "installed":true,
+@@ -1,262 +1,2 @@
+ {
+- "16010":{
+- "inLabel":16010,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16010,
+- "installed":true,
- "nexthop":"10.0.3.2",
- "backupIndex":[
- 0
- ]
-+ "nexthop":"10.0.3.2"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "installed":true,
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16010,
+- "installed":true,
- "nexthop":"10.0.2.2",
- "backupIndex":[
- 0
@@ -25,24 +30,26 @@
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "nexthop":"10.0.6.5"
-+ "nexthop":"10.0.2.2"
- }
- ]
- },
-@@ -38,26 +25,13 @@
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "installed":true,
+- }
+- ]
+- },
+- "16011":{
+- "inLabel":16011,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16011,
+- "installed":true,
- "interface":"eth-rt2-2",
- "backupIndex":[
- 0
- ]
-+ "interface":"eth-rt2-2"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "installed":true,
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16011,
+- "installed":true,
- "interface":"eth-rt2-1",
- "backupIndex":[
- 0
@@ -54,24 +61,26 @@
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "interface":"eth-rt5"
-+ "interface":"eth-rt2-1"
- }
- ]
- },
-@@ -69,26 +43,13 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- }
+- ]
+- },
+- "16020":{
+- "inLabel":16020,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "nexthop":"10.0.3.2",
- "backupIndex":[
- 0
- ]
-+ "nexthop":"10.0.3.2"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "nexthop":"10.0.2.2",
- "backupIndex":[
- 0
@@ -83,24 +92,26 @@
- "type":"SR (IS-IS)",
- "outLabel":16030,
- "nexthop":"10.0.6.5"
-+ "nexthop":"10.0.2.2"
- }
- ]
- },
-@@ -100,26 +61,13 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- }
+- ]
+- },
+- "16021":{
+- "inLabel":16021,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "interface":"eth-rt2-2",
- "backupIndex":[
- 0
- ]
-+ "interface":"eth-rt2-2"
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "interface":"eth-rt2-1",
- "backupIndex":[
- 0
@@ -112,14 +123,65 @@
- "type":"SR (IS-IS)",
- "outLabel":16031,
- "interface":"eth-rt5"
-+ "interface":"eth-rt2-1"
- }
- ]
- },
-@@ -179,17 +127,7 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- }
+- ]
+- },
+- "16030":{
+- "inLabel":16030,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "installed":true,
+- "nexthop":"10.0.3.2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "installed":true,
+- "nexthop":"10.0.2.2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "installed":true,
+- "nexthop":"10.0.6.5"
+- }
+- ]
+- },
+- "16031":{
+- "inLabel":16031,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "installed":true,
+- "interface":"eth-rt2-2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "installed":true,
+- "interface":"eth-rt2-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "installed":true,
+- "interface":"eth-rt5"
+- }
+- ]
+- },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "nexthop":"10.0.6.5",
- "backupIndex":[
- 0
@@ -131,14 +193,17 @@
- "type":"SR (IS-IS)",
- "outLabel":16050,
- "nexthop":"10.0.7.6"
-+ "nexthop":"10.0.6.5"
- }
- ]
- },
-@@ -201,17 +139,7 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "interface":"eth-rt5",
- "backupIndex":[
- 0
@@ -150,14 +215,17 @@
- "type":"SR (IS-IS)",
- "outLabel":16051,
- "interface":"eth-rt6"
-+ "interface":"eth-rt5"
- }
- ]
- },
-@@ -223,17 +151,7 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- }
+- ]
+- },
+- "16060":{
+- "inLabel":16060,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "nexthop":"10.0.7.6",
- "backupIndex":[
- 0
@@ -169,14 +237,17 @@
- "type":"SR (IS-IS)",
- "outLabel":16060,
- "nexthop":"10.0.6.5"
-+ "nexthop":"10.0.7.6"
- }
- ]
- },
-@@ -245,17 +163,7 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
+- }
+- ]
+- },
+- "16061":{
+- "inLabel":16061,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
- "interface":"eth-rt6",
- "backupIndex":[
- 0
@@ -188,7 +259,7 @@
- "type":"SR (IS-IS)",
- "outLabel":16061,
- "interface":"eth-rt5"
-+ "interface":"eth-rt6"
- }
- ]
- }
+- }
+- ]
+- }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff
index 484a3147dc..4385df2c36 100644
--- a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff
@@ -1,19 +1,25 @@
--- a/rt4/step4/show_ip_route.ref
+++ b/rt4/step5/show_ip_route.ref
-@@ -15,6 +15,9 @@
+@@ -14,14 +14,37 @@
+ "ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 16010
- ]
-@@ -25,6 +28,20 @@
++ "labels":[
++ 16010
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
@@ -28,30 +34,39 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
- "labels":[
- 16010
- ]
-@@ -48,6 +65,9 @@
++ "labels":[
++ 16010
++ ]
+ }
+ ]
+ }
+@@ -41,14 +64,38 @@
+ "ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
-@@ -58,10 +78,25 @@
++ "labels":[
++ 3
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
- }
++ "labels":[
++ 3
++ ]
++ }
+ ],
+ "backupNexthops":[
+ {
@@ -63,21 +78,56 @@
+ 16030,
+ 16020
+ ]
-+ }
+ }
+ ]
+ }
+@@ -68,21 +115,30 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ }
]
}
- ],
-@@ -124,10 +159,24 @@
+@@ -102,7 +158,24 @@
+ "ip":"10.0.6.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
- }
++ "labels":[
++ 3
++ ]
++ }
+ ],
+ "backupNexthops":[
+ {
@@ -88,21 +138,22 @@
+ "labels":[
+ 16050
+ ]
-+ }
+ }
]
}
- ],
-@@ -147,10 +196,24 @@
+@@ -122,7 +195,24 @@
+ "ip":"10.0.7.6",
"afi":"ipv4",
"interfaceName":"eth-rt6",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
- }
++ "labels":[
++ 3
++ ]
++ }
+ ],
+ "backupNexthops":[
+ {
@@ -113,11 +164,10 @@
+ "labels":[
+ 16060
+ ]
-+ }
+ }
]
}
- ],
-@@ -169,13 +232,27 @@
+@@ -142,13 +232,27 @@
"ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
@@ -146,7 +196,7 @@
"active":true
}
]
-@@ -191,13 +268,30 @@
+@@ -164,13 +268,30 @@
{
"ip":"10.0.2.2",
"afi":"ipv4",
@@ -179,7 +229,7 @@
}
]
}
-@@ -213,12 +307,29 @@
+@@ -186,12 +307,29 @@
"ip":"10.0.2.2",
"afi":"ipv4",
"interfaceName":"eth-rt2-1",
@@ -211,7 +261,7 @@
}
]
}
-@@ -238,6 +349,31 @@
+@@ -211,6 +349,31 @@
"ip":"10.0.6.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
@@ -243,7 +293,7 @@
"active":true
}
]
-@@ -258,6 +394,31 @@
+@@ -231,6 +394,31 @@
"ip":"10.0.6.5",
"afi":"ipv4",
"interfaceName":"eth-rt5",
@@ -275,7 +325,7 @@
"active":true
}
]
-@@ -273,7 +434,18 @@
+@@ -246,7 +434,18 @@
{
"ip":"10.0.6.5",
"afi":"ipv4",
@@ -295,7 +345,7 @@
}
]
}
-@@ -288,7 +460,18 @@
+@@ -261,7 +460,18 @@
{
"ip":"10.0.7.6",
"afi":"ipv4",
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff
index 3ad0085120..54a1dc23c5 100644
--- a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff
@@ -1,19 +1,24 @@
--- a/rt4/step4/show_ipv6_route.ref
+++ b/rt4/step5/show_ipv6_route.ref
-@@ -14,6 +14,9 @@
+@@ -13,13 +13,35 @@
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 16011
- ]
-@@ -23,6 +26,19 @@
++ "labels":[
++ 16011
++ ]
+ },
+ {
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
@@ -27,30 +32,38 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
- "labels":[
- 16011
- ]
-@@ -45,6 +61,9 @@
++ "labels":[
++ 16011
++ ]
+ }
+ ]
+ }
+@@ -38,13 +60,36 @@
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-2",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
-@@ -54,10 +73,24 @@
++ "labels":[
++ 3
++ ]
+ },
+ {
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt2-1",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
- }
++ "labels":[
++ 3
++ ]
++ }
+ ],
+ "backupNexthops":[
+ {
@@ -61,21 +74,54 @@
+ 16031,
+ 16021
+ ]
-+ }
+ }
]
}
- ],
-@@ -116,10 +149,23 @@
+@@ -63,19 +108,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ },
+ {
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt5",
- "active":true,
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ }
+ ]
+ }
+@@ -94,7 +148,23 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
- }
++ "labels":[
++ 3
++ ]
++ }
+ ],
+ "backupNexthops":[
+ {
@@ -85,21 +131,22 @@
+ "labels":[
+ 16051
+ ]
-+ }
+ }
]
}
- ],
-@@ -138,10 +184,23 @@
+@@ -113,7 +183,23 @@
+ "fib":true,
"afi":"ipv6",
"interfaceName":"eth-rt6",
- "active":true,
+- "active":true
++ "active":true,
+ "backupIndex":[
+ 0
+ ],
- "labels":[
- 3
- ]
- }
++ "labels":[
++ 3
++ ]
++ }
+ ],
+ "backupNexthops":[
+ {
@@ -109,7 +156,6 @@
+ "labels":[
+ 16061
+ ]
-+ }
+ }
]
}
- ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff
index 20e363375b..fb6a119281 100644
--- a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff
@@ -1,20 +1,24 @@
--- a/rt4/step4/show_mpls_table.ref
+++ b/rt4/step5/show_mpls_table.ref
-@@ -7,13 +7,26 @@
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "installed":true,
-- "nexthop":"10.0.3.2"
+@@ -1,2 +1,262 @@
+ {
++ "16010":{
++ "inLabel":16010,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16010,
++ "installed":true,
+ "nexthop":"10.0.3.2",
+ "backupIndex":[
+ 0
+ ]
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16010,
- "installed":true,
-- "nexthop":"10.0.2.2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16010,
++ "installed":true,
+ "nexthop":"10.0.2.2",
+ "backupIndex":[
+ 0
@@ -26,24 +30,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "nexthop":"10.0.6.5"
- }
- ]
- },
-@@ -25,13 +38,26 @@
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "installed":true,
-- "interface":"eth-rt2-2"
++ }
++ ]
++ },
++ "16011":{
++ "inLabel":16011,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16011,
++ "installed":true,
+ "interface":"eth-rt2-2",
+ "backupIndex":[
+ 0
+ ]
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":16011,
- "installed":true,
-- "interface":"eth-rt2-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16011,
++ "installed":true,
+ "interface":"eth-rt2-1",
+ "backupIndex":[
+ 0
@@ -55,24 +61,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "interface":"eth-rt5"
- }
- ]
- },
-@@ -43,13 +69,26 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "nexthop":"10.0.3.2"
++ }
++ ]
++ },
++ "16020":{
++ "inLabel":16020,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "nexthop":"10.0.3.2",
+ "backupIndex":[
+ 0
+ ]
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "nexthop":"10.0.2.2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "nexthop":"10.0.2.2",
+ "backupIndex":[
+ 0
@@ -84,24 +92,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "nexthop":"10.0.6.5"
- }
- ]
- },
-@@ -61,13 +100,26 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "interface":"eth-rt2-2"
++ }
++ ]
++ },
++ "16021":{
++ "inLabel":16021,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "interface":"eth-rt2-2",
+ "backupIndex":[
+ 0
+ ]
- },
- {
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "interface":"eth-rt2-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "interface":"eth-rt2-1",
+ "backupIndex":[
+ 0
@@ -113,14 +123,65 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "interface":"eth-rt5"
- }
- ]
- },
-@@ -127,7 +179,17 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "nexthop":"10.0.6.5"
++ }
++ ]
++ },
++ "16030":{
++ "inLabel":16030,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "installed":true,
++ "nexthop":"10.0.3.2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "installed":true,
++ "nexthop":"10.0.2.2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "installed":true,
++ "nexthop":"10.0.6.5"
++ }
++ ]
++ },
++ "16031":{
++ "inLabel":16031,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "installed":true,
++ "interface":"eth-rt2-2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "installed":true,
++ "interface":"eth-rt2-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "installed":true,
++ "interface":"eth-rt5"
++ }
++ ]
++ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "nexthop":"10.0.6.5",
+ "backupIndex":[
+ 0
@@ -132,14 +193,17 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.7.6"
- }
- ]
- },
-@@ -139,7 +201,17 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "interface":"eth-rt5"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "interface":"eth-rt5",
+ "backupIndex":[
+ 0
@@ -151,14 +215,17 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt6"
- }
- ]
- },
-@@ -151,7 +223,17 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "nexthop":"10.0.7.6"
++ }
++ ]
++ },
++ "16060":{
++ "inLabel":16060,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "nexthop":"10.0.7.6",
+ "backupIndex":[
+ 0
@@ -170,14 +237,17 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "nexthop":"10.0.6.5"
- }
- ]
- },
-@@ -163,7 +245,17 @@
- "type":"SR (IS-IS)",
- "outLabel":3,
- "installed":true,
-- "interface":"eth-rt6"
++ }
++ ]
++ },
++ "16061":{
++ "inLabel":16061,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
+ "interface":"eth-rt6",
+ "backupIndex":[
+ 0
@@ -189,6 +259,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "interface":"eth-rt5"
- }
- ]
- }
++ }
++ ]
++ }
+ }
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 5a5c7e3df4..3f70dbddf7 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -3245,7 +3245,7 @@ def configure_interface_mac(tgen, input_dict):
#############################################
# Verification APIs
#############################################
-@retry(retry_timeout=12)
+@retry(retry_timeout=40)
def verify_rib(
tgen,
addr_type,
@@ -3383,6 +3383,10 @@ def verify_rib(
st_found = True
found_routes.append(st_rt)
+ if "queued" in rib_routes_json[st_rt][0]:
+ errormsg = "Route {} is queued\n".format(st_rt)
+ return errormsg
+
if fib and next_hop:
if type(next_hop) is not list:
next_hop = [next_hop]
@@ -3607,6 +3611,10 @@ def verify_rib(
st_found = True
found_routes.append(st_rt)
+ if "queued" in rib_routes_json[st_rt][0]:
+ errormsg = "Route {} is queued\n".format(st_rt)
+ return errormsg
+
if next_hop:
if type(next_hop) is not list:
next_hop = [next_hop]
diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py
index d7b680da18..02f66e9c26 100644
--- a/tests/topotests/lib/micronet.py
+++ b/tests/topotests/lib/micronet.py
@@ -555,8 +555,11 @@ class LinuxNamespace(Commander):
self.base_pre_cmd.append("-F")
self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd])
- # Remount /sys to pickup any changes
+ # Remount sysfs and cgroup to pickup any changes
self.cmd_raises("mount -t sysfs sysfs /sys")
+ self.cmd_raises(
+ "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup"
+ )
# Set the hostname to the namespace name
if uts and set_hostname:
diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py
index 1423f3fecc..b0889373ce 100644
--- a/tests/topotests/lib/pim.py
+++ b/tests/topotests/lib/pim.py
@@ -273,18 +273,20 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False):
config_data.append(cmd)
protocol = "igmp"
del_action = intf_data[intf_name]["igmp"].setdefault("delete", False)
+ del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False)
cmd = "ip igmp"
if del_action:
cmd = "no {}".format(cmd)
- config_data.append(cmd)
+ if not del_attr:
+ config_data.append(cmd)
- del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False)
for attribute, data in intf_data[intf_name]["igmp"].items():
if attribute == "version":
cmd = "ip {} {} {}".format(protocol, attribute, data)
if del_action:
cmd = "no {}".format(cmd)
- config_data.append(cmd)
+ if not del_attr:
+ config_data.append(cmd)
if attribute == "join":
for group in data:
@@ -3550,6 +3552,69 @@ class McastTesterHelper(HostApplicationHelper):
return True
+
+def verify_pim_interface_traffic(tgen, input_dict, return_stats=True):
+ """
+ Verify ip pim interface traffice by running
+ "show ip pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict(dict)`: defines DUT, what and from which interfaces
+ traffic needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "r1-r0-eth0": {
+ "helloRx": 0,
+ "helloTx": 1,
+ "joinRx": 0,
+ "joinTx": 0
+ }
+ }
+ }
+
+ result = verify_pim_interface_traffic(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ output_dict = {}
+ for dut in input_dict.keys():
+ if dut not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying pim interface traffic", dut)
+ show_pim_intf_traffic_json = run_frr_cmd(
+ rnode, "show ip pim interface traffic json", isjson=True
+ )
+
+ output_dict[dut] = {}
+ for intf, data in input_dict[dut].items():
+ interface_json = show_pim_intf_traffic_json[intf]
+ for state in data:
+
+ # Verify Tx/Rx
+ if state in interface_json:
+ output_dict[dut][state] = interface_json[state]
+ else:
+ errormsg = (
+ "[DUT %s]: %s is not present"
+ "for interface %s [FAILED]!! " % (dut, state, intf)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True if return_stats == False else output_dict
+
# def cleanup(self):
# super(McastTesterHelper, self).cleanup()
diff --git a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
index c0ea1b5561..ceac78d88b 100644
--- a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
+++ b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
@@ -377,7 +377,6 @@ def test_BSR_higher_prefer_ip_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
step("pre-configure BSM packet")
step("Configure cisco-1 as BSR1 1.1.2.7")
result = pre_config_to_bsm(
@@ -583,7 +582,6 @@ def test_BSR_CRP_with_blackhole_address_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
step("pre-configure BSM packet")
step("Configure cisco-1 as BSR1 1.1.2.7")
result = pre_config_to_bsm(
@@ -760,8 +758,6 @@ def test_new_router_fwd_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -901,8 +897,6 @@ def test_int_bsm_config_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -1062,8 +1056,6 @@ def test_static_rp_override_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -1213,8 +1205,6 @@ def test_bsmp_stress_add_del_restart_p2(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -1538,8 +1528,6 @@ def test_iif_join_state_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
diff --git a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
index 9f771b8a2b..2d6062bf3c 100644
--- a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
+++ b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
@@ -335,8 +335,6 @@ def test_starg_mroute_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -487,8 +485,6 @@ def test_overlapping_group_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -593,8 +589,6 @@ def test_RP_priority_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -724,8 +718,6 @@ def test_BSR_election_p0(request):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -840,8 +832,6 @@ def test_RP_hash_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -933,8 +923,6 @@ def test_BSM_fragmentation_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py
index cba37bfc29..d0422e2f72 100755
--- a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py
@@ -894,37 +894,37 @@ def test_configuring_igmp_local_join_on_reciever_dr_non_dr_nodes_p1(request):
assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
step("Delete local join from DR node")
- input_dict = {
- "r1": {
- "igmp": {
- "interfaces": {
- vlan_intf_r1_s1: {
- "igmp": {
- "version": "2",
- "join": IGMP_JOIN_RANGE_3,
- "delete_attr": True,
+ for _join in IGMP_JOIN_RANGE_3:
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ vlan_intf_r1_s1: {
+ "igmp": {
+ "join": [_join],
+ "delete_attr": True,
+ }
}
}
}
}
}
- }
-
- result = create_igmp_config(tgen, topo, input_dict)
- assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
- step(
- "After removing local join 227.1.1.1 group removed from IGMP join "
- "of R1, R2 node , using 'show ip igmp groups json'"
- )
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
- for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
- result = verify_igmp_groups(tgen, dut, intf, IGMP_JOIN_RANGE_3, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \n "
- "IGMP groups are still present \n Error: {}".format(tc_name, result)
+ step(
+ "After removing local join 227.1.1.1 group removed from IGMP join "
+ "of R1, R2 node , using 'show ip igmp groups json'"
)
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ result = verify_igmp_groups(tgen, dut, intf, IGMP_JOIN_RANGE_3, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP groups are still present \n Error: {}".format(tc_name, result)
+ )
+
step("(*,G) mroute for 227.1.1.1 group removed from R1 node")
step(
"After remove of local join from R1 and R2 node verify (*,G) and (S,G) "
diff --git a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
index cbd1090648..b71c2d65eb 100755
--- a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
+++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
@@ -54,6 +54,7 @@ import sys
import time
import datetime
import pytest
+from time import sleep
pytestmark = pytest.mark.pimd
@@ -95,6 +96,7 @@ from lib.pim import (
verify_pim_rp_info,
verify_multicast_flag_state,
McastTesterHelper,
+ verify_pim_interface_traffic,
)
from lib.topolog import logger
from lib.topojson import build_config_from_json
@@ -354,6 +356,45 @@ def find_tos_in_tcpdump(tgen, router, message, cap_file):
return True
+def verify_pim_stats_increament(stats_before, stats_after):
+ """
+ API to compare pim interface control plane traffic
+
+ Parameters
+ ----------
+ * `stats_before` : Stats dictionary for any particular instance
+ * `stats_after` : Stats dictionary for any particular instance
+ """
+
+ for router, stats_data in stats_before.items():
+ for stats, value in stats_data.items():
+ if stats_before[router][stats] >= stats_after[router][stats]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ stats,
+ stats_before[router][stats],
+ stats_after[router][stats],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ stats,
+ stats_before[router][stats],
+ stats_after[router][stats],
+ )
+
+ return True
+
+
def test_verify_oil_when_join_prune_sent_scenario_1_p1(request):
"""
TC_21_1:
@@ -4490,6 +4531,259 @@ def test_verify_multicast_traffic_when_FHR_connected_to_RP_p1(request):
write_test_footer(tc_name)
+def test_PIM_passive_p1(request):
+ """
+ TC Verify PIM passive functionality"
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ app_helper.stop_all_hosts()
+ # Creating configuration from JSON
+ clear_mroute(tgen)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"]
+
+ step(
+ "configure PIM passive on receiver interface to verify no impact on IGMP join"
+ "and multicast traffic on pim passive interface"
+ )
+
+ raw_config = {
+ "c1": {"raw_config": ["interface {}".format(intf_c1_i4), "ip pim passive"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("configure IGMPv2 and send IGMP joinon on PIM passive interface")
+ input_dict = {
+ "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]}
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send Mcast traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)")
+
+ input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]}
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0]
+
+ input_dict_starg = [
+ {
+ "dut": "c1",
+ "src_address": "*",
+ "iif": topo["routers"]["c1"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "c1",
+ "src_address": source_i5,
+ "iif": topo["routers"]["c1"]["links"]["c2"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ step("(*,G) and (S,G) created on f1 and node verify using 'show ip mroute'")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"]
+ intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"]
+
+ step(
+ "configure PIM passive on upstream interface to verify"
+ "hello tx/rx counts are not incremented"
+ )
+
+ # Changing hello timer to 3sec for checking more number of packets
+
+ raw_config = {
+ "c1": {
+ "raw_config": [
+ "interface {}".format(intf_c1_c2),
+ "ip pim passive",
+ "ip pim hello 3",
+ ]
+ },
+ "c2": {"raw_config": ["interface {}".format(intf_c2_c1), "ip pim hello 3"]},
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify PIM hello tx/rx stats on C1")
+ state_dict = {
+ "c1": {
+ intf_c1_c2: ["helloTx", "helloRx"],
+ }
+ }
+
+ logger.info("waiting for 5 sec config to get apply and hello count update")
+ sleep(5)
+
+ c1_state_before = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "sleeping for 30 sec hello interval timer to verify count are not increamented"
+ )
+ sleep(35)
+
+ c1_state_after = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify stats not increamented on c1")
+ result = verify_pim_stats_increament(c1_state_before, c1_state_after)
+ assert (
+ result is not True
+ ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result)
+
+ step("No impact observed on mroutes")
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("remove PIM passive and verify hello tx/rx is increamented")
+ raw_config = {
+ "c1": {
+ "raw_config": [
+ "interface {}".format(intf_c1_c2),
+ "no ip pim passive",
+ "ip pim hello 3",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ logger.info("waiting for 30 sec for pim hello to receive")
+ sleep(30)
+
+ c1_state_after = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify stats increamented on c1 after removing pim passive")
+ result = verify_pim_stats_increament(c1_state_before, c1_state_after)
+ assert result is True, "Testcase{} : Failed Error: {}" "stats incremented".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py
index ff182be66f..8c855620be 100644
--- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper.py
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py
@@ -267,7 +267,11 @@ def test_ospf_gr_helper_tc1_p0(request):
ospf_gr_r0 = {
"r0": {
"ospf": {
- "graceful-restart": {"helper enable": [], "opaque": True, "delete": True}
+ "graceful-restart": {
+ "helper enable": [],
+ "opaque": True,
+ "delete": True,
+ }
}
}
}
@@ -386,336 +390,6 @@ def test_ospf_gr_helper_tc2_p0(request):
write_test_footer(tc_name)
-def test_ospf_gr_helper_tc3_p1(request):
- """
- OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
- sends grace lsa, helps RR to restart gracefully (RR = BDR)
- """
- tc_name = request.node.name
- write_test_header(tc_name)
- tgen = get_topogen()
-
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- global topo, intf, intf1, pkt
-
- step("Bring up the base config as per the topology")
- step(
- "Configure DR priority as 99 in RR , DUT dr priority = 98 "
- "& reset ospf process in all the routers"
- )
- reset_config_on_routers(tgen)
- ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert (
- ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
- step(
- "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
- )
-
- input_dict = {
- "r0": {
- "links": {
- sw_name: {
- "interface": topo["routers"]["r0"]["links"][sw_name]["interface"],
- "ospf": {"priority": 100},
- }
- }
- }
- }
-
- result = create_interfaces_cfg(tgen, input_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
-
- step("Clear ospf neighbours in all routers")
- for rtr in topo["routers"]:
- clear_ospf(tgen, rtr)
-
- step("Verify that DR election is triggered and R0 is elected as DR")
- input_dict = {
- "r0": {
- "ospf": {
- "neighbors": {
- "r1": {"state": "Full", "role": "Backup"},
- "r2": {"state": "Full", "role": "DROther"},
- "r3": {"state": "Full", "role": "DROther"},
- }
- }
- }
- }
- dut = "r0"
- result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r0)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- step("Verify that DUT enters into helper mode.")
-
- input_dict = {"activeRestarterCnt": 1}
- gracelsa_sent = False
- repeat = 0
- dut = "r0"
- while not gracelsa_sent and repeat < Iters:
- gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
- if isinstance(result, str):
- repeat += 1
- gracelsa_sent = False
-
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- delete_ospf()
- write_test_footer(tc_name)
-
-
-def test_ospf_gr_helper_tc4_p1(request):
- """
- OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
- sends grace lsa, helps RR to restart gracefully (RR = DRother)
- """
- tc_name = request.node.name
- write_test_header(tc_name)
- tgen = get_topogen()
-
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- global topo, intf, intf1, pkt
-
- step("Bring up the base config as per the topology")
- step(
- "Configure DR priority as 99 in RR , DUT dr priority = 98 "
- "& reset ospf process in all the routers"
- )
- reset_config_on_routers(tgen)
- ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert (
- ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
- step(
- "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
- )
-
- input_dict = {
- "r0": {
- "links": {
- sw_name: {
- "interface": topo["routers"]["r0"]["links"][sw_name]["interface"],
- "ospf": {"priority": 0},
- }
- }
- }
- }
-
- result = create_interfaces_cfg(tgen, input_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
-
- step("Clear ospf neighbours in all routers")
- for rtr in topo["routers"]:
- clear_ospf(tgen, rtr)
-
- step("Verify that DR election is triggered and R0 is elected as 2-Way")
- input_dict = {
- "r0": {
- "ospf": {
- "neighbors": {
- "r1": {"state": "Full", "role": "DR"},
- "r2": {"state": "2-Way", "role": "DROther"},
- "r3": {"state": "2-Way", "role": "DROther"},
- }
- }
- }
- }
- dut = "r0"
- result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r0)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- step("Verify that DUT enters into helper mode.")
-
- input_dict = {"activeRestarterCnt": 1}
- gracelsa_sent = False
- repeat = 0
- dut = "r0"
- while not gracelsa_sent and repeat < Iters:
- gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
- if isinstance(result, str):
- repeat += 1
- gracelsa_sent = False
-
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- delete_ospf()
-
- write_test_footer(tc_name)
-
-
-def test_ospf_gr_helper_tc7_p1(request):
- """
- Test ospf gr helper
- Verify helper when grace lsa is received with different configured
- value in process level (higher, lower, grace lsa timer above 1800)
- """
- tc_name = request.node.name
- write_test_header(tc_name)
- tgen = get_topogen()
-
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- global topo, intf, intf1, pkt
-
- step("Bring up the base config as per the topology")
- step(
- "Configure DR priority as 99 in RR , DUT dr priority = 98 "
- "& reset ospf process in all the routers"
- )
- step(
- "Enable GR on RR and DUT with grace period on RR = 333"
- "and grace period on DUT = 300"
- )
- reset_config_on_routers(tgen)
- ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert (
- ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
- ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r0)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- input_dict = {"supportedGracePeriod": 1800}
- dut = "r0"
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- step("Configure grace period = 1801 on RR and restart ospf .")
- grace_period_1801 = "01005e00000570708bd051ef080045c0005cbeb10000015907d111010101e00000050204004801010101000000009714000000000000000000000000000100010209030000000101010180000001c8e9002c000100040000016800020001010000000003000411010101"
- gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, grace_period_1801)
-
- step("Verify R0 does not enter helper mode.")
- input_dict = {"activeRestarterCnt": 1}
- dut = "r0"
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
- tc_name, result
- )
-
- delete_ospf()
-
- write_test_footer(tc_name)
-
-
-def test_ospf_gr_helper_tc8_p1(request):
- """
- Test ospf gr helper
-
- Verify helper functionality when dut is helping RR and new grace lsa
- is received from RR.
- """
- tc_name = request.node.name
- write_test_header(tc_name)
- tgen = get_topogen()
-
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- global topo, intf, intf1, pkt
-
- step("Bring up the base config as per the topology")
- step("Enable GR")
- reset_config_on_routers(tgen)
- ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert (
- ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
- ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r0)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
- }
- result = create_router_ospf(tgen, topo, ospf_gr_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- input_dict = {"supportedGracePeriod": 1800}
- dut = "r0"
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- step("Verify that DUT enters into helper mode.")
-
- input_dict = {"activeRestarterCnt": 1}
- gracelsa_sent = False
- repeat = 0
- dut = "r0"
- while not gracelsa_sent and repeat < Iters:
- gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
- if isinstance(result, str):
- repeat += 1
- gracelsa_sent = False
-
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- step("Send the Grace LSA again to DUT when RR is in GR.")
- input_dict = {"activeRestarterCnt": 1}
- gracelsa_sent = False
- repeat = 0
- dut = "r0"
- while not gracelsa_sent and repeat < Iters:
- gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
- result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
- if isinstance(result, str):
- repeat += 1
- gracelsa_sent = False
-
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- delete_ospf()
-
- write_test_footer(tc_name)
-
-
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py
new file mode 100644
index 0000000000..e7d0621df8
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py
@@ -0,0 +1,373 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ scapy_send_raw_packet,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_gr_helper,
+ create_router_ospf,
+)
+
+# Global variables
+topo = None
+Iters = 5
+sw_name = None
+intf = None
+intf1 = None
+pkt = None
+
+"""
+Topology:
+
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ DUT - HR RR
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+Testcases:
+
+TC1. Verify by default helper support is disabled for FRR ospf
+TC2. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DR)
+TC3. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+TC4. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+TC5. OSPF GR on P2P : Verify DUT enters Helper mode when neighbor sends
+ grace lsa, helps RR to restart gracefully.
+TC6. Verify all the show commands newly introducted as part of ospf
+ helper support - Json Key verification wrt to show commands.
+TC7. Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+TC8. Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, intf, intf1, sw_name, pkt
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/ospf_gr_helper.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ sw_name = "s1"
+ intf = topo["routers"]["r0"]["links"][sw_name]["interface"]
+ intf1 = topo["routers"]["r1"]["links"][sw_name]["interface"]
+ pkt = topo["routers"]["r1"]["opq_lsa_hex"]
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+def delete_ospf():
+ """delete ospf process after each test"""
+ tgen = get_topogen()
+ step("Delete ospf process")
+ for rtr in topo["routers"]:
+ ospf_del = {rtr: {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_gr_helper_tc3_p1(request):
+ """
+ OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ step(
+ "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ sw_name: {
+ "interface": topo["routers"]["r0"]["links"][sw_name]["interface"],
+ "ospf": {"priority": 100},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as DR")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+ write_test_footer(tc_name)
+
+
+def test_ospf_gr_helper_tc4_p1(request):
+ """
+ OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ step(
+ "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ sw_name: {
+ "interface": topo["routers"]["r0"]["links"][sw_name]["interface"],
+ "ospf": {"priority": 0},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as 2-Way")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "DR"},
+ "r2": {"state": "2-Way", "role": "DROther"},
+ "r3": {"state": "2-Way", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py
new file mode 100644
index 0000000000..4cb3747c56
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py
@@ -0,0 +1,325 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ scapy_send_raw_packet,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_gr_helper,
+ create_router_ospf,
+)
+
+# Global variables
+topo = None
+Iters = 5
+sw_name = None
+intf = None
+intf1 = None
+pkt = None
+
+"""
+Topology:
+
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ DUT - HR RR
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+Testcases:
+
+TC1. Verify by default helper support is disabled for FRR ospf
+TC2. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DR)
+TC3. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+TC4. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+TC5. OSPF GR on P2P : Verify DUT enters Helper mode when neighbor sends
+ grace lsa, helps RR to restart gracefully.
+TC6. Verify all the show commands newly introducted as part of ospf
+ helper support - Json Key verification wrt to show commands.
+TC7. Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+TC8. Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, intf, intf1, sw_name, pkt
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/ospf_gr_helper.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ sw_name = "s1"
+ intf = topo["routers"]["r0"]["links"][sw_name]["interface"]
+ intf1 = topo["routers"]["r1"]["links"][sw_name]["interface"]
+ pkt = topo["routers"]["r1"]["opq_lsa_hex"]
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+def delete_ospf():
+ """delete ospf process after each test"""
+ tgen = get_topogen()
+ step("Delete ospf process")
+ for rtr in topo["routers"]:
+ ospf_del = {rtr: {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_gr_helper_tc7_p1(request):
+ """
+ Test ospf gr helper
+ Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ step(
+ "Enable GR on RR and DUT with grace period on RR = 333"
+ "and grace period on DUT = 300"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {"supportedGracePeriod": 1800}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure grace period = 1801 on RR and restart ospf .")
+ grace_period_1801 = "01005e00000570708bd051ef080045c0005cbeb10000015907d111010101e00000050204004801010101000000009714000000000000000000000000000100010209030000000101010180000001c8e9002c000100040000016800020001010000000003000411010101"
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, grace_period_1801)
+
+ step("Verify R0 does not enter helper mode.")
+ input_dict = {"activeRestarterCnt": 1}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+ tc_name, result
+ )
+
+ delete_ospf()
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_gr_helper_tc8_p1(request):
+ """
+ Test ospf gr helper
+
+ Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step("Enable GR")
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {"supportedGracePeriod": 1800}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Send the Grace LSA again to DUT when RR is in GR.")
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfapi/ctester.py b/tests/topotests/ospfapi/ctester.py
new file mode 100755
index 0000000000..243fc0613f
--- /dev/null
+++ b/tests/topotests/ospfapi/ctester.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# January 17 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2022, LabN Consulting, L.L.C.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+import argparse
+import asyncio
+import logging
+import os
+import sys
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
+if not os.path.exists(CLIENTDIR):
+ CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
+assert os.path.exists(
+ os.path.join(CLIENTDIR, "ospfclient.py")
+), "can't locate ospfclient.py"
+
+sys.path[0:0] = [CLIENTDIR]
+
+import ospfclient as api # pylint: disable=E0401 # noqa: E402
+
+
+async def do_wait(c, args):
+ cv = asyncio.Condition()
+
+ async def cb(added, removed):
+ logging.debug("callback: added: %s removed: %s", added, removed)
+ sys.stdout.flush()
+ async with cv:
+ cv.notify_all()
+
+ logging.debug("API using callback")
+ await c.monitor_reachable(callback=cb)
+
+ for w in args.wait:
+ check = ",".join(sorted(list(w.split(","))))
+ logging.info("Waiting for %s", check)
+
+ while True:
+ async with cv:
+ got = ",".join(sorted([str(x) for x in c.reachable_routers]))
+ if check == got:
+ break
+ logging.debug("expected '%s' != '%s'\nwaiting on notify", check, got)
+ await cv.wait()
+
+ logging.info("SUCCESS: %s", check)
+ print("SUCCESS: {}".format(check))
+ sys.stdout.flush()
+
+
+async def async_main(args):
+ c = api.OspfOpaqueClient(args.server)
+ await c.connect()
+ if sys.version_info[1] > 6:
+ asyncio.create_task(c._handle_msg_loop()) # pylint: disable=W0212
+ else:
+ asyncio.get_event_loop().create_task(
+ c._handle_msg_loop() # pylint: disable=W0212
+ )
+
+ if args.wait:
+ await do_wait(c, args)
+ return 0
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ ap.add_argument("--server", default="localhost", help="OSPF API server")
+ ap.add_argument(
+ "--wait", action="append", help="wait for comma-sep set of reachable routers"
+ )
+ ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ args = ap.parse_args()
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level, format="%(asctime)s %(levelname)s: TESTER: %(name)s: %(message)s"
+ )
+
+ # We need to flush this output to stdout right away
+ h = logging.StreamHandler(sys.stdout)
+ h.flush = sys.stdout.flush
+ f = logging.Formatter("%(asctime)s %(name)s: %(levelname)s: %(message)s")
+ h.setFormatter(f)
+ logger = logging.getLogger("ospfclient")
+ logger.addHandler(h)
+ logger.propagate = False
+
+ logging.info("ctester: starting")
+ sys.stdout.flush()
+
+ status = 3
+ try:
+ if sys.version_info[1] > 6:
+ status = asyncio.run(async_main(args))
+ else:
+ loop = asyncio.get_event_loop()
+ try:
+ status = loop.run_until_complete(async_main(args))
+ finally:
+ loop.close()
+ except KeyboardInterrupt:
+ logging.info("Exiting, received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("Exiting, unexpected exception %s", error, exc_info=True)
+ else:
+ logging.info("api: clean exit")
+
+ return status
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/tests/topotests/ospfapi/lib b/tests/topotests/ospfapi/lib
new file mode 120000
index 0000000000..dc598c56dc
--- /dev/null
+++ b/tests/topotests/ospfapi/lib
@@ -0,0 +1 @@
+../lib \ No newline at end of file
diff --git a/tests/topotests/ospfapi/r1/ospfd.conf b/tests/topotests/ospfapi/r1/ospfd.conf
new file mode 100644
index 0000000000..8d13556847
--- /dev/null
+++ b/tests/topotests/ospfapi/r1/ospfd.conf
@@ -0,0 +1,10 @@
+!
+interface r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.1
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r1/zebra.conf b/tests/topotests/ospfapi/r1/zebra.conf
new file mode 100644
index 0000000000..aae87408c3
--- /dev/null
+++ b/tests/topotests/ospfapi/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
diff --git a/tests/topotests/ospfapi/r2/ospfd.conf b/tests/topotests/ospfapi/r2/ospfd.conf
new file mode 100644
index 0000000000..be0712742b
--- /dev/null
+++ b/tests/topotests/ospfapi/r2/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.2
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r2/zebra.conf b/tests/topotests/ospfapi/r2/zebra.conf
new file mode 100644
index 0000000000..e66d52da49
--- /dev/null
+++ b/tests/topotests/ospfapi/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r2-eth0
+ ip address 10.0.1.2/24
+!
+interface r2-eth1
+ ip address 10.0.2.2/24
+!
diff --git a/tests/topotests/ospfapi/r3/ospfd.conf b/tests/topotests/ospfapi/r3/ospfd.conf
new file mode 100644
index 0000000000..77cd86a975
--- /dev/null
+++ b/tests/topotests/ospfapi/r3/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.3
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r3/zebra.conf b/tests/topotests/ospfapi/r3/zebra.conf
new file mode 100644
index 0000000000..072c297926
--- /dev/null
+++ b/tests/topotests/ospfapi/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r3-eth0
+ ip address 10.0.2.3/24
+!
+interface r3-eth1
+ ip address 10.0.3.3/24
+!
diff --git a/tests/topotests/ospfapi/r4/ospfd.conf b/tests/topotests/ospfapi/r4/ospfd.conf
new file mode 100644
index 0000000000..32e55d546b
--- /dev/null
+++ b/tests/topotests/ospfapi/r4/ospfd.conf
@@ -0,0 +1,10 @@
+!
+interface r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.4
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r4/zebra.conf b/tests/topotests/ospfapi/r4/zebra.conf
new file mode 100644
index 0000000000..702219720d
--- /dev/null
+++ b/tests/topotests/ospfapi/r4/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r4-eth0
+ ip address 10.0.3.4/24
+!
diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py
new file mode 100644
index 0000000000..dca91412dc
--- /dev/null
+++ b/tests/topotests/ospfapi/test_ospf_clientapi.py
@@ -0,0 +1,470 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+#
+# 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
+#
+
+"""
+test_ospf_clientapi.py: Test the OSPF client API.
+"""
+
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import time
+from datetime import datetime, timedelta
+
+import pytest
+
+from lib.common_config import retry, run_frr_cmd, step
+from lib.micronet import comm_error
+from lib.topogen import Topogen, TopoRouter
+from lib.topotest import interface_set_status, json_cmp
+
+pytestmark = [pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+TESTDIR = os.path.abspath(CWD)
+
+CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
+if not os.path.exists(CLIENTDIR):
+ CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
+
+assert os.path.exists(
+ os.path.join(CLIENTDIR, "ospfclient.py")
+), "can't locate ospfclient.py"
+
+
+# ----------
+# Test Setup
+# ----------
+
+
+@pytest.fixture(scope="function", name="tgen")
+def _tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+ nrouters = request.param
+ topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)}
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for _, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF, "ospfd.conf")
+ router.net.daemons_options["ospfd"] = "--apiserver"
+
+ tgen.start_router()
+
+ yield tgen
+
+ tgen.stop_topology()
+
+
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+# ------------
+# Test Utility
+# ------------
+
+
+@retry(retry_timeout=45)
+def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json"):
+ del tgen
+ show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
+ if not bool(show_ospf_json):
+ return "ospf is not running"
+ result = json_cmp(show_ospf_json, input_dict)
+ return str(result) if result else None
+
+
+def myreadline(f):
+ buf = b""
+ while True:
+ # logging.info("READING 1 CHAR")
+ c = f.read(1)
+ if not c:
+ return buf if buf else None
+ buf += c
+ # logging.info("READ CHAR: '%s'", c)
+ if c == b"\n":
+ return buf
+
+
+def _wait_output(p, regex, timeout=120):
+ retry_until = datetime.now() + timedelta(seconds=timeout)
+ while datetime.now() < retry_until:
+ # line = p.stdout.readline()
+ line = myreadline(p.stdout)
+ if not line:
+ assert None, "Timeout waiting for '{}'".format(regex)
+ line = line.decode("utf-8")
+ line = line.rstrip()
+ if line:
+ logging.debug("GOT LINE: '%s'", line)
+ m = re.search(regex, line)
+ if m:
+ return m
+ assert None, "Failed to get output withint {}s".format(timeout)
+
+
+# -----
+# Tests
+# -----
+
+
+def _test_reachability(tgen, testbin):
+ waitlist = [
+ "192.168.0.1,192.168.0.2,192.168.0.4",
+ "192.168.0.2,192.168.0.4",
+ "192.168.0.1,192.168.0.2,192.168.0.4",
+ ]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ wait_args = [f"--wait={x}" for x in waitlist]
+
+ p = None
+ try:
+ step("reachable: check for initial reachability")
+ p = r3.popen(
+ ["/usr/bin/timeout", "120", testbin, "-v", *wait_args],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ _wait_output(p, "SUCCESS: {}".format(waitlist[0]))
+
+ step("reachable: check for modified reachability")
+ interface_set_status(r2, "r2-eth0", False)
+ _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
+
+ step("reachable: check for restored reachability")
+ interface_set_status(r2, "r2-eth0", True)
+ _wait_output(p, "SUCCESS: {}".format(waitlist[2]))
+ except Exception as error:
+ logging.error("ERROR: %s", error)
+ raise
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [4], indirect=True)
+def test_ospf_reachability(tgen):
+ testbin = os.path.join(TESTDIR, "ctester.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"])
+ logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
+ _test_reachability(tgen, testbin)
+
+
+def _test_add_data(tgen, apibin):
+ "Test adding opaque data to domain"
+
+ r1 = tgen.gears["r1"]
+
+ step("add opaque: add opaque link local")
+
+ p = None
+ try:
+ p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ }
+ ],
+ }
+ },
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "230.0.0.2",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "00000202",
+ },
+ ],
+ }
+ },
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-link json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("reset client, add opaque area, verify link local flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+ p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ }
+ ],
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ }
+ },
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "00010101",
+ },
+ ],
+ }
+ },
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-area json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("reset client, add opaque AS, verify area flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "areas": {
+ "1.2.3.4": {
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ },
+ ],
+ }
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.3",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "deadbeaf01234567",
+ },
+ ]
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-as json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("stop client, verify AS flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ },
+ ],
+ }
+ # Wait for it to be flushed
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ step("start client adding opaque domain, verify new sequence number and data")
+
+ # Originate it again
+ p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000002",
+ },
+ ],
+ }
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.3",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000002",
+ "opaqueData": "ebadf00d",
+ },
+ ]
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-as json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_add_data3(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_add_data(tgen, apibin)
+
+
+def _test_opaque_add_del(tgen, apibin):
+ "Test adding opaque data to domain"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ p = None
+ pread = None
+ try:
+ step("reachable: check for add notification")
+ pread = r2.popen(
+ ["/usr/bin/timeout", "120", apibin, "-v"],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
+
+ # Wait for add notification
+ # RECV: LSA update msg for LSA 232.0.0.3 in area 0.0.0.0 seq 0x80000001 len 24 age 9
+
+ ls_id = "232.0.0.3"
+ waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
+ _ = _wait_output(pread, waitfor)
+
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+
+ # step("reachable: check for flush/age out")
+ # # Wait for max age notification
+ # waitfor = "RECV:.*update msg.*LSA {}.*age 3600".format(ls_id)
+ # _wait_output(pread, waitfor)
+
+ step("reachable: check for delete")
+ # Wait for delete notification
+ waitfor = "RECV:.*delete msg.*LSA {}.*".format(ls_id)
+ _wait_output(pread, waitfor)
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if pread:
+ pread.terminate()
+ pread.wait()
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_delete_data3(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_opaque_add_del(tgen, apibin)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/pbr_topo1/test_pbr_topo1.py b/tests/topotests/pbr_topo1/test_pbr_topo1.py
index 586d9217d2..8506a15135 100644
--- a/tests/topotests/pbr_topo1/test_pbr_topo1.py
+++ b/tests/topotests/pbr_topo1/test_pbr_topo1.py
@@ -234,15 +234,23 @@ def test_rule_linux_installation():
logger.info("Checking for installed PBR rules in OS")
+ def _get_router_rules(router, expected):
+ actual = topotest.ip_rules(router)
+
+ logger.info(actual)
+ return topotest.json_cmp(actual, expected)
+
router_list = tgen.routers().values()
for router in router_list:
rules_file = "{}/{}/linux-rules.json".format(CWD, router.name)
- actual = topotest.ip_rules(router)
expected = json.loads(open(rules_file).read())
+ test_func = partial(_get_router_rules, router, expected)
+
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
assertmsg = "Router {} OS rules mismatch".format(router.name)
- assert topotest.json_cmp(actual, expected) is None, assertmsg
+ assert result is None, assertmsg
if __name__ == "__main__":
diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
index 6c76c928ec..088ac600c1 100644
--- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
@@ -435,11 +435,9 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request):
pytest.skip(tgen.errors)
reset_config_on_routers(tgen)
- NEXT_HOP_IP = populate_nh()
step("Configure 8 interfaces / links between R1 and R2,")
step("Configure IBGP IPv4 peering between R2 and R3 router.")
- reset_config_on_routers(tgen)
NEXT_HOP_IP = populate_nh()
nh_all = {}
for addr_type in ADDR_TYPES:
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
index 1861d9ad49..cdd7e13f75 100644
--- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
@@ -434,11 +434,9 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request):
pytest.skip(tgen.errors)
reset_config_on_routers(tgen)
- NEXT_HOP_IP = populate_nh()
step("Configure 8 interfaces / links between R1 and R2,")
step("Configure IBGP IPv4 peering between R2 and R3 router.")
- reset_config_on_routers(tgen)
NEXT_HOP_IP = populate_nh()
nh_all = {}
for addr_type in ADDR_TYPES:
diff --git a/tools/coccinelle/argv_find.cocci b/tools/coccinelle/argv_find.cocci
index f13b035d7a..1ab19b749b 100644
--- a/tools/coccinelle/argv_find.cocci
+++ b/tools/coccinelle/argv_find.cocci
@@ -4,8 +4,10 @@ identifier argv;
identifier argc;
expression e1;
expression e2;
+identifier I;
@@
+(
- argv_find(argv, argc, e1, &idx);
if (
- idx
@@ -14,3 +16,8 @@ expression e2;
{
e2;
}
+|
+- argv_find(argv, argc, e1, &idx);
+... when != I = idx;
+ when strict
+)
diff --git a/tools/coccinelle/memset.cocci b/tools/coccinelle/memset.cocci
new file mode 100644
index 0000000000..0da576cda6
--- /dev/null
+++ b/tools/coccinelle/memset.cocci
@@ -0,0 +1,21 @@
+//
+
+@@
+identifier src, dst;
+identifier str, len;
+type t =~ "struct";
+
+@@
+
+(
+- memset(&dst, 0, sizeof(t));
++ memset(&dst, 0, sizeof(dst));
+|
+- memcpy(&dst, &src, sizeof(t));
++ memcpy(&dst, &src, sizeof(dst));
+|
+- char str[...];
+...
+- memset(&str, 0, ...);
++ memset(&str, 0, sizeof(str));
+)
diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf
index 46e0625d8c..ff2c633ccc 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -160,6 +160,7 @@ show ip igmp groups
show ip igmp interface
show ip igmp join
show ip igmp sources
+show ip igmp statistics
show ip pim upstream
show ip mroute
show ip pim join
diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c
index fbcf9222d2..8ce754fecc 100644
--- a/tools/frr-llvm-cg.c
+++ b/tools/frr-llvm-cg.c
@@ -561,7 +561,6 @@ static void process_call(struct json_object *js_calls,
unsigned n_args = LLVMGetNumArgOperands(instr);
bool is_external = LLVMIsDeclaration(called);
- enum called_fn called_type = FN_GENERIC;
js_call = json_object_new_object();
json_object_array_add(js_calls, js_call);
@@ -570,7 +569,6 @@ static void process_call(struct json_object *js_calls,
json_object_new_boolean(is_external));
if (!called_name || called_len == 0) {
- called_type = FN_NONAME;
json_object_object_add(js_call, "type",
json_object_new_string("indirect"));
@@ -653,8 +651,6 @@ static void process_call(struct json_object *js_calls,
}
#ifdef FRR_SPECIFIC
} else if (!strcmp(called_name, "_install_element")) {
- called_type = FN_INSTALL_ELEMENT;
-
LLVMValueRef param0 = LLVMGetOperand(instr, 0);
if (!LLVMIsAConstantInt(param0))
goto out_nonconst;
@@ -694,8 +690,6 @@ static void process_call(struct json_object *js_calls,
json_object_new_string("install_element"));
return;
} else if (is_thread_sched(called_name, called_len)) {
- called_type = FN_THREAD_ADD;
-
json_object_object_add(js_call, "type",
json_object_new_string("thread_sched"));
json_object_object_add(
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index f47f9a7eb0..743d32d5e2 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -1894,6 +1894,7 @@ if __name__ == "__main__":
"ldpd",
"pathd",
"bfdd",
+ "eigrpd",
]:
msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon
print(msg)
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 4683ceef14..e5286d14f6 100644..100755
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -194,10 +194,14 @@ daemon_stop() {
is_user_root || exit 1
+ all=false
+ [ "$2" = "--reallyall" ] && all=true
+
pidfile="$V_PATH/$daemon${inst:+-$inst}.pid"
vtyfile="$V_PATH/$daemon${inst:+-$inst}.vty"
[ -r "$pidfile" ] || fail="pid file not found"
+ $all && [ -n "$fail" ] && return 0
[ -z "$fail" ] && pid="`cat \"$pidfile\"`"
[ -z "$fail" -a -z "$pid" ] && fail="pid file is empty"
[ -n "$fail" ] || kill -0 "$pid" 2>/dev/null || fail="pid $pid not running"
diff --git a/tools/releasedate.py b/tools/releasedate.py
index 3df1ea48fb..3df1ea48fb 100644..100755
--- a/tools/releasedate.py
+++ b/tools/releasedate.py
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 74886254bd..828d2d73b0 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -4119,7 +4119,7 @@ static int vtysh_connect(struct vtysh_client *vclient)
return -1;
}
- memset(&addr, 0, sizeof(struct sockaddr_un));
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c
index 51e4f802c9..fc285c748a 100644
--- a/watchfrr/watchfrr.c
+++ b/watchfrr/watchfrr.c
@@ -798,7 +798,7 @@ static int try_connect(struct daemon *dmn)
zlog_debug("%s: attempting to connect", dmn->name);
dmn->connect_tries++;
- memset(&addr, 0, sizeof(struct sockaddr_un));
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s.vty", gs.vtydir,
dmn->name);
diff --git a/yang/frr-gmp.yang b/yang/frr-gmp.yang
index f48fcd45f1..298b2aafda 100644
--- a/yang/frr-gmp.yang
+++ b/yang/frr-gmp.yang
@@ -96,6 +96,7 @@ module frr-gmp {
type uint8 {
range "1..2";
}
+ default "2";
description
"MLD version.";
}
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index defb2b2038..0812c86fac 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -733,11 +733,11 @@ module frr-isisd {
container multi-topology {
description
"IS-IS topologies configured on this circuit.";
- leaf ipv4-unicast {
+ leaf standard {
type boolean;
default "true";
description
- "IPv4 unicast topology.";
+ "Standard (IPV4 unicast) topology.";
}
leaf ipv4-multicast {
diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang
index 08bc9ce0a6..9e40d78602 100644
--- a/yang/frr-pim.yang
+++ b/yang/frr-pim.yang
@@ -334,6 +334,13 @@ module frr-pim {
"Enable PIM flag on the interface.";
}
+ leaf pim-passive-enable {
+ type boolean;
+ default "false";
+ description
+ "Disable exchange of protocol packets.";
+ }
+
leaf hello-interval {
type uint8 {
range "1..max";
diff --git a/zebra/connected.c b/zebra/connected.c
index eb2720335e..c01be58e82 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -512,14 +512,14 @@ void connected_delete_ipv4(struct interface *ifp, int flags,
struct prefix p, d;
struct connected *ifc;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET;
p.u.prefix4 = *addr;
p.prefixlen =
CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_BITLEN : prefixlen;
if (dest) {
- memset(&d, 0, sizeof(struct prefix));
+ memset(&d, 0, sizeof(d));
d.family = AF_INET;
d.u.prefix4 = *dest;
d.prefixlen = prefixlen;
@@ -603,7 +603,7 @@ void connected_delete_ipv6(struct interface *ifp,
struct prefix p, d;
struct connected *ifc;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET6;
memcpy(&p.u.prefix6, address, sizeof(struct in6_addr));
p.prefixlen = prefixlen;
@@ -613,7 +613,7 @@ void connected_delete_ipv6(struct interface *ifp,
rtadv_delete_prefix(ifp->info, &p);
if (dest) {
- memset(&d, 0, sizeof(struct prefix));
+ memset(&d, 0, sizeof(d));
d.family = AF_INET6;
IPV6_ADDR_COPY(&d.u.prefix6, dest);
d.prefixlen = prefixlen;
diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c
index b7d12bf537..89ef7a2076 100644
--- a/zebra/debug_nl.c
+++ b/zebra/debug_nl.c
@@ -26,12 +26,14 @@
#include <linux/rtnetlink.h>
#include <net/if_arp.h>
#include <linux/fib_rules.h>
+#include <linux/lwtunnel.h>
#include <stdio.h>
#include <stdint.h>
#include "zebra/rt_netlink.h"
#include "zebra/kernel_netlink.h"
+#include "lib/vxlan.h"
const char *nlmsg_type2str(uint16_t type)
{
@@ -91,6 +93,13 @@ const char *nlmsg_type2str(uint16_t type)
case RTM_GETNEXTHOP:
return "GETNEXTHOP";
+ case RTM_NEWTUNNEL:
+ return "NEWTUNNEL";
+ case RTM_DELTUNNEL:
+ return "DELTUNNEL";
+ case RTM_GETTUNNEL:
+ return "GETTUNNEL";
+
case RTM_NEWNETCONF:
return "RTM_NEWNETCONF";
case RTM_DELNETCONF:
@@ -452,6 +461,12 @@ const char *rtm_protocol2str(int type)
return "MRT";
case RTPROT_ZEBRA:
return "ZEBRA";
+ case RTPROT_BGP:
+ return "BGP";
+ case RTPROT_ISIS:
+ return "ISIS";
+ case RTPROT_OSPF:
+ return "OSPF";
case RTPROT_BIRD:
return "BIRD";
case RTPROT_DNROUTED:
@@ -1234,6 +1249,80 @@ next_rta:
goto next_rta;
}
+static void nltnl_dump(struct tunnel_msg *tnlm, size_t msglen)
+{
+ struct rtattr *attr;
+ vni_t vni_start = 0, vni_end = 0;
+ struct rtattr *ttb[VXLAN_VNIFILTER_ENTRY_MAX + 1];
+ uint8_t rta_type;
+
+ attr = TUNNEL_RTA(tnlm);
+next_attr:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(attr, msglen) == 0)
+ return;
+
+ rta_type = attr->rta_type & NLA_TYPE_MASK;
+
+ if (rta_type != VXLAN_VNIFILTER_ENTRY) {
+ attr = RTA_NEXT(attr, msglen);
+ goto next_attr;
+ }
+
+ memset(ttb, 0, sizeof(ttb));
+
+ netlink_parse_rtattr_flags(ttb, VXLAN_VNIFILTER_ENTRY_MAX,
+ RTA_DATA(attr), RTA_PAYLOAD(attr),
+ NLA_F_NESTED);
+
+ if (ttb[VXLAN_VNIFILTER_ENTRY_START])
+ vni_start =
+ *(uint32_t *)RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_START]);
+
+ if (ttb[VXLAN_VNIFILTER_ENTRY_END])
+ vni_end = *(uint32_t *)RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_END]);
+ zlog_debug(" vni_start %u, vni_end %u", vni_start, vni_end);
+
+ attr = RTA_NEXT(attr, msglen);
+ goto next_attr;
+}
+
+static const char *lwt_type2str(uint16_t type)
+{
+ switch (type) {
+ case LWTUNNEL_ENCAP_NONE:
+ return "NONE";
+ case LWTUNNEL_ENCAP_MPLS:
+ return "MPLS";
+ case LWTUNNEL_ENCAP_IP:
+ return "IPv4";
+ case LWTUNNEL_ENCAP_ILA:
+ return "ILA";
+ case LWTUNNEL_ENCAP_IP6:
+ return "IPv6";
+ case LWTUNNEL_ENCAP_SEG6:
+ return "SEG6";
+ case LWTUNNEL_ENCAP_BPF:
+ return "BPF";
+ case LWTUNNEL_ENCAP_SEG6_LOCAL:
+ return "SEG6_LOCAL";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char *nhg_type2str(uint16_t type)
+{
+ switch (type) {
+ case NEXTHOP_GRP_TYPE_MPATH:
+ return "MULTIPATH";
+ case NEXTHOP_GRP_TYPE_RES:
+ return "RESILIENT MULTIPATH";
+ default:
+ return "UNKNOWN";
+ }
+}
+
static void nlnh_dump(struct nhmsg *nhm, size_t msglen)
{
struct rtattr *rta;
@@ -1275,9 +1364,12 @@ next_rta:
nhgrp[i].weight);
break;
case NHA_ENCAP_TYPE:
+ u16v = *(uint16_t *)RTA_DATA(rta);
+ zlog_debug(" %s", lwt_type2str(u16v));
+ break;
case NHA_GROUP_TYPE:
u16v = *(uint16_t *)RTA_DATA(rta);
- zlog_debug(" %d", u16v);
+ zlog_debug(" %s", nhg_type2str(u16v));
break;
case NHA_BLACKHOLE:
/* NOTHING */
@@ -1483,6 +1575,7 @@ void nl_dump(void *msg, size_t msglen)
struct nhmsg *nhm;
struct netconfmsg *ncm;
struct ifinfomsg *ifi;
+ struct tunnel_msg *tnlm;
struct fib_rule_hdr *frh;
char fbuf[128];
char ibuf[128];
@@ -1599,6 +1692,18 @@ next_header:
nlnh_dump(nhm, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*nhm)));
break;
+ case RTM_NEWTUNNEL:
+ case RTM_DELTUNNEL:
+ case RTM_GETTUNNEL:
+ tnlm = NLMSG_DATA(nlmsg);
+ zlog_debug(" tnlm [family=(%d) %s ifindex=%d ", tnlm->family,
+ af_type2str(tnlm->family), tnlm->ifindex);
+ nltnl_dump(tnlm,
+ nlmsg->nlmsg_len -
+ NLMSG_LENGTH(sizeof(struct tunnel_msg)));
+ break;
+
+
case RTM_NEWNETCONF:
case RTM_DELNETCONF:
ncm = NLMSG_DATA(nlmsg);
diff --git a/zebra/interface.c b/zebra/interface.c
index 677ec4650f..7eb98faeb1 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -155,48 +155,8 @@ static int if_zebra_new_hook(struct interface *ifp)
zebra_ptm_if_init(zebra_if);
ifp->ptm_enable = zebra_ptm_get_enable_state();
-#if defined(HAVE_RTADV)
- {
- /* Set default router advertise values. */
- struct rtadvconf *rtadv;
-
- rtadv = &zebra_if->rtadv;
-
- rtadv->AdvSendAdvertisements = 0;
- rtadv->MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
- rtadv->MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
- rtadv->AdvIntervalTimer = 0;
- rtadv->AdvManagedFlag = 0;
- rtadv->AdvOtherConfigFlag = 0;
- rtadv->AdvHomeAgentFlag = 0;
- rtadv->AdvLinkMTU = 0;
- rtadv->AdvReachableTime = 0;
- rtadv->AdvRetransTimer = 0;
- rtadv->AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT;
- memset(&rtadv->lastadvcurhoplimit, 0,
- sizeof(rtadv->lastadvcurhoplimit));
- memset(&rtadv->lastadvmanagedflag, 0,
- sizeof(rtadv->lastadvmanagedflag));
- memset(&rtadv->lastadvotherconfigflag, 0,
- sizeof(rtadv->lastadvotherconfigflag));
- memset(&rtadv->lastadvreachabletime, 0,
- sizeof(rtadv->lastadvreachabletime));
- memset(&rtadv->lastadvretranstimer, 0,
- sizeof(rtadv->lastadvretranstimer));
- rtadv->AdvDefaultLifetime =
- -1; /* derive from MaxRtrAdvInterval */
- rtadv->HomeAgentPreference = 0;
- rtadv->HomeAgentLifetime =
- -1; /* derive from AdvDefaultLifetime */
- rtadv->AdvIntervalOption = 0;
- rtadv->UseFastRexmit = true;
- rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
-
- rtadv->AdvPrefixList = list_new();
- rtadv->AdvRDNSSList = list_new();
- rtadv->AdvDNSSLList = list_new();
- }
-#endif /* HAVE_RTADV */
+
+ rtadv_if_init(zebra_if);
memset(&zebra_if->neigh_mac[0], 0, 6);
@@ -271,15 +231,8 @@ static int if_zebra_delete_hook(struct interface *ifp)
/* Free installed address chains tree. */
if (zebra_if->ipv4_subnets)
route_table_finish(zebra_if->ipv4_subnets);
-#if defined(HAVE_RTADV)
-
- struct rtadvconf *rtadv;
- rtadv = &zebra_if->rtadv;
- list_delete(&rtadv->AdvPrefixList);
- list_delete(&rtadv->AdvRDNSSList);
- list_delete(&rtadv->AdvDNSSLList);
-#endif /* HAVE_RTADV */
+ rtadv_if_fini(zebra_if);
zebra_evpn_if_cleanup(zebra_if);
zebra_evpn_mac_ifp_del(ifp);
@@ -1078,15 +1031,7 @@ void if_up(struct interface *ifp, bool install_connected)
if_nbr_ipv6ll_to_ipv4ll_neigh_add_all(ifp);
-#if defined(HAVE_RTADV)
- /* Enable fast tx of RA if enabled && RA interval is not in msecs */
- if (zif->rtadv.AdvSendAdvertisements
- && (zif->rtadv.MaxRtrAdvInterval >= 1000)
- && zif->rtadv.UseFastRexmit) {
- zif->rtadv.inFastRexmit = 1;
- zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS;
- }
-#endif
+ rtadv_if_up(zif);
/* Install connected routes to the kernel. */
if (install_connected)
@@ -3557,12 +3502,20 @@ DEFUN (link_params_delay,
uint8_t update = 0;
if (argc == 2) {
- /* Check new delay value against old Min and Max delays if set
+ /*
+ * Check new delay value against old Min and Max delays if set
+ *
+ * RFC 7471 Section 4.2.7:
+ * It is possible for min delay and max delay to be
+ * the same value.
+ *
+ * Therefore, it is also allowed that the average
+ * delay be equal to the min delay or max delay.
*/
if (IS_PARAM_SET(iflp, LP_MM_DELAY)
- && (delay <= iflp->min_delay || delay >= iflp->max_delay)) {
+ && (delay < iflp->min_delay || delay > iflp->max_delay)) {
vty_out(vty,
- "Average delay should be comprise between Min (%d) and Max (%d) delay\n",
+ "Average delay should be in range Min (%d) - Max (%d) delay\n",
iflp->min_delay, iflp->max_delay);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -3580,10 +3533,13 @@ DEFUN (link_params_delay,
update = 1;
}
} else {
- /* Check new delays value coherency */
- if (delay <= low || delay >= high) {
+ /*
+ * Check new delays value coherency. See above note
+ * regarding average delay equal to min/max allowed
+ */
+ if (delay < low || delay > high) {
vty_out(vty,
- "Average delay should be comprise between Min (%d) and Max (%d) delay\n",
+ "Average delay should be in range Min (%d) - Max (%d) delay\n",
low, high);
return CMD_WARNING_CONFIG_FAILED;
}
diff --git a/zebra/interface.h b/zebra/interface.h
index c6930ce816..5569711aa7 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -30,6 +30,7 @@
#include "zebra/zebra_l2.h"
#include "zebra/zebra_nhg_private.h"
#include "zebra/zebra_router.h"
+#include "zebra/rtadv.h"
#ifdef __cplusplus
extern "C" {
@@ -46,218 +47,6 @@ extern "C" {
#define IF_VLAN_BITMAP_MAX 4096
-#if defined(HAVE_RTADV)
-/* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */
-struct rtadvconf {
- /* A flag indicating whether or not the router sends periodic Router
- Advertisements and responds to Router Solicitations.
- Default: false */
- int AdvSendAdvertisements;
-
- /* The maximum time allowed between sending unsolicited multicast
- Router Advertisements from the interface, in milliseconds.
- MUST be no less than 70 ms [RFC6275 7.5] and no greater
- than 1800000 ms [RFC4861 6.2.1].
-
- Default: 600000 milliseconds */
- int MaxRtrAdvInterval;
-#define RTADV_MAX_RTR_ADV_INTERVAL 600000
-
- /* The minimum time allowed between sending unsolicited multicast
- Router Advertisements from the interface, in milliseconds.
- MUST be no less than 30 ms [RFC6275 7.5].
- MUST be no greater than .75 * MaxRtrAdvInterval.
-
- Default: 0.33 * MaxRtrAdvInterval */
- int MinRtrAdvInterval; /* This field is currently unused. */
-#define RTADV_MIN_RTR_ADV_INTERVAL (0.33 * RTADV_MAX_RTR_ADV_INTERVAL)
-
- /* Unsolicited Router Advertisements' interval timer. */
- int AdvIntervalTimer;
-
- /* The true/false value to be placed in the "Managed address
- configuration" flag field in the Router Advertisement. See
- [ADDRCONF].
-
- Default: false */
- int AdvManagedFlag;
- struct timeval lastadvmanagedflag;
-
-
- /* The true/false value to be placed in the "Other stateful
- configuration" flag field in the Router Advertisement. See
- [ADDRCONF].
-
- Default: false */
- int AdvOtherConfigFlag;
- struct timeval lastadvotherconfigflag;
-
- /* The value to be placed in MTU options sent by the router. A
- value of zero indicates that no MTU options are sent.
-
- Default: 0 */
- int AdvLinkMTU;
-
-
- /* The value to be placed in the Reachable Time field in the Router
- Advertisement messages sent by the router. The value zero means
- unspecified (by this router). MUST be no greater than 3,600,000
- milliseconds (1 hour).
-
- Default: 0 */
- uint32_t AdvReachableTime;
-#define RTADV_MAX_REACHABLE_TIME 3600000
- struct timeval lastadvreachabletime;
-
- /* The value to be placed in the Retrans Timer field in the Router
- Advertisement messages sent by the router. The value zero means
- unspecified (by this router).
-
- Default: 0 */
- int AdvRetransTimer;
- struct timeval lastadvretranstimer;
-
- /* The default value to be placed in the Cur Hop Limit field in the
- Router Advertisement messages sent by the router. The value
- should be set to that current diameter of the Internet. The
- value zero means unspecified (by this router).
-
- Default: The value specified in the "Assigned Numbers" RFC
- [ASSIGNED] that was in effect at the time of implementation. */
- int AdvCurHopLimit;
- struct timeval lastadvcurhoplimit;
-
-#define RTADV_DEFAULT_HOPLIMIT 64 /* 64 hops */
-
- /* The value to be placed in the Router Lifetime field of Router
- Advertisements sent from the interface, in seconds. MUST be
- either zero or between MaxRtrAdvInterval and 9000 seconds. A
- value of zero indicates that the router is not to be used as a
- default router.
-
- Default: 3 * MaxRtrAdvInterval */
- int AdvDefaultLifetime;
-#define RTADV_MAX_RTRLIFETIME 9000 /* 2.5 hours */
-
- /* A list of prefixes to be placed in Prefix Information options in
- Router Advertisement messages sent from the interface.
-
- Default: all prefixes that the router advertises via routing
- protocols as being on-link for the interface from which the
- advertisement is sent. The link-local prefix SHOULD NOT be
- included in the list of advertised prefixes. */
- struct list *AdvPrefixList;
-
- /* The true/false value to be placed in the "Home agent"
- flag field in the Router Advertisement. See [RFC6275 7.1].
-
- Default: false */
- int AdvHomeAgentFlag;
-#ifndef ND_RA_FLAG_HOME_AGENT
-#define ND_RA_FLAG_HOME_AGENT 0x20
-#endif
-
- /* The value to be placed in Home Agent Information option if Home
- Flag is set.
- Default: 0 */
- int HomeAgentPreference;
-
- /* The value to be placed in Home Agent Information option if Home
- Flag is set. Lifetime (seconds) MUST not be greater than 18.2
- hours.
- The value 0 has special meaning: use of AdvDefaultLifetime value.
-
- Default: 0 */
- int HomeAgentLifetime;
-#define RTADV_MAX_HALIFETIME 65520 /* 18.2 hours */
-
- /* The true/false value to insert or not an Advertisement Interval
- option. See [RFC 6275 7.3]
-
- Default: false */
- int AdvIntervalOption;
-
- /* The value to be placed in the Default Router Preference field of
- a router advertisement. See [RFC 4191 2.1 & 2.2]
-
- Default: 0 (medium) */
- int DefaultPreference;
-#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
-
- /*
- * List of recursive DNS servers to include in the RDNSS option.
- * See [RFC8106 5.1]
- *
- * Default: empty list; do not emit RDNSS option
- */
- struct list *AdvRDNSSList;
-
- /*
- * List of DNS search domains to include in the DNSSL option.
- * See [RFC8106 5.2]
- *
- * Default: empty list; do not emit DNSSL option
- */
- struct list *AdvDNSSLList;
-
- /*
- * rfc4861 states RAs must be sent at least 3 seconds apart.
- * We allow faster retransmits to speed up convergence but can
- * turn that capability off to meet the rfc if needed.
- */
- bool UseFastRexmit; /* True if fast rexmits are enabled */
-
- uint8_t inFastRexmit; /* True if we're rexmits faster than usual */
-
- /* Track if RA was configured by BGP or by the Operator or both */
- uint8_t ra_configured; /* Was RA configured? */
-#define BGP_RA_CONFIGURED (1<<0) /* BGP configured RA? */
-#define VTY_RA_CONFIGURED (1<<1) /* Operator configured RA? */
-#define VTY_RA_INTERVAL_CONFIGURED (1<<2) /* Operator configured RA interval */
- int NumFastReXmitsRemain; /* Loaded first with number of fast
- rexmits to do */
-
-#define RTADV_FAST_REXMIT_PERIOD 1 /* 1 sec */
-#define RTADV_NUM_FAST_REXMITS 4 /* Fast Rexmit RA 4 times on certain events */
-};
-
-struct rtadv_rdnss {
- /* Address of recursive DNS server to advertise */
- struct in6_addr addr;
-
- /*
- * Lifetime in seconds; all-ones means infinity, zero
- * stop using it.
- */
- uint32_t lifetime;
-
- /* If lifetime not set, use a default of 3*MaxRtrAdvInterval */
- int lifetime_set;
-};
-
-/*
- * [RFC1035 2.3.4] sets the maximum length of a domain name (a sequence of
- * labels, each prefixed by a length octet) at 255 octets.
- */
-#define RTADV_MAX_ENCODED_DOMAIN_NAME 255
-
-struct rtadv_dnssl {
- /* Domain name without trailing root zone dot (NUL-terminated) */
- char name[RTADV_MAX_ENCODED_DOMAIN_NAME - 1];
-
- /* Name encoded as in [RFC1035 3.1] */
- uint8_t encoded_name[RTADV_MAX_ENCODED_DOMAIN_NAME];
-
- /* Actual length of encoded_name */
- size_t encoded_len;
-
- /* Lifetime as for RDNSS */
- uint32_t lifetime;
- int lifetime_set;
-};
-
-#endif /* HAVE_RTADV */
-
/* Zebra interface type - ones of interest. */
enum zebra_iftype {
ZEBRA_IF_OTHER = 0, /* Anything else */
@@ -361,10 +150,8 @@ struct zebra_if {
unsigned int down_count;
char down_last[FRR_TIMESTAMP_LEN];
-#if defined(HAVE_RTADV)
struct rtadvconf rtadv;
unsigned int ra_sent, ra_rcvd;
-#endif /* HAVE_RTADV */
struct irdp_interface *irdp;
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index a895ed9410..239763754b 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.c
@@ -48,6 +48,7 @@ void ifreq_set_name(struct ifreq *ifreq, struct interface *ifp)
strlcpy(ifreq->ifr_name, ifp->name, sizeof(ifreq->ifr_name));
}
+#ifndef HAVE_NETLINK
/* call ioctl system call */
int if_ioctl(unsigned long request, caddr_t buffer)
{
@@ -73,6 +74,7 @@ int if_ioctl(unsigned long request, caddr_t buffer)
}
return 0;
}
+#endif
/* call ioctl system call */
int vrf_if_ioctl(unsigned long request, caddr_t buffer, vrf_id_t vrf_id)
@@ -127,7 +129,6 @@ static int if_ioctl_ipv6(unsigned long request, caddr_t buffer)
}
return 0;
}
-#endif /* ! HAVE_NETLINK */
/*
* get interface metric
@@ -174,6 +175,7 @@ void if_get_mtu(struct interface *ifp)
ifp->mtu6 = ifp->mtu = -1;
#endif
}
+#endif /* ! HAVE_NETLINK */
/*
* Handler for interface address programming via the zebra dplane,
@@ -217,13 +219,6 @@ enum zebra_dplane_result kernel_address_update_ctx(
ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
}
-#endif /* !HAVE_NETLINK */
-
-#ifdef HAVE_NETLINK
-
-/* TODO -- remove; no use of these apis with netlink any longer */
-
-#else /* ! HAVE_NETLINK */
#ifdef HAVE_STRUCT_IFALIASREQ
/*
@@ -242,7 +237,7 @@ static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx)
strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
sizeof(addreq.ifra_name));
- memset(&addr, 0, sizeof(struct sockaddr_in));
+ memset(&addr, 0, sizeof(addr));
addr.sin_addr = p->prefix;
addr.sin_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -252,7 +247,7 @@ static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx)
if (dplane_ctx_intf_is_connected(ctx)) {
p = (struct prefix_ipv4 *)dplane_ctx_get_intf_dest(ctx);
- memset(&mask, 0, sizeof(struct sockaddr_in));
+ memset(&mask, 0, sizeof(mask));
peer.sin_addr = p->prefix;
peer.sin_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -262,7 +257,7 @@ static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx)
sizeof(struct sockaddr_in));
}
- memset(&mask, 0, sizeof(struct sockaddr_in));
+ memset(&mask, 0, sizeof(mask));
masklen2ip(p->prefixlen, &mask.sin_addr);
mask.sin_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -293,7 +288,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx)
strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
sizeof(addreq.ifra_name));
- memset(&addr, 0, sizeof(struct sockaddr_in));
+ memset(&addr, 0, sizeof(addr));
addr.sin_addr = p->prefix;
addr.sin_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -303,7 +298,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx)
if (dplane_ctx_intf_is_connected(ctx)) {
p = (struct prefix_ipv4 *)dplane_ctx_get_intf_dest(ctx);
- memset(&mask, 0, sizeof(struct sockaddr_in));
+ memset(&mask, 0, sizeof(mask));
peer.sin_addr = p->prefix;
peer.sin_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -313,7 +308,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx)
sizeof(struct sockaddr_in));
}
- memset(&mask, 0, sizeof(struct sockaddr_in));
+ memset(&mask, 0, sizeof(mask));
masklen2ip(p->prefixlen, &mask.sin_addr);
mask.sin_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -394,7 +389,7 @@ int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx)
strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx),
sizeof(ifreq.ifr_name));
- memset(&addr, 0, sizeof(struct sockaddr_in));
+ memset(&addr, 0, sizeof(addr));
addr.sin_family = p->family;
memcpy(&ifreq.ifr_addr, &addr, sizeof(struct sockaddr_in));
ret = if_ioctl(SIOCSIFADDR, (caddr_t)&ifreq);
@@ -508,7 +503,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags)
int ret;
struct ifreq ifreq;
- memset(&ifreq, 0, sizeof(struct ifreq));
+ memset(&ifreq, 0, sizeof(ifreq));
ifreq_set_name(&ifreq, ifp);
ifreq.ifr_flags = ifp->flags;
@@ -529,7 +524,7 @@ int if_unset_flags(struct interface *ifp, uint64_t flags)
int ret;
struct ifreq ifreq;
- memset(&ifreq, 0, sizeof(struct ifreq));
+ memset(&ifreq, 0, sizeof(ifreq));
ifreq_set_name(&ifreq, ifp);
ifreq.ifr_flags = ifp->flags;
@@ -568,7 +563,7 @@ static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
strlcpy((char *)&addreq.ifra_name,
dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name));
- memset(&addr, 0, sizeof(struct sockaddr_in6));
+ memset(&addr, 0, sizeof(addr));
addr.sin6_addr = p->prefix;
addr.sin6_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -576,7 +571,7 @@ static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
#endif
memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in6));
- memset(&mask, 0, sizeof(struct sockaddr_in6));
+ memset(&mask, 0, sizeof(mask));
masklen2ip6(p->prefixlen, &mask.sin6_addr);
mask.sin6_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -615,7 +610,7 @@ static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
strlcpy((char *)&addreq.ifra_name,
dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name));
- memset(&addr, 0, sizeof(struct sockaddr_in6));
+ memset(&addr, 0, sizeof(addr));
addr.sin6_addr = p->prefix;
addr.sin6_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
@@ -623,7 +618,7 @@ static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
#endif
memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in6));
- memset(&mask, 0, sizeof(struct sockaddr_in6));
+ memset(&mask, 0, sizeof(mask));
masklen2ip6(p->prefixlen, &mask.sin6_addr);
mask.sin6_family = p->family;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 35f3274c65..7e47822a2c 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -289,7 +289,7 @@ static int netlink_recvbuf(struct nlsock *nl, uint32_t newsize)
/* Make socket for Linux netlink interface. */
static int netlink_socket(struct nlsock *nl, unsigned long groups,
- ns_id_t ns_id)
+ unsigned long ext_groups, ns_id_t ns_id)
{
int ret;
struct sockaddr_nl snl;
@@ -308,6 +308,19 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
snl.nl_family = AF_NETLINK;
snl.nl_groups = groups;
+#if defined SOL_NETLINK
+ if (ext_groups) {
+ ret = setsockopt(sock, SOL_NETLINK,
+ NETLINK_ADD_MEMBERSHIP, &ext_groups,
+ sizeof(ext_groups));
+ if (ret < 0) {
+ zlog_notice(
+ "can't setsockopt NETLINK_ADD_MEMBERSHIP: %s(%d)",
+ safe_strerror(errno), errno);
+ }
+ }
+#endif
+
/* Bind the socket to the netlink structure for anything. */
ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl));
}
@@ -587,6 +600,21 @@ void netlink_parse_rtattr_nested(struct rtattr **tb, int max,
netlink_parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta));
}
+bool nl_addraw_l(struct nlmsghdr *n, unsigned int maxlen, const void *data,
+ unsigned int len)
+{
+ if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) {
+ zlog_err("ERROR message exceeded bound of %d", maxlen);
+ return false;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((uint8_t *)NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+
+ return true;
+}
+
bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type,
const void *data, unsigned int alen)
{
@@ -667,6 +695,58 @@ void nl_attr_rtnh_end(struct nlmsghdr *n, struct rtnexthop *rtnh)
rtnh->rtnh_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)rtnh;
}
+bool nl_rta_put(struct rtattr *rta, unsigned int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ zlog_err("ERROR max allowed bound %d exceeded for rtattr",
+ maxlen);
+ return false;
+ }
+ subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ if (alen)
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+
+ return true;
+}
+
+bool nl_rta_put16(struct rtattr *rta, unsigned int maxlen, int type,
+ uint16_t data)
+{
+ return nl_rta_put(rta, maxlen, type, &data, sizeof(uint16_t));
+}
+
+bool nl_rta_put64(struct rtattr *rta, unsigned int maxlen, int type,
+ uint64_t data)
+{
+ return nl_rta_put(rta, maxlen, type, &data, sizeof(uint64_t));
+}
+
+struct rtattr *nl_rta_nest(struct rtattr *rta, unsigned int maxlen, int type)
+{
+ struct rtattr *nest = RTA_TAIL(rta);
+
+ if (nl_rta_put(rta, maxlen, type, NULL, 0))
+ return NULL;
+
+ nest->rta_type |= NLA_F_NESTED;
+
+ return nest;
+}
+
+int nl_rta_nest_end(struct rtattr *rta, struct rtattr *nest)
+{
+ nest->rta_len = (uint8_t *)RTA_TAIL(rta) - (uint8_t *)nest;
+
+ return rta->rta_len;
+}
+
const char *nl_msg_type_to_str(uint16_t msg_type)
{
return lookup_msg(nlmsg_str, msg_type, "");
@@ -1611,7 +1691,7 @@ static bool kernel_netlink_nlsock_hash_equal(const void *arg1, const void *arg2)
netlink_socket (). */
void kernel_init(struct zebra_ns *zns)
{
- uint32_t groups, dplane_groups;
+ uint32_t groups, dplane_groups, ext_groups;
#if defined SOL_NETLINK
int one, ret;
#endif
@@ -1643,11 +1723,13 @@ void kernel_init(struct zebra_ns *zns)
((uint32_t) 1 << (RTNLGRP_IPV6_NETCONF - 1)) |
((uint32_t) 1 << (RTNLGRP_MPLS_NETCONF - 1)));
+ /* Use setsockopt for > 31 group */
+ ext_groups = RTNLGRP_TUNNEL;
snprintf(zns->netlink.name, sizeof(zns->netlink.name),
"netlink-listen (NS %u)", zns->ns_id);
zns->netlink.sock = -1;
- if (netlink_socket(&zns->netlink, groups, zns->ns_id) < 0) {
+ if (netlink_socket(&zns->netlink, groups, ext_groups, zns->ns_id) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink.name);
exit(-1);
@@ -1658,7 +1740,7 @@ void kernel_init(struct zebra_ns *zns)
snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name),
"netlink-cmd (NS %u)", zns->ns_id);
zns->netlink_cmd.sock = -1;
- if (netlink_socket(&zns->netlink_cmd, 0, zns->ns_id) < 0) {
+ if (netlink_socket(&zns->netlink_cmd, 0, 0, zns->ns_id) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink_cmd.name);
exit(-1);
@@ -1671,7 +1753,7 @@ void kernel_init(struct zebra_ns *zns)
sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)",
zns->ns_id);
zns->netlink_dplane_out.sock = -1;
- if (netlink_socket(&zns->netlink_dplane_out, 0, zns->ns_id) < 0) {
+ if (netlink_socket(&zns->netlink_dplane_out, 0, 0, zns->ns_id) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink_dplane_out.name);
exit(-1);
@@ -1684,7 +1766,7 @@ void kernel_init(struct zebra_ns *zns)
sizeof(zns->netlink_dplane_in.name), "netlink-dp-in (NS %u)",
zns->ns_id);
zns->netlink_dplane_in.sock = -1;
- if (netlink_socket(&zns->netlink_dplane_in, dplane_groups,
+ if (netlink_socket(&zns->netlink_dplane_in, dplane_groups, 0,
zns->ns_id) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink_dplane_in.name);
diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h
index 9421ea1c61..08cd706a9f 100644
--- a/zebra/kernel_netlink.h
+++ b/zebra/kernel_netlink.h
@@ -90,6 +90,43 @@ extern void netlink_parse_rtattr_flags(struct rtattr **tb, int max,
unsigned short flags);
extern void netlink_parse_rtattr_nested(struct rtattr **tb, int max,
struct rtattr *rta);
+/*
+ * nl_addraw_l copies raw form the netlink message buffer into netlink
+ * message header pointer. It ensures the aligned data buffer does not
+ * override past max length.
+ * return value is 0 if its successful
+ */
+extern bool nl_addraw_l(struct nlmsghdr *n, unsigned int maxlen,
+ const void *data, unsigned int len);
+/*
+ * nl_rta_put - add an additional optional attribute(rtattr) to the
+ * Netlink message buffer.
+ *
+ * Returns true if the attribute could be added to the message (fits into the
+ * buffer), otherwise false is returned.
+ */
+extern bool nl_rta_put(struct rtattr *rta, unsigned int maxlen, int type,
+ const void *data, int alen);
+extern bool nl_rta_put16(struct rtattr *rta, unsigned int maxlen, int type,
+ uint16_t data);
+extern bool nl_rta_put64(struct rtattr *rta, unsigned int maxlen, int type,
+ uint64_t data);
+/*
+ * nl_rta_nest - start an additional optional attribute (rtattr) nest.
+ *
+ * Returns a valid pointer to the beginning of the nest if the attribute
+ * describing the nest could be added to the message (fits into the buffer),
+ * otherwise NULL is returned.
+ */
+extern struct rtattr *nl_rta_nest(struct rtattr *rta, unsigned int maxlen,
+ int type);
+/*
+ * nl_rta_nest_end - finalize nesting of an aditionl optionl attributes.
+ *
+ * Updates the length field of the attribute header to include the appeneded
+ * attributes. Returns a total length of the Netlink message.
+ */
+extern int nl_rta_nest_end(struct rtattr *rta, struct rtattr *nest);
extern const char *nl_msg_type_to_str(uint16_t msg_type);
extern const char *nl_rtproto_to_str(uint8_t rtproto);
extern const char *nl_family_to_str(uint8_t family);
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index d6ca92f54e..57fd304ae8 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1156,7 +1156,7 @@ int rtm_write(int message, union sockunion *dest, union sockunion *mask,
return ZEBRA_ERR_EPERM;
/* Clear and set rt_msghdr values */
- memset(&msg, 0, sizeof(struct rt_msghdr));
+ memset(&msg, 0, sizeof(msg));
msg.rtm.rtm_version = RTM_VERSION;
msg.rtm.rtm_type = message;
msg.rtm.rtm_seq = msg_seq++;
diff --git a/zebra/main.c b/zebra/main.c
index 7ef30d1d49..87f3de2d97 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -79,8 +79,9 @@ int graceful_restart;
bool v6_rr_semantics = false;
/* Receive buffer size for kernel control sockets */
+#define RCVBUFSIZE_MIN 4194304
#ifdef HAVE_NETLINK
-uint32_t rcvbufsize = 4194304;
+uint32_t rcvbufsize = RCVBUFSIZE_MIN;
#else
uint32_t rcvbufsize = 128 * 1024;
#endif
@@ -365,6 +366,10 @@ int main(int argc, char **argv)
break;
case 's':
rcvbufsize = atoi(optarg);
+ if (rcvbufsize < RCVBUFSIZE_MIN)
+ fprintf(stderr,
+ "Rcvbufsize is smaller than recommended value: %d\n",
+ RCVBUFSIZE_MIN);
break;
#ifdef HAVE_NETLINK
case 'n':
@@ -403,9 +408,7 @@ int main(int argc, char **argv)
zebra_vty_init();
access_list_init();
prefix_list_init();
-#if defined(HAVE_RTADV)
rtadv_cmd_init();
-#endif
/* PTM socket */
#ifdef ZEBRA_PTM_SUPPORT
zebra_ptm_init();
diff --git a/zebra/rib.h b/zebra/rib.h
index 281791d1f8..60092c9632 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -288,33 +288,6 @@ DECLARE_LIST(re_list, struct route_entry, next);
#define RNODE_NEXT_RE(rn, re) RE_DEST_NEXT_ROUTE(rib_dest_from_rnode(rn), re)
-#if defined(HAVE_RTADV)
-PREDECL_SORTLIST_UNIQ(adv_if_list);
-/* Structure which hold status of router advertisement. */
-struct rtadv {
- int sock;
-
- struct adv_if_list_head adv_if;
- struct adv_if_list_head adv_msec_if;
-
- struct thread *ra_read;
- struct thread *ra_timer;
-};
-
-/* adv list node */
-struct adv_if {
- char name[INTERFACE_NAMSIZ];
- struct adv_if_list_item list_item;
-};
-
-static int adv_if_cmp(const struct adv_if *a, const struct adv_if *b)
-{
- return if_cmp_name_func(a->name, b->name);
-}
-
-DECLARE_SORTLIST_UNIQ(adv_if_list, struct adv_if, list_item, adv_if_cmp);
-#endif /* HAVE_RTADV */
-
/*
* rib_table_info_t
*
@@ -357,8 +330,6 @@ enum rib_update_event {
RIB_UPDATE_MAX
};
-extern void route_entry_copy_nexthops(struct route_entry *re,
- struct nexthop *nh);
int route_entry_update_nhe(struct route_entry *re,
struct nhg_hash_entry *new_nhghe);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 4e97323cf6..93b2d94671 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -3783,7 +3783,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
return 0;
}
- memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&ip, 0, sizeof(ip));
ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
memcpy(&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
@@ -3863,7 +3863,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
return 0;
}
- memset(&mac, 0, sizeof(struct ethaddr));
+ memset(&mac, 0, sizeof(mac));
if (h->nlmsg_type == RTM_NEWNEIGH) {
if (tb[NDA_LLADDR]) {
if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) {
@@ -4493,8 +4493,6 @@ static int netlink_fdb_nh_update(uint32_t nh_id, struct in_addr vtep_ip)
struct zebra_ns *zns;
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return -1;
zns = zvrf->zns;
memset(&req, 0, sizeof(req));
@@ -4534,8 +4532,6 @@ static int netlink_fdb_nh_del(uint32_t nh_id)
struct zebra_ns *zns;
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return -1;
zns = zvrf->zns;
memset(&req, 0, sizeof(req));
@@ -4572,8 +4568,6 @@ static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt,
uint32_t i;
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return -1;
zns = zvrf->zns;
memset(&req, 0, sizeof(req));
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
index ca833999cb..b24dc89a68 100644
--- a/zebra/rtadv.c
+++ b/zebra/rtadv.c
@@ -73,6 +73,28 @@ DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface");
#define ALLNODE "ff02::1"
#define ALLROUTER "ff02::2"
+/* adv list node */
+struct adv_if {
+ char name[INTERFACE_NAMSIZ];
+ struct adv_if_list_item list_item;
+};
+
+static int adv_if_cmp(const struct adv_if *a, const struct adv_if *b)
+{
+ return if_cmp_name_func(a->name, b->name);
+}
+
+DECLARE_SORTLIST_UNIQ(adv_if_list, struct adv_if, list_item, adv_if_cmp);
+
+static int rtadv_prefix_cmp(const struct rtadv_prefix *a,
+ const struct rtadv_prefix *b)
+{
+ return prefix_cmp(&a->prefix, &b->prefix);
+}
+
+DECLARE_RBTREE_UNIQ(rtadv_prefixes, struct rtadv_prefix, item,
+ rtadv_prefix_cmp);
+
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_RDNSS, "Router Advertisement RDNSS");
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_DNSSL, "Router Advertisement DNSSL");
@@ -315,7 +337,7 @@ static void rtadv_send_packet(int sock, struct interface *ifp,
}
/* Fill in prefix. */
- for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvPrefixList, node, rprefix)) {
+ frr_each (rtadv_prefixes, zif->rtadv.prefixes, rprefix) {
struct nd_opt_prefix_info *pinfo;
pinfo = (struct nd_opt_prefix_info *)(buf + len);
@@ -1065,31 +1087,20 @@ static void rtadv_prefix_free(struct rtadv_prefix *rtadv_prefix)
XFREE(MTYPE_RTADV_PREFIX, rtadv_prefix);
}
-static struct rtadv_prefix *rtadv_prefix_lookup(struct list *rplist,
- struct prefix_ipv6 *p)
-{
- struct listnode *node;
- struct rtadv_prefix *rprefix;
-
- for (ALL_LIST_ELEMENTS_RO(rplist, node, rprefix))
- if (prefix_same((struct prefix *)&rprefix->prefix,
- (struct prefix *)p))
- return rprefix;
- return NULL;
-}
-
-static struct rtadv_prefix *rtadv_prefix_get(struct list *rplist,
+static struct rtadv_prefix *rtadv_prefix_get(struct rtadv_prefixes_head *list,
struct prefix_ipv6 *p)
{
- struct rtadv_prefix *rprefix;
+ struct rtadv_prefix *rprefix, ref;
+
+ ref.prefix = *p;
- rprefix = rtadv_prefix_lookup(rplist, p);
+ rprefix = rtadv_prefixes_find(list, &ref);
if (rprefix)
return rprefix;
rprefix = rtadv_prefix_new();
memcpy(&rprefix->prefix, p, sizeof(struct prefix_ipv6));
- listnode_add(rplist, rprefix);
+ rtadv_prefixes_add(list, rprefix);
return rprefix;
}
@@ -1107,7 +1118,7 @@ static void rtadv_prefix_set(struct zebra_if *zif, struct rtadv_prefix *rp)
{
struct rtadv_prefix *rprefix;
- rprefix = rtadv_prefix_get(zif->rtadv.AdvPrefixList, &rp->prefix);
+ rprefix = rtadv_prefix_get(zif->rtadv.prefixes, &rp->prefix);
/*
* Set parameters based on where the prefix is created.
@@ -1142,7 +1153,7 @@ static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp)
{
struct rtadv_prefix *rprefix;
- rprefix = rtadv_prefix_lookup(zif->rtadv.AdvPrefixList, &rp->prefix);
+ rprefix = rtadv_prefixes_find(zif->rtadv.prefixes, rp);
if (rprefix != NULL) {
/*
@@ -1165,7 +1176,7 @@ static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp)
}
}
- listnode_delete(zif->rtadv.AdvPrefixList, (void *)rprefix);
+ rtadv_prefixes_del(zif->rtadv.prefixes, rprefix);
rtadv_prefix_free(rprefix);
return 1;
} else
@@ -1358,7 +1369,6 @@ void rtadv_stop_ra_all(void)
{
struct vrf *vrf;
struct interface *ifp;
- struct listnode *node, *nnode;
struct zebra_if *zif;
struct rtadv_prefix *rprefix;
@@ -1366,8 +1376,8 @@ void rtadv_stop_ra_all(void)
FOR_ALL_INTERFACES (vrf, ifp) {
zif = ifp->info;
- for (ALL_LIST_ELEMENTS(zif->rtadv.AdvPrefixList,
- node, nnode, rprefix))
+ frr_each_safe (rtadv_prefixes, zif->rtadv.prefixes,
+ rprefix)
rtadv_prefix_reset(zif, rprefix);
rtadv_stop_ra(ifp);
@@ -2671,7 +2681,7 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp)
if (zif->rtadv.AdvLinkMTU)
vty_out(vty, " ipv6 nd mtu %d\n", zif->rtadv.AdvLinkMTU);
- for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvPrefixList, node, rprefix)) {
+ frr_each (rtadv_prefixes, zif->rtadv.prefixes, rprefix) {
if ((rprefix->AdvPrefixCreate == PREFIX_SRC_MANUAL)
|| (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH)) {
vty_out(vty, " ipv6 nd prefix %pFX", &rprefix->prefix);
@@ -2768,6 +2778,72 @@ static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
return;
}
+void rtadv_if_up(struct zebra_if *zif)
+{
+ /* Enable fast tx of RA if enabled && RA interval is not in msecs */
+ if (zif->rtadv.AdvSendAdvertisements &&
+ (zif->rtadv.MaxRtrAdvInterval >= 1000) &&
+ zif->rtadv.UseFastRexmit) {
+ zif->rtadv.inFastRexmit = 1;
+ zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS;
+ }
+}
+
+void rtadv_if_init(struct zebra_if *zif)
+{
+ /* Set default router advertise values. */
+ struct rtadvconf *rtadv;
+
+ rtadv = &zif->rtadv;
+
+ rtadv->AdvSendAdvertisements = 0;
+ rtadv->MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
+ rtadv->MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
+ rtadv->AdvIntervalTimer = 0;
+ rtadv->AdvManagedFlag = 0;
+ rtadv->AdvOtherConfigFlag = 0;
+ rtadv->AdvHomeAgentFlag = 0;
+ rtadv->AdvLinkMTU = 0;
+ rtadv->AdvReachableTime = 0;
+ rtadv->AdvRetransTimer = 0;
+ rtadv->AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT;
+ memset(&rtadv->lastadvcurhoplimit, 0,
+ sizeof(rtadv->lastadvcurhoplimit));
+ memset(&rtadv->lastadvmanagedflag, 0,
+ sizeof(rtadv->lastadvmanagedflag));
+ memset(&rtadv->lastadvotherconfigflag, 0,
+ sizeof(rtadv->lastadvotherconfigflag));
+ memset(&rtadv->lastadvreachabletime, 0,
+ sizeof(rtadv->lastadvreachabletime));
+ memset(&rtadv->lastadvretranstimer, 0,
+ sizeof(rtadv->lastadvretranstimer));
+ rtadv->AdvDefaultLifetime = -1; /* derive from MaxRtrAdvInterval */
+ rtadv->HomeAgentPreference = 0;
+ rtadv->HomeAgentLifetime = -1; /* derive from AdvDefaultLifetime */
+ rtadv->AdvIntervalOption = 0;
+ rtadv->UseFastRexmit = true;
+ rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
+
+ rtadv_prefixes_init(rtadv->prefixes);
+
+ rtadv->AdvRDNSSList = list_new();
+ rtadv->AdvDNSSLList = list_new();
+}
+
+void rtadv_if_fini(struct zebra_if *zif)
+{
+ struct rtadvconf *rtadv;
+ struct rtadv_prefix *rp;
+
+ rtadv = &zif->rtadv;
+
+ while ((rp = rtadv_prefixes_pop(rtadv->prefixes)))
+ rtadv_prefix_free(rp);
+
+ list_delete(&rtadv->AdvRDNSSList);
+ list_delete(&rtadv->AdvDNSSLList);
+}
+
void rtadv_vrf_init(struct zebra_vrf *zvrf)
{
if (!vrf_is_backend_netns() && (zvrf_id(zvrf) != VRF_DEFAULT))
@@ -2847,7 +2923,7 @@ static int if_join_all_router(int sock, struct interface *ifp)
struct ipv6_mreq mreq;
- memset(&mreq, 0, sizeof(struct ipv6_mreq));
+ memset(&mreq, 0, sizeof(mreq));
inet_pton(AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr);
mreq.ipv6mr_interface = ifp->ifindex;
@@ -2873,7 +2949,7 @@ static int if_leave_all_router(int sock, struct interface *ifp)
struct ipv6_mreq mreq;
- memset(&mreq, 0, sizeof(struct ipv6_mreq));
+ memset(&mreq, 0, sizeof(mreq));
inet_pton(AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr);
mreq.ipv6mr_interface = ifp->ifindex;
@@ -2899,37 +2975,7 @@ bool rtadv_compiled_in(void)
return true;
}
-#else
-void rtadv_vrf_init(struct zebra_vrf *zvrf)
-{
- /* Empty.*/;
-}
-
-void rtadv_cmd_init(void)
-{
- /* Empty.*/;
-}
-
-void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p)
-{
- /* Empty.*/;
-}
-
-void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p)
-{
- /* Empty.*/;
-}
-
-void rtadv_stop_ra(struct interface *ifp)
-{
- /* Empty.*/;
-}
-
-void rtadv_stop_ra_all(void)
-{
- /* Empty.*/;
-}
-
+#else /* !HAVE_RTADV */
/*
* If the end user does not have RADV enabled we should
* handle this better
diff --git a/zebra/rtadv.h b/zebra/rtadv.h
index a95174b22b..26c7823747 100644
--- a/zebra/rtadv.h
+++ b/zebra/rtadv.h
@@ -24,17 +24,248 @@
#include "zebra.h"
#include "vty.h"
-#include "zebra/interface.h"
+#include "typesafe.h"
+
+#include "zebra/zserv.h"
#ifdef __cplusplus
extern "C" {
#endif
-/* NB: RTADV is defined in zebra/interface.h above */
+struct interface;
+struct zebra_if;
+
#if defined(HAVE_RTADV)
+PREDECL_SORTLIST_UNIQ(adv_if_list);
+/* Structure which hold status of router advertisement. */
+struct rtadv {
+ int sock;
+
+ struct adv_if_list_head adv_if;
+ struct adv_if_list_head adv_msec_if;
+
+ struct thread *ra_read;
+ struct thread *ra_timer;
+};
+
+PREDECL_RBTREE_UNIQ(rtadv_prefixes);
+
+/* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */
+struct rtadvconf {
+ /* A flag indicating whether or not the router sends periodic Router
+ Advertisements and responds to Router Solicitations.
+ Default: false */
+ int AdvSendAdvertisements;
+
+ /* The maximum time allowed between sending unsolicited multicast
+ Router Advertisements from the interface, in milliseconds.
+ MUST be no less than 70 ms [RFC6275 7.5] and no greater
+ than 1800000 ms [RFC4861 6.2.1].
+
+ Default: 600000 milliseconds */
+ int MaxRtrAdvInterval;
+#define RTADV_MAX_RTR_ADV_INTERVAL 600000
+
+ /* The minimum time allowed between sending unsolicited multicast
+ Router Advertisements from the interface, in milliseconds.
+ MUST be no less than 30 ms [RFC6275 7.5].
+ MUST be no greater than .75 * MaxRtrAdvInterval.
+
+ Default: 0.33 * MaxRtrAdvInterval */
+ int MinRtrAdvInterval; /* This field is currently unused. */
+#define RTADV_MIN_RTR_ADV_INTERVAL (0.33 * RTADV_MAX_RTR_ADV_INTERVAL)
+
+ /* Unsolicited Router Advertisements' interval timer. */
+ int AdvIntervalTimer;
+
+ /* The true/false value to be placed in the "Managed address
+ configuration" flag field in the Router Advertisement. See
+ [ADDRCONF].
+
+ Default: false */
+ int AdvManagedFlag;
+ struct timeval lastadvmanagedflag;
+
+
+ /* The true/false value to be placed in the "Other stateful
+ configuration" flag field in the Router Advertisement. See
+ [ADDRCONF].
+
+ Default: false */
+ int AdvOtherConfigFlag;
+ struct timeval lastadvotherconfigflag;
+
+ /* The value to be placed in MTU options sent by the router. A
+ value of zero indicates that no MTU options are sent.
+
+ Default: 0 */
+ int AdvLinkMTU;
+
+
+ /* The value to be placed in the Reachable Time field in the Router
+ Advertisement messages sent by the router. The value zero means
+ unspecified (by this router). MUST be no greater than 3,600,000
+ milliseconds (1 hour).
+
+ Default: 0 */
+ uint32_t AdvReachableTime;
+#define RTADV_MAX_REACHABLE_TIME 3600000
+ struct timeval lastadvreachabletime;
+
+ /* The value to be placed in the Retrans Timer field in the Router
+ Advertisement messages sent by the router. The value zero means
+ unspecified (by this router).
+
+ Default: 0 */
+ int AdvRetransTimer;
+ struct timeval lastadvretranstimer;
+
+ /* The default value to be placed in the Cur Hop Limit field in the
+ Router Advertisement messages sent by the router. The value
+ should be set to that current diameter of the Internet. The
+ value zero means unspecified (by this router).
+
+ Default: The value specified in the "Assigned Numbers" RFC
+ [ASSIGNED] that was in effect at the time of implementation. */
+ int AdvCurHopLimit;
+ struct timeval lastadvcurhoplimit;
+
+#define RTADV_DEFAULT_HOPLIMIT 64 /* 64 hops */
+
+ /* The value to be placed in the Router Lifetime field of Router
+ Advertisements sent from the interface, in seconds. MUST be
+ either zero or between MaxRtrAdvInterval and 9000 seconds. A
+ value of zero indicates that the router is not to be used as a
+ default router.
+
+ Default: 3 * MaxRtrAdvInterval */
+ int AdvDefaultLifetime;
+#define RTADV_MAX_RTRLIFETIME 9000 /* 2.5 hours */
+
+ /* A list of prefixes to be placed in Prefix Information options in
+ Router Advertisement messages sent from the interface.
+
+ Default: all prefixes that the router advertises via routing
+ protocols as being on-link for the interface from which the
+ advertisement is sent. The link-local prefix SHOULD NOT be
+ included in the list of advertised prefixes. */
+ struct rtadv_prefixes_head prefixes[1];
+
+ /* The true/false value to be placed in the "Home agent"
+ flag field in the Router Advertisement. See [RFC6275 7.1].
+
+ Default: false */
+ int AdvHomeAgentFlag;
+#ifndef ND_RA_FLAG_HOME_AGENT
+#define ND_RA_FLAG_HOME_AGENT 0x20
+#endif
+
+ /* The value to be placed in Home Agent Information option if Home
+ Flag is set.
+ Default: 0 */
+ int HomeAgentPreference;
+
+ /* The value to be placed in Home Agent Information option if Home
+ Flag is set. Lifetime (seconds) MUST not be greater than 18.2
+ hours.
+ The value 0 has special meaning: use of AdvDefaultLifetime value.
+
+ Default: 0 */
+ int HomeAgentLifetime;
+#define RTADV_MAX_HALIFETIME 65520 /* 18.2 hours */
+
+ /* The true/false value to insert or not an Advertisement Interval
+ option. See [RFC 6275 7.3]
+
+ Default: false */
+ int AdvIntervalOption;
+
+ /* The value to be placed in the Default Router Preference field of
+ a router advertisement. See [RFC 4191 2.1 & 2.2]
+
+ Default: 0 (medium) */
+ int DefaultPreference;
+#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
+
+ /*
+ * List of recursive DNS servers to include in the RDNSS option.
+ * See [RFC8106 5.1]
+ *
+ * Default: empty list; do not emit RDNSS option
+ */
+ struct list *AdvRDNSSList;
+
+ /*
+ * List of DNS search domains to include in the DNSSL option.
+ * See [RFC8106 5.2]
+ *
+ * Default: empty list; do not emit DNSSL option
+ */
+ struct list *AdvDNSSLList;
+
+ /*
+ * rfc4861 states RAs must be sent at least 3 seconds apart.
+ * We allow faster retransmits to speed up convergence but can
+ * turn that capability off to meet the rfc if needed.
+ */
+ bool UseFastRexmit; /* True if fast rexmits are enabled */
+
+ uint8_t inFastRexmit; /* True if we're rexmits faster than usual */
+
+ /* Track if RA was configured by BGP or by the Operator or both */
+ uint8_t ra_configured; /* Was RA configured? */
+#define BGP_RA_CONFIGURED (1 << 0) /* BGP configured RA? */
+#define VTY_RA_CONFIGURED (1 << 1) /* Operator configured RA? */
+#define VTY_RA_INTERVAL_CONFIGURED \
+ (1 << 2) /* Operator configured RA interval */
+ int NumFastReXmitsRemain; /* Loaded first with number of fast
+ rexmits to do */
+
+#define RTADV_FAST_REXMIT_PERIOD 1 /* 1 sec */
+#define RTADV_NUM_FAST_REXMITS 4 /* Fast Rexmit RA 4 times on certain events \
+ */
+};
+
+struct rtadv_rdnss {
+ /* Address of recursive DNS server to advertise */
+ struct in6_addr addr;
+
+ /*
+ * Lifetime in seconds; all-ones means infinity, zero
+ * stop using it.
+ */
+ uint32_t lifetime;
+
+ /* If lifetime not set, use a default of 3*MaxRtrAdvInterval */
+ int lifetime_set;
+};
+
+/*
+ * [RFC1035 2.3.4] sets the maximum length of a domain name (a sequence of
+ * labels, each prefixed by a length octet) at 255 octets.
+ */
+#define RTADV_MAX_ENCODED_DOMAIN_NAME 255
+
+struct rtadv_dnssl {
+ /* Domain name without trailing root zone dot (NUL-terminated) */
+ char name[RTADV_MAX_ENCODED_DOMAIN_NAME - 1];
+
+ /* Name encoded as in [RFC1035 3.1] */
+ uint8_t encoded_name[RTADV_MAX_ENCODED_DOMAIN_NAME];
+
+ /* Actual length of encoded_name */
+ size_t encoded_len;
+
+ /* Lifetime as for RDNSS */
+ uint32_t lifetime;
+ int lifetime_set;
+};
+
/* Router advertisement prefix. */
struct rtadv_prefix {
+ struct rtadv_prefixes_item item;
+
/* Prefix to be advertised. */
struct prefix_ipv6 prefix;
@@ -135,8 +366,6 @@ struct nd_opt_dnssl { /* DNS search list option [RFC8106 5.2] */
} __attribute__((__packed__));
#endif
-#endif /* HAVE_RTADV */
-
/*
* ipv6 nd prefixes can be manually defined, derived from the kernel interface
* configs or both. If both, manual flag/timer settings are used.
@@ -158,10 +387,60 @@ extern void rtadv_vrf_terminate(struct zebra_vrf *zvrf);
extern void rtadv_stop_ra(struct interface *ifp);
extern void rtadv_stop_ra_all(void);
extern void rtadv_cmd_init(void);
-extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS);
-extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS);
+extern void rtadv_if_init(struct zebra_if *zif);
+extern void rtadv_if_up(struct zebra_if *zif);
+extern void rtadv_if_fini(struct zebra_if *zif);
extern void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p);
extern void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p);
+
+#else /* !HAVE_RTADV */
+struct rtadv {
+ /* empty structs aren't valid ISO C */
+ char dummy;
+};
+
+struct rtadvconf {
+ /* same again, empty structs aren't valid ISO C */
+ char dummy;
+};
+
+static inline void rtadv_vrf_init(struct zebra_vrf *zvrf)
+{
+}
+static inline void rtadv_vrf_terminate(struct zebra_vrf *zvrf)
+{
+}
+static inline void rtadv_cmd_init(void)
+{
+}
+static inline void rtadv_if_init(struct zebra_if *zif)
+{
+}
+static inline void rtadv_if_up(struct zebra_if *zif)
+{
+}
+static inline void rtadv_if_fini(struct zebra_if *zif)
+{
+}
+static inline void rtadv_add_prefix(struct zebra_if *zif,
+ const struct prefix_ipv6 *p)
+{
+}
+static inline void rtadv_delete_prefix(struct zebra_if *zif,
+ const struct prefix *p)
+{
+}
+static inline void rtadv_stop_ra(struct interface *ifp)
+{
+}
+static inline void rtadv_stop_ra_all(void)
+{
+}
+#endif
+
+extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS);
+extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS);
+
extern uint32_t rtadv_get_interfaces_configured_from_bgp(void);
extern bool rtadv_compiled_in(void);
diff --git a/zebra/table_manager.c b/zebra/table_manager.c
index ffc7a48eb9..a3daca6c59 100644
--- a/zebra/table_manager.c
+++ b/zebra/table_manager.c
@@ -119,8 +119,6 @@ struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
}
/* otherwise create a new one */
tmc = XCALLOC(MTYPE_TM_CHUNK, sizeof(struct table_manager_chunk));
- if (!tmc)
- return NULL;
if (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end)
manual_conf = true;
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index 9972d32fb7..9a30c2b78f 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -876,9 +876,24 @@ void zsend_iptable_notify_owner(const struct zebra_dplane_ctx *ctx,
struct stream *s;
struct zebra_pbr_iptable ipt;
uint16_t cmd = ZEBRA_IPTABLE_NOTIFY_OWNER;
+ struct zebra_pbr_iptable *ipt_hash;
+ enum dplane_op_e op = dplane_ctx_get_op(ctx);
dplane_ctx_get_pbr_iptable(ctx, &ipt);
+ ipt_hash = hash_lookup(zrouter.iptable_hash, &ipt);
+ if (ipt_hash) {
+ if (op == DPLANE_OP_IPTABLE_ADD &&
+ CHECK_FLAG(ipt_hash->internal_flags,
+ IPTABLE_INSTALL_QUEUED))
+ UNSET_FLAG(ipt_hash->internal_flags,
+ IPTABLE_INSTALL_QUEUED);
+ else if (op == DPLANE_OP_IPTABLE_DELETE &&
+ CHECK_FLAG(ipt_hash->internal_flags,
+ IPTABLE_UNINSTALL_QUEUED))
+ UNSET_FLAG(ipt_hash->internal_flags,
+ IPTABLE_UNINSTALL_QUEUED);
+ }
if (IS_ZEBRA_DEBUG_PACKET)
zlog_debug("%s: Notifying %s id %u note %u", __func__,
zserv_command_string(cmd), ipt.unique, note);
@@ -1592,7 +1607,7 @@ static struct nexthop *nexthop_from_zapi(const struct zapi_nexthop *api_nh,
* the nexthop and associated MAC need to be installed.
*/
if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
- memset(&vtep_ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(vtep_ip));
vtep_ip.ipa_type = IPADDR_V4;
memcpy(&(vtep_ip.ipaddr_v4), &(api_nh->gate.ipv4),
sizeof(struct in_addr));
@@ -1625,7 +1640,7 @@ static struct nexthop *nexthop_from_zapi(const struct zapi_nexthop *api_nh,
* the nexthop and associated MAC need to be installed.
*/
if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
- memset(&vtep_ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(vtep_ip));
vtep_ip.ipa_type = IPADDR_V6;
memcpy(&vtep_ip.ipaddr_v6, &(api_nh->gate.ipv6),
sizeof(struct in6_addr));
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 4e753c9d1a..0da44e3c4e 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -4550,6 +4550,17 @@ iptable_update_internal(enum dplane_op_e op, struct zebra_pbr_iptable *iptable)
struct zebra_dplane_ctx *ctx;
int ret;
+ if ((op == DPLANE_OP_IPTABLE_ADD &&
+ CHECK_FLAG(iptable->internal_flags, IPTABLE_INSTALL_QUEUED)) ||
+ (op == DPLANE_OP_IPTABLE_DELETE &&
+ CHECK_FLAG(iptable->internal_flags, IPTABLE_UNINSTALL_QUEUED))) {
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug(
+ "update dplane ctx %s: iptable %s already in progress",
+ dplane_op2str(op), iptable->ipset_name);
+ return result;
+ }
+
ctx = dplane_ctx_alloc();
ret = dplane_ctx_iptable_init(ctx, op, iptable);
@@ -4562,14 +4573,19 @@ done:
atomic_fetch_add_explicit(&zdplane_info.dg_iptable_in, 1,
memory_order_relaxed);
- if (ret == AOK)
+ if (ret == AOK) {
result = ZEBRA_DPLANE_REQUEST_QUEUED;
- else {
+ if (op == DPLANE_OP_IPTABLE_ADD)
+ SET_FLAG(iptable->internal_flags,
+ IPTABLE_INSTALL_QUEUED);
+ else
+ SET_FLAG(iptable->internal_flags,
+ IPTABLE_UNINSTALL_QUEUED);
+ } else {
atomic_fetch_add_explicit(&zdplane_info.dg_iptable_errors, 1,
memory_order_relaxed);
dplane_ctx_free(&ctx);
}
-
return result;
}
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
index 121092a55b..f2ad98c97b 100644
--- a/zebra/zebra_evpn.c
+++ b/zebra/zebra_evpn.c
@@ -73,7 +73,7 @@ int advertise_gw_macip_enabled(struct zebra_evpn *zevpn)
struct zebra_vrf *zvrf;
zvrf = zebra_vrf_get_evpn();
- if (zvrf && zvrf->advertise_gw_macip)
+ if (zvrf->advertise_gw_macip)
return 1;
if (zevpn && zevpn->advertise_gw_macip)
@@ -87,7 +87,7 @@ int advertise_svi_macip_enabled(struct zebra_evpn *zevpn)
struct zebra_vrf *zvrf;
zvrf = zebra_vrf_get_evpn();
- if (zvrf && zvrf->advertise_svi_macip)
+ if (zvrf->advertise_svi_macip)
return 1;
if (zevpn && zevpn->advertise_svi_macip)
@@ -999,8 +999,7 @@ struct zebra_evpn *zebra_evpn_lookup(vni_t vni)
struct zebra_evpn *zevpn = NULL;
zvrf = zebra_vrf_get_evpn();
- assert(zvrf);
- memset(&tmp_vni, 0, sizeof(struct zebra_evpn));
+ memset(&tmp_vni, 0, sizeof(tmp_vni));
tmp_vni.vni = vni;
zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni);
@@ -1018,8 +1017,7 @@ struct zebra_evpn *zebra_evpn_add(vni_t vni)
struct zebra_evpn *zevpn = NULL;
zvrf = zebra_vrf_get_evpn();
- assert(zvrf);
- memset(&tmp_zevpn, 0, sizeof(struct zebra_evpn));
+ memset(&tmp_zevpn, 0, sizeof(tmp_zevpn));
tmp_zevpn.vni = vni;
zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc);
@@ -1046,7 +1044,6 @@ int zebra_evpn_del(struct zebra_evpn *zevpn)
struct zebra_evpn *tmp_zevpn;
zvrf = zebra_vrf_get_evpn();
- assert(zvrf);
zevpn->svi_if = NULL;
@@ -1454,10 +1451,6 @@ void zebra_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr,
}
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
-
-
if (zebra_evpn_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len,
ipaddr, &mac, vtep_ip, flags, seq,
esi)
diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c
index 5a1f5af5d7..3bdd70e207 100644
--- a/zebra/zebra_evpn_mac.c
+++ b/zebra/zebra_evpn_mac.c
@@ -625,9 +625,6 @@ void zebra_evpn_print_mac(struct zebra_mac *mac, void *ctxt, json_object *json)
char up_str[MONOTIME_STRLEN];
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
-
vty = (struct vty *)ctxt;
prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
@@ -1102,7 +1099,7 @@ struct zebra_mac *zebra_evpn_mac_add(struct zebra_evpn *zevpn,
struct zebra_mac tmp_mac;
struct zebra_mac *mac = NULL;
- memset(&tmp_mac, 0, sizeof(struct zebra_mac));
+ memset(&tmp_mac, 0, sizeof(tmp_mac));
memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN);
mac = hash_get(zevpn->mac_table, &tmp_mac, zebra_evpn_mac_alloc);
@@ -1255,7 +1252,7 @@ void zebra_evpn_mac_del_all(struct zebra_evpn *zevpn, int uninstall,
if (!zevpn->mac_table)
return;
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.uninstall = uninstall;
wctx.upd_client = upd_client;
@@ -1930,7 +1927,7 @@ void zebra_evpn_send_mac_list_to_client(struct zebra_evpn *zevpn)
if (!zevpn->mac_table)
return;
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
hash_iterate(zevpn->mac_table, zebra_evpn_send_mac_hash_entry_to_client,
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
index 7728f50123..043ea0f248 100644
--- a/zebra/zebra_evpn_mh.c
+++ b/zebra/zebra_evpn_mh.c
@@ -2066,11 +2066,6 @@ static void zebra_evpn_mh_dup_addr_detect_off(void)
return;
zvrf = zebra_vrf_get_evpn();
- if (!zvrf) {
- zmh_info->flags |= ZEBRA_EVPN_MH_DUP_ADDR_DETECT_OFF;
- return;
- }
-
old_detect = zebra_evpn_do_dup_addr_detect(zvrf);
zmh_info->flags |= ZEBRA_EVPN_MH_DUP_ADDR_DETECT_OFF;
new_detect = zebra_evpn_do_dup_addr_detect(zvrf);
@@ -2317,7 +2312,6 @@ static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi)
struct zebra_evpn_es *old_es = zif->es_info.es;
struct zebra_evpn_es *es;
- memcpy(&zif->es_info.esi, esi, sizeof(*esi));
if (old_es && !memcmp(&old_es->esi, esi, sizeof(*esi)))
/* dup - nothing to be done */
return 0;
@@ -2329,15 +2323,14 @@ static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi)
es = zebra_evpn_es_find(esi);
if (es) {
/* if it exists against another interface flag an error */
- if (es->zif && es->zif != zif) {
- memset(&zif->es_info.esi, 0, sizeof(*esi));
+ if (es->zif && es->zif != zif)
return -1;
- }
} else {
/* create new es */
es = zebra_evpn_es_new(esi);
}
+ memcpy(&zif->es_info.esi, esi, sizeof(*esi));
if (es)
zebra_evpn_es_local_info_set(es, zif);
diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h
index 7e288853bb..037648311c 100644
--- a/zebra/zebra_evpn_mh.h
+++ b/zebra/zebra_evpn_mh.h
@@ -38,7 +38,7 @@
* access port is associated with an ES-ID
* - Remotes ESs are added by BGP based on received/remote EAD/Type-1 routes
* (ZEBRA_EVPNES_REMOTE)
- * - An ES can be simulatenously LOCAL and REMOTE; infact all LOCAL ESs are
+ * - An ES can be simultaneously LOCAL and REMOTE; infact all LOCAL ESs are
* expected to have REMOTE ES peers.
*/
struct zebra_evpn_es {
diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c
index 734a53c571..d463411dea 100644
--- a/zebra/zebra_evpn_neigh.c
+++ b/zebra/zebra_evpn_neigh.c
@@ -550,7 +550,7 @@ static struct zebra_neigh *zebra_evpn_neigh_add(struct zebra_evpn *zevpn,
struct zebra_neigh tmp_n;
struct zebra_neigh *n = NULL;
- memset(&tmp_n, 0, sizeof(struct zebra_neigh));
+ memset(&tmp_n, 0, sizeof(tmp_n));
memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
n = hash_get(zevpn->neigh_table, &tmp_n, zebra_evpn_neigh_alloc);
@@ -881,7 +881,7 @@ void zebra_evpn_neigh_del_all(struct zebra_evpn *zevpn, int uninstall,
if (!zevpn->neigh_table)
return;
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.uninstall = uninstall;
wctx.upd_client = upd_client;
@@ -1653,7 +1653,7 @@ void zebra_evpn_send_neigh_to_client(struct zebra_evpn *zevpn)
{
struct neigh_walk_ctx wctx;
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
hash_iterate(zevpn->neigh_table,
@@ -1718,9 +1718,6 @@ void zebra_evpn_print_neigh(struct zebra_neigh *n, void *ctxt,
char up_str[MONOTIME_STRLEN];
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
-
uptime = monotime(NULL);
uptime -= n->uptime;
diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c
index 7c57dfac6a..39b7156ce4 100644
--- a/zebra/zebra_fpm.c
+++ b/zebra/zebra_fpm.c
@@ -1573,7 +1573,7 @@ static int zfpm_trigger_rmac_update(struct zebra_mac *rmac,
vxlan_if = zl3vni_map_to_vxlan_if(zl3vni);
svi_if = zl3vni_map_to_svi_if(zl3vni);
- memset(&key, 0, sizeof(struct fpm_mac_info_t));
+ memset(&key, 0, sizeof(key));
memcpy(&key.macaddr, &rmac->macaddr, ETH_ALEN);
key.vni = zl3vni->vni;
diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h
index 98744f3c1f..1c3e98158d 100644
--- a/zebra/zebra_l2.h
+++ b/zebra/zebra_l2.h
@@ -28,6 +28,7 @@
#include "if.h"
#include "vlan.h"
#include "vxlan.h"
+#include "zebra/zebra_vrf.h"
#ifdef __cplusplus
extern "C" {
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index de7b6a177d..8237bebf3b 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -612,7 +612,7 @@ static int nhlfe_nexthop_active_ipv4(struct zebra_nhlfe *nhlfe,
return 0;
/* Lookup nexthop in IPv4 routing table. */
- memset(&p, 0, sizeof(struct prefix_ipv4));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET;
p.prefixlen = IPV4_MAX_BITLEN;
p.prefix = nexthop->gate.ipv4;
@@ -661,7 +661,7 @@ static int nhlfe_nexthop_active_ipv6(struct zebra_nhlfe *nhlfe,
return 0;
/* Lookup nexthop in IPv6 routing table. */
- memset(&p, 0, sizeof(struct prefix_ipv6));
+ memset(&p, 0, sizeof(p));
p.family = AF_INET6;
p.prefixlen = IPV6_MAX_BITLEN;
p.prefix = nexthop->gate.ipv6;
diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c
index fd9b1ae387..ca66e0310a 100644
--- a/zebra/zebra_mpls_vty.c
+++ b/zebra/zebra_mpls_vty.c
@@ -206,7 +206,7 @@ static int zebra_mpls_bind(struct vty *vty, int add_cmd, const char *prefix,
return CMD_WARNING_CONFIG_FAILED;
}
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
ret = str2prefix(prefix, &p);
if (ret <= 0) {
vty_out(vty, "%% Malformed address\n");
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index de94c2dfc4..87356408d0 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -1148,7 +1148,6 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
struct zebra_vrf *zvrf;
vni_t vni = 0;
struct zebra_l3vni *zl3vni = NULL;
- struct zebra_vrf *zvrf_evpn = NULL;
char err[ERR_STR_SZ];
bool pfx_only = false;
const struct lyd_node *pn_dnode;
@@ -1159,12 +1158,6 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
case NB_EV_ABORT:
return NB_OK;
case NB_EV_VALIDATE:
- zvrf_evpn = zebra_vrf_get_evpn();
- if (!zvrf_evpn) {
- snprintf(args->errmsg, args->errmsg_len,
- "evpn vrf is not present.");
- return NB_ERR_VALIDATION;
- }
vni = yang_dnode_get_uint32(args->dnode, NULL);
/* Get vrf info from parent node, reject configuration
* if zebra vrf already mapped to different vni id.
diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c
index a9cb096aee..219e4d028a 100644
--- a/zebra/zebra_nb_state.c
+++ b/zebra/zebra_nb_state.c
@@ -248,7 +248,7 @@ lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args)
if (args->list_entry == NULL)
rn = route_top(zrt->table);
else
- rn = srcdest_route_next((struct route_node *)rn);
+ rn = srcdest_route_next(rn);
/* Optimization: skip empty route nodes. */
while (rn && rn->info == NULL)
rn = route_next(rn);
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
index af6046c9ad..b3cb061242 100644
--- a/zebra/zebra_netns_notify.c
+++ b/zebra/zebra_netns_notify.c
@@ -221,7 +221,7 @@ static bool zebra_ns_notify_is_default_netns(const char *name)
if (zebra_ns_notify_self_identify(&default_netns_stat))
return false;
- memset(&st, 0, sizeof(struct stat));
+ memset(&st, 0, sizeof(st));
snprintf(netnspath, sizeof(netnspath), "%s/%s", NS_RUN_DIR, name);
/* compare with local stat */
if (stat(netnspath, &st) == 0 &&
diff --git a/zebra/zebra_opaque.c b/zebra/zebra_opaque.c
index 0a98194acb..3d757566e0 100644
--- a/zebra/zebra_opaque.c
+++ b/zebra/zebra_opaque.c
@@ -25,6 +25,7 @@
#include "zebra/debug.h"
#include "zebra/zserv.h"
#include "zebra/zebra_opaque.h"
+#include "zebra/rib.h"
/* Mem type */
DEFINE_MTYPE_STATIC(ZEBRA, OPQ, "ZAPI Opaque Information");
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index e66d7aaf5c..bb963bcc23 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -812,8 +812,11 @@ static void *pbr_iptable_alloc_intern(void *arg)
void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable)
{
- (void)hash_get(zrouter.iptable_hash, iptable, pbr_iptable_alloc_intern);
- (void)dplane_pbr_iptable_add(iptable);
+ struct zebra_pbr_iptable *ipt_hash;
+
+ ipt_hash = hash_get(zrouter.iptable_hash, iptable,
+ pbr_iptable_alloc_intern);
+ (void)dplane_pbr_iptable_add(ipt_hash);
}
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index c5102df4fa..33b8162a88 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -171,6 +171,9 @@ struct zebra_pbr_iptable {
struct list *interface_name_list;
+#define IPTABLE_INSTALL_QUEUED 1 << 1
+#define IPTABLE_UNINSTALL_QUEUED 1 << 2
+ uint8_t internal_flags;
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c
index c28e251e3a..fda5ef02cf 100644
--- a/zebra/zebra_ptm.c
+++ b/zebra/zebra_ptm.c
@@ -114,7 +114,7 @@ void zebra_ptm_init(void)
{
char buf[64];
- memset(&ptm_cb, 0, sizeof(struct zebra_ptm_cb));
+ memset(&ptm_cb, 0, sizeof(ptm_cb));
ptm_cb.out_data = calloc(1, ZEBRA_PTM_SEND_MAX_SOCKBUF);
if (!ptm_cb.out_data) {
@@ -389,7 +389,7 @@ static int zebra_ptm_socket_init(void)
}
/* Make server socket. */
- memset(&addr, 0, sizeof(struct sockaddr_un));
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
memcpy(&addr.sun_path, ZEBRA_PTM_SOCK_NAME,
sizeof(ZEBRA_PTM_SOCK_NAME));
@@ -503,7 +503,7 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt,
return -1;
}
- memset(&src_prefix, 0, sizeof(struct prefix));
+ memset(&src_prefix, 0, sizeof(src_prefix));
if (strcmp(ZEBRA_PTM_INVALID_SRC_IP, src_str)) {
if (str2prefix(src_str, &src_prefix) == 0) {
flog_err(EC_ZEBRA_PREFIX_PARSE_ERROR,
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 2732967ee6..6801280012 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -292,15 +292,6 @@ int zebra_check_addr(const struct prefix *p)
return 1;
}
-/**
- * copy_nexthop - copy a nexthop to the rib structure.
- */
-void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh)
-{
- assert(!re->nhe->nhg.nexthop);
- copy_nexthops(&re->nhe->nhg.nexthop, nh, NULL);
-}
-
static void route_entry_attach_ref(struct route_entry *re,
struct nhg_hash_entry *new)
{
@@ -381,7 +372,7 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
if (!table)
return 0;
- memset(&p, 0, sizeof(struct prefix));
+ memset(&p, 0, sizeof(p));
p.family = afi;
if (afi == AFI_IP) {
p.u.prefix4 = addr->ipv4;
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index f88a65d952..553864d089 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -141,9 +141,8 @@ static int zebra_vrf_enable(struct vrf *vrf)
zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id);
else
zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
-#if defined(HAVE_RTADV)
+
rtadv_vrf_init(zvrf);
-#endif
/* Inform clients that the VRF is now active. This is an
* add for the clients.
@@ -186,9 +185,7 @@ static int zebra_vrf_disable(struct vrf *vrf)
/* Stop any VxLAN-EVPN processing. */
zebra_vxlan_vrf_disable(zvrf);
-#if defined(HAVE_RTADV)
rtadv_vrf_terminate(zvrf);
-#endif
/* Inform clients that the VRF is now inactive. This is a
* delete for the clients.
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index 21e7f286f3..02e3c197c9 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -26,6 +26,7 @@
#include <zebra/zebra_ns.h>
#include <zebra/zebra_pw.h>
+#include <zebra/rtadv.h>
#include <lib/vxlan.h>
#ifdef __cplusplus
@@ -177,9 +178,7 @@ struct zebra_vrf {
struct table_manager *tbl_mgr;
-#if defined(HAVE_RTADV)
struct rtadv rtadv;
-#endif /* HAVE_RTADV */
bool zebra_rnh_ip_default_route;
bool zebra_rnh_ipv6_default_route;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index b20d8daf38..3756f8153c 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -3914,6 +3914,7 @@ DEFUN (show_zebra,
ipforward_ipv6() ? "On" : "Off");
ttable_add_row(table, "MPLS|%s", mpls_enabled ? "On" : "Off");
ttable_add_row(table, "EVPN|%s", is_evpn_enabled() ? "On" : "Off");
+ ttable_add_row(table, "Kernel socket buffer size|%d", rcvbufsize);
#ifdef GNU_LINUX
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index d883336f00..927df14fbe 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -275,7 +275,7 @@ static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket,
* size, we try to be a bit more elegant in display by first computing
* the maximum width.
*/
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
@@ -341,7 +341,7 @@ static void zevpn_print_neigh_hash_all_evpn_detail(struct hash_bucket *bucket,
return;
}
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
@@ -625,7 +625,7 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket,
} else
json_object_int_add(json_evpn, "numNextHops", num_nh);
- memset(&wctx, 0, sizeof(struct nh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = json_evpn;
hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
@@ -668,7 +668,7 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket,
* under the vni. Re-assign primary json object to fill
* next vni information.
*/
- memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = json_evpn;
hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
@@ -928,9 +928,6 @@ static int zevpn_build_hash_table_zns(struct ns *ns,
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return NS_WALK_STOP;
-
/* Walk VxLAN interfaces and create EVPN hash. */
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
vni_t vni;
@@ -1078,9 +1075,7 @@ static int zevpn_build_hash_table_zns(struct ns *ns,
static void zevpn_build_hash_table(void)
{
- ns_walk_func(zevpn_build_hash_table_zns,
- (void *)NULL,
- (void **)NULL);
+ ns_walk_func(zevpn_build_hash_table_zns, NULL, NULL);
}
/*
@@ -1186,7 +1181,7 @@ static struct zebra_mac *zl3vni_rmac_add(struct zebra_l3vni *zl3vni,
struct zebra_mac tmp_rmac;
struct zebra_mac *zrmac = NULL;
- memset(&tmp_rmac, 0, sizeof(struct zebra_mac));
+ memset(&tmp_rmac, 0, sizeof(tmp_rmac));
memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN);
zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc);
zrmac->nh_list = list_new();
@@ -1369,7 +1364,7 @@ static void zl3vni_remote_rmac_del(struct zebra_l3vni *zl3vni,
struct ipaddr ipv4_vtep;
if (!zl3vni_nh_lookup(zl3vni, vtep_ip)) {
- memset(&ipv4_vtep, 0, sizeof(struct ipaddr));
+ memset(&ipv4_vtep, 0, sizeof(ipv4_vtep));
ipv4_vtep.ipa_type = IPADDR_V4;
if (vtep_ip->ipa_type == IPADDR_V6)
ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
@@ -1461,7 +1456,7 @@ static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni,
struct zebra_neigh tmp_n;
struct zebra_neigh *n = NULL;
- memset(&tmp_n, 0, sizeof(struct zebra_neigh));
+ memset(&tmp_n, 0, sizeof(tmp_n));
memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc);
@@ -1674,7 +1669,7 @@ struct zebra_l3vni *zl3vni_lookup(vni_t vni)
struct zebra_l3vni tmp_l3vni;
struct zebra_l3vni *zl3vni = NULL;
- memset(&tmp_l3vni, 0, sizeof(struct zebra_l3vni));
+ memset(&tmp_l3vni, 0, sizeof(tmp_l3vni));
tmp_l3vni.vni = vni;
zl3vni = hash_lookup(zrouter.l3vni_table, &tmp_l3vni);
@@ -1689,7 +1684,7 @@ static struct zebra_l3vni *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
struct zebra_l3vni tmp_zl3vni;
struct zebra_l3vni *zl3vni = NULL;
- memset(&tmp_zl3vni, 0, sizeof(struct zebra_l3vni));
+ memset(&tmp_zl3vni, 0, sizeof(tmp_zl3vni));
tmp_zl3vni.vni = vni;
zl3vni = hash_get(zrouter.l3vni_table, &tmp_zl3vni, zl3vni_alloc);
@@ -1747,9 +1742,6 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return NS_WALK_STOP;
-
assert(_pifp);
/* loop through all vxlan-interface */
@@ -1990,7 +1982,7 @@ static int zl3vni_send_add_to_client(struct zebra_l3vni *zl3vni)
assert(zvrf);
/* get the svi and vrr rmac values */
- memset(&svi_rmac, 0, sizeof(struct ethaddr));
+ memset(&svi_rmac, 0, sizeof(svi_rmac));
zl3vni_get_svi_rmac(zl3vni, &svi_rmac);
zl3vni_get_vrr_rmac(zl3vni, &vrr_rmac);
@@ -2123,7 +2115,7 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
- zebra_evpn_vtep_del_all(zevpn, 0);
+ zebra_evpn_vtep_del_all(zevpn, 1);
/* Delete the hash entry. */
if (zebra_evpn_vxlan_del(zevpn)) {
@@ -2278,7 +2270,7 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
* address. Rmac is programmed against the ipv4 vtep because we only
* support ipv4 tunnels in the h/w right now
*/
- memset(&ipv4_vtep, 0, sizeof(struct ipaddr));
+ memset(&ipv4_vtep, 0, sizeof(ipv4_vtep));
ipv4_vtep.ipa_type = IPADDR_V4;
if (vtep_ip->ipa_type == IPADDR_V6)
ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
@@ -2388,7 +2380,7 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
if (use_json)
json = json_object_new_object();
- memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = json;
if (!use_json) {
@@ -2639,7 +2631,7 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
* size, we try to be a bit more elegant in display by first computing
* the maximum width.
*/
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
@@ -2783,7 +2775,7 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
if (use_json)
json = json_object_new_object();
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
@@ -2836,7 +2828,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
* size, we try to be a bit more elegant in display by first computing
* the maximum width.
*/
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
@@ -2892,7 +2884,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
json_mac = json_object_new_object();
}
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.json = json_mac;
@@ -2933,7 +2925,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
if (use_json)
json = json_object_new_object();
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = json;
wctx.print_dup = print_dup;
@@ -2961,7 +2953,7 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty,
if (use_json)
json = json_object_new_object();
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = json;
wctx.print_dup = print_dup;
@@ -2988,7 +2980,7 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
if (use_json)
json = json_object_new_object();
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
@@ -3073,7 +3065,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
json_mac = json_object_new_object();
}
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.json = json_mac;
@@ -3322,7 +3314,7 @@ static void zevpn_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket,
zvrf = (struct zebra_vrf *)args[0];
if (hashcount(zevpn->neigh_table)) {
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&n_wctx, 0, sizeof(n_wctx));
n_wctx.zevpn = zevpn;
n_wctx.zvrf = zvrf;
hash_iterate(zevpn->neigh_table,
@@ -3330,7 +3322,7 @@ static void zevpn_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket,
}
if (num_valid_macs(zevpn)) {
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&m_wctx, 0, sizeof(m_wctx));
m_wctx.zevpn = zevpn;
m_wctx.zvrf = zvrf;
hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx);
@@ -3370,7 +3362,7 @@ int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni)
}
if (hashcount(zevpn->neigh_table)) {
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&n_wctx, 0, sizeof(n_wctx));
n_wctx.zevpn = zevpn;
n_wctx.zvrf = zvrf;
hash_iterate(zevpn->neigh_table,
@@ -3378,7 +3370,7 @@ int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni)
}
if (num_valid_macs(zevpn)) {
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&m_wctx, 0, sizeof(m_wctx));
m_wctx.zevpn = zevpn;
m_wctx.zvrf = zvrf;
hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx);
@@ -3419,7 +3411,7 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
json_mac = json_object_new_object();
}
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
@@ -3498,8 +3490,6 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
return;
zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
num_l3vnis = hashcount(zrouter.l3vni_table);
num_l2vnis = hashcount(zvrf->evpn_table);
@@ -4201,12 +4191,6 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
}
zvrf = zebra_vrf_get_evpn();
- if (!zvrf) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(" No Evpn Global Vrf found");
- return -1;
- }
-
return zebra_evpn_add_update_local_mac(zvrf, zevpn, ifp, macaddr, vid,
sticky, local_inactive,
dp_static, NULL);
@@ -4469,8 +4453,8 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, const struct prefix *p,
struct ethaddr macaddr;
struct zebra_evpn *zevpn = NULL;
- memset(&ip, 0, sizeof(struct ipaddr));
- memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(ip));
+ memset(&macaddr, 0, sizeof(macaddr));
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4685,13 +4669,19 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
zebra_evpn_send_add_to_client(zevpn);
/* Install any remote neighbors for this VNI. */
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&n_wctx, 0, sizeof(n_wctx));
n_wctx.zevpn = zevpn;
hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash,
&n_wctx);
/* Link the SVI from the access VLAN */
zebra_evpn_acc_bd_svi_set(ifp->info, link_if->info, true);
+
+ /* Update MACIP routes created by advertise-svi-ip */
+ if (advertise_svi_macip_enabled(zevpn)) {
+ zebra_evpn_del_macip_for_intf(ifp, zevpn);
+ zebra_evpn_add_macip_for_intf(ifp, zevpn);
+ }
}
return 0;
@@ -5155,12 +5145,12 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
zebra_evpn_read_mac_neigh(zevpn, ifp);
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+ memset(&m_wctx, 0, sizeof(m_wctx));
m_wctx.zevpn = zevpn;
hash_iterate(zevpn->mac_table,
zebra_evpn_install_mac_hash, &m_wctx);
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ memset(&n_wctx, 0, sizeof(n_wctx));
n_wctx.zevpn = zevpn;
hash_iterate(zevpn->neigh_table,
zebra_evpn_install_neigh_hash, &n_wctx);
@@ -5277,8 +5267,6 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
struct zebra_vrf *zvrf_evpn = NULL;
zvrf_evpn = zebra_vrf_get_evpn();
- if (!zvrf_evpn)
- return -1;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("vrf %s vni %u %s", zvrf_name(zvrf), vni,
@@ -5306,10 +5294,6 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
/* add the L3-VNI to the global table */
zl3vni = zl3vni_add(vni, zvrf_id(zvrf));
- if (!zl3vni) {
- snprintf(err, err_str_sz, "Could not add L3-VNI");
- return -1;
- }
/* associate the vrf with vni */
zvrf->l3vni = vni;
@@ -5875,8 +5859,6 @@ void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf)
{
struct zebra_vrf *evpn_zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf);
zebra_vxlan_cleanup_sg_table(zvrf);
@@ -6254,7 +6236,7 @@ static int zebra_evpn_pim_cfg_clean_up(struct zserv *client)
{
struct zebra_vrf *zvrf = zebra_vrf_get_evpn();
- if (zvrf && CHECK_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG)) {
+ if (CHECK_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("VxLAN SG updates to PIM, stop");
UNSET_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG);
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 464a8e5fc4..757c65d185 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -45,18 +45,13 @@ extern "C" {
#define EVPN_ENABLED(zvrf) (zvrf)->advertise_all_vni
static inline int is_evpn_enabled(void)
{
- struct zebra_vrf *zvrf = NULL;
- zvrf = zebra_vrf_get_evpn();
- return zvrf ? EVPN_ENABLED(zvrf) : 0;
+ return EVPN_ENABLED(zebra_vrf_get_evpn());
}
static inline int
is_vxlan_flooding_head_end(void)
{
struct zebra_vrf *zvrf = zebra_vrf_get_evpn();
-
- if (!zvrf)
- return 0;
return (zvrf->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL);
}
diff --git a/zebra/zserv.h b/zebra/zserv.h
index 7a5be001be..9986cc9f7e 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -36,14 +36,14 @@
#include "lib/linklist.h" /* for list */
#include "lib/workqueue.h" /* for work_queue */
#include "lib/hook.h" /* for DECLARE_HOOK, DECLARE_KOOH */
-
-#include "zebra/zebra_vrf.h" /* for zebra_vrf */
/* clang-format on */
#ifdef __cplusplus
extern "C" {
#endif
+struct zebra_vrf;
+
/* Default port information. */
#define ZEBRA_VTY_PORT 2601