summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Christ <robinchrist@users.noreply.github.com>2026-05-13 17:03:50 +0200
committerGitHub <noreply@github.com>2026-05-13 18:03:50 +0300
commit69b6be91d0eacfd23feb13f96325923f6e26ed25 (patch)
tree17e6d2a5dd5022272abdfa096662a5abc0682c88
parentdb23d7201c9df78c91aeeeade6963f2cc12b50b1 (diff)
downloadvyos-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.j23
-rw-r--r--interface-definitions/policy.xml.in24
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py16
-rwxr-xr-xsrc/completion/list_bgp_neighbors.sh169
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