diff options
149 files changed, 2821 insertions, 922 deletions
diff --git a/.github/workflows/behind-base.yml b/.github/workflows/behind-base.yml new file mode 100644 index 000000000..16b643477 --- /dev/null +++ b/.github/workflows/behind-base.yml @@ -0,0 +1,28 @@ +name: Add rebase label if the branch is > 50 commits behind + +on: + pull_request_target: + types: [synchronize, opened, reopened, labeled, unlabeled] + +jobs: + behind: + if: github.repository == 'frrouting/frr' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Set custom variables + id: vars + run: | + echo "behind_by=$(git log --oneline origin/${{ github.base_ref }} ^${{ github.event.pull_request.head.sha }} | wc -l)" >> $GITHUB_OUTPUT + - name: Add rebase label if needed + if: ${{ steps.vars.outputs.behind_by > 50 }} + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: rebase diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 5b06bc391..195298c81 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1467,7 +1467,7 @@ const uint8_t attr_flags_values[] = { [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_IPV6_EXT_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, - [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -4740,7 +4740,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, */ uint8_t attr_len = BGP_AIGP_TLV_METRIC_LEN; - stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); stream_putc(s, BGP_ATTR_AIGP); stream_putc(s, attr_len); stream_put_bgp_aigp_tlv_metric(s, bpi); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 7bf260901..a55593013 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -445,7 +445,8 @@ bool ecommunity_node_target_match(struct ecommunity *ecom, return match; } -static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr) +static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr, + int format) { /* * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -458,7 +459,11 @@ static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr) IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr); - snprintfrr(buf, bufsz, "NT:%pI4", &node_id); + + snprintfrr(buf, bufsz, "%s%pI4%s", + format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? "nt " : "NT:", + &node_id, + format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? ":0" : ""); (void)ptr; /* consume value */ } @@ -1059,7 +1064,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (sub_type == ECOMMUNITY_NODE_TARGET && type == ECOMMUNITY_ENCODE_IP) { ecommunity_node_target_str( - encbuf, sizeof(encbuf), pnt); + encbuf, sizeof(encbuf), pnt, + format); } else unk_ecom = 1; } else { @@ -1272,8 +1278,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_NODE_TARGET) - ecommunity_node_target_str(encbuf, - sizeof(encbuf), pnt); + ecommunity_node_target_str( + encbuf, sizeof(encbuf), pnt, format); else unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 980f35130..6272bdb88 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -360,7 +360,7 @@ bool bgp_mac_exist(const struct ethaddr *mac) return true; } -/* This API checks EVPN type-2 prefix and comapares +/* This API checks EVPN type-2 prefix and compares * mac against any of local assigned (SVIs) MAC * address. */ @@ -375,8 +375,6 @@ bool bgp_mac_entry_exists(const struct prefix *p) return false; return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac); - - return true; } static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f5ead66f2..4e4dce84f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -14947,35 +14947,42 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, RPKI_NOT_BEING_USED); } -DEFUN (show_ip_bgp_flowspec_routes_detailed, - show_ip_bgp_flowspec_routes_detailed_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" flowspec] detail [json]", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - BGP_AFI_HELP_STR - "SAFI Flowspec\n" - "Detailed information on flowspec entries\n" - JSON_STR) +/* + * Used for "detailed" output for cmds like show bgp <afi> <safi> (or) + * show bgp <vrf> (or) show bgp <vrf> <afi> <safi> + */ +DEFPY(show_ip_bgp_vrf_afi_safi_routes_detailed, + show_ip_bgp_vrf_afi_safi_routes_detailed_cmd, + "show [ip] bgp [<view|vrf> VIEWVRFNAME$vrf_name] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] detail [json$uj]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Detailed information\n" + JSON_STR) { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; int idx = 0; - bool uj = use_json(argc, argv); uint16_t show_flags = BGP_SHOW_OPT_ROUTES_DETAIL; - if (uj) { - argc--; + if (uj) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); - } bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); if (!idx) return CMD_WARNING; + /* 'vrf all' case to iterate all vrfs & show output per vrf instance */ + if (vrf_name && strmatch(vrf_name, "all")) { + bgp_show_all_instances_routes_vty(vty, afi, safi, show_flags); + return CMD_SUCCESS; + } + /* All other cases except vrf all */ return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, show_flags, RPKI_NOT_BEING_USED); } @@ -16091,8 +16098,9 @@ void bgp_route_init(void) install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd); install_element(VIEW_NODE, &show_ip_bgp_large_community_cmd); - /* show bgp ipv4 flowspec detailed */ - install_element(VIEW_NODE, &show_ip_bgp_flowspec_routes_detailed_cmd); + /* show bgp vrf <afi> <safi> detailed */ + install_element(VIEW_NODE, + &show_ip_bgp_vrf_afi_safi_routes_detailed_cmd); install_element(VIEW_NODE, &show_bgp_listeners_cmd); install_element(VIEW_NODE, &show_bgp_peerhash_cmd); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 10fc3ecda..7db110be9 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -872,6 +872,46 @@ static const struct route_map_rule_cmd route_match_ip_next_hop_type_free }; +/* `match source-protocol` */ +static enum route_map_cmd_result_t +route_match_source_protocol(void *rule, const struct prefix *prefix, + void *object) +{ + struct bgp_path_info *path = object; + int *protocol = rule; + + if (!path) + return RMAP_NOMATCH; + + if (path->type == *protocol) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void *route_match_source_protocol_compile(const char *arg) +{ + int *protocol; + + protocol = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*protocol)); + *protocol = proto_name2num(arg); + + return protocol; +} + +static void route_match_source_protocol_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd route_match_source_protocol_cmd = { + "source-protocol", + route_match_source_protocol, + route_match_source_protocol_compile, + route_match_source_protocol_free +}; + + /* `match ip route-source prefix-list PREFIX_LIST' */ static enum route_map_cmd_result_t @@ -7177,6 +7217,42 @@ DEFPY_YANG (match_rpki_extcommunity, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG (match_source_protocol, + match_source_protocol_cmd, + "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto", + MATCH_STR + "Match protocol via which the route was learnt\n" + FRR_REDIST_HELP_STR_ZEBRA) +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:source-protocol']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:source-protocol", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (no_match_source_protocol, + no_match_source_protocol_cmd, + "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]", + NO_STR + MATCH_STR + "Match protocol via which the route was learnt\n" + FRR_REDIST_HELP_STR_ZEBRA) +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:source-protocol']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + /* Initialization of route map. */ void bgp_route_map_init(void) { @@ -7252,6 +7328,7 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_ip_address_prefix_list_cmd); route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd); route_map_install_match(&route_match_ip_next_hop_type_cmd); + route_map_install_match(&route_match_source_protocol_cmd); route_map_install_match(&route_match_ip_route_source_prefix_list_cmd); route_map_install_match(&route_match_aspath_cmd); route_map_install_match(&route_match_community_cmd); @@ -7441,6 +7518,8 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &match_rpki_extcommunity_cmd); + install_element(RMAP_NODE, &match_source_protocol_cmd); + install_element(RMAP_NODE, &no_match_source_protocol_cmd); #ifdef HAVE_SCRIPTING install_element(RMAP_NODE, &match_script_cmd); #endif diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 6e8439cc2..282ebe911 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -74,6 +74,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index bcd1e837e..7066fdb41 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -30,6 +30,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_m struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy( struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(struct nb_cb_modify_args *args); diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 938a5ec31..02564b000 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -369,6 +369,60 @@ lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy( /* * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol + */ +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + enum rmap_compile_rets ret; + const char *proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "source-protocol"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "source-protocol", + proto, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity */ int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify( diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 1be44adde..7ef9db9f0 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -11620,8 +11620,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, &peer->ibuf->count, memory_order_relaxed); - json_object_int_add(json_peer, "tableVersion", - peer->version[afi][safi]); + json_object_int_add( + json_peer, "tableVersion", + (paf && PAF_SUBGRP(paf)) + ? paf->subgroup->version + : 0); json_object_int_add(json_peer, "outq", outq_count); json_object_int_add(json_peer, "inq", @@ -11799,8 +11802,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, " %9u %9u %8" PRIu64 " %4zu %4zu %8s", PEER_TOTAL_RX(peer), PEER_TOTAL_TX(peer), - peer->version[afi][safi], inq_count, - outq_count, + (paf && PAF_SUBGRP(paf)) + ? paf->subgroup->version + : 0, + inq_count, outq_count, peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL)); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 42ad8a563..99c6a46e7 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1543,6 +1543,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->ifname = XSTRDUP(MTYPE_BGP_PEER_IFNAME, peer_src->ifname); } + peer_dst->ttl = peer_src->ttl; } static int bgp_peer_conf_if_to_su_update_v4(struct peer *peer, diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 68b32b594..9cb1d5108 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1119,7 +1119,6 @@ struct peer { /* BGP peer group. */ struct peer_group *group; - uint64_t version[AFI_MAX][SAFI_MAX]; /* BGP peer_af structures, per configured AF on this peer */ struct peer_af *peer_af_array[BGP_AF_MAX]; diff --git a/doc/developer/building-frr-for-ubuntu2204.rst b/doc/developer/building-frr-for-ubuntu2204.rst new file mode 100644 index 000000000..6b941b367 --- /dev/null +++ b/doc/developer/building-frr-for-ubuntu2204.rst @@ -0,0 +1,171 @@ +Ubuntu 22.04 LTS +================ + +This document describes installation from source. If you want to build a +``deb``, see :ref:`packaging-debian`. + +Installing Dependencies +----------------------- + +.. code-block:: console + + sudo apt update + sudo apt-get install \ + git autoconf automake libtool make libreadline-dev texinfo \ + pkg-config libpam0g-dev libjson-c-dev bison flex \ + libc-ares-dev python3-dev python3-sphinx \ + install-info build-essential libsnmp-dev perl \ + libcap-dev python2 libelf-dev libunwind-dev \ + libyang2 libyang2-dev + +.. include:: building-libunwind-note.rst + +Note that Ubuntu >= 20 no longer installs python 2.x, so it must be +installed explicitly. Ensure that your system has a symlink named +``/usr/bin/python`` pointing at ``/usr/bin/python3``. + +.. code-block:: shell + + sudo ln -s /usr/bin/python3 /usr/bin/python + python --version + +In addition, ``pip`` for python2 must be installed if you wish to run +the FRR topotests. That version of ``pip`` is not available from the +ubuntu apt repositories; in order to install it: + +.. code-block:: shell + + curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py + sudo python2 ./get-pip.py + + # And verify the installation + pip2 --version + + +Protobuf +^^^^^^^^ +This is optional + +.. code-block:: console + + sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + +ZeroMQ +^^^^^^ +This is optional + +.. code-block:: console + + sudo apt-get install libzmq5 libzmq3-dev + +Building & Installing FRR +------------------------- + +Add FRR user and groups +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo groupadd -r -g 92 frr + sudo groupadd -r -g 85 frrvty + sudo adduser --system --ingroup frr --home /var/run/frr/ \ + --gecos "FRR suite" --shell /sbin/nologin frr + sudo usermod -a -G frrvty frr + +Compile +^^^^^^^ + +.. include:: include-compile.rst + +Install FRR configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo install -m 775 -o frr -g frr -d /var/log/frr + sudo install -m 775 -o frr -g frrvty -d /etc/frr + sudo install -m 640 -o frr -g frrvty tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/frr.conf /etc/frr/frr.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/daemons.conf /etc/frr/daemons.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/daemons /etc/frr/daemons + +Tweak sysctls +^^^^^^^^^^^^^ + +Some sysctls need to be changed in order to enable IPv4/IPv6 forwarding and +MPLS (if supported by your platform). If your platform does not support MPLS, +skip the MPLS related configuration in this section. + +Edit :file:`/etc/sysctl.conf` and uncomment the following values (ignore the +other settings): + +:: + + # Uncomment the next line to enable packet forwarding for IPv4 + net.ipv4.ip_forward=1 + + # Uncomment the next line to enable packet forwarding for IPv6 + # Enabling this option disables Stateless Address Autoconfiguration + # based on Router Advertisements for this host + net.ipv6.conf.all.forwarding=1 + +Reboot or use ``sysctl -p`` to apply the same config to the running system. + +Add MPLS kernel modules +""""""""""""""""""""""" + +Ubuntu 20.04 ships with kernel 5.4; MPLS modules are present by default. To +enable, add the following lines to :file:`/etc/modules-load.d/modules.conf`: + +:: + + # Load MPLS Kernel Modules + mpls_router + mpls_iptunnel + + +And load the kernel modules on the running system: + +.. code-block:: console + + sudo modprobe mpls-router mpls-iptunnel + +If the above command returns an error, you may need to install the appropriate +or latest linux-modules-extra-<kernel-version>-generic package. For example +``apt-get install linux-modules-extra-`uname -r`-generic`` + +Enable MPLS Forwarding +"""""""""""""""""""""" + +Edit :file:`/etc/sysctl.conf` and the following lines. Make sure to add a line +equal to :file:`net.mpls.conf.eth0.input` for each interface used with MPLS. + +:: + + # Enable MPLS Label processing on all interfaces + net.mpls.conf.eth0.input=1 + net.mpls.conf.eth1.input=1 + net.mpls.conf.eth2.input=1 + net.mpls.platform_labels=100000 + +Install service files +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service + sudo systemctl enable frr + +Enable daemons +^^^^^^^^^^^^^^ + +Open :file:`/etc/frr/daemons` with your text editor of choice. Look for the +section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons +as required by changing the value to ``yes``. + +Start FRR +^^^^^^^^^ + +.. code-block:: shell + + systemctl start frr diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 13936e18e..f44cf9df9 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -17,15 +17,23 @@ Tested with Ubuntu 20.04,Ubuntu 18.04, and Debian 11. Instructions are the same for all setups (i.e. ExaBGP is only used for BGP tests). +Tshark is only required if you enable any packet captures on test runs. + +Valgrind is only required if you enable valgrind on test runs. + Installing Topotest Requirements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: shell - apt-get install gdb - apt-get install iproute2 - apt-get install net-tools - apt-get install python3-pip + apt-get install \ + gdb \ + iproute2 \ + net-tools \ + python3-pip \ + iputils-ping \ + tshark \ + valgrind python3 -m pip install wheel python3 -m pip install 'pytest>=6.2.4' python3 -m pip install 'pytest-xdist>=2.3.0' @@ -119,6 +127,7 @@ If you prefer to manually build FRR, then use the following suggested config: --sysconfdir=/etc/frr \ --enable-vtysh \ --enable-pimd \ + --enable-pim6d \ --enable-sharpd \ --enable-multipath=64 \ --enable-user=frr \ @@ -311,32 +320,6 @@ Test will set exit code which can be used with ``git bisect``. For the simulated topology, see the description in the python file. -StdErr log from daemos after exit -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To enable the reporting of any messages seen on StdErr after the daemons exit, -the following env variable can be set:: - - export TOPOTESTS_CHECK_STDERR=Yes - -(The value doesn't matter at this time. The check is whether the env -variable exists or not.) There is no pass/fail on this reporting; the -Output will be reported to the console. - -Collect Memory Leak Information -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -FRR processes can report unfreed memory allocations upon exit. To -enable the reporting of memory leaks, define an environment variable -``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.:: - - export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" - -This will enable the check and output to console and the writing of -the information to files with the given prefix (followed by testname), -ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case -of a memory leak. - Running Topotests with AddressSanitizer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -562,6 +545,48 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router --gdb-breakpoints=nb_config_diff \ all-protocol-startup +Reporting Memleaks with FRR Memory Statistics +""""""""""""""""""""""""""""""""""""""""""""" + +FRR reports all allocated FRR memory objects on exit to standard error. +Topotest can be run to report such output as errors in order to check for +memleaks in FRR memory allocations. Specifying the CLI argument +``--memleaks`` will enable reporting FRR-based memory allocations at exit as errors. + +.. code:: shell + + sudo -E pytest --memleaks all-protocol-startup + + +StdErr log from daemos after exit +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When running with ``--memleaks``, to enable the reporting of other, +non-memory related, messages seen on StdErr after the daemons exit, +the following env variable can be set:: + + export TOPOTESTS_CHECK_STDERR=Yes + +(The value doesn't matter at this time. The check is whether the env +variable exists or not.) There is no pass/fail on this reporting; the +Output will be reported to the console. + +Collect Memory Leak Information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When running with ``--memleaks``, FRR processes report unfreed memory +allocations upon exit. To enable also reporting of memory leaks to a specific +location, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the +file prefix, i.e.: + + export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" + +For tests that support the TOPOTESTS_CHECK_MEMLEAK environment variable, this +will enable output to the information to files with the given prefix (followed +by testname), e.g.,: +file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case +of a memory leak. + Detecting Memleaks with Valgrind """""""""""""""""""""""""""""""" diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index f11fff5de..28cf5d0ab 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -537,7 +537,8 @@ Programming Languages, Tools and Libraries ========================================== The core of FRR is written in C (gcc or clang supported) and makes -use of GNU compiler extensions. A few non-essential scripts are +use of GNU compiler extensions. Additionally, the CLI generation +tool, `clippy`, requires Python. A few other non-essential scripts are implemented in Perl and Python. FRR requires the following tools to build distribution packages: automake, autoconf, texinfo, libtool and gawk and various libraries (i.e. libpam and libjson-c). diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 3910caf7f..f045ca239 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2032,6 +2032,13 @@ Capability Negotiation Disabled by default. +.. clicmd:: neighbor PEER aigp + + Send and receive AIGP attribute for this neighbor. This is valid only for + eBGP neighbors. + + Disabled by default. iBGP neighbors have this option enabled implicitly. + .. _bgp-as-path-access-lists: AS Path Access Lists @@ -4116,6 +4123,122 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. If ``afi`` is specified, with ``all`` option, routes will be displayed for each SAFI in the selected AFI. +.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] [afi] [safi] detail [json] + + Display the detailed version of all routes from the specified bgp vrf table + for a given afi + safi. + + If no vrf is specified, then it is assumed as a default vrf and routes + are displayed from default vrf table. + + If ``all`` option is specified as vrf name, then all bgp vrf tables routes + from a given afi+safi are displayed in the detailed output of routes. + + If ``json`` option is specified, detailed output is displayed in JSON format. + + Following are sample output for few examples of how to use this command. + +.. code-block:: frr + + torm-23# sh bgp ipv4 unicast detail (OR) sh bgp vrf default ipv4 unicast detail + + !--- Output suppressed. + + BGP routing table entry for 172.16.16.1/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local, (Received from a RR-client) + 172.16.16.1 (metric 20) from torm-22(172.16.16.1) (192.168.0.10) + Origin IGP, metric 0, localpref 100, valid, internal + Last update: Fri May 8 12:54:05 2023 + BGP routing table entry for 172.16.16.2/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local + 0.0.0.0 from 0.0.0.0 (172.16.16.2) + Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received) + Last update: Wed May 8 12:54:41 2023 + + Displayed 2 routes and 2 total paths + +.. code-block:: frr + + torm-23# sh bgp vrf all detail + + Instance default: + + !--- Output suppressed. + + BGP routing table entry for 172.16.16.1/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local, (Received from a RR-client) + 172.16.16.1 (metric 20) from torm-22(172.16.16.1) (192.168.0.10) + Origin IGP, metric 0, localpref 100, valid, internal + Last update: Fri May 8 12:44:05 2023 + BGP routing table entry for 172.16.16.2/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local + 0.0.0.0 from 0.0.0.0 (172.16.16.2) + Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received) + Last update: Wed May 8 12:45:01 2023 + + Displayed 2 routes and 2 total paths + + Instance vrf3: + + !--- Output suppressed. + + BGP routing table entry for 192.168.0.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:12:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.0.2], VNI 1008/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1008 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:41:55 2023 + BGP routing table entry for 192.168.1.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:13:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.1.2], VNI 1009/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:41:55 2023 + + Displayed 2 routes and 2 total paths + + +.. code-block:: frr + + torm-23# sh bgp vrf vrf3 ipv4 unicast detail + + !--- Output suppressed. + + BGP routing table entry for 192.168.0.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:12:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.0.2], VNI 1008/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1008 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:23:35 2023 + BGP routing table entry for 192.168.1.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:13:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.1.2], VNI 1009/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:23:55 2023 + + Displayed 2 routes and 2 total paths + .. _bgp-display-routes-by-community: Displaying Routes by Community Attribute diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 8dacb9c9d..2f4c956ff 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -287,6 +287,19 @@ OSPF6 interface Sets interface's Router Dead Interval. Default value is 40. +.. clicmd:: ipv6 ospf6 graceful-restart hello-delay HELLODELAYINTERVAL + + Set the length of time during which Grace-LSAs are sent at 1-second intervals + while coming back up after an unplanned outage. During this time, no hello + packets are sent. + + A higher hello delay will increase the chance that all neighbors are notified + about the ongoing graceful restart before receiving a hello packet (which is + crucial for the graceful restart to succeed). The hello delay shouldn't be set + too high, however, otherwise the adjacencies might time out. As a best practice, + it's recommended to set the hello delay and hello interval with the same values. + The default value is 10 seconds. + .. clicmd:: ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL Sets interface's Rxmt Interval. Default value is 5. @@ -343,6 +356,10 @@ Graceful Restart To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf" EXEC-level command needs to be issued before restarting the ospf6d daemon. + When Graceful Restart is enabled and the ospf6d daemon crashes or is killed + abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once + it restarts. + .. clicmd:: graceful-restart helper enable [A.B.C.D] diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 517183260..effad0fd0 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -635,6 +635,19 @@ Interfaces :clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also specified for the interface. +.. clicmd:: ip ospf graceful-restart hello-delay (1-1800) + + Set the length of time during which Grace-LSAs are sent at 1-second intervals + while coming back up after an unplanned outage. During this time, no hello + packets are sent. + + A higher hello delay will increase the chance that all neighbors are notified + about the ongoing graceful restart before receiving a hello packet (which is + crucial for the graceful restart to succeed). The hello delay shouldn't be set + too high, however, otherwise the adjacencies might time out. As a best practice, + it's recommended to set the hello delay and hello interval with the same values. + The default value is 10 seconds. + .. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]) When configuring a point-to-point network on an interface and the interface @@ -770,6 +783,10 @@ Graceful Restart To perform a graceful shutdown, the "graceful-restart prepare ip ospf" EXEC-level command needs to be issued before restarting the ospfd daemon. + When Graceful Restart is enabled and the ospfd daemon crashes or is killed + abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once + it restarts. + .. clicmd:: graceful-restart helper enable [A.B.C.D] diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index 67323e61f..f9c772430 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -159,6 +159,11 @@ RIP Configuration If `poisoned-reverse` is also set, the router sends the poisoned routes with highest metric back to the sending router. +.. clicmd:: allow-ecmp [1-MULTIPATH_NUM] + + Control how many ECMP paths RIP can inject for the same prefix. If specified + without a number, a maximum is taken (compiled with ``--enable-multipath``). + .. _rip-version-control: RIP Version Control diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index b7f533656..bd19ae88e 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -212,7 +212,7 @@ Route Map Match Command .. clicmd:: match source-protocol PROTOCOL_NAME - This is a ZEBRA specific match command. Matches the + This is a ZEBRA and BGP specific match command. Matches the originating protocol specified. .. clicmd:: match source-instance NUMBER diff --git a/doc/user/static.rst b/doc/user/static.rst index 05847ba39..6d8aca97b 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -90,7 +90,7 @@ a static prefix and gateway, with several possible forms. Multiple nexthop static route ============================= -To create multiple nexthops to the same NETWORK, just reenter the same +To create multiple nexthops to the same NETWORK (also known as a multipath route), just reenter the same network statement with different nexthop information. .. code-block:: frr @@ -122,7 +122,7 @@ nexthops, if the platform supports this. ip route 10.0.0.0/8 null0 255 -This will install a multihop route via the specified next-hops if they are +This will install a multipath route via the specified next-hops if they are reachable, as well as a high-distance blackhole route, which can be useful to prevent traffic destined for a prefix to match less-specific routes (e.g. default) should the specified gateways not be reachable. E.g.: diff --git a/ldpd/interface.c b/ldpd/interface.c index ad5d853b6..f0e70cbac 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -138,14 +138,13 @@ void if_update_info(struct iface *iface, struct kif *kif) { /* get type */ - if (kif->flags & IFF_POINTOPOINT) + if (CHECK_FLAG(kif->flags, IFF_POINTOPOINT)) iface->type = IF_TYPE_POINTOPOINT; - if (kif->flags & IFF_BROADCAST && - kif->flags & IFF_MULTICAST) + if (CHECK_FLAG(kif->flags, IFF_BROADCAST) && + CHECK_FLAG(kif->flags, IFF_MULTICAST)) iface->type = IF_TYPE_BROADCAST; - if (ldpd_process == PROC_LDP_ENGINE && iface->operative && - !kif->operative) + if (ldpd_process == PROC_LDP_ENGINE && iface->operative && !kif->operative) ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN); /* get index and flags */ @@ -276,8 +275,7 @@ if_start(struct iface *iface, int af) struct iface_af *ia; struct timeval now; - log_debug("%s: %s address-family %s", __func__, iface->name, - af_name(af)); + log_debug("%s: %s address-family %s", __func__, iface->name, af_name(af)); ia = iface_af_get(iface, af); @@ -560,8 +558,7 @@ if_join_ipv4_group(struct iface *iface, struct in_addr *addr) { struct in_addr if_addr; - log_debug("%s: interface %s addr %pI4", __func__, iface->name, - addr); + log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr); if_addr.s_addr = if_get_ipv4_addr(iface); @@ -579,8 +576,7 @@ if_leave_ipv4_group(struct iface *iface, struct in_addr *addr) { struct in_addr if_addr; - log_debug("%s: interface %s addr %pI4", __func__, iface->name, - addr); + log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr); if_addr.s_addr = if_get_ipv4_addr(iface); diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index dcaf1cc10..3c5a5d999 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -47,12 +47,11 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) while ((me = TAILQ_FIRST(mh)) != NULL) { /* generate pdu */ if (first) { - if ((buf = ibuf_open(nbr->max_pdu_len + - LDP_HDR_DEAD_LEN)) == NULL) + if ((buf = ibuf_open(nbr->max_pdu_len + LDP_HDR_DEAD_LEN)) == NULL) fatal(__func__); /* real size will be set up later */ - err |= gen_ldp_hdr(buf, 0); + SET_FLAG(err, gen_ldp_hdr(buf, 0)); size = LDP_HDR_SIZE; first = 0; @@ -63,9 +62,9 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) msg_size += len_fec_tlv(&me->map); if (me->map.label != NO_LABEL) msg_size += LABEL_TLV_SIZE; - if (me->map.flags & F_MAP_REQ_ID) + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) msg_size += REQID_TLV_SIZE; - if (me->map.flags & F_MAP_STATUS) + if (CHECK_FLAG(me->map.flags, F_MAP_STATUS)) msg_size += STATUS_SIZE; /* maximum pdu length exceeded, we need a new ldp pdu */ @@ -78,17 +77,17 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) size += msg_size; /* append message and tlvs */ - err |= gen_msg_hdr(buf, type, msg_size); - err |= gen_fec_tlv(buf, &me->map); + SET_FLAG(err, gen_msg_hdr(buf, type, msg_size)); + SET_FLAG(err, gen_fec_tlv(buf, &me->map)); if (me->map.label != NO_LABEL) - err |= gen_label_tlv(buf, me->map.label); - if (me->map.flags & F_MAP_REQ_ID) - err |= gen_reqid_tlv(buf, me->map.requestid); - if (me->map.flags & F_MAP_PW_STATUS) - err |= gen_pw_status_tlv(buf, me->map.pw_status); - if (me->map.flags & F_MAP_STATUS) - err |= gen_status_tlv(buf, me->map.st.status_code, - me->map.st.msg_id, me->map.st.msg_type); + SET_FLAG(err, gen_label_tlv(buf, me->map.label)); + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) + SET_FLAG(err, gen_reqid_tlv(buf, me->map.requestid)); + if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS)) + SET_FLAG(err, gen_pw_status_tlv(buf, me->map.pw_status)); + if (CHECK_FLAG(me->map.flags, F_MAP_STATUS)) + SET_FLAG(err, gen_status_tlv(buf, me->map.st.status_code, + me->map.st.msg_id, me->map.st.msg_type)); if (err) { ibuf_free(buf); mapping_list_clr(mh); @@ -172,15 +171,13 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) memset(&map, 0, sizeof(map)); map.msg_id = msg.id; - if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, - &map)) == -1) + if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, &map)) == -1) goto err; if (map.type == MAP_TYPE_PWID && - !(map.flags & F_MAP_PW_ID) && + !CHECK_FLAG(map.flags, F_MAP_PW_ID) && type != MSG_TYPE_LABELWITHDRAW && type != MSG_TYPE_LABELRELEASE) { - send_notification(nbr->tcp, S_MISS_MSG, msg.id, - msg.type); + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); goto err; } @@ -193,8 +190,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELMAPPING: case MSG_TYPE_LABELREQUEST: case MSG_TYPE_LABELABORTREQ: - session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, - msg.type); + session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type); goto err; default: break; @@ -211,8 +207,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) switch (type) { case MSG_TYPE_LABELMAPPING: case MSG_TYPE_LABELABORTREQ: - session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, - msg.type); + session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type); goto err; default: break; @@ -223,8 +218,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) * LDP supports the use of multiple FEC Elements per * FEC for the Label Mapping message only. */ - if (type != MSG_TYPE_LABELMAPPING && - tlen != feclen) { + if (type != MSG_TYPE_LABELMAPPING && tlen != feclen) { session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } @@ -262,10 +256,10 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) * For Label Mapping messages the Label TLV is mandatory and * should appear right after the FEC TLV. */ - if (current_tlv == 1 && type == MSG_TYPE_LABELMAPPING && - !(tlv_type & TLV_TYPE_GENERICLABEL)) { - send_notification(nbr->tcp, S_MISS_MSG, msg.id, - msg.type); + if (current_tlv == 1 && + type == MSG_TYPE_LABELMAPPING && + !CHECK_FLAG(tlv_type, TLV_TYPE_GENERICLABEL)) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); goto err; } @@ -275,12 +269,11 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELMAPPING: case MSG_TYPE_LABELREQUEST: if (tlv_len != REQID_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } - flags |= F_MAP_REQ_ID; + SET_FLAG(flags, F_MAP_REQ_ID); memcpy(&reqbuf, buf, sizeof(reqbuf)); reqid = ntohl(reqbuf); break; @@ -299,8 +292,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELWITHDRAW: case MSG_TYPE_LABELRELEASE: if (tlv_len != LABEL_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } @@ -312,8 +304,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) label != MPLS_LABEL_IPV4_EXPLICIT_NULL && label != MPLS_LABEL_IPV6_EXPLICIT_NULL && label != MPLS_LABEL_IMPLICIT_NULL)) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } break; @@ -329,8 +320,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELWITHDRAW: case MSG_TYPE_LABELRELEASE: /* unsupported */ - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; break; default: @@ -340,8 +330,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) break; case TLV_TYPE_STATUS: if (tlv_len != STATUS_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } /* ignore */ @@ -350,12 +339,11 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) switch (type) { case MSG_TYPE_LABELMAPPING: if (tlv_len != PW_STATUS_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } - flags |= F_MAP_PW_STATUS; + SET_FLAG(flags, F_MAP_PW_STATUS); memcpy(&statusbuf, buf, sizeof(statusbuf)); pw_status = ntohl(statusbuf); break; @@ -365,7 +353,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) } break; default: - if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) send_notification_rtlvs(nbr, S_UNKNOWN_TLV, msg.id, msg.type, tlv_type, tlv_len, buf); /* ignore unknown tlv */ @@ -380,14 +368,13 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) while ((me = TAILQ_FIRST(&mh)) != NULL) { int imsg_type = IMSG_NONE; - me->map.flags |= flags; + SET_FLAG(me->map.flags, flags); switch (me->map.type) { case MAP_TYPE_PREFIX: switch (me->map.fec.prefix.af) { case AF_INET: if (label == MPLS_LABEL_IPV6_EXPLICIT_NULL) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } if (!nbr->v4_enabled) @@ -395,8 +382,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) break; case AF_INET6: if (label == MPLS_LABEL_IPV4_EXPLICIT_NULL) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } if (!nbr->v6_enabled) @@ -408,18 +394,17 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) break; case MAP_TYPE_PWID: if (label <= MPLS_LABEL_RESERVED_MAX) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } - if (me->map.flags & F_MAP_PW_STATUS) + if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS)) me->map.pw_status = pw_status; break; default: break; } me->map.label = label; - if (me->map.flags & F_MAP_REQ_ID) + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) me->map.requestid = reqid; log_msg_mapping(0, type, nbr, &me->map); @@ -513,11 +498,11 @@ len_fec_tlv(struct map *map) break; case MAP_TYPE_PWID: len += FEC_PWID_ELM_MIN_LEN; - if (map->flags & F_MAP_PW_ID) + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) len += PW_STATUS_TLV_LEN; - if (map->flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) len += FEC_SUBTLV_IFMTU_SIZE; - if (map->flags & F_MAP_PW_STATUS) + if (CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) len += PW_STATUS_TLV_SIZE; break; case MAP_TYPE_TYPED_WCARD: @@ -552,15 +537,15 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) switch (map->type) { case MAP_TYPE_WILDCARD: ft.length = htons(sizeof(uint8_t)); - err |= ibuf_add(buf, &ft, sizeof(ft)); - err |= ibuf_add(buf, &map->type, sizeof(map->type)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type))); break; case MAP_TYPE_PREFIX: len = PREFIX_SIZE(map->fec.prefix.prefixlen); ft.length = htons(sizeof(map->type) + sizeof(family) + sizeof(map->fec.prefix.prefixlen) + len); - err |= ibuf_add(buf, &ft, sizeof(ft)); - err |= ibuf_add(buf, &map->type, sizeof(map->type)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type))); switch (map->fec.prefix.af) { case AF_INET: family = htons(AF_IPV4); @@ -572,45 +557,45 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) fatalx("gen_fec_tlv: unknown af"); break; } - err |= ibuf_add(buf, &family, sizeof(family)); - err |= ibuf_add(buf, &map->fec.prefix.prefixlen, - sizeof(map->fec.prefix.prefixlen)); + SET_FLAG(err, ibuf_add(buf, &family, sizeof(family))); + SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefixlen, + sizeof(map->fec.prefix.prefixlen))); if (len) - err |= ibuf_add(buf, &map->fec.prefix.prefix, len); + SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefix, len)); break; case MAP_TYPE_PWID: - if (map->flags & F_MAP_PW_ID) + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) pw_len += FEC_PWID_SIZE; - if (map->flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) pw_len += FEC_SUBTLV_IFMTU_SIZE; len = FEC_PWID_ELM_MIN_LEN + pw_len; ft.length = htons(len); - err |= ibuf_add(buf, &ft, sizeof(ft)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); - err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t))); pw_type = map->fec.pwid.type; - if (map->flags & F_MAP_PW_CWORD) - pw_type |= CONTROL_WORD_FLAG; + if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD)) + SET_FLAG(pw_type, CONTROL_WORD_FLAG); pw_type = htons(pw_type); - err |= ibuf_add(buf, &pw_type, sizeof(uint16_t)); - err |= ibuf_add(buf, &pw_len, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t))); + SET_FLAG(err, ibuf_add(buf, &pw_len, sizeof(uint8_t))); group_id = htonl(map->fec.pwid.group_id); - err |= ibuf_add(buf, &group_id, sizeof(uint32_t)); - if (map->flags & F_MAP_PW_ID) { + SET_FLAG(err, ibuf_add(buf, &group_id, sizeof(uint32_t))); + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) { pwid = htonl(map->fec.pwid.pwid); - err |= ibuf_add(buf, &pwid, sizeof(uint32_t)); + SET_FLAG(err, ibuf_add(buf, &pwid, sizeof(uint32_t))); } - if (map->flags & F_MAP_PW_IFMTU) { + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) { struct subtlv stlv; stlv.type = SUBTLV_IFMTU; stlv.length = FEC_SUBTLV_IFMTU_SIZE; - err |= ibuf_add(buf, &stlv, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &stlv, sizeof(uint16_t))); ifmtu = htons(map->fec.pwid.ifmtu); - err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &ifmtu, sizeof(uint16_t))); } break; case MAP_TYPE_TYPED_WCARD: @@ -624,14 +609,14 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) fatalx("gen_fec_tlv: unexpected fec type"); } ft.length = htons(len); - err |= ibuf_add(buf, &ft, sizeof(ft)); - err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); - err |= ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t))); + SET_FLAG(err, ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t))); switch (map->fec.twcard.type) { case MAP_TYPE_PREFIX: twcard_len = sizeof(uint16_t); - err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t))); switch (map->fec.twcard.u.prefix_af) { case AF_INET: @@ -645,13 +630,13 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) break; } - err |= ibuf_add(buf, &family, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &family, sizeof(uint16_t))); break; case MAP_TYPE_PWID: twcard_len = sizeof(uint16_t); - err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t))); pw_type = htons(map->fec.twcard.u.pw_type); - err |= ibuf_add(buf, &pw_type, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t))); break; default: fatalx("gen_fec_tlv: unexpected fec type"); @@ -679,21 +664,18 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, if (len == FEC_ELM_WCARD_LEN) return (off); else { - session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); return (-1); } break; case MAP_TYPE_PREFIX: if (len < FEC_ELM_PREFIX_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } /* Address Family */ - memcpy(&map->fec.prefix.af, buf + off, - sizeof(map->fec.prefix.af)); + memcpy(&map->fec.prefix.af, buf + off, sizeof(map->fec.prefix.af)); off += sizeof(map->fec.prefix.af); map->fec.prefix.af = ntohs(map->fec.prefix.af); switch (map->fec.prefix.af) { @@ -704,8 +686,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, map->fec.prefix.af = AF_INET6; break; default: - send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id, - msg->type); + send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id, msg->type); return (-1); } @@ -716,19 +697,16 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, && map->fec.prefix.prefixlen > IPV4_MAX_BITLEN) || (map->fec.prefix.af == AF_IPV6 && map->fec.prefix.prefixlen > IPV6_MAX_BITLEN)) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); return (-1); } if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } /* Prefix */ - memset(&map->fec.prefix.prefix, 0, - sizeof(map->fec.prefix.prefix)); + memset(&map->fec.prefix.prefix, 0, sizeof(map->fec.prefix.prefix)); memcpy(&map->fec.prefix.prefix, buf + off, PREFIX_SIZE(map->fec.prefix.prefixlen)); @@ -739,17 +717,16 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, return (off + PREFIX_SIZE(map->fec.prefix.prefixlen)); case MAP_TYPE_PWID: if (len < FEC_PWID_ELM_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } /* PW type */ memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t)); map->fec.pwid.type = ntohs(map->fec.pwid.type); - if (map->fec.pwid.type & CONTROL_WORD_FLAG) { - map->flags |= F_MAP_PW_CWORD; - map->fec.pwid.type &= ~CONTROL_WORD_FLAG; + if (CHECK_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG)) { + SET_FLAG(map->flags, F_MAP_PW_CWORD); + UNSET_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG); } off += sizeof(uint16_t); @@ -758,8 +735,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, off += sizeof(uint8_t); if (len != FEC_PWID_ELM_MIN_LEN + pw_len) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } @@ -773,14 +749,13 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, return (off); if (pw_len < sizeof(uint32_t)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t)); map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid); - map->flags |= F_MAP_PW_ID; + SET_FLAG(map->flags, F_MAP_PW_ID); off += sizeof(uint32_t); pw_len -= sizeof(uint32_t); @@ -789,29 +764,26 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, struct subtlv stlv; if (pw_len < sizeof(stlv)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } memcpy(&stlv, buf + off, sizeof(stlv)); if (stlv.length > pw_len) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } switch (stlv.type) { case SUBTLV_IFMTU: if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg->id, msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } memcpy(&map->fec.pwid.ifmtu, buf + off + SUBTLV_HDR_SIZE, sizeof(uint16_t)); map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu); - map->flags |= F_MAP_PW_IFMTU; + SET_FLAG(map->flags, F_MAP_PW_IFMTU); break; default: /* ignore */ @@ -824,8 +796,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, return (off); case MAP_TYPE_TYPED_WCARD: if (len < FEC_ELM_TWCARD_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } @@ -834,23 +805,19 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, memcpy(&twcard_len, buf + off, sizeof(uint8_t)); off += sizeof(uint8_t); if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } switch (map->fec.twcard.type) { case MAP_TYPE_PREFIX: if (twcard_len != sizeof(uint16_t)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } - memcpy(&map->fec.twcard.u.prefix_af, buf + off, - sizeof(uint16_t)); - map->fec.twcard.u.prefix_af = - ntohs(map->fec.twcard.u.prefix_af); + memcpy(&map->fec.twcard.u.prefix_af, buf + off, sizeof(uint16_t)); + map->fec.twcard.u.prefix_af = ntohs(map->fec.twcard.u.prefix_af); off += sizeof(uint16_t); switch (map->fec.twcard.u.prefix_af) { @@ -861,29 +828,24 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, map->fec.twcard.u.prefix_af = AF_INET6; break; default: - session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); return (-1); } break; case MAP_TYPE_PWID: if (twcard_len != sizeof(uint16_t)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } - memcpy(&map->fec.twcard.u.pw_type, buf + off, - sizeof(uint16_t)); - map->fec.twcard.u.pw_type = - ntohs(map->fec.twcard.u.pw_type); + memcpy(&map->fec.twcard.u.pw_type, buf + off, sizeof(uint16_t)); + map->fec.twcard.u.pw_type = ntohs(map->fec.twcard.u.pw_type); /* ignore the reserved bit as per RFC 6667 */ map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT; off += sizeof(uint16_t); break; default: - send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, - msg->type); + send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type); return (-1); } diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 470580ff5..04bff9015 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -17,17 +17,16 @@ #include "mpls.h" static __inline int fec_compare(const struct fec *, const struct fec *); -static int lde_nbr_is_nexthop(struct fec_node *, - struct lde_nbr *); -static void fec_free(void *); -static struct fec_node *fec_add(struct fec *fec); -static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *, +static int lde_nbr_is_nexthop(struct fec_node *, struct lde_nbr *); +static void fec_free(void *); +static struct fec_node *fec_add(struct fec *fec); +static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *, ifindex_t, uint8_t, unsigned short); -static void fec_nh_del(struct fec_nh *); +static void fec_nh_del(struct fec_nh *); RB_GENERATE(fec_tree, fec, entry, fec_compare) -struct fec_tree ft = RB_INITIALIZER(&ft); +struct fec_tree ft = RB_INITIALIZER(&ft); struct event *gc_timer; /* FEC tree functions */ @@ -47,11 +46,9 @@ fec_compare(const struct fec *a, const struct fec *b) switch (a->type) { case FEC_TYPE_IPV4: - if (ntohl(a->u.ipv4.prefix.s_addr) < - ntohl(b->u.ipv4.prefix.s_addr)) + if (ntohl(a->u.ipv4.prefix.s_addr) < ntohl(b->u.ipv4.prefix.s_addr)) return (-1); - if (ntohl(a->u.ipv4.prefix.s_addr) > - ntohl(b->u.ipv4.prefix.s_addr)) + if (ntohl(a->u.ipv4.prefix.s_addr) > ntohl(b->u.ipv4.prefix.s_addr)) return (1); if (a->u.ipv4.prefixlen < b->u.ipv4.prefixlen) return (-1); @@ -79,11 +76,9 @@ fec_compare(const struct fec *a, const struct fec *b) return (-1); if (a->u.pwid.pwid > b->u.pwid.pwid) return (1); - if (ntohl(a->u.pwid.lsr_id.s_addr) < - ntohl(b->u.pwid.lsr_id.s_addr)) + if (ntohl(a->u.pwid.lsr_id.s_addr) < ntohl(b->u.pwid.lsr_id.s_addr)) return (-1); - if (ntohl(a->u.pwid.lsr_id.s_addr) > - ntohl(b->u.pwid.lsr_id.s_addr)) + if (ntohl(a->u.pwid.lsr_id.s_addr) > ntohl(b->u.pwid.lsr_id.s_addr)) return (1); return (0); } @@ -261,8 +256,7 @@ fec_add(struct fec *fec) fn->pw_remote_status = PW_FORWARDING; if (fec_insert(&ft, &fn->fec)) - log_warnx("failed to add %s to ft tree", - log_fec(&fn->fec)); + log_warnx("failed to add %s to ft tree", log_fec(&fn->fec)); return (fn); } @@ -338,14 +332,14 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, * installing in kernel and sending to peer */ iface = if_lookup(ldeconf, ifindex); - if ((ldeconf->flags & F_LDPD_ORDERED_CONTROL) && + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL) && !connected && iface != NULL && fec->type != FEC_TYPE_PWID) - fnh->flags |= F_FEC_NH_DEFER; + SET_FLAG(fnh->flags, F_FEC_NH_DEFER); } - fnh->flags |= F_FEC_NH_NEW; + SET_FLAG(fnh->flags, F_FEC_NH_NEW); if (connected) - fnh->flags |= F_FEC_NH_CONNECTED; + SET_FLAG(fnh->flags, F_FEC_NH_CONNECTED); } void @@ -388,22 +382,22 @@ lde_kernel_update(struct fec *fec) return; LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) { - if (fnh->flags & F_FEC_NH_NEW) { - fnh->flags &= ~F_FEC_NH_NEW; + if (CHECK_FLAG(fnh->flags, F_FEC_NH_NEW)) { + UNSET_FLAG(fnh->flags, F_FEC_NH_NEW); /* * if LDP configured on interface or a static route * clear flag else treat fec as a connected route */ - if (ldeconf->flags & F_LDPD_ENABLED) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ENABLED)) { iface = if_lookup(ldeconf,fnh->ifindex); - if (fnh->flags & F_FEC_NH_CONNECTED || + if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED) || iface || fnh->route_type == ZEBRA_ROUTE_STATIC) - fnh->flags &=~F_FEC_NH_NO_LDP; + UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); else - fnh->flags |= F_FEC_NH_NO_LDP; + SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); } else - fnh->flags |= F_FEC_NH_NO_LDP; + SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); } else { lde_send_delete_klabel(fn, fnh); fec_nh_del(fnh); @@ -510,7 +504,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) /* RFC 4447 control word and status tlv negotiation */ if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) { - if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) + if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) fn->pw_remote_status = map->pw_status; return; @@ -534,8 +528,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) * the possibility of multipath. */ LIST_FOREACH(fnh, &fn->nexthops, entry) { - if (lde_address_find(ln, fnh->af, - &fnh->nexthop) == NULL) + if (lde_address_find(ln, fnh->af, &fnh->nexthop) == NULL) continue; lde_send_delete_klabel(fn, fnh); @@ -561,9 +554,9 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) * NH so clear flag and send labelmap msg to * peer */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { send_map = true; - fnh->flags &= ~F_FEC_NH_DEFER; + UNSET_FLAG(fnh->flags, F_FEC_NH_DEFER); } fnh->remote_label = map->label; if (fn->local_label != NO_LABEL) @@ -575,9 +568,9 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) continue; pw->remote_group = map->fec.pwid.group_id; - if (map->flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) pw->remote_mtu = map->fec.pwid.ifmtu; - if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) { + if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) { pw->remote_status = map->pw_status; fn->pw_remote_status = map->pw_status; } @@ -726,7 +719,7 @@ lde_check_release(struct map *map, struct lde_nbr *ln) /* wildcard label release */ if (map->type == MAP_TYPE_WILDCARD || map->type == MAP_TYPE_TYPED_WCARD || - (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) { + (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) { lde_check_release_wcard(map, ln); return; } @@ -818,7 +811,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) /* wildcard label withdraw */ if (map->type == MAP_TYPE_WILDCARD || map->type == MAP_TYPE_TYPED_WCARD || - (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) { + (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) { lde_check_withdraw_wcard(map, ln); return; } @@ -868,15 +861,14 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) return; /* Ordered Control: additional withdraw steps */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { /* LWd.8: for each neighbor other that src of withdraw msg */ RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) { if (ln->peerid == lnbr->peerid) continue; /* LWd.9: check if previously sent a label mapping */ - me = (struct lde_map *)fec_find(&lnbr->sent_map, - &fn->fec); + me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec); /* * LWd.10: does label sent to peer "map" to withdraw @@ -915,8 +907,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) switch (f->type) { case FEC_TYPE_IPV4: case FEC_TYPE_IPV6: - if (!lde_address_find(ln, fnh->af, - &fnh->nexthop)) + if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) continue; break; case FEC_TYPE_PWID: @@ -929,8 +920,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) default: break; } - if (map->label != NO_LABEL && map->label != - fnh->remote_label) + if (map->label != NO_LABEL && map->label != fnh->remote_label) continue; lde_send_delete_klabel(fn, fnh); @@ -941,8 +931,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL); /* LWd.3: check previously received label mapping */ - if (me && (map->label == NO_LABEL || - map->label == me->map.label)) + if (me && (map->label == NO_LABEL || map->label == me->map.label)) /* * LWd.4: remove record of previously received * label mapping @@ -953,7 +942,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) continue; /* Ordered Control: additional withdraw steps */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { /* * LWd.8: for each neighbor other that src of * withdraw msg @@ -965,16 +954,14 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) /* LWd.9: check if previously sent a label * mapping */ - me = (struct lde_map *)fec_find( - &lnbr->sent_map, &fn->fec); + me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec); /* * LWd.10: does label sent to peer "map" to * withdraw label */ if (me && lde_nbr_is_nexthop(fn, lnbr)) /* LWd.11: send label withdraw */ - lde_send_labelwithdraw(lnbr, fn, NULL, - NULL); + lde_send_labelwithdraw(lnbr, fn, NULL, NULL); } } } diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index d3193b16d..e66b9e92d 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -257,8 +257,7 @@ ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data, { if (iev_lde->ibuf.fd == -1) return (0); - return (imsg_compose_event(iev_lde, type, peerid, pid, -1, - data, datalen)); + return (imsg_compose_event(iev_lde, type, peerid, pid, -1, data, datalen)); } /* ARGSUSED */ @@ -309,8 +308,7 @@ static void ldpe_dispatch_main(struct event *thread) switch (imsg.hdr.type) { case IMSG_IFSTATUS: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kif)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif)) fatalx("IFSTATUS imsg with wrong len"); kif = imsg.data; @@ -336,15 +334,13 @@ static void ldpe_dispatch_main(struct event *thread) } break; case IMSG_NEWADDR: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kaddr)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr)) fatalx("NEWADDR imsg with wrong len"); if_addr_add(imsg.data); break; case IMSG_DELADDR: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kaddr)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr)) fatalx("DELADDR imsg with wrong len"); if_addr_del(imsg.data); @@ -369,8 +365,7 @@ static void ldpe_dispatch_main(struct event *thread) iev_lde->ev_write = NULL; break; case IMSG_INIT: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct ldpd_init)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldpd_init)) fatalx("INIT imsg with wrong len"); memcpy(&init, imsg.data, sizeof(init)); @@ -398,14 +393,11 @@ static void ldpe_dispatch_main(struct event *thread) disc_socket = -1; edisc_socket = -1; session_socket = -1; - if ((ldp_af_conf_get(leconf, af))->flags & - F_LDPD_AF_ENABLED) - ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, - af, NULL, 0); + if (CHECK_FLAG((ldp_af_conf_get(leconf, af))->flags, F_LDPD_AF_ENABLED)) + ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, af, NULL, 0); break; case IMSG_SOCKET_NET: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(enum socket_type)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(enum socket_type)) fatalx("SOCKET_NET imsg with wrong len"); socket_type = imsg.data; @@ -434,15 +426,13 @@ static void ldpe_dispatch_main(struct event *thread) break; } - ldpe_setup_sockets(af, disc_socket, edisc_socket, - session_socket); + ldpe_setup_sockets(af, disc_socket, edisc_socket, session_socket); if_update_all(af); tnbr_update_all(af); RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (nbr->af != af) continue; - nbr->laddr = (ldp_af_conf_get(leconf, - af))->trans_addr; + nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr; #ifdef __OpenBSD__ nbrp = nbr_params_find(leconf, nbr->id); if (nbrp) { @@ -456,8 +446,7 @@ static void ldpe_dispatch_main(struct event *thread) } break; case IMSG_RTRID_UPDATE: - memcpy(&global.rtr_id, imsg.data, - sizeof(global.rtr_id)); + memcpy(&global.rtr_id, imsg.data, sizeof(global.rtr_id)); if (leconf->rtr_id.s_addr == INADDR_ANY) { ldpe_reset_nbrs(AF_UNSPEC); } @@ -465,8 +454,7 @@ static void ldpe_dispatch_main(struct event *thread) tnbr_update_all(AF_UNSPEC); break; case IMSG_RECONF_CONF: - if ((nconf = malloc(sizeof(struct ldpd_conf))) == - NULL) + if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL) fatal(NULL); memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); @@ -546,16 +534,13 @@ static void ldpe_dispatch_main(struct event *thread) memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); break; case IMSG_FILTER_UPDATE: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct ldp_access)) { + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldp_access)) { log_warnx("%s: wrong imsg len", __func__); break; } laccess = imsg.data; - ldpe_check_filter_af(AF_INET, &leconf->ipv4, - laccess->name); - ldpe_check_filter_af(AF_INET6, &leconf->ipv6, - laccess->name); + ldpe_check_filter_af(AF_INET, &leconf->ipv4, laccess->name); + ldpe_check_filter_af(AF_INET6, &leconf->ipv6, laccess->name); break; case IMSG_LDP_SYNC_IF_STATE_REQUEST: if (imsg.hdr.len != IMSG_HEADER_SIZE + @@ -605,8 +590,7 @@ static void ldpe_dispatch_main(struct event *thread) } break; default: - log_debug("%s: error handling imsg %d", - __func__, imsg.hdr.type); + log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); @@ -650,8 +634,7 @@ static void ldpe_dispatch_lde(struct event *thread) case IMSG_RELEASE_ADD: case IMSG_REQUEST_ADD: case IMSG_WITHDRAW_ADD: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct map)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map)) fatalx("invalid size of map request"); map = imsg.data; @@ -706,8 +689,7 @@ static void ldpe_dispatch_lde(struct event *thread) } break; case IMSG_NOTIFICATION_SEND: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct notify_msg)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg)) fatalx("invalid size of OE request"); nm = imsg.data; @@ -741,8 +723,7 @@ static void ldpe_dispatch_lde(struct event *thread) session_shutdown(nbr,S_SHUTDOWN,0,0); break; default: - log_debug("%s: error handling imsg %d", - __func__, imsg.hdr.type); + log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); @@ -860,7 +841,7 @@ ldpe_remove_dynamic_tnbrs(int af) if (tnbr->af != af) continue; - tnbr->flags &= ~F_TNBR_DYNAMIC; + UNSET_FLAG(tnbr->flags, F_TNBR_DYNAMIC); tnbr_check(leconf, tnbr); } } diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 6f9177fe8..5209c55bb 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -19,10 +19,8 @@ DEFINE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state), (nbr, old_state)); static __inline int nbr_id_compare(const struct nbr *, const struct nbr *); -static __inline int nbr_addr_compare(const struct nbr *, - const struct nbr *); -static __inline int nbr_pid_compare(const struct nbr *, - const struct nbr *); +static __inline int nbr_addr_compare(const struct nbr *, const struct nbr *); +static __inline int nbr_pid_compare(const struct nbr *, const struct nbr *); static void nbr_update_peerid(struct nbr *); static void nbr_ktimer(struct event *thread); static void nbr_start_ktimer(struct nbr *); @@ -127,7 +125,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) old_state = nbr->state; for (i = 0; nbr_fsm_tbl[i].state != -1; i++) - if ((nbr_fsm_tbl[i].state & old_state) && + if (CHECK_FLAG(nbr_fsm_tbl[i].state, old_state) && (nbr_fsm_tbl[i].event == event)) { new_state = nbr_fsm_tbl[i].new_state; break; @@ -196,8 +194,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) send_keepalive(nbr); break; case NBR_ACT_CLOSE_SESSION: - ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, - NULL, 0); + ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0); session_close(nbr); break; case NBR_ACT_NOTHING: @@ -606,8 +603,7 @@ nbr_establish_connection(struct nbr *nbr) return (-1); } #else - sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, - nbrp->auth.md5key); + sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, nbrp->auth.md5key); #endif } @@ -646,8 +642,7 @@ nbr_establish_connection(struct nbr *nbr) send_hello(adj->source.type, adj->source.link.ia, adj->source.target); - if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) - == -1) { + if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) == -1) { if (errno == EINPROGRESS) { event_add_write(master, nbr_connect_cb, nbr, nbr->fd, &nbr->ev_connect); @@ -674,14 +669,14 @@ nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp) * statically (e.g., via configuration) and/or dynamically override the * default behavior and enable/disable GTSM on a per-peer basis". */ - if (nbrp && (nbrp->flags & F_NBRP_GTSM)) + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM)) return (nbrp->gtsm_enabled); - if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM) + if (CHECK_FLAG((ldp_af_conf_get(leconf, nbr->af))->flags, F_LDPD_AF_NO_GTSM)) return (0); /* By default, GTSM support has to be negotiated for LDPv4 */ - if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED)) + if (nbr->af == AF_INET && !CHECK_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED)) return (0); return (1); @@ -692,7 +687,7 @@ nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp) { int ttl = 255; - if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS)) + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM_HOPS)) ttl = 256 - nbrp->gtsm_hops; switch (af) { @@ -740,8 +735,7 @@ nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp) } if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) { - log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__, - &nbr->id); + log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__, &nbr->id); return (-1); } @@ -772,8 +766,7 @@ nbr_act_session_operational(struct nbr *nbr) static void nbr_send_labelmappings(struct nbr *nbr) { - ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, - NULL, 0); + ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, NULL, 0); } static __inline int @@ -810,7 +803,7 @@ nbr_get_keepalive(int af, struct in_addr lsr_id) struct nbr_params *nbrp; nbrp = nbr_params_find(leconf, lsr_id); - if (nbrp && (nbrp->flags & F_NBRP_KEEPALIVE)) + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_KEEPALIVE)) return (nbrp->keepalive); return ((ldp_af_conf_get(leconf, af))->keepalive); @@ -834,8 +827,7 @@ nbr_to_ctl(struct nbr *nbr) nctl.stats = nbr->stats; nctl.flags = nbr->flags; nctl.max_pdu_len = nbr->max_pdu_len; - nctl.hold_time_remaining = - event_timer_remain_second(nbr->keepalive_timer); + nctl.hold_time_remaining = event_timer_remain_second(nbr->keepalive_timer); gettimeofday(&now, NULL); if (nbr->state == NBR_STA_OPER) { diff --git a/ldpd/packet.c b/ldpd/packet.c index 71eeb1adb..d2d9305e7 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -15,13 +15,12 @@ #include "sockopt.h" -static struct iface *disc_find_iface(unsigned int, int, - union ldpd_addr *); +static struct iface *disc_find_iface(unsigned int, int, union ldpd_addr *); static void session_read(struct event *thread); static void session_write(struct event *thread); -static ssize_t session_get_pdu(struct ibuf_read *, char **); -static void tcp_close(struct tcp_conn *); -static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); +static ssize_t session_get_pdu(struct ibuf_read *, char **); +static void tcp_close(struct tcp_conn *); +static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); static void pending_conn_timeout(struct event *thread); int @@ -144,8 +143,7 @@ void disc_recv_packet(struct event *thread) if ((r = recvmsg(fd, &m, 0)) == -1) { if (errno != EAGAIN && errno != EINTR) - log_debug("%s: read error: %s", __func__, - strerror(errno)); + log_debug("%s: read error: %s", __func__, strerror(errno)); return; } @@ -154,10 +152,10 @@ void disc_recv_packet(struct event *thread) multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0; #else multicast = 0; - for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; - cmsg = CMSG_NXTHDR(&m, cmsg)) { + for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; cmsg = CMSG_NXTHDR(&m, cmsg)) { #if defined(HAVE_IP_PKTINFO) - if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + if (af == AF_INET && + cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo *pktinfo; @@ -167,7 +165,8 @@ void disc_recv_packet(struct event *thread) break; } #elif defined(HAVE_IP_RECVDSTADDR) - if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + if (af == AF_INET && + cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { struct in_addr *addr; @@ -179,7 +178,8 @@ void disc_recv_packet(struct event *thread) #else #error "Unsupported socket API" #endif - if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 && + if (af == AF_INET6 && + cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo *pktinfo; @@ -191,8 +191,7 @@ void disc_recv_packet(struct event *thread) } #endif /* MSG_MCAST */ if (bad_addr(af, &src)) { - log_debug("%s: invalid source address: %s", __func__, - log_addr(af, &src)); + log_debug("%s: invalid source address: %s", __func__, log_addr(af, &src)); return; } ifindex = getsockopt_ifindex(af, &m); @@ -207,8 +206,7 @@ void disc_recv_packet(struct event *thread) /* check packet size */ len = (uint16_t)r; if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) { - log_debug("%s: bad packet size, source %s", __func__, - log_addr(af, &src)); + log_debug("%s: bad packet size, source %s", __func__, log_addr(af, &src)); return; } @@ -309,10 +307,8 @@ void session_accept(struct event *thread) */ if (errno == ENFILE || errno == EMFILE) { accept_pause(); - } else if (errno != EWOULDBLOCK && errno != EINTR && - errno != ECONNABORTED) - log_debug("%s: accept error: %s", __func__, - strerror(errno)); + } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) + log_debug("%s: accept error: %s", __func__, strerror(errno)); return; } sock_set_nonblock(newfd); @@ -445,15 +441,13 @@ static void session_read(struct event *thread) max_pdu_len = nbr->max_pdu_len; else max_pdu_len = LDP_MAX_LEN; - if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || - pdu_len > max_pdu_len) { + if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || pdu_len > max_pdu_len) { session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); free(buf); return; } pdu_len -= LDP_HDR_PDU_LEN; - if (ldp_hdr->lsr_id != nbr->id.s_addr || - ldp_hdr->lspace_id != 0) { + if (ldp_hdr->lsr_id != nbr->id.s_addr || ldp_hdr->lspace_id != 0) { session_shutdown(nbr, S_BAD_LDP_ID, 0, 0); free(buf); return; @@ -469,10 +463,8 @@ static void session_read(struct event *thread) msg = (struct ldp_msg *)pdu; type = ntohs(msg->type); msg_len = ntohs(msg->length); - if (msg_len < LDP_MSG_LEN || - (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) { - session_shutdown(nbr, S_BAD_MSG_LEN, msg->id, - msg->type); + if (msg_len < LDP_MSG_LEN || (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) { + session_shutdown(nbr, S_BAD_MSG_LEN, msg->id, msg->type); free(buf); return; } @@ -484,8 +476,7 @@ static void session_read(struct event *thread) case MSG_TYPE_INIT: if ((nbr->state != NBR_STA_INITIAL) && (nbr->state != NBR_STA_OPENSENT)) { - session_shutdown(nbr, S_SHUTDOWN, - msg->id, msg->type); + session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); return; } @@ -493,8 +484,7 @@ static void session_read(struct event *thread) case MSG_TYPE_KEEPALIVE: if ((nbr->state == NBR_STA_INITIAL) || (nbr->state == NBR_STA_OPENSENT)) { - session_shutdown(nbr, S_SHUTDOWN, - msg->id, msg->type); + session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); return; } @@ -503,8 +493,7 @@ static void session_read(struct event *thread) break; default: if (nbr->state != NBR_STA_OPER) { - session_shutdown(nbr, S_SHUTDOWN, - msg->id, msg->type); + session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); return; } @@ -534,16 +523,14 @@ static void session_read(struct event *thread) case MSG_TYPE_LABELWITHDRAW: case MSG_TYPE_LABELRELEASE: case MSG_TYPE_LABELABORTREQ: - ret = recv_labelmessage(nbr, pdu, msg_size, - type); + ret = recv_labelmessage(nbr, pdu, msg_size, type); break; default: log_debug("%s: unknown LDP message from nbr %pI4", __func__, &nbr->id); if (!(ntohs(msg->type) & UNKNOWN_FLAG)) { nbr->stats.unknown_msg++; - send_notification(nbr->tcp, - S_UNKNOWN_MSG, msg->id, msg->type); + send_notification(nbr->tcp, S_UNKNOWN_MSG, msg->id, msg->type); } /* ignore the message */ ret = 0; @@ -664,8 +651,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, void session_close(struct nbr *nbr) { - log_debug("%s: closing session with lsr-id %pI4", __func__, - &nbr->id); + log_debug("%s: closing session with lsr-id %pI4", __func__, &nbr->id); ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_SESSION_CLOSE); @@ -788,8 +774,7 @@ pending_conn_find(int af, union ldpd_addr *addr) struct pending_conn *pconn; TAILQ_FOREACH(pconn, &global.pending_conns, entry) - if (af == pconn->af && - ldp_addrcmp(af, addr, &pconn->addr) == 0) + if (af == pconn->af && ldp_addrcmp(af, addr, &pconn->addr) == 0) return (pconn); return (NULL); diff --git a/lib/command.c b/lib/command.c index 27cd3a04b..7a7ce3f5d 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1303,6 +1303,14 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num) while (fgets(vty->buf, VTY_BUFSIZ, fp)) { ++(*line_num); + if (vty_log_commands) { + int len = strlen(vty->buf); + + /* now log the command */ + zlog_notice("config-from-file# %.*s", len ? len - 1 : 0, + vty->buf); + } + ret = command_config_read_one_line(vty, NULL, *line_num, 0); if (ret != CMD_SUCCESS && ret != CMD_WARNING diff --git a/lib/libfrr.c b/lib/libfrr.c index 07e2eafec..e89005726 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -130,6 +130,7 @@ static const struct optspec os_always = { " --limit-fds Limit number of fds supported\n", lo_always}; +static bool logging_to_stdout = false; /* set when --log stdout specified */ static const struct option lo_cfg[] = { {"config_file", required_argument, NULL, 'f'}, @@ -738,6 +739,11 @@ struct event_loop *frr_init(void) while ((log_arg = log_args_pop(di->early_logging))) { command_setup_early_logging(log_arg->target, di->early_loglevel); + /* this is a bit of a hack, + but need to notice when + the target is stdout */ + if (strcmp(log_arg->target, "stdout") == 0) + logging_to_stdout = true; XFREE(MTYPE_TMP, log_arg); } @@ -1088,9 +1094,15 @@ static void frr_terminal_close(int isexit) "%s: failed to open /dev/null: %s", __func__, safe_strerror(errno)); } else { - dup2(nullfd, 0); - dup2(nullfd, 1); - dup2(nullfd, 2); + int fd; + /* + * only redirect stdin, stdout, stderr to null when a tty also + * don't redirect when stdout is set with --log stdout + */ + for (fd = 2; fd >= 0; fd--) + if (isatty(fd) && + (fd != STDOUT_FILENO || !logging_to_stdout)) + dup2(nullfd, fd); close(nullfd); } } @@ -1168,9 +1180,16 @@ void frr_run(struct event_loop *master) "%s: failed to open /dev/null: %s", __func__, safe_strerror(errno)); } else { - dup2(nullfd, 0); - dup2(nullfd, 1); - dup2(nullfd, 2); + int fd; + /* + * only redirect stdin, stdout, stderr to null when a + * tty also don't redirect when stdout is set with --log + * stdout + */ + for (fd = 2; fd >= 0; fd--) + if (isatty(fd) && + (fd != STDOUT_FILENO || !logging_to_stdout)) + dup2(nullfd, fd); close(nullfd); } @@ -1194,7 +1213,7 @@ void frr_fini(void) { FILE *fp; char filename[128]; - int have_leftovers; + int have_leftovers = 0; hook_call(frr_fini); @@ -1220,16 +1239,15 @@ void frr_fini(void) /* frrmod_init -> nothing needed / hooks */ rcu_shutdown(); - if (!debug_memstats_at_exit) - return; - - have_leftovers = log_memstats(stderr, di->name); + /* also log memstats to stderr when stderr goes to a file*/ + if (debug_memstats_at_exit || !isatty(STDERR_FILENO)) + have_leftovers = log_memstats(stderr, di->name); /* in case we decide at runtime that we want exit-memstats for - * a daemon, but it has no stderr because it's daemonized + * a daemon * (only do this if we actually have something to print though) */ - if (!have_leftovers) + if (!debug_memstats_at_exit || !have_leftovers) return; snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", diff --git a/lib/libospf.h b/lib/libospf.h index 3262534de..676b563ff 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -55,6 +55,7 @@ extern "C" { #define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40 #define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1 #define OSPF_HELLO_INTERVAL_DEFAULT 10 +#define OSPF_HELLO_DELAY_DEFAULT 10 #define OSPF_ROUTER_PRIORITY_DEFAULT 1 #define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 #define OSPF_TRANSMIT_DELAY_DEFAULT 1 diff --git a/lib/nexthop.c b/lib/nexthop.c index b04c95c05..dcbb76b68 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -1076,3 +1076,12 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, } return -1; } + +bool nexthop_is_ifindex_type(const struct nexthop *nh) +{ + if (nh->type == NEXTHOP_TYPE_IFINDEX || + nh->type == NEXTHOP_TYPE_IPV4_IFINDEX || + nh->type == NEXTHOP_TYPE_IPV6_IFINDEX) + return true; + return false; +} diff --git a/lib/nexthop.h b/lib/nexthop.h index 1d95a3eee..43dd71e11 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -234,6 +234,9 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, struct nexthop *rparent); +/* Check nexthop of IFINDEX type */ +extern bool nexthop_is_ifindex_type(const struct nexthop *nh); + /* * Parse one or more backup index values, as comma-separated numbers, * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS diff --git a/lib/routemap.h b/lib/routemap.h index 9b2e18b4a..7277744dc 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -259,6 +259,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(C, "frr-zebra-route-map:ipv4-next-hop-prefix-length")) #define IS_MATCH_SRC_PROTO(C) \ (strmatch(C, "frr-zebra-route-map:source-protocol")) +#define IS_MATCH_BGP_SRC_PROTO(C) \ + (strmatch(C, "frr-bgp-route-map:source-protocol")) #define IS_MATCH_SRC_INSTANCE(C) \ (strmatch(C, "frr-zebra-route-map:source-instance")) /* BGP route-map match conditions */ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 419086c4c..0ccc78e83 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -599,11 +599,14 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length")); - } else if (IS_MATCH_SRC_PROTO(condition)) { + } else if (IS_MATCH_SRC_PROTO(condition) || + IS_MATCH_BGP_SRC_PROTO(condition)) { vty_out(vty, " match source-protocol %s\n", yang_dnode_get_string( dnode, - "./rmap-match-condition/frr-zebra-route-map:source-protocol")); + IS_MATCH_SRC_PROTO(condition) + ? "./rmap-match-condition/frr-zebra-route-map:source-protocol" + : "./rmap-match-condition/frr-bgp-route-map:source-protocol")); } else if (IS_MATCH_SRC_INSTANCE(condition)) { vty_out(vty, " match source-instance %s\n", yang_dnode_get_string( @@ -125,8 +125,8 @@ static int no_password_check = 0; /* Integrated configuration file path */ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; -static bool do_log_commands; -static bool do_log_commands_perm; +bool vty_log_commands; +static bool vty_log_commands_perm; void vty_mgmt_resume_response(struct vty *vty, bool success) { @@ -508,7 +508,7 @@ static int vty_command(struct vty *vty, char *buf) /* * Log non empty command lines */ - if (do_log_commands && + if (vty_log_commands && strncmp(buf, "echo PING", strlen("echo PING")) != 0) cp = buf; if (cp != NULL) { @@ -3160,15 +3160,15 @@ DEFPY (log_commands, "Log all commands\n") { if (no) { - if (do_log_commands_perm) { + if (vty_log_commands_perm) { vty_out(vty, "Daemon started with permanent logging turned on for commands, ignoring\n"); return CMD_WARNING; } - do_log_commands = false; + vty_log_commands = false; } else - do_log_commands = true; + vty_log_commands = true; return CMD_SUCCESS; } @@ -3196,7 +3196,7 @@ static int vty_config_write(struct vty *vty) vty_endframe(vty, "exit\n"); - if (do_log_commands) + if (vty_log_commands) vty_out(vty, "log commands\n"); vty_out(vty, "!\n"); @@ -3677,8 +3677,8 @@ void vty_init(struct event_loop *master_thread, bool do_command_logging) install_element(CONFIG_NODE, &log_commands_cmd); if (do_command_logging) { - do_log_commands = true; - do_log_commands_perm = true; + vty_log_commands = true; + vty_log_commands_perm = true; } install_element(ENABLE_NODE, &terminal_monitor_cmd); @@ -335,6 +335,7 @@ struct vty_arg { #endif extern struct nb_config *vty_mgmt_candidate_config; +extern bool vty_log_commands; /* Prototypes. */ extern void vty_init(struct event_loop *m, bool do_command_logging); diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h index 83353e817..603296bb3 100644 --- a/mgmtd/mgmt.h +++ b/mgmtd/mgmt.h @@ -108,16 +108,4 @@ extern void mgmt_master_init(struct event_loop *master, const int buffer_size); extern void mgmt_init(void); extern void mgmt_vty_init(void); -static inline char *mgmt_realtime_to_string(struct timeval *tv, char *buf, - size_t sz) -{ - struct tm tm; - size_t n; - - localtime_r((const time_t *)&tv->tv_sec, &tm); - n = strftime(buf, sz, "%Y-%m-%dT%H:%M:%S", &tm); - snprintf(&buf[n], sz - n, ",%06u000", (unsigned int)tv->tv_usec); - return buf; -} - #endif /* _FRR_MGMTD_H */ diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h index 8d01f3d5b..2a32eb641 100644 --- a/mgmtd/mgmt_ds.h +++ b/mgmtd/mgmt_ds.h @@ -28,12 +28,9 @@ for ((id) = MGMTD_DS_NONE; (id) < MGMTD_DS_MAX_ID; (id)++) #define MGMTD_MAX_COMMIT_LIST 10 -#define MGMTD_MD5_HASH_LEN 16 -#define MGMTD_MD5_HASH_STR_HEX_LEN 33 #define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json" #define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat" -#define MGMTD_COMMIT_TIME_STR_LEN 100 extern struct nb_config *running_config; diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index abf1f954d..262741b66 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -1714,7 +1714,7 @@ static void mgmt_fe_adapter_cmt_stats_write(struct vty *vty, struct mgmt_fe_client_adapter *adapter) { - char buf[100] = {0}; + char buf[MGMT_LONG_TIME_MAX_LEN]; if (!mm->perf_stats_en) return; @@ -1795,7 +1795,7 @@ static void mgmt_fe_adapter_setcfg_stats_write(struct vty *vty, struct mgmt_fe_client_adapter *adapter) { - char buf[100] = {0}; + char buf[MGMT_LONG_TIME_MAX_LEN]; if (!mm->perf_stats_en) return; diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c index 2251c49f1..a49718a49 100644 --- a/mgmtd/mgmt_history.c +++ b/mgmtd/mgmt_history.c @@ -18,8 +18,8 @@ struct mgmt_cmt_info_t { struct mgmt_cmt_infos_item cmts; - char cmtid_str[MGMTD_MD5_HASH_STR_HEX_LEN]; - char time_str[MGMTD_COMMIT_TIME_STR_LEN]; + char cmtid_str[MGMT_SHORT_TIME_MAX_LEN]; + char time_str[MGMT_LONG_TIME_MAX_LEN]; char cmt_json_file[PATH_MAX]; }; @@ -54,36 +54,30 @@ static void mgmt_history_remove_file(char *name) zlog_err("Old commit info deletion failed"); } -static void mgmt_history_hash(const char *input_str, char *hash) +static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void) { - int i; - unsigned char digest[MGMTD_MD5_HASH_LEN]; - MD5_CTX ctx; - - memset(&ctx, 0, sizeof(ctx)); - MD5Init(&ctx); - MD5Update(&ctx, input_str, strlen(input_str)); - MD5Final(digest, &ctx); - - for (i = 0; i < MGMTD_MD5_HASH_LEN; i++) - snprintf(&hash[i * 2], MGMTD_MD5_HASH_STR_HEX_LEN, "%02x", - (unsigned int)digest[i]); + struct mgmt_cmt_info_t *new; + struct timespec tv; + struct tm tm; + + new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t)); + + clock_gettime(CLOCK_REALTIME, &tv); + localtime_r(&tv.tv_sec, &tm); + + mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str)); + mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str)); + snprintf(new->cmt_json_file, sizeof(new->cmt_json_file), + MGMTD_COMMIT_FILE_PATH, new->cmtid_str); + + return new; } static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void) { - struct mgmt_cmt_info_t *new; + struct mgmt_cmt_info_t *new = mgmt_history_new_cmt_info(); struct mgmt_cmt_info_t *cmt_info; struct mgmt_cmt_info_t *last_cmt_info = NULL; - struct timeval cmt_recd_tv; - - new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t)); - gettimeofday(&cmt_recd_tv, NULL); - mgmt_realtime_to_string(&cmt_recd_tv, new->time_str, - sizeof(new->time_str)); - mgmt_history_hash(new->time_str, new->cmtid_str); - snprintf(new->cmt_json_file, sizeof(new->cmt_json_file) - 1, - MGMTD_COMMIT_FILE_PATH, new->cmtid_str); if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) { FOREACH_CMT_REC (mm, cmt_info) @@ -106,8 +100,7 @@ mgmt_history_find_cmt_record(const char *cmtid_str) struct mgmt_cmt_info_t *cmt_info; FOREACH_CMT_REC (mm, cmt_info) { - if (strncmp(cmt_info->cmtid_str, cmtid_str, - MGMTD_MD5_HASH_STR_HEX_LEN) == 0) + if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) return cmt_info; } @@ -282,8 +275,7 @@ int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str) } FOREACH_CMT_REC (mm, cmt_info) { - if (strncmp(cmt_info->cmtid_str, cmtid_str, - MGMTD_MD5_HASH_STR_HEX_LEN) == 0) { + if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) { ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false); return ret; @@ -349,9 +341,9 @@ void show_mgmt_cmt_history(struct vty *vty) int slno = 0; vty_out(vty, "Last 10 commit history:\n"); - vty_out(vty, " Sl.No\tCommit-ID(HEX)\t\t\t Commit-Record-Time\n"); + vty_out(vty, "Slot Commit-ID Commit-Record-Time\n"); FOREACH_CMT_REC (mm, cmt_info) { - vty_out(vty, " %d\t%s %s\n", slno, cmt_info->cmtid_str, + vty_out(vty, "%4d %23s %s\n", slno, cmt_info->cmtid_str, cmt_info->time_str); slno++; } diff --git a/mgmtd/mgmt_history.h b/mgmtd/mgmt_history.h index 5f96cf90e..d3f795895 100644 --- a/mgmtd/mgmt_history.h +++ b/mgmtd/mgmt_history.h @@ -54,4 +54,42 @@ extern void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx); extern void mgmt_history_destroy(void); extern void mgmt_history_init(void); +/* + * 012345678901234567890123456789 + * 2023-12-31T12:12:12,012345678 + * 20231231121212012345678 + */ +#define MGMT_LONG_TIME_FMT "%Y-%m-%dT%H:%M:%S" +#define MGMT_LONG_TIME_MAX_LEN 30 +#define MGMT_SHORT_TIME_FMT "%Y%m%d%H%M%S" +#define MGMT_SHORT_TIME_MAX_LEN 24 + +static inline const char * +mgmt_time_to_string(struct timespec *tv, bool long_fmt, char *buffer, size_t sz) +{ + struct tm tm; + size_t n; + + localtime_r(&tv->tv_sec, &tm); + + if (long_fmt) { + n = strftime(buffer, sz, MGMT_LONG_TIME_FMT, &tm); + snprintf(&buffer[n], sz - n, ",%09lu", tv->tv_nsec); + } else { + n = strftime(buffer, sz, MGMT_SHORT_TIME_FMT, &tm); + snprintf(&buffer[n], sz - n, "%09lu", tv->tv_nsec); + } + + return buffer; +} + +static inline const char *mgmt_realtime_to_string(struct timeval *tv, char *buf, + size_t sz) +{ + struct timespec ts = {.tv_sec = tv->tv_sec, + .tv_nsec = tv->tv_usec * 1000}; + + return mgmt_time_to_string(&ts, true, buf, sz); +} + #endif /* _FRR_MGMTD_HISTORY_H_ */ diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index 976eb529d..3d5d4d259 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -14,6 +14,7 @@ #include "log.h" #include "hook.h" #include "printfrr.h" +#include "lib_errors.h" #include "ospf6d/ospf6_lsa.h" #include "ospf6d/ospf6_lsdb.h" @@ -25,21 +26,25 @@ #include "ospf6d/ospf6_zebra.h" #include "ospf6d/ospf6_message.h" #include "ospf6d/ospf6_neighbor.h" +#include "ospf6d/ospf6_network.h" #include "ospf6d/ospf6_flood.h" #include "ospf6d/ospf6_intra.h" #include "ospf6d/ospf6_spf.h" #include "ospf6d/ospf6_gr.h" #include "ospf6d/ospf6_gr_clippy.c" -static void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +static void ospf6_gr_grace_period_expired(struct event *thread); /* Originate and install Grace-LSA for a given interface. */ -static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) +static int ospf6_gr_lsa_originate(struct ospf6_interface *oi, + enum ospf6_gr_restart_reason reason) { - struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info; + struct ospf6 *ospf6 = oi->area->ospf6; + struct ospf6_gr_info *gr_info = &ospf6->gr_info; struct ospf6_lsa_header *lsa_header; struct ospf6_grace_lsa *grace_lsa; struct ospf6_lsa *lsa; + uint16_t lsa_length; char buffer[OSPF6_MAX_LSASIZE]; if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) @@ -61,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) /* Put restart reason. */ grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE); grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH); - if (gr_info->restart_support) - grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART; - else - grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART; + grace_lsa->tlv_reason.reason = reason; /* Fill LSA Header */ + lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa); lsa_header->age = 0; lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA); lsa_header->id = htonl(oi->interface->ifindex); - lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->adv_router = ospf6->router_id; lsa_header->seqnum = ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, lsa_header->adv_router, oi->lsdb); - lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa)); + lsa_header->length = htons(lsa_length); /* LSA checksum */ ospf6_lsa_checksum(lsa_header); - /* create LSA */ - lsa = ospf6_lsa_create(lsa_header); - - /* Originate */ - ospf6_lsa_originate_interface(lsa, oi); + if (reason == OSPF6_GR_UNKNOWN_RESTART) { + struct ospf6_header *oh; + uint32_t *uv32; + int n; + uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length; + struct iovec iovector[2] = {}; + + /* Reserve space for OSPFv3 header. */ + memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length); + + /* Fill in the OSPFv3 header. */ + oh = (struct ospf6_header *)buffer; + oh->version = OSPFV3_VERSION; + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + oh->instance_id = oi->instance_id; + oh->reserved = 0; + oh->length = htons(length); + + /* Fill LSA header. */ + uv32 = (uint32_t *)&buffer[sizeof(*oh)]; + *uv32 = htonl(1); + + /* Send packet. */ + iovector[0].iov_base = lsa_header; + iovector[0].iov_len = length; + n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6, + oi->interface->ifindex, iovector, ospf6->fd); + if (n != length) + flog_err(EC_LIB_DEVELOPMENT, + "%s: could not send entire message", __func__); + } else { + /* Create and install LSA. */ + lsa = ospf6_lsa_create(lsa_header); + ospf6_lsa_originate_interface(lsa, oi); + } return 0; } @@ -134,11 +169,10 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) ospf6->gr_info.restart_in_progress = false; ospf6->gr_info.finishing_restart = true; + XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason); + ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); EVENT_OFF(ospf6->gr_info.t_grace_period); - /* Record in non-volatile memory that the restart is complete. */ - ospf6_gr_nvm_delete(ospf6); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) { struct ospf6_interface *oi; @@ -156,6 +190,14 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area); for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { + /* Disable hello delay. */ + if (oi->gr.hello_delay.t_grace_send) { + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + event_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); + } + /* Reoriginate Link-LSA. */ if (oi->type != OSPF_IFTYPE_VIRTUALLINK) OSPF6_LINK_LSA_EXECUTE(oi); @@ -195,6 +237,26 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) ospf6_gr_flush_grace_lsas(ospf6); } +/* Enter the Graceful Restart mode. */ +void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, int timestamp) +{ + unsigned long remaining_time; + + ospf6->gr_info.restart_in_progress = true; + ospf6->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf6_gr_grace_period_expired, ospf6, + remaining_time, &ospf6->gr_info.t_grace_period); +} + #define RTR_LSA_MISSING 0 #define RTR_LSA_ADJ_FOUND 1 #define RTR_LSA_ADJ_NOT_FOUND 2 @@ -466,11 +528,26 @@ static void ospf6_gr_grace_period_expired(struct event *thread) ospf6_gr_restart_exit(ospf6, "grace period has expired"); } +/* Send extra Grace-LSA out the interface (unplanned outages only). */ +void ospf6_gr_iface_send_grace_lsa(struct event *thread) +{ + struct ospf6_interface *oi = EVENT_ARG(thread); + + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); + + if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval) + event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); + else + event_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); +} + /* * Record in non-volatile memory that the given OSPF instance is attempting to * perform a graceful restart. */ -static void ospf6_gr_nvm_update(struct ospf6 *ospf6) +static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare) { const char *inst_name; json_object *json; @@ -496,16 +573,18 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6) json_instance); } + json_object_int_add(json_instance, "gracePeriod", + ospf6->gr_info.grace_period); + /* * Record not only the grace period, but also a UNIX timestamp * corresponding to the end of that period. That way, once ospf6d is * restarted, it will be possible to take into account the time that * passed while ospf6d wasn't running. */ - json_object_int_add(json_instance, "gracePeriod", - ospf6->gr_info.grace_period); - json_object_int_add(json_instance, "timestamp", - time(NULL) + ospf6->gr_info.grace_period); + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf6->gr_info.grace_period); json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, JSON_C_TO_STRING_PRETTY); @@ -516,7 +595,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6) * Delete GR status information about the given OSPF instance from non-volatile * memory. */ -static void ospf6_gr_nvm_delete(struct ospf6 *ospf6) +void ospf6_gr_nvm_delete(struct ospf6 *ospf6) { const char *inst_name; json_object *json; @@ -552,6 +631,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_object *json_instances; json_object *json_instance; json_object *json_timestamp; + json_object *json_grace_period; time_t timestamp = 0; inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; @@ -573,29 +653,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_instance); } + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); if (json_timestamp) { time_t now; - unsigned long remaining_time; - /* Check if the grace period has already expired. */ + /* Planned GR: check if the grace period has already expired. */ now = time(NULL); timestamp = json_object_get_int(json_timestamp); if (now > timestamp) { ospf6_gr_restart_exit( ospf6, "grace period has expired already"); - } else { - /* Schedule grace period timeout. */ - ospf6->gr_info.restart_in_progress = true; - remaining_time = timestamp - time(NULL); - if (IS_DEBUG_OSPF6_GR) - zlog_debug( - "GR: remaining time until grace period expires: %lu(s)", - remaining_time); - event_add_timer(master, ospf6_gr_grace_period_expired, - ospf6, remaining_time, - &ospf6->gr_info.t_grace_period); - } + } else + ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf6->gr_info.grace_period = grace_period; + ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART, + time(NULL) + + ospf6->gr_info.grace_period); } json_object_object_del(json_instances, inst_name); @@ -605,6 +689,24 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_object_free(json); } +void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi) +{ + /* + * Can't check OSPF interface state as the OSPF instance might not be + * enabled yet. + */ + if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface)) + return; + + /* Send Grace-LSA. */ + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); + + /* Start GR hello-delay interval. */ + oi->gr.hello_delay.elapsed_seconds = 0; + event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); +} + /* Prepare to start a Graceful Restart. */ static void ospf6_gr_prepare(void) { @@ -625,25 +727,17 @@ static void ospf6_gr_prepare(void) ospf6->gr_info.grace_period, ospf6_vrf_id_to_name(ospf6->vrf_id)); - /* Freeze OSPF routes in the RIB. */ - if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) { - zlog_warn( - "%s: failed to activate graceful restart: not connected to zebra", - __func__); - continue; - } - /* Send a Grace-LSA to all neighbors. */ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { if (oi->state < OSPF6_INTERFACE_POINTTOPOINT) continue; - ospf6_gr_lsa_originate(oi); + ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART); } } /* Record end of the grace period in non-volatile memory. */ - ospf6_gr_nvm_update(ospf6); + ospf6_gr_nvm_update(ospf6, true); /* * Mark that a Graceful Restart preparation is in progress, to @@ -714,6 +808,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd, ospf6->gr_info.restart_support = true; ospf6->gr_info.grace_period = grace_period; + /* Freeze OSPF routes in the RIB. */ + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6, false); + return CMD_SUCCESS; } @@ -736,6 +836,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd, ospf6->gr_info.restart_support = false; ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL; + ospf6_gr_nvm_delete(ospf6); + ospf6_zebra_gr_disable(ospf6); return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h index 2c7e8b341..e6566a609 100644 --- a/ospf6d/ospf6_gr.h +++ b/ospf6d/ospf6_gr.h @@ -155,9 +155,15 @@ extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_debug_gr_helper(struct vty *vty); +extern void ospf6_gr_iface_send_grace_lsa(struct event *thread); +extern void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, + int timestamp); extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf, struct ospf6_area *area); extern void ospf6_gr_nvm_read(struct ospf6 *ospf); +extern void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +extern void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi); extern void ospf6_gr_init(void); #endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index e7148d66b..ea059c4be 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -35,6 +35,7 @@ #include "ospf6_proto.h" #include "lib/keychain.h" #include "ospf6_auth_trailer.h" +#include "ospf6d/ospf6_interface_clippy.c" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain"); @@ -202,6 +203,7 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) oi->priority = OSPF6_INTERFACE_PRIORITY; oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT; oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; oi->type = ospf6_default_iftype(ifp); @@ -324,6 +326,9 @@ void ospf6_interface_disable(struct ospf6_interface *oi) EVENT_OFF(oi->thread_intra_prefix_lsa); EVENT_OFF(oi->thread_as_extern_lsa); EVENT_OFF(oi->thread_wait_timer); + + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); } static struct in6_addr * @@ -772,6 +777,17 @@ void interface_up(struct event *thread) return; } + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->area->ospf6->gr_info.restart_in_progress && + oi->area->ospf6->gr_info.reason == OSPF6_GR_UNKNOWN_RESTART) + ospf6_gr_unplanned_start_interface(oi); + #ifdef __FreeBSD__ /* * There's a delay in FreeBSD between issuing a command to leave a @@ -1180,6 +1196,9 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, json_arr, json_object_new_string(lsa->name)); json_object_object_add(json_obj, "pendingLsaLsAck", json_arr); + if (oi->gr.hello_delay.interval != 0) + json_object_int_add(json_obj, "grHelloDelaySecs", + oi->gr.hello_delay.interval); } else { timerclear(&res); if (event_is_scheduled(oi->thread_send_lsupdate)) @@ -1205,6 +1224,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, : "off")); for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); + + if (oi->gr.hello_delay.interval != 0) + vty_out(vty, " Graceful Restart hello delay: %us\n", + oi->gr.hello_delay.interval); } /* BFD specific. */ @@ -2157,6 +2180,50 @@ ALIAS (ipv6_ospf6_deadinterval, "Interval time after which a neighbor is declared down\n" SECONDS_STR) +DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd, + "ipv6 ospf6 graceful-restart hello-delay (1-1800)", + IP6_STR + OSPF6_STR + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + /* Note: new or updated value won't affect ongoing graceful restart. */ + oi->gr.hello_delay.interval = hello_delay; + + return CMD_SUCCESS; +} + +DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd, + "no ipv6 ospf6 graceful-restart hello-delay [(1-1800)]", + NO_STR + IP6_STR + OSPF6_STR + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT; + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + + return CMD_SUCCESS; +} + /* interface variable set command */ DEFUN (ipv6_ospf6_transmitdelay, ipv6_ospf6_transmitdelay_cmd, @@ -2624,6 +2691,11 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) vty_out(vty, " ipv6 ospf6 network broadcast\n"); + if (oi->gr.hello_delay.interval != OSPF_HELLO_DELAY_DEFAULT) + vty_out(vty, + " ipv6 ospf6 graceful-restart hello-delay %u\n", + oi->gr.hello_delay.interval); + ospf6_bfd_write_config(vty, oi); ospf6_auth_write_config(vty, &oi->at_data); @@ -2722,12 +2794,14 @@ void ospf6_interface_init(void) install_element(INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_gr_hdelay_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_priority_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_instance_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_deadinterval_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_hellointerval_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_gr_hdelay_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_priority_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_retransmitinterval_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_transmitdelay_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index ae0744b25..5942df0ab 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -74,6 +74,15 @@ struct ospf6_interface { uint16_t dead_interval; uint32_t rxmt_interval; + /* Graceful-Restart data. */ + struct { + struct { + uint16_t interval; + uint16_t elapsed_seconds; + struct event *t_grace_send; + } hello_delay; + } gr; + uint32_t state_change; /* Cost */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 14b02dac7..032988a91 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -2244,6 +2244,10 @@ void ospf6_hello_send(struct event *thread) oi = (struct ospf6_interface *)EVENT_ARG(thread); + /* Check if the GR hello-delay is active. */ + if (oi->gr.hello_delay.t_grace_send) + return; + if (oi->state <= OSPF6_INTERFACE_DOWN) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR)) zlog_debug("Unable to send Hello on down interface %s", diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 0990b1430..e39ae504a 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -1262,6 +1262,7 @@ static void ospf6_ase_calculate_timer(struct event *t) * no longer valid. */ ospf6_zebra_gr_disable(ospf6); + ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); ospf6->gr_info.finishing_restart = false; } } diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index c2aa3abee..01c962194 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -455,6 +455,13 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->router_id == 0) ospf6_router_id_update(ospf6, true); ospf6_add(ospf6); + + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf6_gr_nvm_read(ospf6); + if (ospf6->vrf_id != VRF_UNKNOWN) { vrf = vrf_lookup_by_id(ospf6->vrf_id); FOR_ALL_INTERFACES (vrf, ifp) { @@ -465,12 +472,6 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->fd < 0) return ospf6; - /* - * Read from non-volatile memory whether this instance is performing a - * graceful restart or not. - */ - ospf6_gr_nvm_read(ospf6); - event_add_read(master, ospf6_receive, ospf6, ospf6->fd, &ospf6->t_ospf6_receive); @@ -490,6 +491,7 @@ void ospf6_delete(struct ospf6 *o) ospf6_gr_helper_deinit(o); if (!o->gr_info.prepare_in_progress) ospf6_flush_self_originated_lsas_now(o); + XFREE(MTYPE_TMP, o->gr_info.exit_reason); ospf6_disable(o); ospf6_del(o); @@ -693,6 +695,9 @@ DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]", if (ospf6 == NULL) vty_out(vty, "OSPFv3 is not configured\n"); else { + if (ospf6->gr_info.restart_support) + ospf6_gr_nvm_delete(ospf6); + ospf6_delete(ospf6); ospf6 = NULL; } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 8fdd29112..a38dad8fc 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -51,6 +51,8 @@ struct ospf6_gr_info { bool prepare_in_progress; bool finishing_restart; uint32_t grace_period; + int reason; + char *exit_reason; struct event *t_grace_period; }; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 6b3d4955d..0f631c4d0 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -239,12 +239,18 @@ static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command, int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("Zebra enable GR [stale time %u]", stale_time); + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES, stale_time); } int ospf6_zebra_gr_disable(struct ospf6 *ospf6) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("Zebra disable GR"); + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0); } @@ -735,10 +741,20 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or, static void ospf6_zebra_connected(struct zclient *zclient) { + struct ospf6 *ospf6; + struct listnode *node; + /* Send the client registration */ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (!ospf6->gr_info.restart_support) + continue; + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + } } static zclient_handler *const ospf6_handlers[] = { diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 3dff03956..c34db3012 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -78,6 +78,7 @@ clippy_scan += \ ospf6d/ospf6_top.c \ ospf6d/ospf6_area.c \ ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_interface.c \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_gr.c \ diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index ded520889..28d526870 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -66,9 +66,11 @@ static void ospf_area_range_add(struct ospf_area *area, apply_mask_ipv4(&p); rn = route_node_get(ranges, (struct prefix *)&p); - if (rn->info) + if (rn->info) { route_unlock_node(rn); - else + ospf_area_range_free(rn->info); + rn->info = range; + } else rn->info = range; } diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 1aacc341a..34e59c545 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -1764,6 +1764,12 @@ int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old) * session. Dump it, but increment past it's seqnum. */ assert(!ospf_opaque_is_owned(old)); + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug( + "LSA[Type%d:%pI4]: OSPF API Server Originate LSA Old Seq: 0x%x Age: %d", + old->data->type, &old->data->id, + ntohl(old->data->ls_seqnum), + ntohl(old->data->ls_age)); if (IS_LSA_MAX_SEQ(old)) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: old LSA at maxseq", __func__); @@ -1772,6 +1778,11 @@ int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old) lsa->data->ls_seqnum = lsa_seqnum_increment(old); ospf_discard_from_db(ospf, old->lsdb, old); } + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug( + "LSA[Type%d:%pI4]: OSPF API Server Originate LSA New Seq: 0x%x Age: %d", + lsa->data->type, &lsa->data->id, + ntohl(lsa->data->ls_seqnum), ntohl(lsa->data->ls_age)); /* Install this LSA into LSDB. */ if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) { @@ -1846,6 +1857,11 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: OSPF API Server LSA Refresher", + lsa->data->type, &lsa->data->id); + } + apiserv = lookup_apiserver_by_lsa(lsa); if (!apiserv) { zlog_warn("%s: LSA[%s]: No apiserver?", __func__, @@ -1855,14 +1871,14 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) goto out; } - if (IS_LSA_MAXAGE(lsa)) { - ospf_opaque_lsa_flush_schedule(lsa); - goto out; - } - /* Check if updated version of LSA instance has already prepared. */ new = ospf_lsdb_lookup(&apiserv->reserve, lsa); if (!new) { + if (IS_LSA_MAXAGE(lsa)) { + ospf_opaque_lsa_flush_schedule(lsa); + goto out; + } + /* This is a periodic refresh, driven by core OSPF mechanism. */ new = ospf_apiserver_opaque_lsa_new(lsa->area, lsa->oi, lsa->data); diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index cc2110d43..610b5fc08 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -615,6 +615,7 @@ static void ospf_ase_calculate_timer(struct event *t) */ if (ospf->gr_info.finishing_restart) { ospf_zebra_gr_disable(ospf); + ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); ospf->gr_info.finishing_restart = false; } } diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c index f60f0e863..6999b3f62 100644 --- a/ospfd/ospf_gr.c +++ b/ospfd/ospf_gr.c @@ -33,7 +33,7 @@ #include "ospfd/ospf_dump.h" #include "ospfd/ospf_gr_clippy.c" -static void ospf_gr_nvm_delete(struct ospf *ospf); +static void ospf_gr_grace_period_expired(struct event *thread); /* Lookup self-originated Grace-LSA in the LSDB. */ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf, @@ -53,7 +53,9 @@ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf, /* Fill in fields of the Grace-LSA that is being originated. */ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, - struct ospf_interface *oi, struct stream *s) + struct ospf_interface *oi, + enum ospf_gr_restart_reason reason, + struct stream *s) { struct grace_tlv_graceperiod tlv_period = {}; struct grace_tlv_restart_reason tlv_reason = {}; @@ -68,10 +70,7 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, /* Put restart reason. */ tlv_reason.header.type = htons(RESTART_REASON_TYPE); tlv_reason.header.length = htons(RESTART_REASON_LENGTH); - if (gr_info->restart_support) - tlv_reason.reason = OSPF_GR_SW_RESTART; - else - tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART; + tlv_reason.reason = reason; stream_put(s, &tlv_reason, sizeof(tlv_reason)); /* Put IP address. */ @@ -85,7 +84,8 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, } /* Generate Grace-LSA for a given interface. */ -static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) +static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi, + enum ospf_gr_restart_reason reason) { struct stream *s; struct lsa_header *lsah; @@ -112,7 +112,7 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id); /* Set opaque-LSA body fields. */ - ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s); + ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s); /* Set length. */ length = stream_get_endp(s); @@ -135,15 +135,20 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) } /* Originate and install Grace-LSA for a given interface. */ -static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage) +static void ospf_gr_lsa_originate(struct ospf_interface *oi, + enum ospf_gr_restart_reason reason, + bool maxage) { struct ospf_lsa *lsa, *old; - if (ospf_interface_neighbor_count(oi) == 0) + /* Skip originating a Grace-LSA when not necessary. */ + if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) || + (reason != OSPF_GR_UNKNOWN_RESTART && + ospf_interface_neighbor_count(oi) == 0)) return; /* Create new Grace-LSA. */ - lsa = ospf_gr_lsa_new(oi); + lsa = ospf_gr_lsa_new(oi, reason); if (!lsa) { zlog_warn("%s: ospf_gr_lsa_new() failed", __func__); return; @@ -157,18 +162,36 @@ static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage) if (old) lsa->data->ls_seqnum = lsa_seqnum_increment(old); - /* Install this LSA into LSDB. */ - if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { - zlog_warn("%s: ospf_lsa_install() failed", __func__); - ospf_lsa_unlock(&lsa); - return; + if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) { + struct list *update; + struct in_addr addr; + + /* + * When performing an unplanned restart, send a handcrafted + * Grace-LSA since the interface isn't fully initialized yet. + */ + ospf_lsa_checksum(lsa->data); + ospf_lsa_lock(lsa); + update = list_new(); + listnode_add(update, lsa); + addr.s_addr = htonl(OSPF_ALLSPFROUTERS); + ospf_ls_upd_queue_send(oi, update, addr, true); + list_delete(&update); + ospf_lsa_discard(lsa); + } else { + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { + zlog_warn("%s: ospf_lsa_install() failed", __func__); + ospf_lsa_unlock(&lsa); + return; + } + + /* Flood the LSA through out the interface */ + ospf_flood_through_interface(oi, NULL, lsa); } /* Update new LSA origination count. */ oi->ospf->lsa_originate_count++; - - /* Flood the LSA through out the interface */ - ospf_flood_through_interface(oi, NULL, lsa); } /* Flush all self-originated Grace-LSAs. */ @@ -181,13 +204,14 @@ static void ospf_gr_flush_grace_lsas(struct ospf *ospf) struct ospf_interface *oi; struct listnode *inode; - if (IS_DEBUG_OSPF_GR) - zlog_debug( - "GR: flushing self-originated Grace-LSAs [area %pI4]", - &area->area_id); + for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]", + &area->area_id, oi->ifp->name); - for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) - ospf_gr_lsa_originate(oi, true); + ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true); + } } } @@ -203,9 +227,6 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) ospf->gr_info.restart_in_progress = false; EVENT_OFF(ospf->gr_info.t_grace_period); - /* Record in non-volatile memory that the restart is complete. */ - ospf_gr_nvm_delete(ospf); - for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) { struct ospf_interface *oi; @@ -216,13 +237,22 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) */ ospf_router_lsa_update_area(area); - /* - * 2) The router should reoriginate network-LSAs on all segments - * where it is the Designated Router. - */ - for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) + for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) { + /* Disable hello delay. */ + if (oi->gr.hello_delay.t_grace_send) { + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, + ospf_hello_timer, 1); + } + + /* + * 2) The router should reoriginate network-LSAs on all + * segments where it is the Designated Router. + */ if (oi->state == ISM_DR) ospf_network_lsa_update(oi); + } } /* @@ -242,12 +272,34 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) * should be removed. */ ospf->gr_info.finishing_restart = true; + XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); + ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH); /* 6) Any grace-LSAs that the router originated should be flushed. */ ospf_gr_flush_grace_lsas(ospf); } +/* Enter the Graceful Restart mode. */ +void ospf_gr_restart_enter(struct ospf *ospf, + enum ospf_gr_restart_reason reason, int timestamp) +{ + unsigned long remaining_time; + + ospf->gr_info.restart_in_progress = true; + ospf->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf_gr_grace_period_expired, ospf, + remaining_time, &ospf->gr_info.t_grace_period); +} + /* Check if a Router-LSA contains a given link. */ static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa, struct in_addr *id) @@ -518,11 +570,26 @@ static char *ospf_gr_nvm_filepath(struct ospf *ospf) return filepath; } +/* Send extra Grace-LSA out the interface (unplanned outages only). */ +void ospf_gr_iface_send_grace_lsa(struct event *thread) +{ + struct ospf_interface *oi = EVENT_ARG(thread); + struct ospf_if_params *params = IF_DEF_PARAMS(oi->ifp); + + ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); + + if (++oi->gr.hello_delay.elapsed_seconds < params->v_gr_hello_delay) + event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); + else + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); +} + /* * Record in non-volatile memory that the given OSPF instance is attempting to * perform a graceful restart. */ -static void ospf_gr_nvm_update(struct ospf *ospf) +static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) { char *filepath; const char *inst_name; @@ -550,16 +617,18 @@ static void ospf_gr_nvm_update(struct ospf *ospf) json_instance); } + json_object_int_add(json_instance, "gracePeriod", + ospf->gr_info.grace_period); + /* * Record not only the grace period, but also a UNIX timestamp * corresponding to the end of that period. That way, once ospfd is * restarted, it will be possible to take into account the time that * passed while ospfd wasn't running. */ - json_object_int_add(json_instance, "gracePeriod", - ospf->gr_info.grace_period); - json_object_int_add(json_instance, "timestamp", - time(NULL) + ospf->gr_info.grace_period); + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf->gr_info.grace_period); json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); json_object_free(json); @@ -569,7 +638,7 @@ static void ospf_gr_nvm_update(struct ospf *ospf) * Delete GR status information about the given OSPF instance from non-volatile * memory. */ -static void ospf_gr_nvm_delete(struct ospf *ospf) +void ospf_gr_nvm_delete(struct ospf *ospf) { char *filepath; const char *inst_name; @@ -607,6 +676,7 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_object *json_instances; json_object *json_instance; json_object *json_timestamp; + json_object *json_grace_period; time_t timestamp = 0; filepath = ospf_gr_nvm_filepath(ospf); @@ -629,29 +699,33 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_instance); } + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); + if (json_timestamp) { time_t now; - unsigned long remaining_time; - /* Check if the grace period has already expired. */ + /* Planned GR: check if the grace period has already expired. */ now = time(NULL); timestamp = json_object_get_int(json_timestamp); if (now > timestamp) { ospf_gr_restart_exit( ospf, "grace period has expired already"); - } else { - /* Schedule grace period timeout. */ - ospf->gr_info.restart_in_progress = true; - remaining_time = timestamp - time(NULL); - if (IS_DEBUG_OSPF_GR) - zlog_debug( - "GR: remaining time until grace period expires: %lu(s)", - remaining_time); - event_add_timer(master, ospf_gr_grace_period_expired, - ospf, remaining_time, - &ospf->gr_info.t_grace_period); - } + } else + ospf_gr_restart_enter(ospf, OSPF_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf->gr_info.grace_period = grace_period; + ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART, + time(NULL) + ospf->gr_info.grace_period); } json_object_object_del(json_instances, inst_name); @@ -660,6 +734,17 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_object_free(json); } +void ospf_gr_unplanned_start_interface(struct ospf_interface *oi) +{ + /* Send Grace-LSA. */ + ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); + + /* Start GR hello-delay interval. */ + oi->gr.hello_delay.elapsed_seconds = 0; + event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); +} + /* Prepare to start a Graceful Restart. */ static void ospf_gr_prepare(void) { @@ -687,20 +772,12 @@ static void ospf_gr_prepare(void) continue; } - /* Freeze OSPF routes in the RIB. */ - if (ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period)) { - zlog_warn( - "%s: failed to activate graceful restart: not connected to zebra", - __func__); - continue; - } - /* Send a Grace-LSA to all neighbors. */ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) - ospf_gr_lsa_originate(oi, false); + ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false); /* Record end of the grace period in non-volatile memory. */ - ospf_gr_nvm_update(ospf); + ospf_gr_nvm_update(ospf, true); /* * Mark that a Graceful Restart preparation is in progress, to @@ -749,6 +826,12 @@ DEFPY(graceful_restart, graceful_restart_cmd, ospf->gr_info.restart_support = true; ospf->gr_info.grace_period = grace_period; + /* Freeze OSPF routes in the RIB. */ + (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf_gr_nvm_update(ospf, false); + return CMD_SUCCESS; } @@ -771,6 +854,8 @@ DEFPY(no_graceful_restart, no_graceful_restart_cmd, ospf->gr_info.restart_support = false; ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL; + ospf_gr_nvm_delete(ospf); + ospf_zebra_gr_disable(ospf); return CMD_SUCCESS; } diff --git a/ospfd/ospf_gr.h b/ospfd/ospf_gr.h index 9760bb172..750d77381 100644 --- a/ospfd/ospf_gr.h +++ b/ospfd/ospf_gr.h @@ -166,11 +166,16 @@ extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf, uint32_t interval); extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf, bool planned_only); - +extern void ospf_gr_iface_send_grace_lsa(struct event *thread); +extern void ospf_gr_restart_enter(struct ospf *ospf, + enum ospf_gr_restart_reason reason, + int timestamp); extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area); extern void ospf_gr_check_adjs(struct ospf *ospf); extern void ospf_gr_nvm_read(struct ospf *ospf); +extern void ospf_gr_nvm_delete(struct ospf *ospf); +extern void ospf_gr_unplanned_start_interface(struct ospf_interface *oi); extern void ospf_gr_init(void); #endif /* _ZEBRA_OSPF_GR_H */ diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 5742ece1f..8da982aed 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -101,6 +101,9 @@ int ospf_if_get_output_cost(struct ospf_interface *oi) cost = 1; else if (cost > 65535) cost = 65535; + + if (if_is_loopback(oi->ifp)) + cost = 0; } return cost; @@ -527,6 +530,7 @@ static struct ospf_if_params *ospf_new_if_params(void) UNSET_IF_PARAM(oip, passive_interface); UNSET_IF_PARAM(oip, v_hello); UNSET_IF_PARAM(oip, fast_hello); + UNSET_IF_PARAM(oip, v_gr_hello_delay); UNSET_IF_PARAM(oip, v_wait); UNSET_IF_PARAM(oip, priority); UNSET_IF_PARAM(oip, type); @@ -676,6 +680,9 @@ int ospf_if_new_hook(struct interface *ifp) SET_IF_PARAM(IF_DEF_PARAMS(ifp), fast_hello); IF_DEF_PARAMS(ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT; + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_gr_hello_delay); + IF_DEF_PARAMS(ifp)->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT; + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait); IF_DEF_PARAMS(ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 649df437a..24768b9ab 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -72,6 +72,9 @@ struct ospf_if_params { DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */ bool is_v_wait_set; /* Check for Dead Interval set */ + /* GR Hello Delay Interval */ + DECLARE_IF_PARAM(uint16_t, v_gr_hello_delay); + /* MTU mismatch check (see RFC2328, chap 10.6) */ DECLARE_IF_PARAM(uint8_t, mtu_ignore); @@ -214,6 +217,14 @@ struct ospf_interface { /* List of configured NBMA neighbor. */ struct list *nbr_nbma; + /* Graceful-Restart data. */ + struct { + struct { + uint16_t elapsed_seconds; + struct event *t_grace_send; + } hello_delay; + } gr; + /* self-originated LSAs. */ struct ospf_lsa *network_lsa_self; /* network-LSA. */ struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */ diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index 9f795ea91..2516fa75d 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -244,6 +244,10 @@ void ospf_hello_timer(struct event *thread) oi = EVENT_ARG(thread); oi->t_hello = NULL; + /* Check if the GR hello-delay is active. */ + if (oi->gr.hello_delay.t_grace_send) + return; + if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi)); @@ -282,6 +286,7 @@ static void ism_timer_set(struct ospf_interface *oi) EVENT_OFF(oi->t_hello); EVENT_OFF(oi->t_wait); EVENT_OFF(oi->t_ls_ack); + EVENT_OFF(oi->gr.hello_delay.t_grace_send); break; case ISM_Loopback: /* In this state, the interface may be looped back and will be @@ -289,6 +294,7 @@ static void ism_timer_set(struct ospf_interface *oi) EVENT_OFF(oi->t_hello); EVENT_OFF(oi->t_wait); EVENT_OFF(oi->t_ls_ack); + EVENT_OFF(oi->gr.hello_delay.t_grace_send); break; case ISM_Waiting: /* The router is trying to determine the identity of DRouter and diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 452a3ba37..67f1faf8a 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -608,7 +608,8 @@ static int lsa_link_loopback_set(struct stream **s, struct ospf_interface *oi) mask.s_addr = 0xffffffff; id.s_addr = oi->address->u.prefix4.s_addr; - return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); + return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, + oi->output_cost); } /* Describe Virtual Link. */ diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index c2b40af1c..6894c6a00 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -2120,14 +2120,21 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, lsa->data->type, &lsa->data->id); /* - * Since these LSA entries are not yet installed into corresponding - * LSDB, just flush them without calling ospf_ls_maxage() afterward. + * Install the stale LSA into the Link State Database, add it to the + * MaxAge list, and flush it from the OSPF routing domain. For other + * LSA types, the installation is done in the refresh function. It is + * done inline here since the opaque refresh function is dynamically + * registered when opaque LSAs are originated (which is not the case + * for stale LSAs). */ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + ospf_lsa_install( + top, (lsa->data->type == OSPF_OPAQUE_LINK_LSA) ? nbr->oi : NULL, + lsa); + ospf_lsa_maxage(top, lsa); + switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: - ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa); - break; case OSPF_OPAQUE_AREA_LSA: ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa); break; @@ -2139,7 +2146,6 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, __func__, lsa->data->type); return; } - ospf_lsa_discard(lsa); /* List "lsas" will be deleted by caller. */ } /*------------------------------------------------------------------------* diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 552acfd6d..d010b8b6e 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2031,7 +2031,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, if (current == NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "LSA[%s]: Previously originated Opaque-LSA,not found in the LSDB.", + "LSA[%s]: Previously originated Opaque-LSA, not found in the LSDB.", dump_lsa_key(lsa)); SET_FLAG(lsa->flags, OSPF_LSA_SELF); @@ -4040,9 +4040,8 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update, return ospf_packet_new(size - sizeof(struct ip)); } -static void ospf_ls_upd_queue_send(struct ospf_interface *oi, - struct list *update, struct in_addr addr, - int send_lsupd_now) +void ospf_ls_upd_queue_send(struct ospf_interface *oi, struct list *update, + struct in_addr addr, int send_lsupd_now) { struct ospf_packet *op; uint16_t length = OSPF_HEADER_SIZE; diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h index 4003e2add..234738979 100644 --- a/ospfd/ospf_packet.h +++ b/ospfd/ospf_packet.h @@ -132,6 +132,9 @@ extern void ospf_ls_req_send(struct ospf_neighbor *); extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *, int); extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int); +extern void ospf_ls_upd_queue_send(struct ospf_interface *oi, + struct list *update, struct in_addr addr, + int send_lsupd_now); extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *); extern void ospf_ls_ack_send_delayed(struct ospf_interface *); extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *); diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c index da9428aba..07fc50394 100644 --- a/ospfd/ospf_ti_lfa.c +++ b/ospfd/ospf_ti_lfa.c @@ -1078,6 +1078,7 @@ void ospf_ti_lfa_free_p_spaces(struct ospf_area *area) q_spaces_fini(p_space->q_spaces); XFREE(MTYPE_OSPF_Q_SPACE, p_space->q_spaces); + XFREE(MTYPE_OSPF_P_SPACE, p_space); } p_spaces_fini(area->p_spaces); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 09dd0081e..f92be3fca 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -232,9 +232,12 @@ DEFUN (no_router_ospf, return CMD_NOT_MY_INSTANCE; ospf = ospf_lookup(instance, vrf_name); - if (ospf) + if (ospf) { + if (ospf->gr_info.restart_support) + ospf_gr_nvm_delete(ospf); + ospf_finish(ospf); - else + } else ret = CMD_WARNING_CONFIG_FAILED; return ret; @@ -3617,6 +3620,7 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, struct ospf_neighbor *nbr; struct route_node *rn; uint32_t bandwidth = ifp->bandwidth ? ifp->bandwidth : ifp->speed; + struct ospf_if_params *params; /* Is interface up? */ if (use_json) { @@ -3936,6 +3940,20 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, ospf_nbr_count(oi, 0), ospf_nbr_count(oi, NSM_Full)); + + params = IF_DEF_PARAMS(ifp); + if (params && + OSPF_IF_PARAM_CONFIGURED(params, v_gr_hello_delay)) { + if (use_json) { + json_object_int_add(json_interface_sub, + "grHelloDelaySecs", + params->v_gr_hello_delay); + } else + vty_out(vty, + " Graceful Restart hello delay: %us\n", + params->v_gr_hello_delay); + } + ospf_interface_bfd_show(vty, ifp, json_interface_sub); /* OSPF Authentication information */ @@ -3996,16 +4014,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, /* Interface name is specified. */ ifp = if_lookup_by_name(intf_name, ospf->vrf_id); if (ifp == NULL) { - if (use_json) + if (use_json) { json_object_boolean_true_add(json_vrf, "noSuchIface"); - else + json_object_free(json_interface); + } else vty_out(vty, "No such interface name\n"); } else { - if (use_json) { + if (use_json) json_interface_sub = json_object_new_object(); - json_interface = json_object_new_object(); - } show_ip_ospf_interface_sub( vty, ospf, ifp, json_interface_sub, use_json); @@ -8640,6 +8657,59 @@ DEFUN_HIDDEN (no_ospf_retransmit_interval, return no_ip_ospf_retransmit_interval(self, vty, argc, argv); } +DEFPY (ip_ospf_gr_hdelay, + ip_ospf_gr_hdelay_cmd, + "ip ospf graceful-restart hello-delay (1-1800)", + IP_STR + "OSPF interface commands\n" + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + /* Note: new or updated value won't affect ongoing graceful restart. */ + SET_IF_PARAM(params, v_gr_hello_delay); + params->v_gr_hello_delay = hello_delay; + + return CMD_SUCCESS; +} + +DEFPY (no_ip_ospf_gr_hdelay, + no_ip_ospf_gr_hdelay_cmd, + "no ip ospf graceful-restart hello-delay [(1-1800)]", + NO_STR + IP_STR + "OSPF interface commands\n" + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + UNSET_IF_PARAM(params, v_gr_hello_delay); + params->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi; + + oi = rn->info; + if (!oi) + continue; + + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + } + + return CMD_SUCCESS; +} + DEFUN (ip_ospf_transmit_delay, ip_ospf_transmit_delay_addr_cmd, "ip ospf transmit-delay (1-65535) [A.B.C.D]", @@ -11832,6 +11902,15 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, "\n"); } + /* Hello Graceful-Restart Delay print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, + v_gr_hello_delay) && + params->v_gr_hello_delay != + OSPF_HELLO_DELAY_DEFAULT) + vty_out(vty, + " ip ospf graceful-restart hello-delay %u\n", + params->v_gr_hello_delay); + /* Router Priority print. */ if (OSPF_IF_PARAM_CONFIGURED(params, priority) && params->priority diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 0b770a836..27d74cd4f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -1252,12 +1252,18 @@ static int ospf_zebra_gr_update(struct ospf *ospf, int command, int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("Zebra enable GR [stale time %u]", stale_time); + return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES, stale_time); } int ospf_zebra_gr_disable(struct ospf *ospf) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("Zebra disable GR"); + return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0); } @@ -2120,10 +2126,20 @@ int ospf_zebra_label_manager_connect(void) static void ospf_zebra_connected(struct zclient *zclient) { + struct ospf *ospf; + struct listnode *node; + /* Send the client registration */ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->gr_info.restart_support) + continue; + (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + } } /* diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 7e83714c0..eae1f301a 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -720,6 +720,7 @@ static void ospf_finish_final(struct ospf *ospf) if (!ospf->gr_info.prepare_in_progress) ospf_flush_self_originated_lsas_now(ospf); + XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); /* Unregister redistribution */ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { @@ -1131,6 +1132,17 @@ struct ospf_interface *add_ospf_interface(struct connected *co, && if_is_operative(co->ifp)) ospf_if_up(oi); + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->ospf->gr_info.restart_in_progress && + oi->ospf->gr_info.reason == OSPF_GR_UNKNOWN_RESTART) + ospf_gr_unplanned_start_interface(oi); + return oi; } diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 1f8d1a32e..36936b16f 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -148,6 +148,8 @@ struct ospf_gr_info { bool prepare_in_progress; bool finishing_restart; uint32_t grace_period; + int reason; + char *exit_reason; struct event *t_grace_period; }; diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index 5eedfb7dc..aa39be63d 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -344,7 +344,8 @@ static const char *const gm_states[] = { }; /* clang-format on */ -CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported"); +/* 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 @@ -645,7 +646,7 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, */ gm_packet_sg_drop(old_grp); gm_sg_update(grp, false); - CPP_NOTICE("need S,G PRUNE => NO_INFO transition here"); +/* TODO "need S,G PRUNE => NO_INFO transition here" */ } break; @@ -793,7 +794,8 @@ static void gm_handle_v2_pass2_excl(struct gm_packet_state *pkt, size_t offs) gm_sg_update(sg_grp, false); } -CPP_NOTICE("TODO: QRV/QQIC are not copied from queries to local state"); +/* 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 @@ -949,7 +951,8 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp, item = gm_packet_sg_setup(pkt, grp, true, false); item->n_exclude = 0; - CPP_NOTICE("set v1-seen timer on grp here"); + +/* TODO "set v1-seen timer on grp here" */ /* } */ @@ -1012,7 +1015,9 @@ static void gm_handle_v1_leave(struct gm_if *gm_ifp, if (old_grp) { gm_packet_sg_drop(old_grp); gm_sg_update(grp, false); - CPP_NOTICE("need S,G PRUNE => NO_INFO transition here"); + +/* TODO "need S,G PRUNE => NO_INFO transition here" */ + } } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index b053e422e..b1beb4563 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -979,13 +979,13 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) } if (ifp->ifindex < 0) { - zlog_warn("%s: ifindex=%d < 1 on interface %s", __func__, + zlog_warn("%s: ifindex=%d < 0 on interface %s", __func__, ifp->ifindex, ifp->name); return -2; - } else if ((ifp->ifindex == 0) && + } else if ((ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) && ((strncmp(ifp->name, "pimreg", 6)) && (strncmp(ifp->name, "pim6reg", 7)))) { - zlog_warn("%s: ifindex=%d == 0 on interface %s", __func__, + zlog_warn("%s: ifindex=%d on interface %s", __func__, ifp->ifindex, ifp->name); return -2; } diff --git a/ripd/rip_bfd.c b/ripd/rip_bfd.c index ac5035f54..b59db11a3 100644 --- a/ripd/rip_bfd.c +++ b/ripd/rip_bfd.c @@ -13,6 +13,8 @@ #include "ripd/rip_bfd.h" #include "ripd/rip_debug.h" +DEFINE_MTYPE(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name"); + extern struct zclient *zclient; static const char *rip_bfd_interface_profile(struct rip_interface *ri) diff --git a/ripd/rip_bfd.h b/ripd/rip_bfd.h index d49ca1515..7621498b1 100644 --- a/ripd/rip_bfd.h +++ b/ripd/rip_bfd.h @@ -9,6 +9,8 @@ #include "frrevent.h" +DECLARE_MTYPE(RIP_BFD_PROFILE); + struct rip; struct rip_interface; struct rip_peer; diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index ac9fc4b1a..6f45bb5d9 100644 --- a/ripd/rip_cli.c +++ b/ripd/rip_cli.c @@ -85,14 +85,33 @@ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/allow-ecmp */ -DEFPY_YANG (rip_allow_ecmp, +DEFUN_YANG (rip_allow_ecmp, rip_allow_ecmp_cmd, - "[no] allow-ecmp", + "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", + "Allow Equal Cost MultiPath\n" + "Number of paths\n") +{ + int idx_number = 1; + char mpaths[3] = {}; + uint32_t paths = MULTIPATH_NUM; + + if (argv[idx_number]) + paths = strtol(argv[idx_number]->arg, NULL, 10); + snprintf(mpaths, sizeof(mpaths), "%u", paths); + + nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_rip_allow_ecmp, + no_rip_allow_ecmp_cmd, + "no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", NO_STR - "Allow Equal Cost MultiPath\n") + "Allow Equal Cost MultiPath\n" + "Number of paths\n") { - nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, - no ? "false" : "true"); + nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0); return nb_cli_apply_changes(vty, NULL); } @@ -100,10 +119,14 @@ DEFPY_YANG (rip_allow_ecmp, void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - if (!yang_dnode_get_bool(dnode, NULL)) - vty_out(vty, " no"); + uint8_t paths; + + paths = yang_dnode_get_uint8(dnode, NULL); - vty_out(vty, " allow-ecmp\n"); + if (!paths) + vty_out(vty, " no allow-ecmp\n"); + else + vty_out(vty, " allow-ecmp %d\n", paths); } /* @@ -1156,6 +1179,7 @@ void rip_cli_init(void) install_element(RIP_NODE, &rip_no_distribute_list_cmd); install_element(RIP_NODE, &rip_allow_ecmp_cmd); + install_element(RIP_NODE, &no_rip_allow_ecmp_cmd); install_element(RIP_NODE, &rip_default_information_originate_cmd); install_element(RIP_NODE, &rip_default_metric_cmd); install_element(RIP_NODE, &no_rip_default_metric_cmd); diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index b383be042..b58015a67 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -25,6 +25,7 @@ #include "zebra/connected.h" #include "ripd/ripd.h" +#include "ripd/rip_bfd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" @@ -457,7 +458,7 @@ static void rip_interface_reset(struct rip_interface *ri) ri->sent_updates = 0; ri->passive = 0; - XFREE(MTYPE_TMP, ri->bfd.profile); + XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile); rip_interface_clean(ri); } @@ -1110,9 +1111,10 @@ void rip_interface_sync(struct interface *ifp) struct rip_interface *ri; ri = ifp->info; - ri->ifp = ifp; - if (ri) + if (ri) { ri->rip = ifp->vrf->info; + ri->ifp = ifp; + } } /* Called when interface structure allocated. */ diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c index 8fe34705c..1117ec9ff 100644 --- a/ripd/rip_nb_config.c +++ b/ripd/rip_nb_config.c @@ -25,8 +25,6 @@ #include "ripd/rip_interface.h" #include "ripd/rip_bfd.h" -DEFINE_MTYPE_STATIC(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name"); - /* * XPath: /frr-ripd:ripd/instance */ @@ -101,9 +99,13 @@ int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args) return NB_OK; rip = nb_running_get_entry(args->dnode, NULL, true); - rip->ecmp = yang_dnode_get_bool(args->dnode, NULL); - if (!rip->ecmp) + rip->ecmp = yang_dnode_get_uint8(args->dnode, NULL); + if (!rip->ecmp) { rip_ecmp_disable(rip); + return NB_OK; + } + + rip_ecmp_change(rip); return NB_OK; } diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 698dcb982..834c7d2d6 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -20,6 +20,7 @@ /* All information about zebra. */ struct zclient *zclient = NULL; +uint32_t zebra_ecmp_count = MULTIPATH_NUM; /* Send ECMP routes to zebra. */ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, @@ -30,7 +31,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, struct zapi_nexthop *api_nh; struct listnode *listnode = NULL; struct rip_info *rinfo = NULL; - int count = 0; + uint32_t count = 0; memset(&api, 0, sizeof(api)); api.vrf_id = rip->vrf->vrf_id; @@ -39,7 +40,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - if (count >= MULTIPATH_NUM) + if (count >= zebra_ecmp_count) break; api_nh = &api.nexthops[count]; api_nh->vrf_id = rip->vrf->vrf_id; @@ -227,6 +228,11 @@ zclient_handler *const rip_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = rip_zebra_read_route, }; +static void rip_zebra_capabilities(struct zclient_capabilities *cap) +{ + zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count); +} + void rip_zclient_init(struct event_loop *master) { /* Set default value to the zebra client structure. */ @@ -234,6 +240,7 @@ void rip_zclient_init(struct event_loop *master) array_size(rip_handlers)); zclient_init(zclient, ZEBRA_ROUTE_RIP, 0, &ripd_privs); zclient->zebra_connected = rip_zebra_connected; + zclient->zebra_capabilities = rip_zebra_capabilities; } void rip_zclient_stop(void) diff --git a/ripd/ripd.c b/ripd/ripd.c index cb8dd4945..04a8cad56 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -34,6 +34,7 @@ #include "ripd/ripd.h" #include "ripd/rip_nb.h" +#include "ripd/rip_bfd.h" #include "ripd/rip_debug.h" #include "ripd/rip_errors.h" #include "ripd/rip_interface.h" @@ -155,7 +156,10 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new) { struct route_node *rp = rinfo_new->rp; struct rip_info *rinfo = NULL; + struct rip_info *rinfo_exist = NULL; struct list *list = NULL; + struct listnode *node = NULL; + struct listnode *nnode = NULL; if (rp->info == NULL) rp->info = list_new(); @@ -166,6 +170,33 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new) if (listcount(list) && !rip->ecmp) return NULL; + /* Add or replace an existing ECMP path with lower neighbor IP */ + if (listcount(list) && listcount(list) >= rip->ecmp) { + struct rip_info *from_highest = NULL; + + /* Find the rip_info struct that has the highest nexthop IP */ + for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist)) + if (!from_highest || + (from_highest && + IPV4_ADDR_CMP(&rinfo_exist->from, + &from_highest->from) > 0)) { + from_highest = rinfo_exist; + } + + /* If we have a route in ECMP group, delete the old + * one that has a higher next-hop address. Lower IP is + * preferred. + */ + if (rip->ecmp > 1 && from_highest && + IPV4_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) { + rip_ecmp_delete(rip, from_highest); + goto add_or_replace; + } + + return NULL; + } + +add_or_replace: rinfo = rip_info_new(); memcpy(rinfo, rinfo_new, sizeof(struct rip_info)); listnode_add(list, rinfo); @@ -2631,6 +2662,36 @@ struct rip *rip_lookup_by_vrf_name(const char *vrf_name) return RB_FIND(rip_instance_head, &rip_instances, &rip); } +/* Update ECMP routes to zebra when `allow-ecmp` changed. */ +void rip_ecmp_change(struct rip *rip) +{ + struct route_node *rp; + struct rip_info *rinfo; + struct list *list; + struct listnode *node, *nextnode; + + for (rp = route_top(rip->table); rp; rp = route_next(rp)) { + list = rp->info; + if (list && listcount(list) > 1) { + while (listcount(list) > rip->ecmp) { + struct rip_info *from_highest = NULL; + + for (ALL_LIST_ELEMENTS(list, node, nextnode, + rinfo)) { + if (!from_highest || + (from_highest && + IPV4_ADDR_CMP( + &rinfo->from, + &from_highest->from) > 0)) + from_highest = rinfo; + } + + rip_ecmp_delete(rip, from_highest); + } + } + } +} + /* Create new RIP instance and set it to global variable. */ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket) { @@ -2640,7 +2701,7 @@ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket) rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name); /* Set initial value. */ - rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE); + rip->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIP_INSTANCE); rip->default_metric = yang_get_default_uint8("%s/default-metric", RIP_INSTANCE); rip->distance = @@ -3346,7 +3407,7 @@ void rip_clean(struct rip *rip) route_table_finish(rip->distance_table); RB_REMOVE(rip_instance_head, &rip_instances, rip); - XFREE(MTYPE_TMP, rip->default_bfd_profile); + XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile); XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name); XFREE(MTYPE_RIP, rip); } diff --git a/ripd/ripd.h b/ripd/ripd.h index bba3c2806..2db1e5a6b 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -141,7 +141,7 @@ struct rip { struct route_table *distance_table; /* RIP ECMP flag */ - bool ecmp; + uint8_t ecmp; /* Are we in passive-interface default mode? */ bool passive_default; @@ -537,4 +537,6 @@ extern struct event_loop *master; DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc)); DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc)); +extern void rip_ecmp_change(struct rip *rip); + #endif /* _ZEBRA_RIP_H */ diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am index e950d0120..c3a1a3e2c 100644 --- a/tests/lib/subdir.am +++ b/tests/lib/subdir.am @@ -15,8 +15,14 @@ tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c +EXTRA_tests_lib_test_frrscript_DEPENDENCIES = copy_script EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua +# For out-of-tree build, lua script needs to be in the build dir, rather than +# just available somewhere in the VPATH +copy_script: tests/lib/script1.lua + test -e tests/lib/script1.lua || \ + $(INSTALL_SCRIPT) $< tests/lib/script1.lua ############################################################################## GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref index cb63da471..7e28f04e1 100644 --- a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref +++ b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref @@ -10,6 +10,7 @@ r1-eth0 is up Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5 Hello due in XX.XXXs Neighbor Count is 0, Adjacent neighbor count is 0 + Graceful Restart hello delay: 10s r1-eth3 is up ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST> Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0 @@ -22,3 +23,4 @@ r1-eth3 is up Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5 Hello due in XX.XXXs Neighbor Count is 0, Adjacent neighbor count is 0 + Graceful Restart hello delay: 10s diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index 92bb99c8f..4b7c4de80 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -198,6 +198,12 @@ def test_error_messages_daemons(): if fatal_error != "": pytest.skip(fatal_error) + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + print( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") + print("\n\n** Check for error messages in daemons") print("******************************************\n") diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf index ae72f215c..4db468753 100644 --- a/tests/topotests/bgp_aigp/r2/bgpd.conf +++ b/tests/topotests/bgp_aigp/r2/bgpd.conf @@ -4,7 +4,8 @@ router bgp 65001 neighbor 10.0.0.1 remote-as internal neighbor 10.0.0.1 timers 1 3 neighbor 10.0.0.1 timers connect 1 - neighbor 192.168.24.4 remote-as external + neighbor 10.0.0.1 route-reflector-client + neighbor 192.168.24.4 remote-as internal neighbor 192.168.24.4 timers 1 3 neighbor 192.168.24.4 timers connect 1 neighbor 192.168.24.4 aigp diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf index 7572e268c..5ab712eab 100644 --- a/tests/topotests/bgp_aigp/r3/bgpd.conf +++ b/tests/topotests/bgp_aigp/r3/bgpd.conf @@ -4,7 +4,8 @@ router bgp 65001 neighbor 10.0.0.1 remote-as internal neighbor 10.0.0.1 timers 1 3 neighbor 10.0.0.1 timers connect 1 - neighbor 192.168.35.5 remote-as external + neighbor 10.0.0.1 route-reflector-client + neighbor 192.168.35.5 remote-as internal neighbor 192.168.35.5 timers 1 3 neighbor 192.168.35.5 timers connect 1 neighbor 192.168.35.5 aigp diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf index d2b96b7b0..aa88bac91 100644 --- a/tests/topotests/bgp_aigp/r4/bgpd.conf +++ b/tests/topotests/bgp_aigp/r4/bgpd.conf @@ -1,10 +1,11 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check - neighbor 192.168.24.2 remote-as external + neighbor 192.168.24.2 remote-as internal neighbor 192.168.24.2 timers 1 3 neighbor 192.168.24.2 timers connect 1 neighbor 192.168.24.2 aigp + neighbor 192.168.24.2 route-reflector-client neighbor 10.0.0.6 remote-as internal neighbor 10.0.0.6 timers 1 3 neighbor 10.0.0.6 timers connect 1 diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf index 944872289..4fde26205 100644 --- a/tests/topotests/bgp_aigp/r5/bgpd.conf +++ b/tests/topotests/bgp_aigp/r5/bgpd.conf @@ -1,10 +1,11 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check - neighbor 192.168.35.3 remote-as external + neighbor 192.168.35.3 remote-as internal neighbor 192.168.35.3 timers 1 3 neighbor 192.168.35.3 timers connect 1 neighbor 192.168.35.3 aigp + neighbor 192.168.35.3 route-reflector-client neighbor 10.0.0.6 remote-as internal neighbor 10.0.0.6 timers 1 3 neighbor 10.0.0.6 timers connect 1 diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf index 15d9437a8..2faae7720 100644 --- a/tests/topotests/bgp_aigp/r6/bgpd.conf +++ b/tests/topotests/bgp_aigp/r6/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check neighbor 10.0.0.4 remote-as internal diff --git a/tests/topotests/bgp_aigp/r7/bgpd.conf b/tests/topotests/bgp_aigp/r7/bgpd.conf index 00d85cfad..639dcfeca 100644 --- a/tests/topotests/bgp_aigp/r7/bgpd.conf +++ b/tests/topotests/bgp_aigp/r7/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.67.6 remote-as internal diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py index 6fda45940..655e9ad18 100644 --- a/tests/topotests/bgp_aigp/test_bgp_aigp.py +++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py @@ -14,7 +14,7 @@ r6 receives those routes with aigp-metric TLV. r2 and r3 receives those routes with aigp-metric TLV increased by 20, and 30 appropriately. -r1 receives routes with aigp-metric TLV 91,101 and 92,102 appropriately. +r1 receives routes with aigp-metric TLV 111,131 and 112,132 appropriately. """ import os @@ -109,15 +109,15 @@ def test_bgp_aigp(): expected = { "paths": [ { - "aigpMetric": 101, + "aigpMetric": 111, "valid": True, - "bestpath": {"selectionReason": "Router ID"}, - "nexthops": [{"hostname": "r2", "accessible": True}], + "nexthops": [{"hostname": "r3", "accessible": True}], }, { - "aigpMetric": 91, + "aigpMetric": 131, "valid": True, - "nexthops": [{"hostname": "r3", "accessible": True}], + "bestpath": {"selectionReason": "Neighbor IP"}, + "nexthops": [{"hostname": "r2", "accessible": True}], }, ] } @@ -141,28 +141,30 @@ def test_bgp_aigp(): "10.0.0.71/32": { "paths": [ { - "aigpMetric": 101, + "aigpMetric": 111, + "bestpath": {"selectionReason": "AIGP"}, "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], }, { - "aigpMetric": 91, + "aigpMetric": 131, "valid": True, - "bestpath": {"selectionReason": "AIGP"}, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [{"hostname": "r2", "accessible": True}], }, ], }, "10.0.0.72/32": { "paths": [ { - "aigpMetric": 102, + "aigpMetric": 112, + "bestpath": {"selectionReason": "AIGP"}, "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], }, { - "aigpMetric": 92, + "aigpMetric": 132, "valid": True, - "bestpath": {"selectionReason": "AIGP"}, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [{"hostname": "r2", "accessible": True}], }, ], }, @@ -172,7 +174,7 @@ def test_bgp_aigp(): # Initial converge, AIGP is not involved in best-path selection process test_func = functools.partial(_bgp_converge) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "can't converge initially" # Enable `bgp bestpath aigp` @@ -186,27 +188,27 @@ def test_bgp_aigp(): # r4, 10.0.0.71/32 with aigp-metric 71 test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.0.71/32", 71) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.71/32 is not 71" # r5, 10.0.0.72/32 with aigp-metric 72 test_func = functools.partial(_bgp_check_aigp_metric, r5, "10.0.0.72/32", 72) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.72/32 is not 72" # r2, 10.0.0.71/32 with aigp-metric 101 (71 + 30) test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 101) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.71/32 is not 101" # r3, 10.0.0.72/32 with aigp-metric 92 (72 + 20) test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 92) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.72/32 is not 92" # r1, check if AIGP is considered in best-path selection (lowest wins) test_func = functools.partial(_bgp_check_aigp_metric_bestpath) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "AIGP attribute is not considered in best-path selection" diff --git a/tests/topotests/bgp_auth/test_bgp_auth1.py b/tests/topotests/bgp_auth/test_bgp_auth1.py index 566d391f7..9d47106c0 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth1.py +++ b/tests/topotests/bgp_auth/test_bgp_auth1.py @@ -158,8 +158,8 @@ def setup_module(mod): # 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) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_auth/test_bgp_auth2.py b/tests/topotests/bgp_auth/test_bgp_auth2.py index 0e9942a22..6b9203672 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth2.py +++ b/tests/topotests/bgp_auth/test_bgp_auth2.py @@ -158,8 +158,8 @@ def setup_module(mod): # 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) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_auth/test_bgp_auth3.py b/tests/topotests/bgp_auth/test_bgp_auth3.py index 99a8953b3..2237c6b1b 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth3.py +++ b/tests/topotests/bgp_auth/test_bgp_auth3.py @@ -157,8 +157,8 @@ def setup_module(mod): # 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) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_auth/test_bgp_auth4.py b/tests/topotests/bgp_auth/test_bgp_auth4.py index dffef0eef..d6fe42504 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth4.py +++ b/tests/topotests/bgp_auth/test_bgp_auth4.py @@ -157,8 +157,8 @@ def setup_module(mod): # 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) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json index 23afa2c91..5528b590f 100755 --- a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json +++ b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json @@ -22,12 +22,7 @@ "routers": { "r1": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r2": { "ipv4": "auto", "ipv6": "auto", @@ -129,12 +124,7 @@ }, "r2": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r1": { "ipv4": "auto", "ipv6": "auto", @@ -193,12 +183,7 @@ }, "r3": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r1": { "ipv4": "auto", "ipv6": "auto", @@ -287,12 +272,7 @@ }, "r4": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r3": { "ipv4": "auto", "ipv6": "auto", @@ -336,12 +316,7 @@ }, "r5": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r3": { "ipv4": "auto", "ipv6": "auto", diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index c0a5a1905..e033a0f00 100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -176,6 +176,19 @@ def test_bgp_convergence(): # tgen.mininet_cli() +def get_shut_msg_count(tgen): + shuts = {} + for rtrNum in [2, 4]: + shutmsg = tgen.net["r{}".format(rtrNum)].cmd_nostatus( + 'grep -c "NOTIFICATION.*Cease/Administrative Shutdown" bgpd.log', warn=False + ) + try: + shuts[rtrNum] = int(shutmsg.strip()) + except ValueError: + shuts[rtrNum] = 0 + return shuts + + def test_bgp_shutdown(): "Test BGP instance shutdown" @@ -185,6 +198,8 @@ def test_bgp_shutdown(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) + shuts_before = get_shut_msg_count(tgen) + tgen.net["r1"].cmd( 'vtysh -c "conf t" -c "router bgp 65000" -c "bgp shutdown message ABCDabcd"' ) @@ -208,6 +223,11 @@ def test_bgp_shutdown(): ) assert res is None, assertmsg + shuts_after = get_shut_msg_count(tgen) + + for k in shuts_before: + assert shuts_before[k] + 1 == shuts_after[k] + def test_bgp_shutdown_message(): "Test BGP Peer Shutdown Message" @@ -222,18 +242,11 @@ def test_bgp_shutdown_message(): logger.info("Checking BGP shutdown received on router r{}".format(rtrNum)) shut_message = tgen.net["r{}".format(rtrNum)].cmd( - 'tail bgpd.log | grep "NOTIFICATION.*Cease/Administrative Shutdown"' + 'grep -e "NOTIFICATION.*Cease/Administrative Shutdown.*ABCDabcd" bgpd.log' ) assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum) assert shut_message != "", assertmsg - assertmsg = "Incorrect BGP shutdown message received on router R{}".format( - rtrNum - ) - assert "ABCDabcd" in shut_message, assertmsg - - # tgen.mininet_cli() - def test_bgp_no_shutdown(): "Test BGP instance no shutdown" diff --git a/tests/topotests/bgp_route_map_match_source_protocol/__init__.py b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf new file mode 100644 index 000000000..7c3efeea4 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf @@ -0,0 +1,32 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! +ip route 10.10.10.10/32 192.168.2.2 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + redistribute connected + redistribute static + neighbor 192.168.1.2 route-map r2 out + neighbor 192.168.2.2 route-map r3 out + exit-address-family +! +route-map r2 permit 10 + match source-protocol static +route-map r3 permit 10 + match source-protocol connected +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf new file mode 100644 index 000000000..7213975cc --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf new file mode 100644 index 000000000..4a1d830b0 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf @@ -0,0 +1,10 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py new file mode 100644 index 000000000..282879640 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if r1 can announce only static routes to r2, and only connected +routes to r3 using `match source-protocol` with route-maps. +""" + +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +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_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_match_source_protocol(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_advertised_routes_r2(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.10/32": { + "valid": True, + } + }, + "totalPrefixCounter": 1, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to filter routes by source-protocol for r2" + + def _bgp_check_advertised_routes_r3(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.2.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "192.168.1.0/24": { + "valid": True, + }, + "192.168.2.0/24": { + "valid": True, + }, + "172.16.255.1/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 3, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to filter routes by source-protocol for r3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json index 159879a85..39ba7dd62 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json @@ -48,11 +48,11 @@ { "ip":"192:2::12", "afi":"ipv6", - "scope":"global" + "scope":"global", + "used":true }, { - "scope": "link-local", - "used":true + "scope": "link-local" } ] } @@ -69,7 +69,8 @@ { "ip":"192:168::255:13", "afi":"ipv6", - "scope": "global" + "scope": "global", + "used":true }, { "scope": "link-local" @@ -123,7 +124,8 @@ { "ip":"192:2::11", "afi":"ipv6", - "scope":"global" + "scope":"global", + "used":true } ] } @@ -140,7 +142,8 @@ { "ip":"192:2::11", "afi":"ipv6", - "scope":"global" + "scope":"global", + "used":true } ] } diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf index 74e3e6fb5..6bb0a88f0 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf @@ -24,7 +24,7 @@ router bgp 65500 vrf vrf1 neighbor 192:168::255:13 activate neighbor 192:168::255:13 route-map rmap in redistribute connected - redistribute static + redistribute static route-map rmap label vpn export allocation-mode per-nexthop label vpn export auto rd vpn export 444:1 @@ -39,7 +39,6 @@ interface r1-eth0 bgp community-list 1 seq 5 permit 10:10 ! route-map rmap permit 1 - match community 1 set ipv6 next-hop prefer-global ! route-map rmap permit 2 diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf index d0d4e3dc4..cb653d61b 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf @@ -7,12 +7,9 @@ router bgp 65500 ! address-family ipv6 unicast neighbor 192:2::100 activate - neighbor 192:2::100 route-map rmap out network 172:31::11/128 network 172:31::111/128 network 172:31::20/128 exit-address-family ! -route-map rmap permit 1 - set community 10:10 -! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf index 201b905b3..04378825a 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf @@ -7,10 +7,6 @@ router bgp 65500 exit-address-family address-family ipv6 unicast neighbor 192:168::255:1 activate - neighbor 192:168::255:1 route-map rmap out network 172:31::0:13/128 exit-address-family ! -route-map rmap permit 1 - set community 10:10 -! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py index c58242e7c..719ba801c 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -52,9 +52,7 @@ from lib.topolog import logger pytestmark = [pytest.mark.bgpd] PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"] -PREFIXES_R12 = ["172:31::12/128"] -PREFIXES_REDIST_R12 = ["172:31::15/128"] -PREFIXES_R13 = ["172:31::13/128"] +PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"] PREFIXES_REDIST_R14 = ["172:31::14/128"] PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"] @@ -105,11 +103,6 @@ def _populate_iface(): cmds_list_plus = [ "ip link set dev {0}-eth2 master vrf1", ] - cmd_no_ll = [ - "ip link set dev {0}-eth0 down", - "echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/addr_gen_mode", - "ip link set dev {0}-eth0 up", - ] for cmd in cmds_list: input = cmd.format("r1") @@ -129,13 +122,6 @@ def _populate_iface(): output = tgen.net["r2"].cmd(cmd.format("r2")) logger.info("output: " + output) - for rtr in ("r11", "r13"): - for cmd in cmd_no_ll: - input = cmd.format(rtr) - logger.info("input: " + cmd) - output = tgen.net[rtr].cmd(cmd.format(rtr)) - logger.info("output: " + output) - def setup_module(mod): "Sets up the pytest environment" @@ -226,8 +212,6 @@ def bgp_vpnv6_table_check_all(router, label_list=None, same=False): router, group=PREFIXES_R11 + PREFIXES_R12 - + PREFIXES_REDIST_R12 - + PREFIXES_R13 + PREFIXES_REDIST_R14 + PREFIXES_CONNECTED, label_list=label_list, @@ -236,8 +220,6 @@ def bgp_vpnv6_table_check_all(router, label_list=None, same=False): for group in ( PREFIXES_R11, PREFIXES_R12, - PREFIXES_REDIST_R12, - PREFIXES_R13, PREFIXES_REDIST_R14, PREFIXES_CONNECTED, ): diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 74e308dbc..b78a2f105 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -87,6 +87,12 @@ def pytest_addoption(parser): ) parser.addoption( + "--memleaks", + action="store_true", + help="Report memstat results as errors", + ) + + parser.addoption( "--pause", action="store_true", help="Pause after each test", @@ -189,7 +195,7 @@ def pytest_addoption(parser): ) -def check_for_memleaks(): +def check_for_valgrind_memleaks(): assert topotest.g_pytest_config.option.valgrind_memleaks leaks = [] @@ -232,11 +238,47 @@ def check_for_memleaks(): pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons)) +def check_for_memleaks(): + leaks = [] + tgen = get_topogen() # pylint: disable=redefined-outer-name + latest = [] + existing = [] + if tgen is not None: + logdir = tgen.logdir + if hasattr(tgen, "memstat_existing_files"): + existing = tgen.memstat_existing_files + latest = glob.glob(os.path.join(logdir, "*/*.err")) + + daemons = [] + for vfile in latest: + if vfile in existing: + continue + with open(vfile, encoding="ascii") as vf: + vfcontent = vf.read() + num = vfcontent.count("memstats:") + if num: + existing.append(vfile) # have summary don't check again + emsg = "{} types in {}".format(num, vfile) + leaks.append(emsg) + daemon = re.match(r".*test[a-z_A-Z0-9\+]*/(.*)\.err", vfile).group(1) + daemons.append("{}({})".format(daemon, num)) + + if tgen is not None: + tgen.memstat_existing_files = existing + + if leaks: + logger.error("memleaks found:\n\t%s", "\n\t".join(leaks)) + pytest.fail("memleaks found for daemons: " + " ".join(daemons)) + + @pytest.fixture(autouse=True, scope="module") def module_check_memtest(request): yield if request.config.option.valgrind_memleaks: if get_topogen() is not None: + check_for_valgrind_memleaks() + if request.config.option.memleaks: + if get_topogen() is not None: check_for_memleaks() @@ -264,6 +306,8 @@ def pytest_runtest_call(item: pytest.Item) -> None: # Check for leaks if requested if item.config.option.valgrind_memleaks: + check_for_valgrind_memleaks() + if item.config.option.memleaks: check_for_memleaks() @@ -370,10 +414,22 @@ def pytest_configure(config): if config.option.topology_only and is_xdist: pytest.exit("Cannot use --topology-only with distributed test mode") + pytest.exit("Cannot use --topology-only with distributed test mode") + # Check environment now that we have config if not diagnose_env(rundir): pytest.exit("environment has errors, please read the logs in %s" % rundir) + # slave TOPOTESTS_CHECK_MEMLEAK to memleaks flag + if config.option.memleaks: + if "TOPOTESTS_CHECK_MEMLEAK" not in os.environ: + os.environ["TOPOTESTS_CHECK_MEMLEAK"] = "/dev/null" + else: + if "TOPOTESTS_CHECK_MEMLEAK" in os.environ: + del os.environ["TOPOTESTS_CHECK_MEMLEAK"] + if "TOPOTESTS_CHECK_STDERR" in os.environ: + del os.environ["TOPOTESTS_CHECK_STDERR"] + @pytest.fixture(autouse=True, scope="session") def setup_session_auto(): diff --git a/tests/topotests/evpn_pim_1/spine/bgp.summ.json b/tests/topotests/evpn_pim_1/spine/bgp.summ.json index 5ff4b096f..7f37cedb2 100644 --- a/tests/topotests/evpn_pim_1/spine/bgp.summ.json +++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json @@ -8,7 +8,6 @@ "192.168.1.2":{ "remoteAs":65002, "version":4, - "tableVersion":0, "outq":0, "inq":0, "pfxRcd":3, @@ -21,7 +20,6 @@ "192.168.2.3":{ "remoteAs":65003, "version":4, - "tableVersion":0, "outq":0, "inq":0, "pfxRcd":3, diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index a0b12b2d6..c58da996d 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -164,7 +164,6 @@ def create_router_bgp(tgen, topo=None, input_dict=None, build=False, load_config router, ) else: - ipv4_data = bgp_addr_data.setdefault("ipv4", {}) ipv6_data = bgp_addr_data.setdefault("ipv6", {}) l2vpn_data = bgp_addr_data.setdefault("l2vpn", {}) @@ -645,7 +644,6 @@ def __create_l2vpn_evpn_address_family( if advertise_data: for address_type, unicast_type in advertise_data.items(): - if type(unicast_type) is dict: for key, value in unicast_type.items(): cmd = "advertise {} {}".format(address_type, key) @@ -1651,7 +1649,6 @@ def modify_as_number(tgen, topo, input_dict): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) try: - new_topo = deepcopy(topo["routers"]) router_dict = {} for router in input_dict.keys(): @@ -1953,7 +1950,6 @@ def clear_bgp_and_verify(tgen, topo, router, rid=None): total_peer = 0 for addr_type in bgp_addr_type.keys(): - if not check_address_types(addr_type): continue @@ -2022,7 +2018,6 @@ def clear_bgp_and_verify(tgen, topo, router, rid=None): peer_uptime_after_clear_bgp = {} # Verifying BGP convergence after bgp clear command for retry in range(50): - # Waiting for BGP to converge logger.info( "Waiting for %s sec for BGP to converge on router" " %s...", @@ -3278,7 +3273,6 @@ def verify_graceful_restart( if lmode is None: if "graceful-restart" in input_dict[dut]["bgp"]: - if ( "graceful-restart" in input_dict[dut]["bgp"]["graceful-restart"] and input_dict[dut]["bgp"]["graceful-restart"][ @@ -3326,7 +3320,6 @@ def verify_graceful_restart( if rmode is None: if "graceful-restart" in input_dict[peer]["bgp"]: - if ( "graceful-restart" in input_dict[peer]["bgp"]["graceful-restart"] @@ -3628,7 +3621,6 @@ def verify_eor(tgen, topo, addr_type, input_dict, dut, peer, expected=True): eor_json = show_bgp_graceful_json_out[afi]["endOfRibStatus"] if "endOfRibSend" in eor_json: - if eor_json["endOfRibSend"]: logger.info( "[DUT: %s]: EOR Send true for %s " "%s", dut, neighbor_ip, afi @@ -3918,7 +3910,6 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) "timer" ].items(): if rs_timer == "restart-time": - receivedTimer = value if ( show_bgp_graceful_json_out["timers"][ @@ -4777,7 +4768,6 @@ def get_prefix_count_route( tgen = get_topogen() for router, rnode in tgen.routers().items(): if router == dut: - if vrf: ipv4_cmd = "sh ip bgp vrf {} summary json".format(vrf) show_bgp_json_ipv4 = run_frr_cmd(rnode, ipv4_cmd, isjson=True) @@ -4871,7 +4861,6 @@ def verify_rib_default_route( connected_routes = {} for router, rnode in tgen.routers().items(): if router == dut: - ipv4_routes = run_frr_cmd(rnode, "sh ip bgp json", isjson=True) ipv6_routes = run_frr_cmd(rnode, "sh ip bgp ipv6 unicast json", isjson=True) is_ipv4_default_attrib_found = False diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py index cd449cb50..699c7a4da 100644 --- a/tests/topotests/lib/bgprib.py +++ b/tests/topotests/lib/bgprib.py @@ -25,6 +25,7 @@ from lib.lutil import luCommand, luResult, LUtil import json import re + # gpz: get rib in json form and compare against desired routes class BgpRib: def log(self, str): diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index d19d8db75..67afe8739 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -243,7 +243,6 @@ def run_frr_cmd(rnode, cmd, isjson=False): def apply_raw_config(tgen, input_dict): - """ API to configure raw configuration on device. This can be used for any cli which has not been implemented in JSON. @@ -447,7 +446,6 @@ def check_router_status(tgen): try: router_list = tgen.routers() for router, rnode in router_list.items(): - result = rnode.check_router_running() if result != "": daemons = [] @@ -1914,7 +1912,6 @@ def interface_status(tgen, topo, input_dict): rlist = [] for router in input_dict.keys(): - interface_list = input_dict[router]["interface_list"] status = input_dict[router].setdefault("status", "up") for intf in interface_list: @@ -2529,7 +2526,6 @@ def create_route_maps(tgen, input_dict, build=False): continue rmap_data = [] for rmap_name, rmap_value in input_dict[router]["route_maps"].items(): - for rmap_dict in rmap_value: del_action = rmap_dict.setdefault("delete", False) @@ -3002,7 +2998,6 @@ def addKernelRoute( group_addr_range = [group_addr_range] for grp_addr in group_addr_range: - addr_type = validate_ip_address(grp_addr) if addr_type == "ipv4": if next_hop is not None: @@ -3193,7 +3188,6 @@ def configure_brctl(tgen, topo, input_dict): if "brctl" in input_dict[dut]: for brctl_dict in input_dict[dut]["brctl"]: - brctl_names = brctl_dict.setdefault("brctl_name", []) addvxlans = brctl_dict.setdefault("addvxlan", []) stp_values = brctl_dict.setdefault("stp", []) @@ -3203,7 +3197,6 @@ def configure_brctl(tgen, topo, input_dict): for brctl_name, vxlan, vrf, stp in zip( brctl_names, addvxlans, vrfs, stp_values ): - ip_cmd_list = [] cmd = "ip link add name {} type bridge stp_state {}".format( brctl_name, stp @@ -3614,7 +3607,6 @@ def verify_rib( for static_route in static_routes: if "vrf" in static_route and static_route["vrf"] is not None: - logger.info( "[DUT: {}]: Verifying routes for VRF:" " {}".format(router, static_route["vrf"]) @@ -4053,7 +4045,6 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None, protocol= for static_route in static_routes: if "vrf" in static_route and static_route["vrf"] is not None: - logger.info( "[DUT: {}]: Verifying routes for VRF:" " {}".format(router, static_route["vrf"]) diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 9aef41cd1..1eb88f26d 100644 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -177,7 +177,17 @@ Total %-4d %-4d %d\n\ self.log("unable to read: " + tstFile) sys.exit(1) - def command(self, target, command, regexp, op, result, returnJson, startt=None, force_result=False): + def command( + self, + target, + command, + regexp, + op, + result, + returnJson, + startt=None, + force_result=False, + ): global net if op == "jsoncmp_pass" or op == "jsoncmp_fail": returnJson = True @@ -326,7 +336,9 @@ Total %-4d %-4d %d\n\ if strict and (wait_count == 1): force_result = True - found = self.command(target, command, regexp, op, result, returnJson, startt, force_result) + found = self.command( + target, command, regexp, op, result, returnJson, startt, force_result + ) if found is not False: break @@ -342,6 +354,7 @@ Total %-4d %-4d %d\n\ # initialized by luStart LUtil = None + # entry calls def luStart( baseScriptDir=".", @@ -455,6 +468,7 @@ def luShowFail(): if printed > 0: logger.error("See %s for details of errors" % LUtil.fout_name) + # # Sets default wait type for luCommand(op="wait) (may be overridden by # specifying luCommand(op="wait-strict") or luCommand(op="wait-nostrict")). diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py index c5c2adc54..d648a120a 100644 --- a/tests/topotests/lib/micronet_compat.py +++ b/tests/topotests/lib/micronet_compat.py @@ -273,7 +273,7 @@ ff02::2\tip6-allrouters shellopt = self.cfgopt.get_option_list("--shell") if "all" in shellopt or "." in shellopt: - self.run_in_window("bash") + self.run_in_window("bash", title="munet") # This is expected by newer munet CLI code self.config_dirname = "" diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index dad87440b..ffe81fbd9 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -302,7 +302,6 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): # ospf gr information gr_data = ospf_data.setdefault("graceful-restart", {}) if gr_data: - if "opaque" in gr_data and gr_data["opaque"]: cmd = "capability opaque" if gr_data.setdefault("delete", False): @@ -710,6 +709,7 @@ def verify_ospf_neighbor( else: data_ip = topo["routers"][ospf_nbr]["links"] data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"] + logger.info("ospf neighbor %s: router-id: %s", router, data_rid) if ospf_nbr in data_ip: nbr_details = nbr_data[ospf_nbr] elif lan: @@ -728,8 +728,10 @@ def verify_ospf_neighbor( try: nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] except KeyError: - errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format( - router, nbr_rid, ospf_nbr + errormsg = ( + "[DUT: {}] missing OSPF neighbor {} with router-id {}".format( + router, ospf_nbr, nbr_rid + ) ) return errormsg @@ -843,7 +845,6 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) return errormsg for ospf_nbr, nbr_data in ospf_nbr_list.items(): - try: data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"] except KeyError: @@ -914,7 +915,6 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) return errormsg continue else: - for router, rnode in tgen.routers().items(): if "ospf6" not in topo["routers"][router]: continue @@ -945,7 +945,7 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][ "router_id" ] - + logger.info("ospf neighbor %s: router-id: %s", ospf_nbr, data_rid) if ospf_nbr in data_ip: nbr_details = nbr_data[ospf_nbr] elif lan: @@ -968,8 +968,10 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) nh_state = get_index_val.get(neighbor_ip)["state"] intf_state = get_index_val.get(neighbor_ip)["ifState"] except TypeError: - errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format( - router, nbr_rid, ospf_nbr + errormsg = ( + "[DUT: {}] missing OSPF neighbor {} with router-id {}".format( + router, ospf_nbr, nbr_rid + ) ) return errormsg @@ -1761,7 +1763,6 @@ def verify_ospf6_rib( continue if st_rt in ospf_rib_json: - st_found = True found_routes.append(st_rt) diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 925890b32..e26bdb3af 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -593,7 +593,6 @@ def find_rp_details(tgen, topo): topo_data = topo["routers"] for router in router_list.keys(): - if "pim" not in topo_data[router]: continue @@ -1495,7 +1494,6 @@ def verify_mroutes( and data["outboundInterface"] in oil ): if return_uptime: - uptime_dict[grp_addr][src_address] = data["upTime"] logger.info( @@ -1917,7 +1915,6 @@ def get_pim_interface_traffic(tgen, input_dict): 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] @@ -1990,7 +1987,6 @@ def get_pim6_interface_traffic(tgen, input_dict): 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] @@ -3007,7 +3003,6 @@ def verify_pim_upstream_rpf( logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) if "pim" in topo["routers"][dut]: - logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut) rnode = tgen.routers()[dut] @@ -3245,7 +3240,6 @@ def verify_pim_join( grp_addr = grp_addr.split("/")[0] for source, data in interface_json[grp_addr].items(): - # Verify pim join if pim_join: if data["group"] == grp_addr and data["channelJoinName"] == "JOIN": @@ -3338,7 +3332,6 @@ def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True): rnode = tgen.routers()[dut] for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): - statistics = False report = False if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: @@ -3623,7 +3616,6 @@ def verify_pim_config(tgen, input_dict, expected=True): rnode = tgen.routers()[dut] for interface, data in input_dict[dut]["pim"]["interfaces"].items(): - logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface) show_ip_igmp_intf_json = run_frr_cmd( @@ -3772,7 +3764,6 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=Tr elif ( interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0 ): - traffic_dict[traffic_type][interface][ "pktsIn" ] = interface_json["pktsIn"] @@ -3836,7 +3827,6 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=Tr interface_json["pktsOut"] != 0 and interface_json["bytesOut"] != 0 ): - traffic_dict[traffic_type][interface][ "pktsOut" ] = interface_json["pktsOut"] @@ -4232,7 +4222,6 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses): group_addresses = [group_addresses] if interface not in show_ip_local_igmp_json: - errormsg = ( "[DUT %s]: Verifying local IGMP group received" " from interface %s [FAILED]!! " % (dut, interface) @@ -4319,7 +4308,6 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type= 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] @@ -4525,7 +4513,6 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): for dut in input_dict.keys(): rnode = tgen.routers()[dut] for interface, data in input_dict[dut]["mld"]["interfaces"].items(): - statistics = False report = False if "statistics" in input_dict[dut]["mld"]["interfaces"][interface]["mld"]: @@ -5040,7 +5027,6 @@ def verify_pim6_config(tgen, input_dict, expected=True): rnode = tgen.routers()[dut] for interface, data in input_dict[dut]["pim6"]["interfaces"].items(): - logger.info( "[DUT: %s]: Verifying PIM6 interface %s detail:", dut, interface ) @@ -5158,7 +5144,6 @@ def verify_local_mld_groups(tgen, dut, interface, group_addresses): group_addresses = [group_addresses] if interface not in show_ipv6_local_mld_json["default"]: - errormsg = ( "[DUT %s]: Verifying local MLD group received" " from interface %s [FAILED]!! " % (dut, interface) diff --git a/tests/topotests/lib/test/test_json.py b/tests/topotests/lib/test/test_json.py index ae58d9b2d..230c2bd7f 100755 --- a/tests/topotests/lib/test/test_json.py +++ b/tests/topotests/lib/test/test_json.py @@ -616,7 +616,6 @@ def test_json_object_asterisk_matching(): def test_json_list_nested_with_objects(): - dcomplete = [{"key": 1, "list": [123]}, {"key": 2, "list": [123]}] dsub1 = [{"key": 2, "list": [123]}, {"key": 1, "list": [123]}] diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 53e6945be..23a6c8699 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -206,7 +206,6 @@ def build_topo_from_json(tgen, topo=None): for destRouterLink, data in sorted( topo["switches"][curSwitch]["links"].items() ): - # Loopback interfaces if "dst_node" in data: destRouter = data["dst_node"] @@ -220,7 +219,6 @@ def build_topo_from_json(tgen, topo=None): destRouter = destRouterLink if destRouter in listAllRouters: - topo["routers"][destRouter]["links"][curSwitch] = deepcopy( topo["switches"][curSwitch]["links"][destRouterLink] ) @@ -316,6 +314,7 @@ def build_config_from_json(tgen, topo=None, save_bkup=True): func_dict = OrderedDict( [ ("vrfs", create_vrf_cfg), + ("ospf", create_router_ospf), ("links", create_interfaces_cfg), ("static_routes", create_static_routes), ("prefix_lists", create_prefix_lists), @@ -325,7 +324,6 @@ def build_config_from_json(tgen, topo=None, save_bkup=True): ("igmp", create_igmp_config), ("mld", create_mld_config), ("bgp", create_router_bgp), - ("ospf", create_router_ospf), ] ) @@ -398,7 +396,7 @@ def setup_module_from_json(testfile, json_file=None): tgen = create_tgen_from_json(testfile, json_file) # Start routers (and their daemons) - start_topology(tgen, topo_daemons(tgen)) + start_topology(tgen) # Configure routers build_config_from_json(tgen) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 967f09ecb..aeb83d429 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1469,21 +1469,22 @@ class Router(Node): if not running: break - if not running: - return "" - - logger.warning( - "%s: sending SIGBUS to: %s", self.name, ", ".join([x[0] for x in running]) - ) - for name, pid in running: - pidfile = "/var/run/{}/{}.pid".format(self.routertype, name) - logger.info("%s: killing %s", self.name, name) - self.cmd("kill -SIGBUS %d" % pid) - self.cmd("rm -- " + pidfile) - - sleep( - 0.5, "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name - ) + if running: + logger.warning( + "%s: sending SIGBUS to: %s", + self.name, + ", ".join([x[0] for x in running]), + ) + for name, pid in running: + pidfile = "/var/run/{}/{}.pid".format(self.routertype, name) + logger.info("%s: killing %s", self.name, name) + self.cmd("kill -SIGBUS %d" % pid) + self.cmd("rm -- " + pidfile) + + sleep( + 0.5, + "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name, + ) errors = self.checkRouterCores(reportOnce=True) if self.checkRouterVersion("<", minErrorVersion): @@ -1526,6 +1527,9 @@ class Router(Node): """ # Unfortunately this API allowsfor source to not exist for any and all routers. + if source is None: + source = f"{daemon}.conf" + if source: head, tail = os.path.split(source) if not head and not self.path_exists(tail): @@ -1557,7 +1561,7 @@ class Router(Node): self.cmd_raises("cp {} {}".format(source, conf_file_mgmt)) self.cmd_raises("cp {} {}".format(source, conf_file)) - if not self.unified_config or daemon == "frr": + if not (self.unified_config or daemon == "frr"): self.cmd_raises("chown {0}:{0} {1}".format(self.routertype, conf_file)) self.cmd_raises("chmod 664 {}".format(conf_file)) @@ -1683,7 +1687,11 @@ class Router(Node): return self.getLog("out", daemon) def getLog(self, log, daemon): - return self.cmd("cat {}/{}/{}.{}".format(self.logdir, self.name, daemon, log)) + filename = "{}/{}/{}.{}".format(self.logdir, self.name, daemon, log) + log = "" + with open(filename) as file: + log = file.read() + return log def startRouterDaemons(self, daemons=None, tgen=None): "Starts FRR daemons for this router." @@ -1841,9 +1849,13 @@ class Router(Node): logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon ) - elif daemon in perfds and (self.name in perfds[daemon] or "all" in perfds[daemon]): + elif daemon in perfds and ( + self.name in perfds[daemon] or "all" in perfds[daemon] + ): cmdopt += rediropt - cmd = " ".join(["perf record {} --".format(perf_options), binary, cmdopt]) + cmd = " ".join( + ["perf record {} --".format(perf_options), binary, cmdopt] + ) p = self.popen(cmd) self.perf_daemons[daemon] = p if p.poll() and p.returncode: @@ -1943,7 +1955,7 @@ class Router(Node): tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name)) for tailf in tail_log_files: - self.run_in_window("tail -f " + tailf, title=tailf, background=True) + self.run_in_window("tail -n10000 -F " + tailf, title=tailf, background=True) return "" diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py index eb4b08844..c6ae70e09 100644 --- a/tests/topotests/munet/base.py +++ b/tests/topotests/munet/base.py @@ -1241,9 +1241,13 @@ class Commander: # pylint: disable=R0904 # XXX not appropriate for ssh cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd - if not isinstance(nscmd, str): - nscmd = shlex.join(nscmd) - cmd.append(nscmd) + if title: + cmd.append("-t") + cmd.append(title) + + if isinstance(nscmd, str): + nscmd = shlex.split(nscmd) + cmd.extend(nscmd) elif "DISPLAY" in os.environ: cmd = [get_exec_path_host("xterm")] if "SUDO_USER" in os.environ: diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py index aa43b977b..45e1bc8db 100755 --- a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py +++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py @@ -430,6 +430,144 @@ def test_gr_rt7(): check_routers(restarting="rt7") +# +# Test rt1 performing an unplanned graceful restart +# +def test_unplanned_gr_rt1(): + logger.info("Test: verify rt1 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt1", ["ospf6d"]) + + expect_grace_lsa(restarting="1.1.1.1", helper="rt2") + ensure_gr_is_in_zebra("rt1") + check_routers(restarting="rt1") + + +# +# Test rt2 performing an unplanned graceful restart +# +def test_unplanned_gr_rt2(): + logger.info("Test: verify rt2 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt2", ["ospf6d"]) + + expect_grace_lsa(restarting="2.2.2.2", helper="rt1") + expect_grace_lsa(restarting="2.2.2.2", helper="rt3") + ensure_gr_is_in_zebra("rt2") + check_routers(restarting="rt2") + + +# +# Test rt3 performing an unplanned graceful restart +# +def test_unplanned_gr_rt3(): + logger.info("Test: verify rt3 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt3", ["ospf6d"]) + + expect_grace_lsa(restarting="3.3.3.3", helper="rt2") + expect_grace_lsa(restarting="3.3.3.3", helper="rt4") + expect_grace_lsa(restarting="3.3.3.3", helper="rt6") + ensure_gr_is_in_zebra("rt3") + check_routers(restarting="rt3") + + +# +# Test rt4 performing an unplanned graceful restart +# +def test_unplanned_gr_rt4(): + logger.info("Test: verify rt4 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt4", ["ospf6d"]) + + expect_grace_lsa(restarting="4.4.4.4", helper="rt3") + expect_grace_lsa(restarting="4.4.4.4", helper="rt5") + ensure_gr_is_in_zebra("rt4") + check_routers(restarting="rt4") + + +# +# Test rt5 performing an unplanned graceful restart +# +def test_unplanned_gr_rt5(): + logger.info("Test: verify rt5 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt5", ["ospf6d"]) + + expect_grace_lsa(restarting="5.5.5.5", helper="rt4") + ensure_gr_is_in_zebra("rt5") + check_routers(restarting="rt5") + + +# +# Test rt6 performing an unplanned graceful restart +# +def test_unplanned_gr_rt6(): + logger.info("Test: verify rt6 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt6", ["ospf6d"]) + + expect_grace_lsa(restarting="6.6.6.6", helper="rt3") + expect_grace_lsa(restarting="6.6.6.6", helper="rt7") + ensure_gr_is_in_zebra("rt6") + check_routers(restarting="rt6") + + +# +# Test rt7 performing an unplanned graceful restart +# +def test_unplanned_gr_rt7(): + logger.info("Test: verify rt7 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt7", ["ospf6d"]) + + expect_grace_lsa(restarting="6.6.6.6", helper="rt6") + ensure_gr_is_in_zebra("rt7") + check_routers(restarting="rt7") + + # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py index 9d7a15833..0531e81d4 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py @@ -84,8 +84,8 @@ SUMMARY = {"ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"]} """ TOPOOLOGY = Please view in a fixed-width font such as Courier. - +---+ A0 +---+ - +R1 +------------+R2 | + +---+ A0 +---+ + |R1 +------------+R2 | +-+-+- +--++ | -- -- | | -- A0 -- | @@ -94,8 +94,8 @@ TOPOOLOGY = | -- -- | | -- -- | +-+-+- +-+-+ - +R0 +-------------+R3 | - +---+ A0 +---+ + |R0 +-------------+R3 | + +---+ A0 +---+ TESTCASES = 1. OSPF summarisation functionality. @@ -977,7 +977,7 @@ def test_ospf_type5_summary_tc42_p0(request): ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] - ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + ip_net = str(ipaddress.ip_interface("{}".format(ip)).network) ospf_summ_r1 = { "r0": { "ospf": {"summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]} @@ -1519,7 +1519,7 @@ def test_ospf_type5_summary_tc45_p0(request): step("Repeat steps 1 to 10 of summarisation in non Back bone area.") reset_config_on_routers(tgen) - step("Change the area id on the interface on R0") + step("Change the area id on the interface on R0 to R1 from 0.0.0.0 to 0.0.0.1") input_dict = { "r0": { "links": { @@ -1549,7 +1549,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = create_interfaces_cfg(tgen, input_dict) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - step("Change the area id on the interface ") + step("Change the area id on the interface on R1 to R0 from 0.0.0.0 to 0.0.0.1") input_dict = { "r1": { "links": { diff --git a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py index ca2a3b287..73185d501 100755 --- a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py +++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py @@ -434,6 +434,144 @@ def test_gr_rt7(): check_routers(restarting="rt7") +# +# Test rt1 performing an unplanned graceful restart +# +def test_unplanned_gr_rt1(): + logger.info("Test: verify rt1 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt1", ["ospfd"]) + + expect_grace_lsa(restarting="1.1.1.1", area="0.0.0.1", helper="rt2") + ensure_gr_is_in_zebra("rt1") + check_routers(restarting="rt1") + + +# +# Test rt2 performing an unplanned graceful restart +# +def test_unplanned_gr_rt2(): + logger.info("Test: verify rt2 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt2", ["ospfd"]) + + expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.1", helper="rt1") + expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.0", helper="rt3") + ensure_gr_is_in_zebra("rt2") + check_routers(restarting="rt2") + + +# +# Test rt3 performing an unplanned graceful restart +# +def test_unplanned_gr_rt3(): + logger.info("Test: verify rt3 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt3", ["ospfd"]) + + expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt2") + expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt4") + expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt6") + ensure_gr_is_in_zebra("rt3") + check_routers(restarting="rt3") + + +# +# Test rt4 performing an unplanned graceful restart +# +def test_unplanned_gr_rt4(): + logger.info("Test: verify rt4 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt4", ["ospfd"]) + + expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.0", helper="rt3") + expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.2", helper="rt5") + ensure_gr_is_in_zebra("rt4") + check_routers(restarting="rt4") + + +# +# Test rt5 performing an unplanned graceful restart +# +def test_unplanned_gr_rt5(): + logger.info("Test: verify rt5 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt5", ["ospfd"]) + + expect_grace_lsa(restarting="5.5.5.5", area="0.0.0.2", helper="rt4") + ensure_gr_is_in_zebra("rt5") + check_routers(restarting="rt5") + + +# +# Test rt6 performing an unplanned graceful restart +# +def test_unplanned_gr_rt6(): + logger.info("Test: verify rt6 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt6", ["ospfd"]) + + expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.0", helper="rt3") + expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.3", helper="rt7") + ensure_gr_is_in_zebra("rt6") + check_routers(restarting="rt6") + + +# +# Test rt7 performing an unplanned graceful restart +# +def test_unplanned_gr_rt7(): + logger.info("Test: verify rt7 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt7", ["ospfd"]) + + expect_grace_lsa(restarting="7.7.7.7", area="0.0.0.3", helper="rt6") + ensure_gr_is_in_zebra("rt7") + check_routers(restarting="rt7") + + # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf index 9f388dbd0..85230494d 100644 --- a/tests/topotests/ospf_metric_propagation/r1/frr.conf +++ b/tests/topotests/ospf_metric_propagation/r1/frr.conf @@ -8,18 +8,18 @@ interface r1-eth0 ip address 10.0.1.1/24 ip ospf cost 100 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r1-eth1 vrf blue ip address 10.0.10.1/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! ! interface r1-eth2 vrf green ip address 10.0.91.1/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! ! router ospf diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json index e3a5cc410..4f1ced81f 100644 --- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json @@ -4,7 +4,6 @@ "prefix":"10.0.94.0/24", "prefixLen":24, "protocol":"bgp", - "vrfId":9, "vrfName":"green", "selected":true, "destSelected":true, @@ -25,7 +24,6 @@ "fib":true, "ip":"10.0.10.5", "afi":"ipv4", - "interfaceIndex":6, "interfaceName":"r1-eth1", "vrf":"blue", "active":true, diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json index f3597bf45..882d3ca4f 100644 --- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json @@ -4,7 +4,6 @@ "prefix":"10.0.94.0/24", "prefixLen":24, "protocol":"bgp", - "vrfId":9, "vrfName":"green", "selected":true, "destSelected":true, @@ -25,7 +24,6 @@ "fib":true, "ip":"10.0.1.2", "afi":"ipv4", - "interfaceIndex":5, "interfaceName":"r1-eth0", "vrf":"default", "active":true, diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json index eebcab83e..cd528459a 100644 --- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json @@ -4,7 +4,6 @@ "prefix":"10.0.94.0/24", "prefixLen":24, "protocol":"bgp", - "vrfId":9, "vrfName":"green", "selected":true, "destSelected":true, @@ -25,7 +24,6 @@ "fib":true, "ip":"10.0.1.2", "afi":"ipv4", - "interfaceIndex":5, "interfaceName":"r1-eth0", "vrf":"default", "active":true, diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json index d0e3d816d..133f37549 100644 --- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json @@ -4,7 +4,6 @@ "prefix":"10.0.94.0/24", "prefixLen":24, "protocol":"bgp", - "vrfId":9, "vrfName":"green", "selected":true, "destSelected":true, @@ -25,7 +24,6 @@ "fib":true, "ip":"10.0.1.2", "afi":"ipv4", - "interfaceIndex":5, "interfaceName":"r1-eth0", "vrf":"default", "active":true, diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json index 989ccf779..5d8050902 100644 --- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json @@ -4,7 +4,6 @@ "prefix":"10.0.94.0/24", "prefixLen":24, "protocol":"bgp", - "vrfId":9, "vrfName":"green", "selected":true, "destSelected":true, @@ -25,7 +24,6 @@ "fib":true, "ip":"10.0.1.2", "afi":"ipv4", - "interfaceIndex":5, "interfaceName":"r1-eth0", "vrf":"default", "active":true, diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json index 84b11886e..1b59707b9 100644 --- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json @@ -4,7 +4,6 @@ "prefix":"10.0.94.0/24", "prefixLen":24, "protocol":"bgp", - "vrfId":9, "vrfName":"green", "selected":true, "destSelected":true, @@ -25,7 +24,6 @@ "fib":true, "ip":"10.0.10.5", "afi":"ipv4", - "interfaceIndex":6, "interfaceName":"r1-eth1", "vrf":"blue", "active":true, diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf index 469ae5da8..e67a374ff 100644 --- a/tests/topotests/ospf_metric_propagation/r2/frr.conf +++ b/tests/topotests/ospf_metric_propagation/r2/frr.conf @@ -8,18 +8,18 @@ interface r2-eth0 ip address 10.0.1.2/24 ip ospf cost 100 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r2-eth1 vrf blue ip address 10.0.20.2/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r2-eth2 vrf green ip address 10.0.70.2/24 ip ospf cost 1000 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! router ospf ospf router-id 10.0.255.2 diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf index 8dbbaf0fc..175851d42 100644 --- a/tests/topotests/ospf_metric_propagation/r3/frr.conf +++ b/tests/topotests/ospf_metric_propagation/r3/frr.conf @@ -8,18 +8,18 @@ interface r3-eth0 ip address 10.0.3.3/24 ip ospf cost 100 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r3-eth1 vrf blue ip address 10.0.30.3/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r3-eth2 vrf green ip address 10.0.80.3/24 ip ospf cost 1000 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! router ospf ospf router-id 10.0.255.3 diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf index af1005063..70a47e34f 100644 --- a/tests/topotests/ospf_metric_propagation/r4/frr.conf +++ b/tests/topotests/ospf_metric_propagation/r4/frr.conf @@ -8,17 +8,17 @@ interface r4-eth0 ip address 10.0.3.4/24 ip ospf cost 100 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r4-eth1 vrf blue ip address 10.0.40.4/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface r4-eth2 vrf green ip address 10.0.94.4/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! router ospf ospf router-id 10.0.255.4 diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf index 0bc2ec567..7be9e5c33 100644 --- a/tests/topotests/ospf_metric_propagation/ra/frr.conf +++ b/tests/topotests/ospf_metric_propagation/ra/frr.conf @@ -7,17 +7,17 @@ ip forwarding interface ra-eth0 ip address 10.0.50.5/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface ra-eth1 ip address 10.0.10.5/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface ra-eth2 ip address 10.0.20.5/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! router ospf ospf router-id 10.0.255.5 diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf index 6f540d125..a7dbf8227 100644 --- a/tests/topotests/ospf_metric_propagation/rb/frr.conf +++ b/tests/topotests/ospf_metric_propagation/rb/frr.conf @@ -7,17 +7,17 @@ ip forwarding interface rb-eth0 ip address 10.0.50.6/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface rb-eth1 ip address 10.0.30.6/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface rb-eth2 ip address 10.0.40.6/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! router ospf ospf router-id 10.0.255.6 diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf index 9fc0ef718..f5a2ed7c4 100644 --- a/tests/topotests/ospf_metric_propagation/rc/frr.conf +++ b/tests/topotests/ospf_metric_propagation/rc/frr.conf @@ -7,12 +7,12 @@ ip forwarding interface rc-eth0 ip address 10.0.70.7/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! interface rc-eth1 ip address 10.0.80.7/24 ip ospf hello-interval 1 - ip ospf dead-interval 3 + ip ospf dead-interval 30 ! router ospf ospf router-id 10.0.255.7 diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py index 709e07649..085eb1f9c 100644 --- a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py +++ b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py @@ -71,7 +71,6 @@ def build_topo(tgen): tgen.add_router("h1") tgen.add_router("h2") - # Interconect router 1, 2 switch = tgen.add_switch("s1-2") switch.add_link(tgen.gears["r1"]) @@ -127,6 +126,7 @@ def build_topo(tgen): switch.add_link(tgen.gears["r3"]) switch.add_link(tgen.gears["rc"]) + def setup_module(mod): logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY)) @@ -148,8 +148,12 @@ def setup_module(mod): for cmd in vrf_setup_cmds: tgen.net["r{}".format(routern)].cmd(cmd) for routern in range(1, 5): - tgen.net["r{}".format(routern)].cmd("ip link set dev r{}-eth1 vrf blue up".format(routern)) - tgen.net["r{}".format(routern)].cmd("ip link set dev r{}-eth2 vrf green up".format(routern)) + tgen.net["r{}".format(routern)].cmd( + "ip link set dev r{}-eth1 vrf blue up".format(routern) + ) + tgen.net["r{}".format(routern)].cmd( + "ip link set dev r{}-eth2 vrf green up".format(routern) + ) for rname, router in router_list.items(): logger.info("Loading router %s" % rname) @@ -181,7 +185,7 @@ def test_all_links_up(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg @@ -202,7 +206,7 @@ def test_link_1_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg @@ -216,6 +220,10 @@ def test_link_1_2_down(): pytest.skip("skipped because of router(s) failure") tgen.net["r2"].cmd("ip link set dev r2-eth1 down") + tgen.net["r1"].cmd("ip link set dev r1-eth0 down") + tgen.net["r2"].cmd("ip link set dev r2-eth2 down") + tgen.net["r2"].cmd("ip link set dev r2-eth2 up") + tgen.net["r1"].cmd("ip link set dev r1-eth0 up") r1 = tgen.gears["r1"] json_file = "{}/r1/show_ip_route-3.json".format(CWD) @@ -223,7 +231,7 @@ def test_link_1_2_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg @@ -236,11 +244,11 @@ def test_link_1_2_3_down(): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - tgen.net["r3"].cmd("ip link set dev r3-eth0 down") tgen.net["r3"].cmd("ip link set dev r3-eth1 down") - # ospf dead-interval is set to 3 seconds, wait 5 seconds to clear the neighbor - sleep(5) + tgen.net["r1"].cmd("ip link set dev r1-eth0 down") + tgen.net["r3"].cmd("ip link set dev r3-eth0 down") tgen.net["r3"].cmd("ip link set dev r3-eth0 up") + tgen.net["r1"].cmd("ip link set dev r1-eth0 up") r1 = tgen.gears["r1"] json_file = "{}/r1/show_ip_route-4.json".format(CWD) @@ -248,11 +256,12 @@ def test_link_1_2_3_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg + def test_link_1_2_3_4_down(): "Test path R1 -> R2 -> Rc -> R3 -> R4" tgen = get_topogen() @@ -268,11 +277,12 @@ def test_link_1_2_3_4_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg + def test_link_1_2_4_down(): "Test path R1 -> R2 -> Rc -> R3 -> R4" tgen = get_topogen() @@ -289,11 +299,12 @@ def test_link_1_2_4_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg + def test_link_1_4_down(): "Test path R1 -> R2 -> Ra -> Rb -> R3 -> R4" tgen = get_topogen() @@ -310,7 +321,7 @@ def test_link_1_4_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg @@ -332,7 +343,7 @@ def test_link_4_down(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg @@ -354,7 +365,7 @@ def test_link_1_2_3_4_up(): test_func = partial( topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 JSON output mismatches" assert result is None, assertmsg diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index b01c96226..2e8bea265 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -20,7 +20,15 @@ from datetime import datetime, timedelta import pytest -from lib.common_config import retry, run_frr_cmd, step +from lib.common_config import ( + retry, + run_frr_cmd, + step, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, +) + from lib.micronet import Timeout, comm_error from lib.topogen import Topogen, TopoRouter from lib.topotest import interface_set_status, json_cmp @@ -936,6 +944,191 @@ def test_ospf_opaque_delete_data3(tgen): _test_opaque_add_del(tgen, apibin) +def _test_opaque_add_restart_add(tgen, apibin): + "Test adding an opaque LSA and then restarting ospfd" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + p = None + pread = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+") + try: + step("reachable: check for add notification") + pread = r2.popen( + ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], + encoding=None, # don't buffer + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + p = r1.popen( + [ + apibin, + "-v", + "add,10,1.2.3.4,231,1", + "add,10,1.2.3.4,231,1,feedaceebeef", + "wait, 5", + "add,10,1.2.3.4,231,1,feedaceedeadbeef", + "wait, 5", + "add,10,1.2.3.4,231,1,feedaceebaddbeef", + "wait, 5", + ] + ) + add_input_dict = { + "areas": { + "1.2.3.4": { + "areaLocalOpaqueLsa": [ + { + "lsId": "231.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000004", + "checksum": "3128", + }, + ], + "areaLocalOpaqueLsaCount": 1, + }, + }, + } + step("Check for add LSAs") + json_cmd = "show ip ospf da json" + assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None + assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None + + step("Shutdown the interface on r1 to isolate it for r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", False) + + time.sleep(2) + step("Reset the client") + p.send_signal(signal.SIGINT) + time.sleep(2) + p.wait() + p = None + + step("Kill ospfd on R1") + kill_router_daemons(tgen, "r1", ["ospfd"]) + time.sleep(2) + + step("Bring ospfd on R1 back up") + start_router_daemons(tgen, "r1", ["ospfd"]) + + p = r1.popen( + [ + apibin, + "-v", + "add,10,1.2.3.4,231,1", + "add,10,1.2.3.4,231,1,feedaceecafebeef", + "wait, 5", + ] + ) + + step("Bring the interface on r1 back up for connection to r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", True) + + step("Verify area opaque LSA refresh") + json_cmd = "show ip ospf da opaque-area json" + add_detail_input_dict = { + "areaLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "linkStateId": "231.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000005", + "checksum": "a87e", + "length": 28, + "opaqueDataLength": 8, + }, + ], + }, + }, + } + assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None + assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None + + step("Shutdown the interface on r1 to isolate it for r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", False) + + time.sleep(2) + step("Reset the client") + p.send_signal(signal.SIGINT) + time.sleep(2) + p.wait() + p = None + + step("Kill ospfd on R1") + kill_router_daemons(tgen, "r1", ["ospfd"]) + time.sleep(2) + + step("Bring ospfd on R1 back up") + start_router_daemons(tgen, "r1", ["ospfd"]) + + step("Bring the interface on r1 back up for connection to r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", True) + + step("Verify area opaque LSA Purging") + json_cmd = "show ip ospf da opaque-area json" + add_detail_input_dict = { + "areaLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "lsaAge": 3600, + "linkStateId": "231.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000005", + "checksum": "a87e", + "length": 28, + "opaqueDataLength": 8, + }, + ], + }, + }, + } + assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None + assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None + step("Verify Area Opaque LSA removal after timeout (60 seconds)") + time.sleep(60) + json_cmd = "show ip ospf da opaque-area json" + timeout_detail_input_dict = { + "areaLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [], + }, + }, + } + assert ( + verify_ospf_database(tgen, r1, timeout_detail_input_dict, json_cmd) is None + ) + assert ( + verify_ospf_database(tgen, r2, timeout_detail_input_dict, json_cmd) is None + ) + + 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_restart(tgen): + apibin = os.path.join(CLIENTDIR, "ospfclient.py") + rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + _test_opaque_add_restart_add(tgen, apibin) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py index b0e56e619..49c25ab8f 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py @@ -565,7 +565,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"] - ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + ip_net = str(ipaddress.ip_interface("{}".format(ip)).network) ospf_summ_r1 = { "r0": { "ospf6": { diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index f779bf0a7..0062cf3de 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,7 +1,7 @@ # Skip pytests example directory [pytest] -asyncio_mode = auto +# asyncio_mode = auto # We always turn this on inside conftest.py, default shown # addopts = --junitxml=<rundir>/topotests.xml diff --git a/tests/topotests/rip_allow_ecmp/r4/frr.conf b/tests/topotests/rip_allow_ecmp/r4/frr.conf new file mode 100644 index 000000000..995c2beca --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r4/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r4-eth0 + ip address 192.168.1.4/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/r5/frr.conf b/tests/topotests/rip_allow_ecmp/r5/frr.conf new file mode 100644 index 000000000..57a06ec0e --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r5/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r5-eth0 + ip address 192.168.1.5/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py index acc0aea9e..7d958fd49 100644 --- a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py +++ b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py @@ -21,12 +21,13 @@ 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.ripd] def setup_module(mod): - topodef = {"s1": ("r1", "r2", "r3")} + topodef = {"s1": ("r1", "r2", "r3", "r4", "r5")} tgen = Topogen(topodef, mod.__name__) tgen.start_topology() @@ -102,11 +103,13 @@ def test_rip_allow_ecmp(): _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`" - def _show_routes(): + def _show_routes(nh_num): output = json.loads(r1.vtysh_cmd("show ip route json")) expected = { "10.10.10.1/32": [ { + "internalNextHopNum": nh_num, + "internalNextHopActiveNum": nh_num, "nexthops": [ { "ip": "192.168.1.2", @@ -116,15 +119,36 @@ def test_rip_allow_ecmp(): "ip": "192.168.1.3", "active": True, }, - ] + ], } ] } return topotest.json_cmp(output, expected) - test_func = functools.partial(_show_routes) + test_func = functools.partial(_show_routes, 4) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) - assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip route`" + assert result is None, "Can't see 10.10.10.1/32 as multipath (4) in `show ip route`" + + step( + "Configure allow-ecmp 2, ECMP group routes SHOULD have next-hops with the lowest IPs" + ) + r1.vtysh_cmd( + """ + configure terminal + router rip + allow-ecmp 2 + """ + ) + + test_func = functools.partial(_show_rip_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Can't see 10.10.10.1/32 as ECMP with the lowest next-hop IPs" + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath (2) in `show ip route`" if __name__ == "__main__": diff --git a/yang/example/ripd.json b/yang/example/ripd.json index 00040622e..799f46a6d 100644 --- a/yang/example/ripd.json +++ b/yang/example/ripd.json @@ -23,7 +23,7 @@ "instance": [ { "vrf": "default", - "allow-ecmp": "true", + "allow-ecmp": 1, "distance": { "source": [ { diff --git a/yang/example/ripd.xml b/yang/example/ripd.xml index 2feddde2d..dad83619c 100644 --- a/yang/example/ripd.xml +++ b/yang/example/ripd.xml @@ -19,7 +19,7 @@ <ripd xmlns="http://frrouting.org/yang/ripd"> <instance> <vrf>default</vrf> - <allow-ecmp>true</allow-ecmp> + <allow-ecmp>1</allow-ecmp> <static-route>10.0.1.0/24</static-route> <distance> <source> diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 23e5b0227..b557cabb2 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -23,6 +23,10 @@ module frr-bgp-route-map { prefix rt-types; } + import frr-route-types { + prefix frr-route-types; + } + organization "Free Range Routing"; contact @@ -168,6 +172,12 @@ module frr-bgp-route-map { "Match IPv6 next hop address"; } + identity source-protocol { + base frr-route-map:rmap-match-type; + description + "Match protocol via which the route was learnt"; + } + identity distance { base frr-route-map:rmap-set-type; description @@ -759,6 +769,13 @@ module frr-bgp-route-map { "IPv6 address"; } } + + case source-protocol { + when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:source-protocol')"; + leaf source-protocol { + type frr-route-types:frr-route-types; + } + } } augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" { diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index a4bf50d95..5f85a4cab 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -119,8 +119,8 @@ module frr-ripd { "VRF name."; } leaf allow-ecmp { - type boolean; - default "false"; + type uint8; + default 0; description "Allow equal-cost multi-path."; } diff --git a/zebra/redistribute.c b/zebra/redistribute.c index d2fa85eb6..6767000f3 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -548,6 +548,10 @@ void zebra_interface_address_add_update(struct interface *ifp, client, ifp, ifc); } } + /* interface associated NHGs may have been deleted, + * re-sync zebra -> dplane NHGs + */ + zebra_interface_nhg_reinstall(ifp); } /* Interface address deletion. */ diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index f6c1fdd78..3653f7152 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -3785,18 +3785,12 @@ tc_qdisc_update_internal(enum dplane_op_e op, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } - /* Init context with info from zebra data structs */ ret = dplane_ctx_tc_qdisc_init(ctx, op, qdisc); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, memory_order_relaxed); @@ -3822,18 +3816,12 @@ tc_class_update_internal(enum dplane_op_e op, struct zebra_tc_class *class) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } - /* Init context with info from zebra data structs */ ret = dplane_ctx_tc_class_init(ctx, op, class); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, memory_order_relaxed); @@ -3859,18 +3847,12 @@ tc_filter_update_internal(enum dplane_op_e op, struct zebra_tc_filter *filter) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } - /* Init context with info from zebra data structs */ ret = dplane_ctx_tc_filter_init(ctx, op, filter); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, memory_order_relaxed); @@ -3943,16 +3925,11 @@ dplane_nexthop_update_internal(struct nhg_hash_entry *nhe, enum dplane_op_e op) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_nexthop_init(ctx, op, nhe); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_nexthops_in, 1, memory_order_relaxed); @@ -4083,8 +4060,6 @@ dplane_route_notif_update(struct route_node *rn, goto done; new_ctx = dplane_ctx_alloc(); - if (new_ctx == NULL) - goto done; /* Init context with info from zebra data structs */ dplane_ctx_route_init(new_ctx, op, rn, re); @@ -4216,10 +4191,6 @@ dplane_lsp_notif_update(struct zebra_lsp *lsp, enum dplane_op_e op, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } /* Copy info from zebra LSP */ ret = dplane_ctx_lsp_init(ctx, op, lsp); @@ -4589,16 +4560,11 @@ dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_intf_init(ctx, op, ifp); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1, memory_order_relaxed); @@ -5337,8 +5303,10 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, ctx = dplane_ctx_alloc(); - if (!ifp) - return result; + if (!ifp) { + ret = EINVAL; + goto done; + } if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { zlog_debug("init dplane ctx %s: if %s link %s%s", @@ -5350,8 +5318,11 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; zns = zebra_ns_lookup(ifp->vrf->vrf_id); - if (!zns) - return result; + if (!zns) { + ret = EINVAL; + goto done; + } + dplane_ctx_ns_init(ctx, zns, false); dplane_ctx_set_ifname(ctx, ifp->name); @@ -5370,6 +5341,7 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, /* Enqueue context for processing */ ret = dplane_update_enqueue(ctx); +done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_gre_set_in, 1, memory_order_relaxed); @@ -5764,6 +5736,21 @@ void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov, memory_order_relaxed); } +static struct zebra_dplane_ctx * +dplane_provider_dequeue_out_ctx(struct zebra_dplane_provider *prov) +{ + struct zebra_dplane_ctx *ctx; + + ctx = dplane_ctx_list_pop(&(prov->dp_ctx_out_list)); + if (!ctx) + return NULL; + + atomic_fetch_sub_explicit(&(prov->dp_out_queued), 1, + memory_order_relaxed); + + return ctx; +} + /* * Accessor for provider object */ @@ -6791,7 +6778,7 @@ static void dplane_thread_loop(struct event *event) dplane_provider_lock(prov); while (counter < limit) { - ctx = dplane_ctx_list_pop(&(prov->dp_ctx_out_list)); + ctx = dplane_provider_dequeue_out_ctx(prov); if (ctx) { dplane_ctx_list_add_tail(&work_list, ctx); counter++; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 8616b1405..96e021292 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1125,13 +1125,23 @@ static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe) zebra_nhg_free(nhe); } -static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe) +static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe, bool install) { /* Update validity of groups depending on it */ struct nhg_connected *rb_node_dep; - frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) + frr_each_safe (nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) { zebra_nhg_set_valid(rb_node_dep->nhe); + /* install dependent NHG into kernel */ + if (install) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s nh id %u (flags 0x%x) associated dependent NHG %pNG install", + __func__, nhe->id, nhe->flags, + rb_node_dep->nhe); + zebra_nhg_install_kernel(rb_node_dep->nhe); + } + } } /* @@ -3080,6 +3090,12 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) /* Resolve it first */ nhe = zebra_nhg_resolve(nhe); + if (zebra_nhg_set_valid_if_active(nhe)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: valid flag set for nh %pNG", __func__, + nhe); + } + /* Make sure all depends are installed/queued */ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { zebra_nhg_install_kernel(rb_node_dep->nhe); @@ -3106,7 +3122,7 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) break; case ZEBRA_DPLANE_REQUEST_SUCCESS: SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); - zebra_nhg_handle_install(nhe); + zebra_nhg_handle_install(nhe, false); break; } } @@ -3180,7 +3196,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); - zebra_nhg_handle_install(nhe); + zebra_nhg_handle_install(nhe, true); /* If daemon nhg, send it an update */ if (PROTO_OWNED(nhe)) @@ -3651,3 +3667,66 @@ static ssize_t printfrr_nhghe(struct fbuf *buf, struct printfrr_eargs *ea, ret += bputs(buf, "]"); return ret; } + +/* + * On interface add the nexthop that resolves to this intf needs + * a re-install. There are following scenarios when the nexthop group update + * gets skipped: + * 1. When upper level protocol sends removal of NHG, there is + * timer running to keep NHG for 180 seconds, during this interval, same route + * with same set of nexthops installation is given , the same NHG is used + * but since NHG is not reinstalled on interface address add, it is not aware + * in Dplan/Kernel. + * 2. Due to a quick port flap due to interface add and delete + * to be processed in same queue one after another. Zebra believes that + * there is no change in nhg in this case. Hence this re-install will + * make sure the nexthop group gets updated to Dplan/Kernel. + */ +void zebra_interface_nhg_reinstall(struct interface *ifp) +{ + struct nhg_connected *rb_node_dep = NULL; + struct zebra_if *zif = ifp->info; + struct nexthop *nh; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s: Installing interface %s associated NHGs into kernel", + __func__, ifp->name); + + frr_each (nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) { + nh = rb_node_dep->nhe->nhg.nexthop; + if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s: Setting the valid flag for nhe %pNG, interface: %s", + __func__, rb_node_dep->nhe, ifp->name); + } + /* Check for singleton NHG associated to interface */ + if (nexthop_is_ifindex_type(nh) && + zebra_nhg_depends_is_empty(rb_node_dep->nhe)) { + struct nhg_connected *rb_node_dependent; + + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s install nhe %pNG nh type %u flags 0x%x", + __func__, rb_node_dep->nhe, nh->type, + rb_node_dep->nhe->flags); + zebra_nhg_install_kernel(rb_node_dep->nhe); + + /* mark depedent uninstall, when interface associated + * singleton is installed, install depedent + */ + frr_each_safe (nhg_connected_tree, + &rb_node_dep->nhe->nhg_dependents, + rb_node_dependent) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s dependent nhe %pNG unset installed flag", + __func__, + rb_node_dependent->nhe); + UNSET_FLAG(rb_node_dependent->nhe->flags, + NEXTHOP_GROUP_INSTALLED); + } + } + } +} diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index b178007b4..6179be344 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -358,6 +358,7 @@ extern uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, /* Dataplane install/uninstall */ extern void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe); extern void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe); +extern void zebra_interface_nhg_reinstall(struct interface *ifp); /* Forward ref of dplane update context type */ struct zebra_dplane_ctx; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index adcaf6404..357f11282 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4704,6 +4704,21 @@ static void rib_process_dplane_results(struct event *thread) struct dplane_ctx_list_head ctxlist; bool shut_p = false; +#ifdef HAVE_SCRIPTING + char *script_name = + frrscript_names_get_script_name(ZEBRA_ON_RIB_PROCESS_HOOK_CALL); + + int ret = 1; + struct frrscript *fs = NULL; + + if (script_name) { + fs = frrscript_new(script_name); + if (fs) + ret = frrscript_load(fs, ZEBRA_ON_RIB_PROCESS_HOOK_CALL, + NULL); + } +#endif /* HAVE_SCRIPTING */ + /* Dequeue a list of completed updates with one lock/unlock cycle */ do { @@ -4737,24 +4752,7 @@ static void rib_process_dplane_results(struct event *thread) continue; } -#ifdef HAVE_SCRIPTING - char *script_name = frrscript_names_get_script_name( - ZEBRA_ON_RIB_PROCESS_HOOK_CALL); - - int ret = 1; - struct frrscript *fs; - - if (script_name) { - fs = frrscript_new(script_name); - if (fs) - ret = frrscript_load( - fs, ZEBRA_ON_RIB_PROCESS_HOOK_CALL, - NULL); - } -#endif /* HAVE_SCRIPTING */ - while (ctx) { - #ifdef HAVE_SCRIPTING if (ret == 0) frrscript_call(fs, @@ -4869,6 +4867,11 @@ static void rib_process_dplane_results(struct event *thread) } } while (1); + +#ifdef HAVE_SCRIPTING + if (fs) + frrscript_delete(fs); +#endif } /* |