diff options
| author | Robin Christ <robinchrist@users.noreply.github.com> | 2026-05-13 17:03:50 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-13 18:03:50 +0300 |
| commit | 69b6be91d0eacfd23feb13f96325923f6e26ed25 (patch) | |
| tree | 17e6d2a5dd5022272abdfa096662a5abc0682c88 | |
| parent | db23d7201c9df78c91aeeeade6963f2cc12b50b1 (diff) | |
| download | vyos-1x-69b6be91d0eacfd23feb13f96325923f6e26ed25.tar.gz vyos-1x-69b6be91d0eacfd23feb13f96325923f6e26ed25.zip | |
bgp: T8588: Add match source-peer to policy route-map (#5149)
| -rw-r--r-- | data/templates/frr/policy.frr.j2 | 3 | ||||
| -rw-r--r-- | interface-definitions/policy.xml.in | 24 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_policy.py | 16 | ||||
| -rwxr-xr-x | src/completion/list_bgp_neighbors.sh | 169 |
4 files changed, 170 insertions, 42 deletions
diff --git a/data/templates/frr/policy.frr.j2 b/data/templates/frr/policy.frr.j2 index 5d6ac30e0..27add3f24 100644 --- a/data/templates/frr/policy.frr.j2 +++ b/data/templates/frr/policy.frr.j2 @@ -245,6 +245,9 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} {% if rule_config.match.peer is vyos_defined %} match peer {{ rule_config.match.peer }} {% endif %} +{% if rule_config.match.source_peer is vyos_defined %} + match src-peer {{ rule_config.match.source_peer }} +{% endif %} {% if rule_config.match.protocol is vyos_defined %} {% set source_protocol = 'ospf6' if rule_config.match.protocol == 'ospfv3' else rule_config.match.protocol %} match source-protocol {{ source_protocol }} diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 3811585b6..53dcfd8ee 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -1038,6 +1038,30 @@ </constraint> </properties> </leafNode> + <leafNode name="source-peer"> + <properties> + <help>Source peer to match (BGP)</help> + <valueHelp> + <format>ipv4</format> + <description>Peer IPv4 address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>Peer IPv6 address</description> + </valueHelp> + <valueHelp> + <format>txt</format> + <description>Interface name of peer</description> + </valueHelp> + <valueHelp> + <format>txt</format> + <description>BGP peer-group name</description> + </valueHelp> + <completionHelp> + <script>${vyos_completion_dir}/list_bgp_neighbors.sh --ipv4 --ipv6 --interfaces --peer-groups --all-vrfs</script> + </completionHelp> + </properties> + </leafNode> <leafNode name="protocol"> <properties> <help>Match protocol via which the route was learnt</help> diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index ad3cc325b..76a9d09a5 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1011,14 +1011,13 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): 'peer' : peer, }, }, - - '31' : { - 'action' : 'permit', - 'match' : { - 'peer' : peerv6, + '31': { + 'action': 'permit', + 'match': { + 'peer': peerv6, + 'source-peer': peer, }, }, - '40' : { 'action' : 'permit', 'match' : { @@ -1279,6 +1278,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): self.cli_set(path + ['rule', rule, 'match', 'origin', 'incomplete']) if 'peer' in rule_config['match']: self.cli_set(path + ['rule', rule, 'match', 'peer', rule_config['match']['peer']]) + if 'source-peer' in rule_config['match']: + self.cli_set(path + ['rule', rule, 'match', 'source-peer', rule_config['match']['source-peer']]) if 'rpki-invalid' in rule_config['match']: self.cli_set(path + ['rule', rule, 'match', 'rpki', 'invalid']) if 'rpki-not-found' in rule_config['match']: @@ -1461,6 +1462,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): if 'peer' in rule_config['match']: tmp = f'match peer {rule_config["match"]["peer"]}' self.assertIn(tmp, config) + if 'source-peer' in rule_config['match']: + tmp = f'match src-peer {rule_config["match"]["source-peer"]}' + self.assertIn(tmp, config) if 'protocol' in rule_config['match']: tmp = f'match source-protocol {rule_config["match"]["protocol"]}' self.assertIn(tmp, config) diff --git a/src/completion/list_bgp_neighbors.sh b/src/completion/list_bgp_neighbors.sh index 72c4fcbcb..7342813ba 100755 --- a/src/completion/list_bgp_neighbors.sh +++ b/src/completion/list_bgp_neighbors.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify @@ -13,55 +13,152 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Return BGP neighbor addresses from CLI, can either request IPv4 only, IPv6 -# only or both address-family neighbors +# Return BGP neighbor identifiers from CLI. Selectors: +# --ipv4 IPv4 address peers +# --ipv6 IPv6 address peers +# --interfaces peers that are neither IPv4 nor IPv6 addresses (interfaces) +# --peer-groups configured peer-group names +# Selectors are additive and may be combined freely. With --all-vrfs, neighbors +# from the default VRF and every configured VRF are merged and deduplicated. ipv4=0 ipv6=0 -vrf="" +interfaces=0 +vrf_name="" +all_vrfs=0 +peer_groups=0 while [[ "$#" -gt 0 ]]; do case $1 in -4|--ipv4) ipv4=1 ;; -6|--ipv6) ipv6=1 ;; - -b|--both) ipv4=1; ipv6=1 ;; - --vrf) vrf="vrf name $2"; shift ;; - *) echo "Unknown parameter passed: $1" ;; + -b|--both) + # Deprecated: alias for --ipv4 --ipv6 --interfaces + ipv4=1; ipv6=1; interfaces=1 + ;; + --interfaces) interfaces=1 ;; + --vrf) vrf_name=$2; shift ;; + --all-vrfs) all_vrfs=1 ;; + --peer-groups) peer_groups=1 ;; + *) echo "Unknown parameter passed: $1" >&2 ;; esac shift done -declare -a vals -eval "vals=($(cli-shell-api listActiveNodes $vrf protocols bgp neighbor))" - -if [ $ipv4 -eq 1 ] && [ $ipv6 -eq 1 ]; then - echo -n '<x.x.x.x>' '<h:h:h:h:h:h:h:h>' ${vals[@]} -elif [ $ipv4 -eq 1 ] ; then - echo -n '<x.x.x.x> ' - for peer in "${vals[@]}" - do - ipaddrcheck --is-ipv4-single $peer - if [ $? -eq "0" ]; then - echo -n "$peer " - fi - done -elif [ $ipv6 -eq 1 ] ; then - echo -n '<h:h:h:h:h:h:h:h> ' - for peer in "${vals[@]}" - do - ipaddrcheck --is-ipv6-single $peer - if [ $? -eq "0" ]; then - echo -n "$peer " +if [[ $all_vrfs -eq 1 && -n $vrf_name ]]; then + echo "Error: --all-vrfs and --vrf are mutually exclusive" >&2 + exit 1 +fi + +# Wrap `cli-shell-api listActiveNodes` and append its (shell-quoted) output +# to the named array. The API is trusted to return safely quoted tokens, +# which is why `eval` is acceptable here -- it is the documented contract +# of cli-shell-api. Keeping the eval in one place makes the trust boundary +# explicit and easy to audit. +# +# Usage: _list_active_nodes <out_array_name> <path...> +_list_active_nodes() { + local _out=$1; shift + local _raw + if ! _raw=$(cli-shell-api listActiveNodes "$@" 2>/dev/null); then + return 0 # node missing or no config session -- treat as empty + fi + + eval "${_out}+=(${_raw})" +} + +declare -a vals=() +declare -a pg_vals=() + +# Build the list of VRFs to traverse. The empty string represents the default +# VRF (no `vrf name <X>` prefix); any other value is the name of a configured +# VRF. This unifies the three cases (--all-vrfs, --vrf <name>, neither) into a +# single loop and avoids duplicating the collection logic. +declare -a _vrf_list=("") # default VRF is always included + +if (( all_vrfs )); then + _list_active_nodes _vrf_list vrf name +elif [[ -n $vrf_name ]]; then + _vrf_list=("$vrf_name") +fi + +# Collect neighbors -- and optionally peer-groups -- from every VRF in the list. +for _vrf in "${_vrf_list[@]}"; do + declare -a _path=() + [[ -n $_vrf ]] && _path=(vrf name "$_vrf") + _list_active_nodes vals "${_path[@]}" protocols bgp neighbor + if (( peer_groups )); then + _list_active_nodes pg_vals "${_path[@]}" protocols bgp peer-group + fi +done + +# Deduplicate when multiple VRFs may have contributed entries. A single source +# cannot produce duplicates, so the sort pipe is skipped in that case. +if (( ${#_vrf_list[@]} > 1 )); then + if (( ${#vals[@]} > 1 )); then + mapfile -t vals < <(printf '%s\n' "${vals[@]}" | LC_ALL=C sort -u) + fi + if (( ${#pg_vals[@]} > 1 )); then + mapfile -t pg_vals < <(printf '%s\n' "${pg_vals[@]}" | LC_ALL=C sort -u) + fi +fi + +# Print neighbors from `vals` matching the requested selectors. The fast path +# below avoids any per-token filtering when all three neighbor categories +# (--ipv4, --ipv6, --interfaces) are requested -- in that case every entry in +# `vals` matches by definition. +_print_neighbors() { + # Fast path: every neighbor is either v4, v6 or an interface, so when all + # three are requested no classification is needed. + if (( ipv4 && ipv6 && interfaces )); then + (( ${#vals[@]} )) && printf '%s ' "${vals[@]}" + return + fi + + local peer + for peer in "${vals[@]}"; do + if ipaddrcheck --is-ipv4-single "$peer" >/dev/null 2>&1; then + (( ipv4 )) && printf '%s ' "$peer" + elif ipaddrcheck --is-ipv6-single "$peer" >/dev/null 2>&1; then + (( ipv6 )) && printf '%s ' "$peer" + else + # Anything that is neither an IPv4 nor an IPv6 address is treated + # as an interface name. + (( interfaces )) && printf '%s ' "$peer" fi - done -else - echo "Usage:" - echo "-4|--ipv4 list only IPv4 peers" - echo "-6|--ipv6 list only IPv6 peers" - echo "--both list both IP4 and IPv6 peers" - echo "--vrf <name> apply command to given VRF (optional)" - echo "" + done +} + +# Require at least one selector. +if (( ipv4 == 0 && ipv6 == 0 && interfaces == 0 && peer_groups == 0 )); then + cat >&2 <<'EOF' +Usage: + -4|--ipv4 list IPv4 address peers + -6|--ipv6 list IPv6 address peers + --interfaces list interface peers (peers that are not IP addresses) + --peer-groups list configured peer-group names + -b|--both deprecated -- alias for --ipv4 --ipv6 --interfaces + --vrf <name> apply command to given VRF (optional) + --all-vrfs list neighbors across all VRFs (deduplicated) +EOF exit 1 fi +# Build the leading completion-help placeholders shown to the user. +declare -a _hdr=() +(( ipv4 )) && _hdr+=('<x.x.x.x>') +(( ipv6 )) && _hdr+=('<h:h:h:h:h:h:h:h>') +(( interfaces )) && _hdr+=('<interface>') +(( peer_groups )) && _hdr+=('<text>') + +(( ${#_hdr[@]} )) && printf '%s ' "${_hdr[@]}" + +if (( ipv4 || ipv6 || interfaces )); then + _print_neighbors +fi + +if (( peer_groups )) && (( ${#pg_vals[@]} )); then + printf '%s ' "${pg_vals[@]}" +fi + exit 0 |
