mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 04:33:00 +02:00
Merge pull request #639 from kernelkit/ipv4ll-request-address
IPv4 autoconf request-address support Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -1,24 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This file is part of avahi.
|
||||
#
|
||||
# avahi is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# avahi is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
# License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with avahi; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
# USA.
|
||||
|
||||
set -e
|
||||
|
||||
# Command line arguments:
|
||||
# $1 event that happened:
|
||||
# BIND: Successfully claimed address
|
||||
@@ -29,60 +9,54 @@ set -e
|
||||
# $3 IP adddress
|
||||
|
||||
PATH="$PATH:/usr/bin:/usr/sbin:/bin:/sbin"
|
||||
NAME="/etc/frr/static.d/$2-zeroconf.conf"
|
||||
NEXT="${NAME}+"
|
||||
|
||||
# Use a different metric for each interface, so that we can set
|
||||
# identical routes to multiple interfaces.
|
||||
log()
|
||||
{
|
||||
logger -I $$ -t zeroconf -p user.notice "$*"
|
||||
}
|
||||
|
||||
METRIC=$((1000 + `cat "/sys/class/net/$2/ifindex" 2>/dev/null || echo 0`))
|
||||
|
||||
if [ -x /bin/ip -o -x /sbin/ip ] ; then
|
||||
|
||||
# We have the Linux ip tool from the iproute package
|
||||
|
||||
case "$1" in
|
||||
BIND)
|
||||
ip addr flush dev "$2" label "$2:avahi"
|
||||
ip addr add "$3"/16 brd 169.254.255.255 label "$2:avahi" scope link dev "$2" proto 6
|
||||
ip route add default dev "$2" metric "$METRIC" scope link proto zeroconf ||:
|
||||
;;
|
||||
|
||||
CONFLICT|UNBIND|STOP)
|
||||
ip route del default dev "$2" metric "$METRIC" scope link ||:
|
||||
ip addr del "$3"/16 brd 169.254.255.255 label "$2:avahi" scope link dev "$2"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown event $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
# Reduce changes needed by comparing with previous route(s)
|
||||
act()
|
||||
{
|
||||
case $1 in
|
||||
add)
|
||||
echo "! Generated by avahi-autoipd" > "$NEXT"
|
||||
echo "ip route 0.0.0.0/0 $2 254" >> "$NEXT"
|
||||
cmp -s "$NAME" "$NEXT" && return
|
||||
mv "$NEXT" "$NAME"
|
||||
;;
|
||||
del)
|
||||
[ -f "$NAME" ] || return
|
||||
rm "$NAME"
|
||||
;;
|
||||
*)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then
|
||||
initctl -nbq restart staticd
|
||||
}
|
||||
|
||||
# We have the old ifconfig tool
|
||||
case "$1" in
|
||||
BIND)
|
||||
ip addr flush dev "$2" proto random
|
||||
ip addr add "$3"/16 brd 169.254.255.255 scope link dev "$2" proto random
|
||||
act add "$2"
|
||||
log "set ipv4ll $3 on iface $2"
|
||||
;;
|
||||
|
||||
case "$1" in
|
||||
BIND)
|
||||
ifconfig "$2:avahi" inet "$3" netmask 255.255.0.0 broadcast 169.254.255.255 up
|
||||
route add default dev "$2:avahi" metric "$METRIC" ||:
|
||||
;;
|
||||
CONFLICT|UNBIND|STOP)
|
||||
act del "$2"
|
||||
ip addr flush dev "$2" proto random
|
||||
log "clr ipv4ll on iface $2"
|
||||
;;
|
||||
|
||||
CONFLICT|STOP|UNBIND)
|
||||
route del default dev "$2:avahi" metric "$METRIC" ||:
|
||||
ifconfig "$2:avahi" down
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown event $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
else
|
||||
|
||||
echo "No network configuration tool found." >&2
|
||||
exit 1
|
||||
|
||||
fi
|
||||
*)
|
||||
log "Unknown event $1 on iface $2"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
! Default settings for staticd, used for both
|
||||
! confd generated routes, udhcpc and zeroconf
|
||||
frr defaults traditional
|
||||
hostname Router
|
||||
password zebra
|
||||
enable password zebra
|
||||
no log unique-id
|
||||
log syslog informational
|
||||
log facility local2
|
||||
@@ -1 +1,6 @@
|
||||
# Micro GNU Emacs default configuration file
|
||||
auto-indent-mode
|
||||
auto-fill-mode
|
||||
make-backup-files 0
|
||||
require-final-newline T
|
||||
set-fill-column 72
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
# Allow client to pass locale environment
|
||||
AcceptEnv LANG LC_*
|
||||
|
||||
Include /etc/ssh/sshd_config.d/*.conf
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Subsystem cli /usr/bin/klish
|
||||
@@ -0,0 +1 @@
|
||||
Subsystem sftp /usr/libexec/sftp-server
|
||||
Executable
+366
@@ -0,0 +1,366 @@
|
||||
#!/bin/sh
|
||||
# Displays basic information about the system
|
||||
# shellcheck disable=SC2048,SC2086
|
||||
. /etc/os-release
|
||||
|
||||
bopt="-c"
|
||||
opt="-br"
|
||||
all=""
|
||||
plain=""
|
||||
|
||||
h1()
|
||||
{
|
||||
STR="$*"
|
||||
if [ -n "$plain" ]; then
|
||||
echo "$STR" | tr '[:lower:]' '[:upper:]'
|
||||
else
|
||||
printf "\033[7m%-${COLUMNS}s\033[0m\n" "$STR"
|
||||
fi
|
||||
}
|
||||
|
||||
h2()
|
||||
{
|
||||
STR="$*"
|
||||
if [ -n "$plain" ]; then
|
||||
echo "$STR"
|
||||
echo "$STR" | sed 's/./-/g'
|
||||
else
|
||||
printf "\033[1m%-${COLUMNS}s\033[0m\n" "$STR"
|
||||
fi
|
||||
}
|
||||
|
||||
dm()
|
||||
{
|
||||
if [ -n "$plain" ]; then
|
||||
echo "${*}"
|
||||
else
|
||||
printf "\033[2m%s\033[0m\n" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
ul()
|
||||
{
|
||||
if [ -n "$plain" ]; then
|
||||
printf "__%s__" "$*"
|
||||
else
|
||||
printf "\033[4%s\033[0m" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
em()
|
||||
{
|
||||
if [ -n "$plain" ]; then
|
||||
printf "**%s**" "$*"
|
||||
else
|
||||
printf "\033[5m%s\033[0m" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
usage:
|
||||
show [opt] cmd
|
||||
|
||||
options:
|
||||
-a Show all, of something
|
||||
-f Show full output, not brief port/iface listings
|
||||
-h Show this help text
|
||||
-n Show output without any footer
|
||||
-p Show plain output, no bells or whistles
|
||||
|
||||
commands:
|
||||
port PORT Show port configuration and link information
|
||||
ports Show ports available for bridging
|
||||
vlans Show port groups in bridge
|
||||
ifaces Show interfaces and their addresses
|
||||
fdb Show forwarding database (unicast)
|
||||
mdb Show multicast forwarding database
|
||||
ip addr Show IPv4 addresses
|
||||
route Show routing table
|
||||
ipv6 addr Show IPv6 addresses
|
||||
route Show routing table
|
||||
log [FILE] Show latest entries from syslog, or other FILE
|
||||
rmon PORT Show RMON counters for PORT (when applicable)
|
||||
system Show OS details
|
||||
version Show OS verson
|
||||
EOF
|
||||
}
|
||||
|
||||
# Usage 1: show port eth0
|
||||
# Usage 2: show port
|
||||
# Usage 3: show ports
|
||||
#
|
||||
# The first show ethtool output for 'eth0' (in this case). The latter
|
||||
# two are the same, showing a summary of all interfaces classified as
|
||||
# access ports.
|
||||
ports()
|
||||
{
|
||||
if [ $# -gt 0 ] && [ -e "/sys/class/net/$1" ]; then
|
||||
for port in $*; do
|
||||
ethtool "$port"
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
h1 "PORT STATE MAC ADDRESS FLAGS"
|
||||
if grep -q port /etc/iproute2/group; then
|
||||
ip $opt link show group port
|
||||
else
|
||||
ip $opt link show
|
||||
fi
|
||||
|
||||
if [ -z "$plain" ] && [ -z "$nofoot" ]; then
|
||||
dm "______________________________________________________________________________"
|
||||
dm "Use: '[ip|bridge] --help' and '[ip|bridge] link help' for more details."
|
||||
fi
|
||||
}
|
||||
|
||||
vlans()
|
||||
{
|
||||
h1 "INTERFACE VLAN FLAGS"
|
||||
bridge $bopt vlan show |tail +2 | awk 'NF { iface=$1; vid=$2; printf("%-16s %4d ", iface, vid); for (i=3; i <= NF; i++) printf("%s ", $i); printf("\n"); }'
|
||||
if [ -z "$plain" ] && [ -z "$nofoot" ]; then
|
||||
dm "______________________________________________________________________________"
|
||||
dm "See: 'bridge --help' and 'bridge vlan help' for more details."
|
||||
fi
|
||||
}
|
||||
|
||||
ifaces()
|
||||
{
|
||||
if [ -n "$all" ]; then
|
||||
if [ $# -gt 0 ]; then
|
||||
for iface in $*; do
|
||||
ip $opt addr show dev "$iface"
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
h1 "INTERFACE STATE ADDRESS"
|
||||
ip $opt addr show
|
||||
if [ -z "$plain" ] && [ -z "$nofoot" ]; then
|
||||
dm "______________________________________________________________________________"
|
||||
dm "See: 'ip --help' and 'ip address help' for more details."
|
||||
fi
|
||||
else
|
||||
if [ $# -gt 0 ]; then
|
||||
for iface in $*; do
|
||||
sysrepocfg -f json -X -d operational -x \
|
||||
"/ietf-interfaces:interfaces/interface[name='$iface']" | \
|
||||
/usr/libexec/statd/cli-pretty "show-interfaces" -n "$iface"
|
||||
done
|
||||
return
|
||||
fi
|
||||
sysrepocfg -f json -X -d operational -m ietf-interfaces | \
|
||||
/usr/libexec/statd/cli-pretty "show-interfaces"
|
||||
fi
|
||||
}
|
||||
|
||||
log()
|
||||
{
|
||||
if [ -n "$1" ] && [ -r "/var/log/$1" ]; then
|
||||
fn="/var/log/$1"
|
||||
else
|
||||
fn="/var/log/syslog"
|
||||
fi
|
||||
less +G $fn
|
||||
}
|
||||
|
||||
rmon()
|
||||
{
|
||||
if [ -z "$*" ]; then
|
||||
echo "Missing argument, see 'show port' for available interfaces"
|
||||
exit 1
|
||||
fi
|
||||
for port in $*; do
|
||||
ethtool -S "$port"
|
||||
done
|
||||
if [ -z "$plain" ] && [ -z "$nofoot" ]; then
|
||||
dm "______________________________________________________________________________"
|
||||
dm "See: 'ethtool --help' for more details."
|
||||
fi
|
||||
}
|
||||
|
||||
rstp()
|
||||
{
|
||||
mstpctl showbridge
|
||||
echo "br0 port info"
|
||||
mstpctl showport br0
|
||||
}
|
||||
|
||||
fdb()
|
||||
{
|
||||
bridge $bopt fdb show
|
||||
}
|
||||
|
||||
mdb()
|
||||
{
|
||||
bridge $bopt mdb show
|
||||
}
|
||||
|
||||
routes()
|
||||
{
|
||||
if [ -n "$1" ]; then
|
||||
arg="-i $1"
|
||||
else
|
||||
arg="-i ipv4"
|
||||
fi
|
||||
sysrepocfg -f json -X -d operational -x "/ietf-routing:routing/ribs" | \
|
||||
/usr/libexec/statd/cli-pretty "show-routing-table" $arg
|
||||
}
|
||||
|
||||
igmp()
|
||||
{
|
||||
mctl $@
|
||||
}
|
||||
|
||||
system()
|
||||
{
|
||||
h1 "SYSTEM INFORMATION"
|
||||
echo "System Name : $(uname -n)"
|
||||
echo "System Variant : $VARIANT"
|
||||
echo "System Description : $PRETTY_NAME"
|
||||
echo "System Contact : $HOME_URL"
|
||||
echo "System Timezone : $(cat /etc/timezone)"
|
||||
echo "System Type : $NAME"
|
||||
echo "System Version : $(cat /etc/version)"
|
||||
echo "System Arch : $(uname -m)"
|
||||
echo "Kernel Version : $(uname -sr)"
|
||||
}
|
||||
|
||||
version()
|
||||
{
|
||||
cat /etc/version
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-a)
|
||||
all=1
|
||||
;;
|
||||
-f)
|
||||
opt=""
|
||||
if [ -n "$plain" ]; then
|
||||
opt="-color=never"
|
||||
bopt="-color=never"
|
||||
fi
|
||||
opt="-d $opt"
|
||||
bopt="-d $bopt"
|
||||
;;
|
||||
-n)
|
||||
nofoot="yes"
|
||||
;;
|
||||
-p)
|
||||
plain="yes"
|
||||
opt="$opt -color=never"
|
||||
bopt="$bopt -color=never"
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
break
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "$plain" ]; then
|
||||
TTY=$(resize)
|
||||
eval "$TTY"
|
||||
|
||||
# COLUMS and ROWS should be set on the console, if not, use fallback
|
||||
if [ -z "$COLUMNS" ]; then
|
||||
if command -v tput; then
|
||||
COLUMNS=$(tput cols)
|
||||
else
|
||||
COLUMNS=80
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
cmd=$1
|
||||
if [ -n "$cmd" ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
case $cmd in
|
||||
help)
|
||||
usage
|
||||
;;
|
||||
port*)
|
||||
ports $*
|
||||
;;
|
||||
vlan*)
|
||||
vlans
|
||||
;;
|
||||
fdb)
|
||||
fdb
|
||||
;;
|
||||
mdb)
|
||||
mdb
|
||||
;;
|
||||
ifa*)
|
||||
ifaces $*
|
||||
;;
|
||||
ip)
|
||||
cmd=$1
|
||||
shift
|
||||
case $cmd in
|
||||
addr*)
|
||||
all=1
|
||||
opt="$opt -d"
|
||||
ifaces $*
|
||||
;;
|
||||
route*)
|
||||
routes ipv4
|
||||
;;
|
||||
igmp*)
|
||||
igmp $*
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
ipv6)
|
||||
cmd=$1
|
||||
shift
|
||||
case $cmd in
|
||||
addr*)
|
||||
ifaces
|
||||
;;
|
||||
route*)
|
||||
routes ipv6
|
||||
;;
|
||||
mld*)
|
||||
igmp $*
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
log)
|
||||
log $1
|
||||
;;
|
||||
rmon)
|
||||
rmon $*
|
||||
;;
|
||||
route*)
|
||||
routes
|
||||
;;
|
||||
span*)
|
||||
rstp
|
||||
;;
|
||||
sys*)
|
||||
system
|
||||
;;
|
||||
ver*)
|
||||
version
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# Sort and collate all /etc/frr/static.d/*.conf files managed by confd,
|
||||
# udhcpc, and avahi-autoipd before starting staticd.
|
||||
|
||||
DIRD=/etc/frr/static.d
|
||||
NAME=/etc/frr/staticd.conf
|
||||
NEXT=${NAME}+
|
||||
|
||||
rm -f "$NEXT"
|
||||
find "$DIRD" -type f -name '*.conf' ! -name '*~' | sort | while read -r file; do
|
||||
cat "$file" >> "$NEXT"
|
||||
done
|
||||
|
||||
cmp -s "$NAME" "$NEXT" && exit 0
|
||||
mv "$NEXT" "$NAME"
|
||||
@@ -7,9 +7,12 @@ ACTION="$1"
|
||||
IP_CACHE="/var/lib/misc/${interface}.cache"
|
||||
RESOLV_CONF="/run/resolvconf/interfaces/${interface}.conf"
|
||||
NTPFILE="/run/chrony/dhcp-sources.d/${interface}.sources"
|
||||
NAME="/etc/frr/static.d/${interface}-dhcp.conf"
|
||||
NEXT="${NAME}+"
|
||||
|
||||
[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
|
||||
[ -n "$subnet" ] || subnet=32
|
||||
[ -n "$metric" ] || metric=100
|
||||
[ -n "$metric" ] || metric=5
|
||||
|
||||
# Handle stateful DHCPv6 like DHCPv4
|
||||
[ -n "$ipv6" ] && ip="$ipv6/128"
|
||||
@@ -23,7 +26,7 @@ log()
|
||||
logger -I $$ -t udhcpc -p user.notice "$*"
|
||||
}
|
||||
|
||||
wwait_for_ipv6_default_route()
|
||||
wait_for_ipv6_default_route()
|
||||
{
|
||||
log "waiting for IPv6 default route to be installed."
|
||||
while [ $IF_WAIT_DELAY -gt 0 ]; do
|
||||
@@ -42,28 +45,35 @@ wwait_for_ipv6_default_route()
|
||||
# client MUST ignore the Router option.
|
||||
set_dhcp_routes()
|
||||
{
|
||||
echo "! Generated by udhcpc" > "$NEXT"
|
||||
if [ -n "$staticroutes" ]; then
|
||||
# format: dest1/mask gw1 ... destn/mask gwn
|
||||
set -- $staticroutes
|
||||
while [ -n "$1" -a -n "$2" ]; do
|
||||
log "adding route $1 via $2 dev $interface proto dhcp"
|
||||
ip route add "$1" via "$2" dev $interface metric $metric proto dhcp
|
||||
echo "ip route $1 $2 $metric tag 100" >> "$NEXT"
|
||||
shift 2
|
||||
done
|
||||
elif [ -n "$router" ] ; then
|
||||
for i in $router ; do
|
||||
ip route add default via $i dev $interface metric $((metric++)) proto dhcp
|
||||
echo "ip route 0.0.0.0/0 $i $metric tag 100" >> "$NEXT"
|
||||
done
|
||||
fi
|
||||
|
||||
# Reduce changes needed by comparing with previous route(s)
|
||||
cmp -s "$NAME" "$NEXT" && return
|
||||
mv "$NEXT" "$NAME"
|
||||
|
||||
initctl -nbq restart staticd
|
||||
}
|
||||
|
||||
clr_dhcp_routes()
|
||||
{
|
||||
log "deleting DHCP routes from $interface"
|
||||
ip route show proto dhcp dev $interface | while read rt via nh dev dev; do
|
||||
log "removing $rt nh $nh on $dev"
|
||||
ip route del $rt via $nh dev $dev proto dhcp
|
||||
done
|
||||
[ -f "$NAME" ] || return
|
||||
rm "$NAME"
|
||||
|
||||
initctl -nbq restart staticd
|
||||
}
|
||||
|
||||
clr_dhcp_addresses()
|
||||
@@ -112,7 +122,6 @@ case "$ACTION" in
|
||||
wait_for_ipv6_default_route
|
||||
fi
|
||||
|
||||
clr_dhcp_routes
|
||||
set_dhcp_routes
|
||||
|
||||
# set hostname if given
|
||||
|
||||
+24
-1
@@ -8,11 +8,21 @@ All notable changes to the project are documented in this file.
|
||||
-------------------------
|
||||
|
||||
### Changes
|
||||
- Upgrade Buildroot to 2024.02.6 (LTS)
|
||||
- Upgrade Linux kernel to 6.6.52 (LTS)
|
||||
- Upgrade libyang to 3.4.2
|
||||
- Upgrade sysrepo to 2.11.7
|
||||
- Upgrade netopeer2 (NETCONF) to 2.2.31
|
||||
- Updated `infix-routing.yang` to declare deviations for unsupported
|
||||
OSPF RPCs and Notifications in `ietf-ospf.yang`
|
||||
- The CLI admin-exec command `show dns` now also shows any configured
|
||||
name servers, not just ones acquired via DHCP. Issue #510
|
||||
- Add support for IPv4 (autoconf) `request-address`. This instructs the
|
||||
ZeroConf client to start with the requested address. If this is not
|
||||
successful the client falls back to its default behavior. Issue #628
|
||||
- Major speedup (10x) in operational data, in particular when querying
|
||||
interface status. Very noticeable in the CLI `show interfaces`
|
||||
command on devices with large port counts. Issue #651
|
||||
- Silence `yanger` log warnings for failing `mctl` command. Caused
|
||||
by `mctl` reporting no multicast filtering enabled on bridge
|
||||
|
||||
@@ -30,20 +40,32 @@ All notable changes to the project are documented in this file.
|
||||
- Fix #613: CLI regression in tab completion of container commands,
|
||||
e.g., `container shell <TAB>`. Regression introduced in v24.08.0
|
||||
- Fix #616: Silent failure when selecting bash as login shell for
|
||||
non-admin user, this silent lock has been removed.
|
||||
non-admin user, this silent lock has been removed
|
||||
- Fix #618: CLI command `show interfaces` does not show bridges and
|
||||
bridge ports, regression introduced in v24.08.0 -- only affects
|
||||
bridges without multicast snooping
|
||||
- Fix #623: CLI command `container upgrade NAME` does not work,
|
||||
regression introduced in v24.06.0
|
||||
- Fix #625: initialize sysrepo startup datastore at boot. Improves
|
||||
usability when working directly against the sysrepo datastores from
|
||||
the shell with `sysrepocfg` and `sysrepoctl` tools
|
||||
- Fix #635: OSPF: all router neighbors reported as neighbor on every
|
||||
interface
|
||||
- Fix #638: Disabling IPv4LL (autoconf) on an interface does not clean
|
||||
up 169.254/16 addresses
|
||||
- Fix #640: unable to set static default route due to priority inversion
|
||||
from DHCP or IPv4LL (ZeroConf) clients setting their routes directly
|
||||
in the kernel. This has resulted in a complete overhaul of route
|
||||
management, using FRRouting for all routes, including DHCP and IPv4LL
|
||||
routes, presentation in the CLI, and also support for custom route
|
||||
preference for static routes.
|
||||
- Spellcheck path to `/var/lib/containers` when unpacking OCI archives
|
||||
on container upgrade
|
||||
- The timeout before giving up on loading the `startup-config` at boot
|
||||
is now 1 minute, just like operations via other front-ends (NETCONF
|
||||
and RESTCONF). This was previously (incorrectly) set to 10 seconds.
|
||||
|
||||
|
||||
[v24.08.0][] - 2024-08-30
|
||||
-------------------------
|
||||
|
||||
@@ -1109,6 +1131,7 @@ Supported YANG models in addition to those used by sysrepo and netopeer:
|
||||
|
||||
[buildroot]: https://buildroot.org/
|
||||
[UNRELEASED]: https://github.com/kernelkit/infix/compare/v24.08.0...HEAD
|
||||
[v24.09.0]: https://github.com/kernelkit/infix/compare/v24.08.0...v24.09.0
|
||||
[v24.08.0]: https://github.com/kernelkit/infix/compare/v24.06.0...v24.08.0
|
||||
[v24.06.0]: https://github.com/kernelkit/infix/compare/v24.04.0...v24.06.0
|
||||
[v24.04.2]: https://github.com/kernelkit/infix/compare/v24.04.1...v24.04.2
|
||||
|
||||
+17
-11
@@ -70,17 +70,6 @@ $ sudo apt install bc binutils build-essential bzip2 cpio \
|
||||
autopoint bison flex autoconf automake
|
||||
```
|
||||
|
||||
For testing, a few more tools and services are required on your system:
|
||||
|
||||
```bash
|
||||
$ sudo apt install jq graphviz qemu-system-x86 qemu-system-arm \
|
||||
ethtool gdb-multiarch tcpdump tshark
|
||||
```
|
||||
|
||||
To be able to build the test specification you also need:
|
||||
```bash
|
||||
$ sudo apt-get install python3-graphviz ruby-asciidoctor-pdf
|
||||
```
|
||||
> For details, see the Getting Started and System Requirements sections
|
||||
> of the [excellent manual][1].
|
||||
|
||||
@@ -98,6 +87,23 @@ To see available defconfigs for supported targets, use:
|
||||
make list-defconfigs
|
||||
|
||||
|
||||
### Test
|
||||
|
||||
Working with the regression test framework, *Infamy*, a few more tools
|
||||
and services are required on your system:
|
||||
|
||||
```bash
|
||||
$ sudo apt install jq graphviz qemu-system-x86 qemu-system-arm \
|
||||
ethtool gdb-multiarch tcpdump tshark
|
||||
```
|
||||
|
||||
To be able to build the test specification you also need:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install python3-graphviz ruby-asciidoctor-pdf
|
||||
```
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
|
||||
+153
-42
@@ -598,6 +598,13 @@ As shown, the link-local IPv4 address is configured with `set autconf
|
||||
enabled true`. The resulting address (169.254.1.3/16) is of type
|
||||
*random* ([ietf-ip.yang][2]).
|
||||
|
||||
The IPv4LL client also supports a `request-address` setting which can be
|
||||
used to "seed" the client's starting address. If the address is free it
|
||||
will be used, otherwise it falls back to the default algorithm.
|
||||
|
||||
admin@example:/config/interface/eth0/ipv4/> set autoconf request-address 169.254.1.2
|
||||
|
||||
|
||||
#### Use of DHCP for IPv4 address assignment
|
||||
|
||||

|
||||
@@ -657,6 +664,7 @@ admin@example:/>
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### Stateless Auto-configuration of Global IPv6 Address
|
||||
|
||||

|
||||
@@ -693,6 +701,7 @@ below.
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### Random Link Identifiers for IPv6 Stateless Autoconfiguration
|
||||
|
||||

|
||||
@@ -731,7 +740,9 @@ possible to specify use of a random identifier ([ietf-ip.yang][2] and
|
||||
Both the link-local address (fe80::) and the global address (2001:)
|
||||
have changed type to *random*.
|
||||
|
||||
|
||||
### IPv4 forwarding
|
||||
|
||||
To be able to route (static or dynamic) on the interface it is
|
||||
required to enable forwarding. This setting controlls if packets
|
||||
received on this interface can be forwarded.
|
||||
@@ -742,7 +753,9 @@ received on this interface can be forwarded.
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
|
||||
### IPv6 forwarding
|
||||
|
||||
This flag behaves totally different than for IPv4. For IPv6 the
|
||||
ability to route between interfaces is always enabled, instead this
|
||||
flag controls if the interface will be in host/router mode.
|
||||
@@ -760,18 +773,36 @@ flag controls if the interface will be in host/router mode.
|
||||
admin@example:/config/interface/eth0/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
|
||||
## Routing support
|
||||
|
||||
| **Yang Model** | **Description** |
|
||||
|:--------------------------|:--------------------------------------------------------------------------------------|
|
||||
| ietf-routing | Base model, used to set configuration and read operational status in the other models |
|
||||
| ietf-ipv4-unicast-routing | Static IPv4 unicast routing |
|
||||
| ietf-ipv6-unicast-routing | Static IPv6 unicast routing |
|
||||
| ietf-ospf | OSPF routing |
|
||||
| infix-routing | Infix deviations |
|
||||
Currently supported YANG models:
|
||||
|
||||
| **YANG Model** | **Description** |
|
||||
|:--------------------------|:--------------------------------|
|
||||
| ietf-routing | Base model for all other models |
|
||||
| ietf-ipv4-unicast-routing | Static IPv4 unicast routing |
|
||||
| ietf-ipv6-unicast-routing | Static IPv6 unicast routing |
|
||||
| ietf-ospf | OSPF routing |
|
||||
| infix-routing | Infix deviations and extensions |
|
||||
|
||||
The base model, ietf-routing, is where all the other models hook in. It
|
||||
is used to set configuration and read operational status (RIB tables) in
|
||||
the other models.
|
||||
|
||||
> **Note:** the standard IETF routing models allows multiple instances,
|
||||
> but Infix currently *only support one instance* per routing protocol!
|
||||
> In the examples presented here, the instance name `default` is used.
|
||||
|
||||
|
||||
### IPv4 Static routes
|
||||
Remember to enable [IPv4 forwarding](#IPv4-forwarding) for the interfaces.
|
||||
|
||||
The standard IETF model for static routes reside under the `static`
|
||||
control plane protocol. For our examples we use the instance name
|
||||
`default`, you can use any name.
|
||||
|
||||
For a route with destination 192.168.200.0/24 via 192.168.1.1:
|
||||
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit routing control-plane-protocol static name default
|
||||
@@ -779,7 +810,18 @@ Remember to enable [IPv4 forwarding](#IPv4-forwarding) for the interfaces.
|
||||
admin@example:/config/routing/control-plane-protocol/static/name/default/> leave
|
||||
admin@example:/>
|
||||
|
||||
> **Note:** You can only have one instance per routing protocol.
|
||||
For a "floating" static route with destination 10.0.0.0/16 via a backup
|
||||
router 192.168.1.1, using the highest possible distance:
|
||||
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit routing control-plane-protocol static name default
|
||||
admin@example:/config/routing/control-plane-protocol/static/name/default/> set ipv4 route 10.0.0.0/16 next-hop next-hop-address 192.168.1.1 route-preference 254
|
||||
admin@example:/config/routing/control-plane-protocol/static/name/default/> leave
|
||||
admin@example:/>
|
||||
|
||||
> Remember to enable [IPv4 forwarding](#IPv4-forwarding) for the
|
||||
> interfaces you want to route between.
|
||||
|
||||
|
||||
### IPv6 Static routes
|
||||
|
||||
@@ -789,24 +831,25 @@ Remember to enable [IPv4 forwarding](#IPv4-forwarding) for the interfaces.
|
||||
admin@example:/config/routing/control-plane-protocol/static/name/default/> leave
|
||||
admin@example:/>
|
||||
|
||||
> **Note:** You can only have one instance per routing protocol.
|
||||
|
||||
### OSPFv2 Routing
|
||||
Infix supports OSPF dynamic routing for IPv4, i.e., OSPFv2.
|
||||
Remember to enable [IPv4 forwarding](#IPv4-forwarding) for the
|
||||
interfaces you want to run OSPFv2.
|
||||
|
||||
The system supports OSPF dynamic routing for IPv4, i.e., OSPFv2. To
|
||||
enable OSPF and set one active interface in area 0:
|
||||
|
||||
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> set ospf area 0.0.0.0 interface e0 enabled true
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> set ospf area 0.0.0.0 interface e0 enabled
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> leave
|
||||
admin@example:/>
|
||||
|
||||
> **Note:** You can only have one instance per routing protocol.
|
||||
> Remember to enable [IPv4 forwarding](#IPv4-forwarding) for all the
|
||||
> interfaces you want to route between.
|
||||
|
||||
|
||||
#### OSPF area types
|
||||
In addition to *regular* OSPF areas, area types *NSSA* and *Stub* are supported.
|
||||
|
||||
To configure a NSSA area with summary routes:
|
||||
In addition to *regular* OSPF areas, area types *NSSA* and *Stub* are
|
||||
also supported. To configure an NSSA area with summary routes:
|
||||
|
||||
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> set ospf area 0.0.0.1 area-type nssa-area
|
||||
@@ -814,8 +857,10 @@ To configure a NSSA area with summary routes:
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> leave
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### Bidirectional Forwarding Detection (BFD)
|
||||
It is possible to enable BFD per interface to speed up detection of
|
||||
|
||||
It is possible to enable BFD per OSPF interface to speed up detection of
|
||||
link loss.
|
||||
|
||||
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default
|
||||
@@ -823,12 +868,13 @@ link loss.
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> leave
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### OSPF interface settings
|
||||
|
||||
We have already seen how to enable OSPF per interface (*enabled true*)
|
||||
and BFD for OSPF per interface (*bfd enabled true*). These and other
|
||||
OSPF interface settings are done in context of an OSFP area, e.g.,
|
||||
*area 0.0.0.0*. Available commands can be listed using the `?` mark.
|
||||
and BFD for OSPF per interface (*bfd enabled true*). These and other
|
||||
OSPF interface settings are done in context of an OSFP area, e.g., *area
|
||||
0.0.0.0*. Available commands can be listed using the `?` mark.
|
||||
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/> edit ospf area 0.0.0.0
|
||||
admin@example:/config/routing/control-plane-protocol/ospfv2/name/default/ospf/area/0.0.0.0/> edit interface e0
|
||||
@@ -870,47 +916,112 @@ routes`.
|
||||
|
||||
### View routing table
|
||||
|
||||
The routing table can be viewed from the operational datastore over
|
||||
NETCONF or using the CLI:
|
||||
The routing table can be inspected from the operational datastore, XPath
|
||||
`/routing/ribs`, using sysrepocfg, NETCONF/RESTCONF, or using the CLI.
|
||||
|
||||
|
||||
#### IPv4 routing table
|
||||
|
||||
admin@example:/> show routes ipv4
|
||||
PREFIX NEXT-HOP PREF PROTOCOL
|
||||
192.168.1.0/24 e0 kernel
|
||||
192.168.200.0/24 192.168.1.1 20 static
|
||||
This CLI example shows the IPv4 routing table with a few connected
|
||||
routes and some routes learned from OSPF. See the next section for
|
||||
an explanation of route preferences (PREF).
|
||||
|
||||
The `>` at the start of a line marks a selected route (in the IETF YANG
|
||||
model referred to as *active*), if there are more than one route with
|
||||
the same destination the `*` marks the next-hop used and installed in
|
||||
the kernel FIB (the YANG model refers to this as *installed*).
|
||||
|
||||
admin@example:/> show ip route
|
||||
DESTINATION PREF NEXT-HOP PROTO UPTIME
|
||||
>* 0.0.0.0/0 110/2 10.0.23.1 ospfv2 4h2m43s
|
||||
>* 10.0.0.1/32 110/4000 10.0.13.1 ospfv2 4h2m43s
|
||||
10.0.0.3/32 110/0 lo ospfv2 4h2m57s
|
||||
>* 10.0.0.3/32 0/0 lo direct 4h2m58s
|
||||
10.0.13.0/30 110/2000 e5 ospfv2 4h2m57s
|
||||
>* 10.0.13.0/30 0/0 e5 direct 4h2m58s
|
||||
10.0.23.0/30 110/1 e6 ospfv2 4h2m57s
|
||||
>* 10.0.23.0/30 0/0 e6 direct 4h2m58s
|
||||
192.168.3.0/24 110/1 e2 ospfv2 4h2m57s
|
||||
>* 192.168.3.0/24 0/0 e2 direct 4h2m58s
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### IPv6 routing table
|
||||
|
||||
admin@example:/> show routes ipv6
|
||||
PREFIX NEXT-HOP PREF PROTOCOL
|
||||
2001:db8:3c4d:50::/64 eth4 256 kernel
|
||||
fe80::/64 eth5 256 kernel
|
||||
fe80::/64 eth3 256 kernel
|
||||
fe80::/64 eth1 256 kernel
|
||||
fe80::/64 eth0 256 kernel
|
||||
fe80::/64 eth2 256 kernel
|
||||
fe80::/64 eth4 256 kernel
|
||||
This CLI example show the IPv6 routing table.
|
||||
|
||||
admin@example:/> show ipv6 route
|
||||
DESTINATION PREF NEXT-HOP PROTO UPTIME
|
||||
>* ::/0 1/0 2001:db8:3c4d:50::1 static 0h1m20s
|
||||
>* 2001:db8:3c4d:50::/64 0/0 e6 direct 0h1m20s
|
||||
>* 2001:db8:3c4d:200::1/128 0/0 lo direct 0h1m20s
|
||||
* fe80::/64 0/0 e7 direct 0h1m20s
|
||||
* fe80::/64 0/0 e6 direct 0h1m20s
|
||||
* fe80::/64 0/0 e5 direct 0h1m20s
|
||||
* fe80::/64 0/0 e4 direct 0h1m20s
|
||||
* fe80::/64 0/0 e3 direct 0h1m20s
|
||||
* fe80::/64 0/0 e2 direct 0h1m20s
|
||||
>* fe80::/64 0/0 e1 direct 0h1m20s
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### Route Preference
|
||||
|
||||
The operating system leverages FRRouting ([Frr][4]) as routing engine
|
||||
for both static and dynamic routing. Even routes injected from a DHCP
|
||||
client, and IPv4 link-local (IPv4) routes, are injected into Frr to let
|
||||
it weigh all routes before installing them into the kernel routing table
|
||||
(sometimes referred to as FIB).
|
||||
|
||||
Routes have different weights made up from a *distance* and a *metric*.
|
||||
The kernel routing table only talks about *metric*, which unfortunately
|
||||
is **not the same** -- this is one of the reasons why the term *route
|
||||
preference* is used instead. It is recommended to use the CLI, or any
|
||||
of the other previously mentioned YANG based front-ends, to inspect the
|
||||
routing table.
|
||||
|
||||
Default distances used (lower numeric value wins):
|
||||
|
||||
| **Distance** | **Protocol** |
|
||||
|--------------|-----------------------------------------|
|
||||
| 0 | Kernel routes, i.e., connected routes |
|
||||
| 1 | Static routes |
|
||||
| 5 | DHCP routes |
|
||||
| 110 | OSPF |
|
||||
| 254 | IPv4LL (ZeroConf) device routes |
|
||||
| 255 | Route will not be used or redistributed |
|
||||
|
||||
Hence, a route learned from OSPF may be overridden by a static route set
|
||||
locally. By default, even a route to the same destination, but with a
|
||||
different next-hop, learned from a DHCP server wins over an OSPF route.
|
||||
|
||||
The distance used for static routes and DHCP routes can be changed by
|
||||
setting a different *routing preference* value.
|
||||
|
||||
> **Note:** the kernel metric is an unsigned 32-bit value, which is read
|
||||
> by Frr as (upper) 8 bits distance and 24 bits metric. But it does not
|
||||
> write it back to the kernel FIB this way, only selected routes are
|
||||
> candidates to be installed in the FIB by Frr.
|
||||
|
||||
|
||||
#### Source protocol
|
||||
|
||||
The source protocol describes the origin of the route.
|
||||
|
||||
| **Protocol** | **Description** |
|
||||
|:-------------|:---------------------------------------------------------------------|
|
||||
| kernel | Added when setting a subnet address on an interface |
|
||||
| static | User created static routes |
|
||||
| dhcp | Routes retrieved from DHCP |
|
||||
| ospf | Routes retreived from OSPFv2 |
|
||||
| **Protocol** | **Description** |
|
||||
|:-------------|:----------------------------------------------------|
|
||||
| kernel | Added when setting a subnet address on an interface |
|
||||
| static | User created, learned from DHCP, or IPv4LL |
|
||||
| ospfv2 | Routes learned from OSPFv2 |
|
||||
|
||||
The YANG model *ietf-routing* support multiple ribs but only two are
|
||||
currently supported, namely `ipv4` and `ipv6`.
|
||||
|
||||
|
||||
[1]: https://www.rfc-editor.org/rfc/rfc8343
|
||||
[2]: https://www.rfc-editor.org/rfc/rfc8344
|
||||
[3]: https://www.rfc-editor.org/rfc/rfc8981
|
||||
[4]: https://frrouting.org/
|
||||
|
||||
[^1]: Please note, link aggregates are not yet supported in Infix.
|
||||
[^2]: Link-local IPv6 addresses are implicitly enabled when enabling
|
||||
|
||||
+8
-5
@@ -281,17 +281,18 @@ or, when running interactively:
|
||||
$ make test-sh
|
||||
09:08:17 infamy0:test # ./9pm/9pm.py -o"--transport=restconf" case/ietf_system/hostname.py
|
||||
|
||||
|
||||
### Test specification
|
||||
|
||||
The test specification is automaticly generated from the test cases,
|
||||
this put some constaints on the test cases:
|
||||
this puts some constraints on the test cases:
|
||||
|
||||
All test cases must be written so that it can be understandable with
|
||||
only the steps so the result can be reproduced
|
||||
manually. For example: Intead of `set ip address on
|
||||
the interface` it should be more human readable, for example:
|
||||
only the steps so the result can be reproduced manually. E.g., instead
|
||||
of `set ip address on the interface` it should be more human readable,
|
||||
for example:
|
||||
|
||||
```
|
||||
```python
|
||||
test.step("Set IPv4 address on the interface dut:cross")
|
||||
test.step("Verify the IP address is set on dut:cross")
|
||||
|
||||
@@ -303,9 +304,11 @@ Each test case must begin with the test name followed by a empty line and
|
||||
the description of the test. For example:
|
||||
|
||||
```
|
||||
"""
|
||||
Set hostname
|
||||
|
||||
Verify that it it possible to change hostname
|
||||
"""
|
||||
```
|
||||
|
||||
The test specifaction can be genererated with:
|
||||
|
||||
@@ -45,8 +45,10 @@ endif
|
||||
|
||||
ifeq ($(BR2_PACKAGE_AVAHI_AUTOIPD),y)
|
||||
define SKELETON_INIT_FINIT_SET_AVAHI_AUTOIPD
|
||||
echo "service [2345789] name:zeroconf :%i avahi-autoipd --force-bind --syslog %i -- ZeroConf for %i" \
|
||||
> $(FINIT_D)/available/zeroconf@.conf
|
||||
echo 'service name:zeroconf :%i env:-/etc/default/zeroconf-%i \' >$(FINIT_D)/available/zeroconf@.conf
|
||||
echo ' <!> kill:5 pid:!/run/avahi-autoipd.%i.pid \'>>$(FINIT_D)/available/zeroconf@.conf
|
||||
echo ' [2345789] log avahi-autoipd $$ZEROCONF_ARGS %i \'>>$(FINIT_D)/available/zeroconf@.conf
|
||||
echo ' -- ZeroConf for %i' >>$(FINIT_D)/available/zeroconf@.conf
|
||||
endef
|
||||
SKELETON_INIT_FINIT_POST_INSTALL_TARGET_HOOKS += SKELETON_INIT_FINIT_SET_AVAHI_AUTOIPD
|
||||
endif
|
||||
@@ -87,6 +89,7 @@ define SKELETON_INIT_FINIT_SET_FRR
|
||||
for svc in babeld bfdd bgpd eigrpd isisd ldpd ospfd ospf6d pathd ripd ripng staticd vrrpd zebra; do \
|
||||
cp $(SKELETON_INIT_FINIT_AVAILABLE)/frr/$$svc.conf $(FINIT_D)/available/$$svc.conf; \
|
||||
done
|
||||
ln -sf ../available/staticd.conf $(FINIT_D)/enabled/staticd.conf
|
||||
ln -sf ../available/zebra.conf $(FINIT_D)/enabled/zebra.conf
|
||||
endef
|
||||
SKELETON_INIT_FINIT_POST_INSTALL_TARGET_HOOKS += SKELETON_INIT_FINIT_SET_FRR
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
service [2345] log:null <!pid/zebra> staticd -A 127.0.0.1 -u frr -g frr -f /etc/frr/staticd.conf -- Static routing daemon
|
||||
# Run staticd-helper to collate /etc/static.d/*.conf before starting staticd
|
||||
service log:null <!pid/zebra> pre:/usr/sbin/staticd-helper \
|
||||
[2345] staticd -A 127.0.0.1 -u frr -g frr -f /etc/frr/staticd.conf \
|
||||
-- Static routing daemon
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
From 74ec0f8335f811e4f5becc8bbea4a52af4d3e749 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 17 Sep 2024 04:08:12 +0200
|
||||
Subject: [PATCH] On SIGTERM, allow dispatcher to process event before exiting
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
On non-systemd systems, like those managed by Finit, when the main PID
|
||||
used by avahi-autoipd call the dispatcher on reception of SIGTERM, it
|
||||
may not have time to complete the callout because the main PID exits
|
||||
almost immediately.
|
||||
|
||||
Even though the dispatcher run as a separate process, when the main PID
|
||||
is collected by FInit it will unconditionally reap lingering processes
|
||||
in the same process group.
|
||||
|
||||
Add a small enough sleep to allow the dispatcher to complete the call to
|
||||
/etc/avahi/avahi-autoipd.action before we exit the main PID. A better
|
||||
patch would be to add support for the dispatcher to messages the main
|
||||
PID when it has run the script, but there does not seem to be any such
|
||||
bidirectional communication available today.
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
avahi-autoipd/main.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
|
||||
index 96c9188..8f7a7e5 100644
|
||||
--- a/avahi-autoipd/main.c
|
||||
+++ b/avahi-autoipd/main.c
|
||||
@@ -1404,8 +1404,11 @@ static int loop(int iface, uint32_t addr) {
|
||||
|
||||
fail:
|
||||
|
||||
- if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
|
||||
+ if (state == STATE_RUNNING || state == STATE_ANNOUNCING) {
|
||||
do_callout(dispatcher, CALLOUT_STOP, iface, addr);
|
||||
+ /* Don't leave yet, allow dispatcher to process event */
|
||||
+ sleep(2);
|
||||
+ }
|
||||
|
||||
avahi_free(out_packet);
|
||||
avahi_free(in_packet);
|
||||
--
|
||||
2.43.0
|
||||
|
||||
+10
-7
@@ -28,12 +28,12 @@ perms()
|
||||
|
||||
note()
|
||||
{
|
||||
logger -s -I $$ -p user.notice -t load "$*"
|
||||
logger -I $$ -p user.notice -t load "$*"
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
logger -s -I $$ -p user.error -t load "$*"
|
||||
logger -I $$ -p user.error -t load "$*"
|
||||
}
|
||||
|
||||
|
||||
@@ -60,12 +60,12 @@ config=$1
|
||||
|
||||
if [ -f "/mnt/aux/test-mode" ] && [ "$config" = "startup-config" ]; then
|
||||
|
||||
if [ -f "/mnt/aux/test-override-startup" ]; then
|
||||
rm -f "/mnt/aux/test-override-startup"
|
||||
else
|
||||
if [ -f "/mnt/aux/test-override-startup" ]; then
|
||||
rm -f "/mnt/aux/test-override-startup"
|
||||
else
|
||||
note "Test mode detected, switching to test-config"
|
||||
config="test-config"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "$config" ]; then
|
||||
@@ -100,7 +100,7 @@ if [ ! -f "$fn" ]; then
|
||||
fi
|
||||
|
||||
note "Loading $config ..."
|
||||
if ! $sysrepocfg -v3 -I"$fn" -f json; then
|
||||
if ! $sysrepocfg -v2 -I"$fn" -f json; then
|
||||
case "$config" in
|
||||
startup-config)
|
||||
err "Failed loading $fn, reverting to Fail Secure mode!"
|
||||
@@ -126,6 +126,9 @@ if ! $sysrepocfg -v3 -I"$fn" -f json; then
|
||||
esac
|
||||
|
||||
exit 1
|
||||
else
|
||||
note "Success, syncing with startup datastore."
|
||||
$sysrepocfg -v2 -d startup -C running
|
||||
fi
|
||||
|
||||
note "Loaded $fn successfully."
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <srx/common.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
struct confd confd;
|
||||
|
||||
|
||||
uint32_t core_hook_prio(void)
|
||||
{
|
||||
static uint32_t hook_prio = CB_PRIO_PASSIVE;
|
||||
@@ -16,6 +16,10 @@ uint32_t core_hook_prio(void)
|
||||
int core_startup_save(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
/* skip in bootstrap, triggered by load script to initialize startup datastore */
|
||||
if (systemf("runlevel >/dev/null 2>&1"))
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (systemf("sysrepocfg -X/cfg/startup-config.cfg -d startup -f json"))
|
||||
return SR_ERR_SYS;
|
||||
|
||||
@@ -177,10 +181,10 @@ err:
|
||||
|
||||
void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *priv)
|
||||
{
|
||||
struct confd *confd = (struct confd *)priv;
|
||||
struct confd *ptr = (struct confd *)priv;
|
||||
|
||||
sr_unsubscribe(confd->sub);
|
||||
sr_unsubscribe(confd->fsub);
|
||||
json_decref(confd->root);
|
||||
sr_unsubscribe(ptr->sub);
|
||||
sr_unsubscribe(ptr->fsub);
|
||||
json_decref(ptr->root);
|
||||
closelog();
|
||||
}
|
||||
|
||||
@@ -100,11 +100,11 @@ int core_startup_save (sr_session_ctx_t *, uint32_t, const char *, const ch
|
||||
static inline int register_change(sr_session_ctx_t *session, const char *module, const char *xpath,
|
||||
int flags, sr_module_change_cb cb, void *arg, sr_subscription_ctx_t **sub)
|
||||
{
|
||||
struct confd *confd = (struct confd *)arg;
|
||||
struct confd *ptr = (struct confd *)arg;
|
||||
int rc;
|
||||
|
||||
if (!flags) {
|
||||
sr_module_change_subscribe(confd->session, module, xpath, core_pre_hook, NULL,
|
||||
sr_module_change_subscribe(ptr->session, module, xpath, core_pre_hook, NULL,
|
||||
0, SR_SUBSCR_PASSIVE, sub);
|
||||
}
|
||||
|
||||
@@ -123,9 +123,9 @@ static inline int register_change(sr_session_ctx_t *session, const char *module,
|
||||
* the startup-save hook for running -> startup copying.
|
||||
*/
|
||||
if (!flags) {
|
||||
sr_module_change_subscribe(confd->session, module, xpath, core_post_hook, NULL,
|
||||
sr_module_change_subscribe(ptr->session, module, xpath, core_post_hook, NULL,
|
||||
core_hook_prio(), SR_SUBSCR_PASSIVE, sub);
|
||||
sr_module_change_subscribe(confd->startup, module, xpath, core_startup_save, NULL,
|
||||
sr_module_change_subscribe(ptr->startup, module, xpath, core_startup_save, NULL,
|
||||
core_hook_prio(), SR_SUBSCR_PASSIVE, sub);
|
||||
}
|
||||
|
||||
|
||||
@@ -537,45 +537,89 @@ static int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if ipv4 is enabled, only then can autoconf be enabled, in all
|
||||
* other cases it must be disabled. Since we have multiple settings in
|
||||
* autoconf, we check if either is modified (diff), in which case we not
|
||||
* only enable, but also "touch" the Finit service for avahi-autoipd to
|
||||
* ensure it is (re)started.
|
||||
*
|
||||
* Note: in Infix, regardless of the IPv4 configuration, any link-local
|
||||
* link-local address is disabled when the interface is being used
|
||||
* as a bridge port.
|
||||
*
|
||||
* Also, IPv4LL is not defined for loopback, so always skip there.
|
||||
*/
|
||||
static int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *ipconf = lydx_get_child(cif, "ipv4");
|
||||
struct lyd_node *ipdiff = lydx_get_child(dif, "ipv4");
|
||||
const char *ifname = lydx_get_cattr(dif, "name");
|
||||
struct lyd_node *node, *zcip;
|
||||
struct lydx_diff nd;
|
||||
struct lyd_node *zcip;
|
||||
char defaults[64];
|
||||
FILE *initctl;
|
||||
int err = 0;
|
||||
|
||||
if (!strcmp(ifname, "lo"))
|
||||
return 0;
|
||||
|
||||
/* client defults for this interface, needed in both cases */
|
||||
snprintf(defaults, sizeof(defaults), "/etc/default/zeroconf-%s", ifname);
|
||||
|
||||
/* no ipv4 at all, ipv4 selectively disabled, or interface is a bridge port */
|
||||
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_bridge_port(cif))
|
||||
goto disable;
|
||||
|
||||
if (lydx_get_op(lydx_get_child(ipdiff, "enabled")) == LYDX_OP_REPLACE) {
|
||||
node = lydx_get_child(ipconf, "autoconf");
|
||||
if (node && lydx_is_enabled(node, "enabled"))
|
||||
goto enable;
|
||||
goto disable;
|
||||
}
|
||||
/*
|
||||
* when enabled, we may have been enabled before, but skipped
|
||||
* for various reasons: was bridge port, ipv4 was disabled...
|
||||
*/
|
||||
zcip = lydx_get_child(ipconf, "autoconf");
|
||||
if (zcip && lydx_is_enabled(zcip, "enabled")) {
|
||||
struct lyd_node *node;
|
||||
const char *addr;
|
||||
int diff = 0;
|
||||
FILE *fp;
|
||||
|
||||
zcip = lydx_get_child(ipdiff, "autoconf");
|
||||
if (!zcip || !(node = lydx_get_child(zcip, "enabled")))
|
||||
return 0;
|
||||
/* check for any changes in this container */
|
||||
node = lydx_get_child(ipdiff, "autoconf");
|
||||
if (node) {
|
||||
const struct lyd_node *tmp;
|
||||
|
||||
lydx_get_diff(node, &nd);
|
||||
if (nd.new && !strcmp(nd.val, "true")) {
|
||||
enable:
|
||||
initctl = dagger_fopen_next(net, "init", ifname,
|
||||
60, "zeroconf-up.sh");
|
||||
tmp = lydx_get_child(node, "enabled");
|
||||
if (tmp)
|
||||
diff++;
|
||||
tmp = lydx_get_child(node, "request-address");
|
||||
if (tmp)
|
||||
diff++;
|
||||
}
|
||||
|
||||
fp = fopen(defaults, "w");
|
||||
if (!fp) {
|
||||
ERRNO("Failed creating %s, cannot enable IPv4LL on %s", defaults, ifname);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(fp, "ZEROCONF_ARGS=\"--force-bind --syslog ");
|
||||
addr = lydx_get_cattr(zcip, "request-address");
|
||||
if (addr)
|
||||
fprintf(fp, "--start=%s", addr);
|
||||
fprintf(fp, "\"\n");
|
||||
fclose(fp);
|
||||
|
||||
initctl = dagger_fopen_next(net, "init", ifname, 60, "zeroconf-up.sh");
|
||||
if (!initctl)
|
||||
return -EIO;
|
||||
|
||||
fprintf(initctl,
|
||||
"initctl -bnq enable zeroconf@%s.conf\n", ifname);
|
||||
/* on enable, or reactivation, it is enough to ensure the service is enabled */
|
||||
fprintf(initctl, "initctl -bnq enable zeroconf@%s.conf\n", ifname);
|
||||
/* on changes to autoconf we must ensure Finit restarts the service */
|
||||
if (diff)
|
||||
fprintf(initctl, "initctl -bnq touch zeroconf@%s.conf\n", ifname);
|
||||
} else {
|
||||
disable:
|
||||
initctl = dagger_fopen_current(net, "exit", ifname,
|
||||
40, "zeroconf-down.sh");
|
||||
initctl = dagger_fopen_current(net, "exit", ifname, 40, "zeroconf-down.sh");
|
||||
if (!initctl) {
|
||||
/* check if in bootstrap (pre gen 0) */
|
||||
if (errno == EUNATCH)
|
||||
@@ -583,9 +627,8 @@ static int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(initctl,
|
||||
"initctl -bnq disable zeroconf@%s.conf\n", ifname);
|
||||
|
||||
fprintf(initctl, "initctl -bnq disable zeroconf@%s.conf\n", ifname);
|
||||
fprintf(initctl, "rm -f %s\n", defaults);
|
||||
err = netdag_exit_reload(net);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#define XPATH_BASE_ "/ietf-routing:routing/control-plane-protocols/control-plane-protocol"
|
||||
#define XPATH_OSPF_ XPATH_BASE_ "/ietf-ospf:ospf"
|
||||
#define STATICD_CONF "/etc/frr/staticd.conf"
|
||||
#define STATICD_CONF "/etc/frr/static.d/confd.conf"
|
||||
#define STATICD_CONF_NEXT STATICD_CONF "+"
|
||||
#define STATICD_CONF_PREV STATICD_CONF "-"
|
||||
#define OSPFD_CONF "/etc/frr/ospfd.conf"
|
||||
@@ -129,11 +129,11 @@ int parse_ospf_areas(sr_session_ctx_t *session, struct lyd_node *areas, FILE *fp
|
||||
int parse_ospf(sr_session_ctx_t *session, struct lyd_node *ospf)
|
||||
{
|
||||
const char *static_debug = "! OSPF default debug\
|
||||
debug ospf bfd\n \
|
||||
debug ospf packet all detail\n \
|
||||
debug ospf ism\n \
|
||||
debug ospf nsm\n \
|
||||
debug ospf default-information\n \
|
||||
debug ospf bfd\n\
|
||||
debug ospf packet all detail\n\
|
||||
debug ospf ism\n\
|
||||
debug ospf nsm\n\
|
||||
debug ospf default-information\n\
|
||||
debug ospf nssa\n\
|
||||
! OSPF configuration\n";
|
||||
struct lyd_node *areas, *default_route;
|
||||
@@ -187,12 +187,14 @@ debug ospf nssa\n\
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_route(struct lyd_node *parent, FILE *fp, char *ip)
|
||||
static int parse_route(struct lyd_node *parent, FILE *fp, const char *ip)
|
||||
{
|
||||
const char *outgoing_interface, *next_hop_address, *special_next_hop, *destination_prefix;
|
||||
const char *outgoing_interface, *next_hop_address, *special_next_hop,
|
||||
*destination_prefix, *route_preference;
|
||||
struct lyd_node *next_hop;
|
||||
|
||||
destination_prefix = lydx_get_cattr(parent, "destination-prefix");
|
||||
route_preference = lydx_get_cattr(parent, "route-preference");
|
||||
next_hop = lydx_get_child(parent, "next-hop");
|
||||
outgoing_interface = lydx_get_cattr(next_hop, "outgoing-interface");
|
||||
next_hop_address = lydx_get_cattr(next_hop, "next-hop-address");
|
||||
@@ -211,7 +213,7 @@ static int parse_route(struct lyd_node *parent, FILE *fp, char *ip)
|
||||
fputs("reject", fp);
|
||||
else if (strcmp(special_next_hop, "receive") == 0)
|
||||
fputs("Null0", fp);
|
||||
fputs("\n", fp);
|
||||
fprintf(fp, " %s\n", route_preference);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
@@ -224,14 +226,13 @@ static int parse_static_routes(sr_session_ctx_t *session, struct lyd_node *paren
|
||||
ipv4 = lydx_get_child(parent, "ipv4");
|
||||
ipv6 = lydx_get_child(parent, "ipv6");
|
||||
|
||||
ipv4 = lydx_get_child(parent, "ipv4");
|
||||
ipv6 = lydx_get_child(parent, "ipv6");
|
||||
v4routes = lydx_get_child(ipv4, "route");
|
||||
v6routes = lydx_get_child(ipv6, "route");
|
||||
LY_LIST_FOR(v4routes, route) {
|
||||
parse_route(route, fp, "ip");
|
||||
num_routes++;
|
||||
}
|
||||
|
||||
v6routes = lydx_get_child(ipv6, "route");
|
||||
LY_LIST_FOR(v6routes, route) {
|
||||
parse_route(route, fp, "ipv6");
|
||||
num_routes++;
|
||||
@@ -245,7 +246,7 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
int staticd_enabled = 0, ospfd_enabled = 0, bfdd_enabled = 0;
|
||||
bool ospfd_running, staticd_running, bfdd_running;
|
||||
bool ospfd_running, bfdd_running;
|
||||
struct lyd_node *cplane, *tmp;
|
||||
bool restart_zebra = false;
|
||||
int rc = SR_ERR_OK;
|
||||
@@ -260,7 +261,7 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
ERROR("Failed to open %s", STATICD_CONF_NEXT);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
fputs(FRR_STATIC_CONFIG, fp);
|
||||
fputs("! Generated by Infix confd\n", fp);
|
||||
break;
|
||||
|
||||
case SR_EV_ABORT: /* User abort, or other plugin failed */
|
||||
@@ -272,18 +273,8 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
staticd_enabled = fexist(STATICD_CONF_NEXT);
|
||||
ospfd_enabled = fexist(OSPFD_CONF_NEXT);
|
||||
bfdd_enabled = fexist(BFDD_CONF_NEXT);
|
||||
staticd_running = !systemf("initctl -bfq status staticd");
|
||||
ospfd_running = !systemf("initctl -bfq status ospfd");
|
||||
bfdd_running = !systemf("initctl -bfq status bfdd");
|
||||
if (staticd_running && !staticd_enabled) {
|
||||
if (systemf("initctl -bfq disable staticd")) {
|
||||
ERROR("Failed to disable static routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_abandon;
|
||||
}
|
||||
/* Remove all generated files */
|
||||
(void)remove(STATICD_CONF);
|
||||
}
|
||||
|
||||
if (bfdd_running && !bfdd_enabled) {
|
||||
if (systemf("initctl -bfq disable bfdd")) {
|
||||
@@ -321,12 +312,10 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
(void)rename(OSPFD_CONF, OSPFD_CONF_PREV);
|
||||
(void)rename(OSPFD_CONF_NEXT, OSPFD_CONF);
|
||||
if (!ospfd_running) {
|
||||
if (ospfd_enabled) {
|
||||
if (systemf("initctl -bfq enable ospfd")) {
|
||||
ERROR("Failed to enable OSPF routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_abandon;
|
||||
}
|
||||
if (systemf("initctl -bnq enable ospfd")) {
|
||||
ERROR("Failed to enable OSPF routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_abandon;
|
||||
}
|
||||
} else {
|
||||
restart_zebra = true;
|
||||
@@ -337,18 +326,17 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
(void)remove(STATICD_CONF_PREV);
|
||||
(void)rename(STATICD_CONF, STATICD_CONF_PREV);
|
||||
(void)rename(STATICD_CONF_NEXT, STATICD_CONF);
|
||||
if (!staticd_running) {
|
||||
if (systemf("initctl -bfq enable staticd")) {
|
||||
ERROR("Failed to enable static routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_abandon;
|
||||
}
|
||||
} else {
|
||||
restart_zebra = true;
|
||||
} else {
|
||||
if (!remove(STATICD_CONF))
|
||||
restart_zebra = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (restart_zebra) {
|
||||
/* skip in runlevel S, no routing daemons run here anyway */
|
||||
if (systemf("runlevel >/dev/null 2>&1"))
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (systemf("initctl -bfq restart zebra")) {
|
||||
ERROR("Failed to restart zebra routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
|
||||
@@ -25,19 +25,19 @@ MODULES=(
|
||||
"ietf-hardware@2018-03-13.yang -e hardware-state"
|
||||
"infix-hardware@2024-04-25.yang"
|
||||
"ieee802-dot1q-types@2022-10-29.yang"
|
||||
"infix-ip@2023-09-14.yang"
|
||||
"infix-ip@2024-09-16.yang"
|
||||
"infix-if-type@2024-01-29.yang"
|
||||
"infix-routing@2024-09-20.yang"
|
||||
"infix-routing@2024-09-23.yang"
|
||||
"ieee802-dot1ab-lldp@2022-03-15.yang"
|
||||
"infix-lldp@2023-08-23.yang"
|
||||
"infix-dhcp-client@2024-04-12.yang"
|
||||
"infix-dhcp-client@2024-09-20.yang"
|
||||
"infix-meta@2024-06-19.yang"
|
||||
"infix-system@2024-09-13.yang"
|
||||
"infix-services@2024-05-30.yang"
|
||||
"ieee802-ethernet-interface@2019-06-21.yang"
|
||||
"infix-ethernet-interface@2024-02-27.yang"
|
||||
"infix-factory-default@2023-06-28.yang"
|
||||
"infix-interfaces@2024-01-15.yang -e vlan-filtering"
|
||||
"infix-interfaces@2024-09-23.yang -e vlan-filtering"
|
||||
|
||||
# from rousette
|
||||
"ietf-restconf@2017-01-26.yang"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- sh -*-
|
||||
# REMEMBER TO UPDATE infix-interfaces ALSO IN confd.inc
|
||||
MODULES=(
|
||||
"infix-interfaces@2024-01-15.yang -e vlan-filtering -e containers"
|
||||
"infix-interfaces@2024-09-23.yang -e vlan-filtering -e containers"
|
||||
"infix-containers@2024-03-27.yang"
|
||||
)
|
||||
|
||||
+11
-6
@@ -10,6 +10,11 @@ module infix-dhcp-client {
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "This module implements an IPv4 DHCP client";
|
||||
|
||||
revision 2024-09-20 {
|
||||
description "Routes are installed in Frr (staticd), clarify preference
|
||||
vs metric and adjust default preference 100 -> 5.";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-04-12 {
|
||||
description "Adjust DHCP client hostname option, max 64 charachters.";
|
||||
reference "internal";
|
||||
@@ -29,7 +34,7 @@ module infix-dhcp-client {
|
||||
|
||||
typedef route-preference {
|
||||
type uint32;
|
||||
description "This type is used for route preferences (metric).";
|
||||
description "This type is used for selecting route preference (distance).";
|
||||
}
|
||||
|
||||
typedef dhcp-options {
|
||||
@@ -149,12 +154,12 @@ module infix-dhcp-client {
|
||||
}
|
||||
leaf route-preference {
|
||||
type route-preference;
|
||||
default 100;
|
||||
default 5;
|
||||
description
|
||||
"The metric any route is installed with, both option 33 and 121.
|
||||
The default (100) is based on a plain Ethernet interface, the
|
||||
interface type determines this, e.g. a WiFi interface would be
|
||||
metric 500.";
|
||||
"The preference (administrative distance) that all DHCP routes are
|
||||
installed with, option 3, 33 and 121. The default preferfence (5)
|
||||
is higher (less worth) than static routes, but lower than those
|
||||
learned via dynamic routing protocols, like OSPF.";
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -20,6 +20,11 @@ module infix-interfaces {
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Linux bridge and lag extensions for ietf-interfaces.";
|
||||
|
||||
revision 2024-09-23 {
|
||||
description "Drop interfaces-state deviation, already marked deprecated.";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
revision 2024-01-15 {
|
||||
description "Add support for container ports (CNI networks).";
|
||||
reference "internal";
|
||||
@@ -71,9 +76,4 @@ module infix-interfaces {
|
||||
config true;
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/if:interfaces-state" {
|
||||
deviate not-supported;
|
||||
description "Deprecated";
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
module infix-ip {
|
||||
yang-version 1.1;
|
||||
namespace "urn:infix:params:xml:ns:yang:infix-ip";
|
||||
prefix infix-ip;
|
||||
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
import ietf-ip {
|
||||
prefix ip;
|
||||
}
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
}
|
||||
|
||||
description
|
||||
"This module augments ietf-ip with an IPv4 link-local autoconf";
|
||||
|
||||
revision 2023-09-14 {
|
||||
description
|
||||
"Added deviations for unsupported parts of ietf-ip.";
|
||||
}
|
||||
|
||||
revision 2023-04-24 {
|
||||
description
|
||||
"Initial revision.";
|
||||
reference
|
||||
"RFC 7277: A YANG Data Model for IP Management";
|
||||
}
|
||||
|
||||
/*
|
||||
* Data nodes
|
||||
*/
|
||||
augment "/if:interfaces/if:interface/ip:ipv4" {
|
||||
container autoconf {
|
||||
description
|
||||
"Parameters to control the autoconfiguration of IPv4 address.";
|
||||
|
||||
leaf enabled {
|
||||
type boolean;
|
||||
default false;
|
||||
description
|
||||
"Use a ZeroConf/IPv4LL agent to retrieve an 169.254/16 address.";
|
||||
reference
|
||||
"RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv4/ip:address/ip:subnet/ip:netmask" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv4/ip:neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv6/ip:address/ip:status" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv6/ip:neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
module infix-ip {
|
||||
yang-version 1.1;
|
||||
namespace "urn:infix:params:xml:ns:yang:infix-ip";
|
||||
prefix infix-ip;
|
||||
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
import ietf-ip {
|
||||
prefix ip;
|
||||
}
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
}
|
||||
|
||||
description "This module augments ietf-ip with Infix extensions and deviations.";
|
||||
|
||||
revision 2024-09-16 {
|
||||
description "Add support for IPv4LL request-address.";
|
||||
reference "Internal.";
|
||||
}
|
||||
revision 2023-09-14 {
|
||||
description "Added deviations for unsupported parts of ietf-ip.";
|
||||
reference "Internal.";
|
||||
}
|
||||
revision 2023-04-24 {
|
||||
description "Initial revision.";
|
||||
reference "RFC 7277: A YANG Data Model for IP Management";
|
||||
}
|
||||
|
||||
/*
|
||||
* Data nodes
|
||||
*/
|
||||
augment "/if:interfaces/if:interface/ip:ipv4" {
|
||||
container autoconf {
|
||||
description "Parameters to control the autoconfiguration of IPv4 address.";
|
||||
reference "RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses";
|
||||
|
||||
leaf enabled {
|
||||
description "Use a ZeroConf/IPv4LL agent to retrieve an 169.254/16 address.";
|
||||
type boolean;
|
||||
}
|
||||
|
||||
leaf request-address {
|
||||
description "Try to acquire the specified IP address, if available.
|
||||
|
||||
With this setting the IPv4LL client will start by
|
||||
requesting this address. However, if it is not
|
||||
available it falls back to the default algorithm.";
|
||||
type inet:ipv4-address;
|
||||
must "substring(., 1, 7) = '169.254'" {
|
||||
error-message "Must be from the IPv4LL range 169.254.0.0/16.";
|
||||
}
|
||||
must "not(substring(., string-length(.) - 1, 2) = '.0' or substring(., string-length(.) - 3, 4) = '.255')" {
|
||||
error-message "Addresses ending in .0 or .255 are reserved.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv4/ip:address/ip:subnet/ip:netmask" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv4/ip:neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv6/ip:address/ip:status" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv6/ip:neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
}
|
||||
@@ -1,496 +0,0 @@
|
||||
module infix-routing {
|
||||
yang-version 1.1;
|
||||
namespace "urn:infix:routing:ns:yang:1.0";
|
||||
prefix infix-r;
|
||||
import ietf-routing {
|
||||
prefix ietf-r;
|
||||
}
|
||||
|
||||
import ietf-ipv4-unicast-routing {
|
||||
prefix v4ur;
|
||||
}
|
||||
|
||||
import ietf-ipv6-unicast-routing {
|
||||
prefix v6ur;
|
||||
}
|
||||
import ietf-ospf {
|
||||
prefix ospf;
|
||||
}
|
||||
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
|
||||
import ietf-routing-types {
|
||||
prefix rt-types;
|
||||
}
|
||||
revision 2024-09-20 {
|
||||
description "Deviate to only show supported routing types";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-09-13 {
|
||||
description "Declare deviations for non-supported OSPF RPCs and Notifications";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-03-06 {
|
||||
description "Obsolete leaf enable in favor for enabled in default-route-advertise";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-01-09 {
|
||||
description "Add operational, area, bfd support
|
||||
and other minor features";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2023-11-23 {
|
||||
description "Limit to one instance per control plane protocol,
|
||||
and change to infix (iproute2) specific source protocol names
|
||||
for routes";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2023-10-27 {
|
||||
description "Initial revision.";
|
||||
reference "internal";
|
||||
}
|
||||
/* General routing */
|
||||
deviation "/ietf-r:routing/ietf-r:interfaces" {
|
||||
description "Initial limitation";
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:router-id" {
|
||||
description "Set in OSPF";
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing-state" {
|
||||
description "Obsolete";
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
identity infix-source-protocol {
|
||||
description "Infix naming of source protocols";
|
||||
}
|
||||
identity kernel {
|
||||
description "Kernel added route";
|
||||
base infix-source-protocol;
|
||||
}
|
||||
identity ospf {
|
||||
description "OSPF added route";
|
||||
base infix-source-protocol;
|
||||
}
|
||||
identity dhcp {
|
||||
description "DHCP added route";
|
||||
base infix-source-protocol;
|
||||
}
|
||||
identity zeroconf {
|
||||
description "ZeroConf added route";
|
||||
base infix-source-protocol;
|
||||
}
|
||||
identity ra {
|
||||
description "RA IPv6 route";
|
||||
base infix-source-protocol;
|
||||
}
|
||||
|
||||
typedef infix-distribute-protocol {
|
||||
type enumeration {
|
||||
enum ospf {
|
||||
description "Redistribute Ospf";
|
||||
}
|
||||
enum static {
|
||||
description "Redistribute Static routes";
|
||||
}
|
||||
enum connected {
|
||||
description "Redistribute Connected routes";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:source-protocol" {
|
||||
description "Use type names from iproute2";
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-source-protocol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
identity infix-routing-type {
|
||||
description "Infix routing type";
|
||||
}
|
||||
identity ospfv2 {
|
||||
base ospf:ospfv2;
|
||||
base infix-routing-type;
|
||||
description "OSPv2 (IPv4) routing protocol";
|
||||
}
|
||||
identity static {
|
||||
base ietf-r:static;
|
||||
base infix-routing-type;
|
||||
base infix-source-protocol;
|
||||
description "Static route";
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:type" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-routing-type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Static routes */
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:static-routes/v4ur:ipv4/v4ur:route/v4ur:next-hop/v4ur:next-hop-options/v4ur:next-hop-list" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:static-routes/v6ur:ipv6/v6ur:route/v6ur:next-hop/v6ur:next-hop-options/v6ur:next-hop-list" {
|
||||
deviate not-supported;
|
||||
}
|
||||
/* show routes */
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:default-rib" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:active-route" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ospf:metric" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ospf:tag" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ospf:route-type" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:active" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:last-updated" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:description" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF */
|
||||
typedef infix-ospf-interface-type {
|
||||
type enumeration {
|
||||
enum broadcast {
|
||||
description
|
||||
"Specifies an OSPF broadcast multi-access network.";
|
||||
}
|
||||
enum point-to-point {
|
||||
description
|
||||
"Specifies an OSPF point-to-point network.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol" {
|
||||
deviate add {
|
||||
must "count(../control-plane-protocol[type = current()/type]) = 1" {
|
||||
error-message "Only one instance per routing protocol is allowed.";
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:type" {
|
||||
deviate add {
|
||||
must "derived-from-or-self(., 'ospf:ospfv2') or "+
|
||||
"derived-from-or-self(., 'ietf-r:static')" {
|
||||
description "Only OSPFv2 and Static routes are supported in Infix.";
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
augment "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf" {
|
||||
description "ietf-ospf lack the setting to generate a default route";
|
||||
container default-route-advertise {
|
||||
description "Distribute default route to network";
|
||||
leaf enable {
|
||||
status obsolete;
|
||||
description "Legacy, replaced by 'enabled'.";
|
||||
type boolean;
|
||||
}
|
||||
leaf enabled {
|
||||
description "Distribute default route";
|
||||
type boolean;
|
||||
}
|
||||
leaf always {
|
||||
description "Always distribute default route, even if no local default route exist.
|
||||
If set to false, a default route will only be distributed if a default route exist
|
||||
in the local routing table.";
|
||||
type boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
augment "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf" {
|
||||
description "ietf-ospf does not contain redistribution
|
||||
7. How should route redistribution be configured? I see ietf-rip.yang has a separate
|
||||
container for that purpose, but ietf-ospf.yang (and other IGP modules) don't do the
|
||||
same. I also noticed the BGP model is using definition from ietf-routing-policy.yang.
|
||||
|
||||
Different vendors handle redistribution in different ways. This could be added with
|
||||
an augmentation if there were agreement.
|
||||
|
||||
https://marc.info/?l=ms-ospf&m=166258444409552&w=2";
|
||||
container redistribute {
|
||||
list redistribute {
|
||||
key "protocol";
|
||||
description "Redistribute protocols into OSPF";
|
||||
leaf protocol {
|
||||
type infix-distribute-protocol;
|
||||
description "Set protocol to redistribute";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface" {
|
||||
deviate add {
|
||||
must "current()/name = 'lo' or count(../../../../ospf:areas/ospf:area/ospf:interfaces/ospf:interface[ospf:name=current()/name]) <= 1" {
|
||||
error-message "Only one area per interface is allowed.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:interface-type" {
|
||||
deviate replace {
|
||||
type infix-ospf-interface-type;
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:auto-cost" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:graceful-restart" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:enabled" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:spf-control" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:preference" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:stub-router" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:topologies" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:statistics" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database-control" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:lsa-log" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:spf-log" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:fast-reroute" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:node-tags" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:mpls" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:nsr" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF Area */
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:virtual-links" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:sham-links" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:ranges" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:database" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:topologies" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:statistics" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF Local RIB */
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:local-rib/ospf:route/ospf:metric" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:local-rib/ospf:route/ospf:route-tag" {
|
||||
deviate not-supported;
|
||||
}
|
||||
/* OSPF Area Interface */
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:static-neighbors"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:multi-areas" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:node-flag" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:bfd/ospf:local-multiplier"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:bfd/ospf:interval-config-type"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:priority"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:fast-reroute"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:authentication"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:statistics"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:neighbors/ospf:neighbor/ospf:statistics"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:neighbors/ospf:neighbor/ospf:cost"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:database"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:topologies"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:instance-id"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:interface-id"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:demand-circuit"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:ttl-security"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:lls"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:mtu-ignore"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:prefix-suppression"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:hello-timer"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:wait-timer"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF database */
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv3" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:opaque" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:external" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:summary" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:router" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:header" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF RPCs */
|
||||
deviation "/ospf:clear-neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:clear-database" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF Notifications */
|
||||
deviation "/ospf:if-state-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:if-config-error" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:nbr-state-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:nbr-restart-helper-status-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:if-rx-bad-packet" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:lsdb-approaching-overflow" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:lsdb-overflow" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:nssa-translator-status-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:restart-status-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
module infix-routing {
|
||||
yang-version 1.1;
|
||||
namespace "urn:infix:routing:ns:yang:1.0";
|
||||
prefix infix-rt;
|
||||
|
||||
import ietf-routing {
|
||||
prefix rt;
|
||||
}
|
||||
import ietf-ipv4-unicast-routing {
|
||||
prefix v4ur;
|
||||
}
|
||||
import ietf-ipv6-unicast-routing {
|
||||
prefix v6ur;
|
||||
}
|
||||
import ietf-ospf {
|
||||
prefix ospf;
|
||||
}
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
import ietf-routing-types {
|
||||
prefix rt-types;
|
||||
}
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Deviations and augments for ietf-routing and ietf-ospf.";
|
||||
|
||||
revision 2024-09-23 {
|
||||
description "Augment static routes with optional route-preference (distance).";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-09-22 {
|
||||
description "- Enable OSPF route metrics in RIB
|
||||
- Enable 'active' route flag for RIB
|
||||
- Enable 'last-updated' route field for RIB
|
||||
- Augment route in RIB with 'installed' flag
|
||||
- Aadjust YANG prefix for consistency: ietf-r: -> rt:";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-09-21 {
|
||||
description "Drop RIB source-protocol deviation for standard, add kernel proto.";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-09-20 {
|
||||
description "Deviate to only show supported routing types";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-09-13 {
|
||||
description "Declare deviations for non-supported OSPF RPCs and Notifications";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-03-06 {
|
||||
description "Obsolete leaf enable in favor for enabled in default-route-advertise";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-01-09 {
|
||||
description "Add operational, area, bfd support
|
||||
and other minor features";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2023-11-23 {
|
||||
description "Limit to one instance per control plane protocol,
|
||||
and change to infix (iproute2) specific source protocol names
|
||||
for routes";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2023-10-27 {
|
||||
description "Initial revision.";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
/* General routing */
|
||||
deviation "/rt:routing/rt:interfaces" {
|
||||
description "Initial limitation";
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:router-id" {
|
||||
description "Set in OSPF";
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing-state" {
|
||||
description "Obsolete";
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
identity kernel {
|
||||
base rt:routing-protocol;
|
||||
description
|
||||
"'Kernel' routing pseudo-protocol.";
|
||||
}
|
||||
|
||||
typedef infix-distribute-protocol {
|
||||
type enumeration {
|
||||
enum ospf {
|
||||
description "Redistribute Ospf";
|
||||
}
|
||||
enum static {
|
||||
description "Redistribute Static routes";
|
||||
}
|
||||
enum connected {
|
||||
description "Redistribute Connected routes";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
identity infix-routing-type {
|
||||
description "Infix routing type";
|
||||
}
|
||||
identity ospfv2 {
|
||||
base ospf:ospfv2;
|
||||
base infix-routing-type;
|
||||
description "OSPv2 (IPv4) routing protocol";
|
||||
}
|
||||
identity static {
|
||||
base rt:static;
|
||||
base infix-routing-type;
|
||||
description "Static route";
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:type" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-routing-type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Static routes */
|
||||
typedef distance {
|
||||
description "Capped IETF route-preference, 0-255, for distance.";
|
||||
type rt:route-preference {
|
||||
range "0..255";
|
||||
}
|
||||
}
|
||||
|
||||
grouping route-pref {
|
||||
leaf route-preference {
|
||||
description "Adjust preference between routes of same destination prefix.
|
||||
|
||||
This attribute, also known as 'administrative distance', allows
|
||||
for selecting the preferred route among routes with the same
|
||||
destination prefix. A smaller value indicates a route that is
|
||||
more preferred.";
|
||||
type distance;
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:static-routes/v4ur:ipv4/v4ur:route/v4ur:next-hop/v4ur:next-hop-options/v4ur:next-hop-list" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:static-routes/v6ur:ipv6/v6ur:route/v6ur:next-hop/v6ur:next-hop-options/v6ur:next-hop-list" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:static-routes/v4ur:ipv4/v4ur:route" {
|
||||
uses route-pref {
|
||||
refine route-preference {
|
||||
default "1";
|
||||
}
|
||||
}
|
||||
}
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:static-routes/v6ur:ipv6/v6ur:route" {
|
||||
uses route-pref {
|
||||
refine route-preference {
|
||||
default "1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* show routes */
|
||||
deviation "/rt:routing/rt:ribs/rt:rib/rt:default-rib" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:ribs/rt:rib/rt:active-route" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/ospf:tag" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/ospf:route-type" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:ribs/rt:rib/rt:description" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/"
|
||||
+ "rt:next-hop/rt:next-hop-options/rt:next-hop-list/"
|
||||
+ "rt:next-hop-list/rt:next-hop" {
|
||||
description "IETF use 'active' for selected routes within a protocol (RIB).
|
||||
This augment adds an 'installed' leaf for marking routes that
|
||||
are installed in the kernel FIB with the given next-hop.";
|
||||
leaf installed {
|
||||
description "The presence of this leaf indicates that the route is
|
||||
installed in the kernel FIB with the given next-hop.";
|
||||
type empty;
|
||||
}
|
||||
}
|
||||
|
||||
/* OSPF */
|
||||
typedef infix-ospf-interface-type {
|
||||
type enumeration {
|
||||
enum broadcast {
|
||||
description
|
||||
"Specifies an OSPF broadcast multi-access network.";
|
||||
}
|
||||
enum point-to-point {
|
||||
description
|
||||
"Specifies an OSPF point-to-point network.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol" {
|
||||
deviate add {
|
||||
must "count(../control-plane-protocol[type = current()/type]) = 1" {
|
||||
error-message "Only one instance per routing protocol is allowed.";
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:type" {
|
||||
deviate add {
|
||||
must "derived-from-or-self(., 'ospf:ospfv2') or "+
|
||||
"derived-from-or-self(., 'rt:static')" {
|
||||
description "Only OSPFv2 and Static routes are supported in Infix.";
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf" {
|
||||
description "ietf-ospf lack the setting to generate a default route";
|
||||
container default-route-advertise {
|
||||
description "Distribute default route to network";
|
||||
leaf enable {
|
||||
status obsolete;
|
||||
description "Legacy, replaced by 'enabled'.";
|
||||
type boolean;
|
||||
}
|
||||
leaf enabled {
|
||||
description "Distribute default route";
|
||||
type boolean;
|
||||
}
|
||||
leaf always {
|
||||
description "Always distribute default route, even if no local default route exist.
|
||||
If set to false, a default route will only be distributed if a default route exist
|
||||
in the local routing table.";
|
||||
type boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf" {
|
||||
description "ietf-ospf does not contain redistribution
|
||||
7. How should route redistribution be configured? I see ietf-rip.yang has a separate
|
||||
container for that purpose, but ietf-ospf.yang (and other IGP modules) don't do the
|
||||
same. I also noticed the BGP model is using definition from ietf-routing-policy.yang.
|
||||
|
||||
Different vendors handle redistribution in different ways. This could be added with
|
||||
an augmentation if there were agreement.
|
||||
|
||||
https://marc.info/?l=ms-ospf&m=166258444409552&w=2";
|
||||
container redistribute {
|
||||
list redistribute {
|
||||
key "protocol";
|
||||
description "Redistribute protocols into OSPF";
|
||||
leaf protocol {
|
||||
type infix-distribute-protocol;
|
||||
description "Set protocol to redistribute";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface" {
|
||||
deviate add {
|
||||
must "current()/name = 'lo' or count(../../../../ospf:areas/ospf:area/ospf:interfaces/ospf:interface[ospf:name=current()/name]) <= 1" {
|
||||
error-message "Only one area per interface is allowed.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:interface-type" {
|
||||
deviate replace {
|
||||
type infix-ospf-interface-type;
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:auto-cost" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:graceful-restart" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:enabled" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:spf-control" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:preference" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:stub-router" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:topologies" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:statistics" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database-control" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:lsa-log" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:spf-log" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:fast-reroute" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:node-tags" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:mpls" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:nsr" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF Area */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:virtual-links" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:sham-links" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:ranges" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:database" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:topologies" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:statistics" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF Local RIB */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:local-rib/ospf:route/ospf:metric" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:local-rib/ospf:route/ospf:route-tag" {
|
||||
deviate not-supported;
|
||||
}
|
||||
/* OSPF Area Interface */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:static-neighbors"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:multi-areas" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:node-flag" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:bfd/ospf:local-multiplier"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:bfd/ospf:interval-config-type"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:priority"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:fast-reroute"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:authentication"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:statistics"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:neighbors/ospf:neighbor/ospf:statistics"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:neighbors/ospf:neighbor/ospf:cost"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:database"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:topologies"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:instance-id"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:interface-id"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:demand-circuit"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:ttl-security"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:lls"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:mtu-ignore"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:prefix-suppression"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:hello-timer"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:wait-timer"
|
||||
{
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF database */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv3" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:opaque" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:external" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:summary" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:body/ospf:router" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database/ospf:as-scope-lsa-type/ospf:as-scope-lsas/ospf:as-scope-lsa/ospf:version/ospf:ospfv2/ospf:ospfv2/ospf:header" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF RPCs */
|
||||
deviation "/ospf:clear-neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:clear-database" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* OSPF Notifications */
|
||||
deviation "/ospf:if-state-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:if-config-error" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:nbr-state-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:nbr-restart-helper-status-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:if-rx-bad-packet" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:lsdb-approaching-overflow" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:lsdb-overflow" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:nssa-translator-status-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ospf:restart-status-change" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -331,13 +331,15 @@
|
||||
</COMPL>
|
||||
</PARAM>
|
||||
<ACTION sym="script">
|
||||
if [ "$KLISH_PARAM_ip" != "ipv4" ]; then
|
||||
if [ "$KLISH_PARAM_ip" != "ipv6" ]; then
|
||||
KLISH_PARAM_ip=ipv4
|
||||
fi
|
||||
fi
|
||||
sysrepocfg -f json -X -d operational -x "/ietf-routing:routing/ribs" | \
|
||||
/usr/libexec/statd/cli-pretty "show-routing-table" -i "$KLISH_PARAM_ip"
|
||||
case $KLISH_PARAM_ip in
|
||||
ipv4)
|
||||
KLISH_PARAM_ip=ip
|
||||
;;
|
||||
ipv6)
|
||||
KLISH_PARAM_ip=ipv6
|
||||
;;
|
||||
esac
|
||||
show -p $KLISH_PARAM_ip route
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
@@ -383,6 +385,20 @@
|
||||
<COMMAND name="multicast" help="Show IGMP/MLD status" mode="switch">
|
||||
<ACTION sym="script">mctl</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="route" help="Show IPv4 routing table" mode="switch">
|
||||
<ACTION sym="script">show -p ip route</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="ipv6" help="Show IPv6 level status (Layer-3)">
|
||||
<ACTION sym="script">ip -color -6 addr</ACTION>
|
||||
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="route" help="Show IPv6 routing table" mode="switch">
|
||||
<ACTION sym="script">show -p ipv6 route</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import json
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
class Pad:
|
||||
iface = 16
|
||||
@@ -10,17 +12,33 @@ class Pad:
|
||||
state = 12
|
||||
data = 41
|
||||
|
||||
|
||||
class PadMdb:
|
||||
bridge = 7
|
||||
vlan = 6
|
||||
group = 20
|
||||
ports = 45
|
||||
|
||||
|
||||
class PadRoute:
|
||||
prefix = 30
|
||||
protocol = 10
|
||||
next_hop = 30
|
||||
dest = 30
|
||||
pref = 8
|
||||
next_hop = 30
|
||||
protocol = 6
|
||||
uptime = 9
|
||||
|
||||
@staticmethod
|
||||
def set(ipv):
|
||||
"""Set default padding based on the IP version ('ipv4' or 'ipv6')."""
|
||||
if ipv == 'ipv4':
|
||||
PadRoute.dest = 18
|
||||
PadRoute.next_hop = 15
|
||||
elif ipv == 'ipv6':
|
||||
PadRoute.dest = 43
|
||||
PadRoute.next_hop = 39
|
||||
else:
|
||||
raise ValueError(f"unknown IP version: {ipv}")
|
||||
|
||||
|
||||
class PadSoftware:
|
||||
name = 10
|
||||
@@ -35,6 +53,7 @@ class PadUsbPort:
|
||||
name = 20
|
||||
state = 10
|
||||
|
||||
|
||||
class Decore():
|
||||
@staticmethod
|
||||
def decorate(sgr, txt, restore="0"):
|
||||
@@ -60,6 +79,7 @@ class Decore():
|
||||
def underline(txt):
|
||||
return Decore.decorate("4", txt, "24")
|
||||
|
||||
|
||||
def get_json_data(default, indata, *args):
|
||||
data = indata
|
||||
for arg in args:
|
||||
@@ -76,22 +96,30 @@ def remove_yang_prefix(key):
|
||||
return parts[1]
|
||||
return key
|
||||
|
||||
|
||||
class Route:
|
||||
def __init__(self,data,ip):
|
||||
def __init__(self, data, ip):
|
||||
self.data = data
|
||||
self.ip = ip
|
||||
self.prefix = data.get(f'ietf-{ip}-unicast-routing:destination-prefix', '')
|
||||
self.protocol = data.get('source-protocol','')[14:]
|
||||
self.pref = data.get('route-preference','')
|
||||
self.protocol = data.get('source-protocol', '').split(':')[-1]
|
||||
self.last_updated = data.get('last-updated', '')
|
||||
self.active = data.get('active', False)
|
||||
self.pref = data.get('route-preference', '')
|
||||
self.metric = data.get('ietf-ospf:metric', 0)
|
||||
self.next_hop = []
|
||||
next_hop_list=get_json_data(None, self.data, 'next-hop', 'next-hop-list')
|
||||
next_hop_list = get_json_data(None, self.data, 'next-hop', 'next-hop-list')
|
||||
if next_hop_list:
|
||||
for nh in next_hop_list["next-hop"]:
|
||||
if(nh.get(f"ietf-{ip}-unicast-routing:address")):
|
||||
self.next_hop.append(nh[f"ietf-{ip}-unicast-routing:address"])
|
||||
elif(nh.get("outgoing-interface")):
|
||||
self.next_hop.append(nh["outgoing-interface"])
|
||||
if nh.get(f"ietf-{ip}-unicast-routing:address"):
|
||||
hop = nh[f"ietf-{ip}-unicast-routing:address"]
|
||||
elif nh.get("outgoing-interface"):
|
||||
hop = nh["outgoing-interface"]
|
||||
else:
|
||||
self.next_hop.append("unspecified")
|
||||
hop = "unspecified"
|
||||
|
||||
fib = nh.get('infix-routing:installed', False)
|
||||
self.next_hop.append((hop, fib))
|
||||
else:
|
||||
interface = get_json_data(None, self.data, 'next-hop', 'outgoing-interface')
|
||||
address = get_json_data(None, self.data, 'next-hop', f'ietf-{ip}-unicast-routing:next-hop-address')
|
||||
@@ -105,17 +133,64 @@ class Route:
|
||||
self.next_hop.append(special)
|
||||
else:
|
||||
self.next_hop.append("unspecified")
|
||||
|
||||
def get_distance_and_metric(self):
|
||||
if isinstance(self.pref, int):
|
||||
distance = self.pref
|
||||
metric = self.metric
|
||||
else:
|
||||
distance, metric = 0, 0
|
||||
|
||||
return distance, metric
|
||||
|
||||
def datetime2uptime(self):
|
||||
"""Convert 'last-updated' string to uptime in AAhBBmCCs format."""
|
||||
if not self.last_updated:
|
||||
return "0h0m0s"
|
||||
|
||||
# Replace the colon in the timezone offset (e.g., +00:00 -> +0000)
|
||||
pos = self.last_updated.rfind('+')
|
||||
if pos != -1:
|
||||
adjusted = self.last_updated[:pos] + self.last_updated[pos:].replace(':', '')
|
||||
else:
|
||||
adjusted = self.last_updated
|
||||
|
||||
last_updated = datetime.strptime(adjusted, '%Y-%m-%dT%H:%M:%S%z')
|
||||
current_time = datetime.now(timezone.utc)
|
||||
uptime_delta = current_time - last_updated
|
||||
|
||||
hours, remainder = divmod(int(uptime_delta.total_seconds()), 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
|
||||
return f"{hours}h{minutes}m{seconds}s"
|
||||
|
||||
def print(self):
|
||||
row = f"{self.prefix:<{PadRoute.prefix}}"
|
||||
row += f"{self.next_hop[0]:<{PadRoute.next_hop}}"
|
||||
row += f"{self.pref:>{PadRoute.pref}} "
|
||||
row += f"{self.protocol:<{PadRoute.protocol}}"
|
||||
PadRoute.set(self.ip)
|
||||
distance, metric = self.get_distance_and_metric()
|
||||
uptime = self.datetime2uptime()
|
||||
pref = f"{distance}/{metric}"
|
||||
hop, fib = self.next_hop[0]
|
||||
|
||||
row = ">" if self.active else " "
|
||||
row += "*" if fib else " "
|
||||
row += " "
|
||||
row += f"{self.prefix:<{PadRoute.dest}} "
|
||||
row += f"{pref:>{PadRoute.pref}} "
|
||||
row += f"{hop:<{PadRoute.next_hop}} "
|
||||
row += f"{self.protocol:<{PadRoute.protocol}} "
|
||||
row += f"{uptime:>{PadRoute.uptime}}"
|
||||
print(row)
|
||||
for nh in self.next_hop[1:]:
|
||||
row = f"{'':<{PadRoute.prefix}}"
|
||||
row += f"{nh:<{PadRoute.next_hop}}"
|
||||
hop, fib = nh
|
||||
row = " "
|
||||
row += "*" if fib else " "
|
||||
row += " "
|
||||
row += f"{'':<{PadRoute.dest}} "
|
||||
row += f"{'':>{PadRoute.pref}} "
|
||||
row += f"{hop:<{PadRoute.next_hop}} "
|
||||
print(row)
|
||||
|
||||
|
||||
class Software:
|
||||
"""Software bundle class """
|
||||
def __init__(self, data):
|
||||
@@ -431,6 +506,7 @@ class Iface:
|
||||
row += f"{ports}"
|
||||
print(row)
|
||||
|
||||
|
||||
def find_iface(_ifaces, name):
|
||||
for _iface in [Iface(data) for data in _ifaces]:
|
||||
if _iface.name == name:
|
||||
@@ -438,8 +514,11 @@ def find_iface(_ifaces, name):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def version_sort(iface):
|
||||
return [int(x) if x.isdigit() else x for x in re.split(r'(\d+)', iface['name'])]
|
||||
return [int(x) if x.isdigit() else x for x in re.split(r'(\d+)',
|
||||
iface['name'])]
|
||||
|
||||
|
||||
def print_interface(iface):
|
||||
iface.pr_name()
|
||||
@@ -447,6 +526,7 @@ def print_interface(iface):
|
||||
iface.pr_proto_ipv4()
|
||||
iface.pr_proto_ipv6()
|
||||
|
||||
|
||||
def pr_interface_list(json):
|
||||
hdr = (f"{'INTERFACE':<{Pad.iface}}"
|
||||
f"{'PROTOCOL':<{Pad.proto}}"
|
||||
@@ -455,8 +535,8 @@ def pr_interface_list(json):
|
||||
|
||||
print(Decore.invert(hdr))
|
||||
|
||||
ifaces = sorted(json["ietf-interfaces:interfaces"]["interface"], key=version_sort)
|
||||
|
||||
ifaces = sorted(json["ietf-interfaces:interfaces"]["interface"],
|
||||
key=version_sort)
|
||||
iface = find_iface(ifaces, "lo")
|
||||
if iface:
|
||||
print_interface(iface)
|
||||
@@ -484,6 +564,7 @@ def pr_interface_list(json):
|
||||
continue
|
||||
print_interface(iface)
|
||||
|
||||
|
||||
def show_interfaces(json, name):
|
||||
if name:
|
||||
if not json.get("ietf-interfaces:interfaces"):
|
||||
@@ -497,16 +578,19 @@ def show_interfaces(json, name):
|
||||
iface.pr_iface()
|
||||
else:
|
||||
if not json.get("ietf-interfaces:interfaces"):
|
||||
print(f"Error, top level \"ietf-interfaces:interfaces\" missing")
|
||||
print("Error, top level \"ietf-interfaces:interfaces\" missing")
|
||||
sys.exit(1)
|
||||
pr_interface_list(json)
|
||||
|
||||
|
||||
def show_bridge_mdb(json):
|
||||
header_printed = False
|
||||
if not json.get("ietf-interfaces:interfaces"):
|
||||
print(f"Error, top level \"ietf-interfaces:interface\" missing")
|
||||
print("Error, top level \"ietf-interfaces:interface\" missing")
|
||||
sys.exit(1)
|
||||
ifaces = sorted(json["ietf-interfaces:interfaces"]["interface"], key=version_sort)
|
||||
|
||||
ifaces = sorted(json["ietf-interfaces:interfaces"]["interface"],
|
||||
key=version_sort)
|
||||
for iface in [Iface(data) for data in ifaces]:
|
||||
if iface.type != "infix-if-type:bridge":
|
||||
continue
|
||||
@@ -520,25 +604,31 @@ def show_bridge_mdb(json):
|
||||
iface.pr_mdb(iface.name)
|
||||
iface.pr_vlans_mdb(iface.name)
|
||||
|
||||
|
||||
def show_routing_table(json, ip):
|
||||
if not json.get("ietf-routing:routing"):
|
||||
print(f"Error, top level \"ietf-routing:routing\" missing")
|
||||
print("Error, top level \"ietf-routing:routing\" missing")
|
||||
sys.exit(1)
|
||||
hdr = (f"{'PREFIX':<{PadRoute.prefix}}"
|
||||
f"{'NEXT-HOP':<{PadRoute.next_hop}}"
|
||||
f"{'PREF':>{PadRoute.pref}} "
|
||||
f"{'PROTOCOL':<{PadRoute.protocol}}")
|
||||
|
||||
PadRoute.set(ip)
|
||||
hdr = (f" {'DESTINATION':<{PadRoute.dest}} "
|
||||
f"{'PREF':>{PadRoute.pref}} "
|
||||
f"{'NEXT-HOP':<{PadRoute.next_hop}} "
|
||||
f"{'PROTO':<{PadRoute.protocol}} "
|
||||
f"{'UPTIME':>{PadRoute.uptime}}")
|
||||
|
||||
print(Decore.invert(hdr))
|
||||
for rib in get_json_data({}, json, 'ietf-routing:routing','ribs', 'rib'):
|
||||
for rib in get_json_data({}, json, 'ietf-routing:routing', 'ribs', 'rib'):
|
||||
if rib["name"] != ip:
|
||||
continue;
|
||||
continue
|
||||
|
||||
routes = get_json_data(None, rib, "routes", "route")
|
||||
if routes:
|
||||
for r in routes:
|
||||
route = Route(r, ip)
|
||||
route.print()
|
||||
|
||||
|
||||
def find_slot(_slots, name):
|
||||
for _slot in [Software(data) for data in _slots]:
|
||||
if _slot.name == name:
|
||||
@@ -546,6 +636,7 @@ def find_slot(_slots, name):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def show_software(json, name):
|
||||
if not json.get("ietf-system:system-state", "infix-system:software"):
|
||||
print("Error, cannot find infix-system:software")
|
||||
|
||||
@@ -6,21 +6,38 @@
|
||||
# This makes the parsing for the operational parts of YANG model more easy
|
||||
#
|
||||
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
def main():
|
||||
iface_out=subprocess.check_output("vtysh -c 'show ip ospf interface json'", shell=True)
|
||||
ospf_out=subprocess.check_output("vtysh -c 'show ip ospf json'", shell=True)
|
||||
neighbor_out=subprocess.check_output("vtysh -c 'show ip ospf neighbor detail json'", shell=True)
|
||||
try:
|
||||
interfaces=json.loads(iface_out)
|
||||
ospf=json.loads(ospf_out)
|
||||
neighbors=json.loads(neighbor_out)
|
||||
except json.JSONDecodeError:
|
||||
return {} # OSPFD is most likely not running
|
||||
|
||||
for ifname,iface in interfaces["interfaces"].items():
|
||||
def run_json_cmd(cmd, default=None, check=True):
|
||||
"""Run a command (array of args) with JSON output and return the JSON"""
|
||||
try:
|
||||
cmd.append("2>/dev/null")
|
||||
result = subprocess.run(cmd, check=check, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True)
|
||||
output = result.stdout
|
||||
data = json.loads(output)
|
||||
except (subprocess.CalledProcessError, json.JSONDecodeError):
|
||||
if default is not None:
|
||||
return default
|
||||
raise
|
||||
return data
|
||||
|
||||
|
||||
def main():
|
||||
ospf_interfaces = ['sudo', 'vtysh', '-c', "show ip ospf interface json"]
|
||||
ip_ospf = ['sudo', 'vtysh', '-c', "show ip ospf json"]
|
||||
ospf_neigh = ['sudo', 'vtysh', '-c', "show ip ospf neighbor detail json"]
|
||||
try:
|
||||
interfaces = run_json_cmd(ospf_interfaces)
|
||||
ospf = run_json_cmd(ip_ospf)
|
||||
neighbors = run_json_cmd(ospf_neigh)
|
||||
except (subprocess.CalledProcessError, json.JSONDecodeError):
|
||||
return {}
|
||||
|
||||
for ifname, iface in interfaces["interfaces"].items():
|
||||
iface["name"] = ifname
|
||||
iface["neighbors"] = []
|
||||
for area_id in ospf["areas"]:
|
||||
@@ -62,5 +79,7 @@ def main():
|
||||
|
||||
print(json.dumps(ospf))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
sys.exit(0)
|
||||
|
||||
@@ -6,7 +6,7 @@ import json
|
||||
import sys # (built-in module)
|
||||
import os
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
TESTPATH = ""
|
||||
logger = None
|
||||
@@ -137,8 +137,9 @@ def run_json_cmd(cmd, testfile, default=None, check=True):
|
||||
return default
|
||||
raise
|
||||
except json.JSONDecodeError as err:
|
||||
logger.error(f"failed parsing JSON output of command: {' '.join(cmd)}"
|
||||
f", error: {err}")
|
||||
if check is True:
|
||||
logger.error("failed parsing JSON output of command: "
|
||||
f"{' '.join(cmd)}, error: {err}")
|
||||
if default is not None:
|
||||
return default
|
||||
raise
|
||||
@@ -229,8 +230,29 @@ def add_hardware(hw_out):
|
||||
insert(hw_out, "component", components)
|
||||
|
||||
|
||||
def uptime2datetime(uptime):
|
||||
"""Convert uptime (HH:MM:SS) to YANG format (YYYY-MM-DDTHH:MM:SS+00:00)"""
|
||||
h, m, s = map(int, uptime.split(':'))
|
||||
uptime_delta = timedelta(hours=h, minutes=m, seconds=s)
|
||||
current_time = datetime.now(timezone.utc)
|
||||
last_updated = current_time - uptime_delta
|
||||
date_timestd = last_updated.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
|
||||
return date_timestd[:-2] + ':' + date_timestd[-2:]
|
||||
|
||||
|
||||
def get_routes(routes, proto, data):
|
||||
"""Populate routes"""
|
||||
"""Populate routes from vtysh JSON output"""
|
||||
|
||||
# Mapping of FRR protocol names to IETF routing-protocol
|
||||
pmap = {
|
||||
'kernel': 'infix-routing:kernel',
|
||||
'connected': 'direct',
|
||||
'static': 'static',
|
||||
'ospf': 'ietf-ospf:ospfv2',
|
||||
'ospf6': 'ietf-ospf:ospfv3',
|
||||
}
|
||||
|
||||
out = {}
|
||||
out["route"] = []
|
||||
|
||||
@@ -241,58 +263,75 @@ def get_routes(routes, proto, data):
|
||||
default = "::/0"
|
||||
host_prefix_length = "128"
|
||||
|
||||
for entry in data:
|
||||
new = {}
|
||||
if entry['dst'] == "default":
|
||||
entry['dst'] = default
|
||||
if entry['dst'].find('/') == -1:
|
||||
entry['dst'] = entry['dst'] + "/" + host_prefix_length
|
||||
new[f'ietf-{proto}-unicast-routing:destination-prefix'] = entry['dst']
|
||||
new['source-protocol'] = "infix-routing:" + entry['protocol']
|
||||
if entry.get("metric"):
|
||||
new['route-preference'] = entry['metric']
|
||||
else:
|
||||
new['route-preference'] = 0
|
||||
for prefix, entries in data.items():
|
||||
for route in entries:
|
||||
new = {}
|
||||
dst = route.get('prefix', default)
|
||||
if '/' not in dst:
|
||||
dst = f"{dst}/{route.get('prefixLen', host_prefix_length)}"
|
||||
|
||||
new[f'ietf-{proto}-unicast-routing:destination-prefix'] = dst
|
||||
frr = route.get('protocol', 'infix-routing:kernel')
|
||||
new['source-protocol'] = pmap.get(frr, 'infix-routing:kernel')
|
||||
new['route-preference'] = route.get('distance', 0)
|
||||
|
||||
# Metric only available in the model for OSPF routes
|
||||
if 'ospf' in frr:
|
||||
new['ietf-ospf:metric'] = route.get('metric', 0)
|
||||
|
||||
# See https://datatracker.ietf.org/doc/html/rfc7951#section-6.9
|
||||
# for details on how presence leaves are encoded in JSON: [null]
|
||||
if route.get('selected', False):
|
||||
new['active'] = [None]
|
||||
|
||||
new['last-updated'] = uptime2datetime(route.get('uptime', 0))
|
||||
installed = route.get('installed', False)
|
||||
|
||||
if entry.get('nexthops'):
|
||||
next_hops = []
|
||||
for hop in entry.get('nexthops'):
|
||||
for hop in route.get('nexthops', []):
|
||||
next_hop = {}
|
||||
if hop.get("dev"):
|
||||
next_hop['outgoing-interface'] = hop['dev']
|
||||
if hop.get("gateway"):
|
||||
next_hop[f'ietf-{proto}-unicast-routing:address'] = hop['gateway']
|
||||
if hop.get('ip'):
|
||||
next_hop[f'ietf-{proto}-unicast-routing:address'] = hop['ip']
|
||||
elif hop.get('interfaceName'):
|
||||
next_hop['outgoing-interface'] = hop['interfaceName']
|
||||
# See zebra/zebra_vty.c:re_status_outpupt_char()
|
||||
if installed and hop.get('fib', False):
|
||||
next_hop['infix-routing:installed'] = [None]
|
||||
next_hops.append(next_hop)
|
||||
|
||||
insert(new, 'next-hop', 'next-hop-list', 'next-hop', next_hops)
|
||||
else:
|
||||
next_hop = {}
|
||||
if entry['type'] == "blackhole":
|
||||
next_hop['special-next-hop'] = "blackhole"
|
||||
if entry['type'] == "unreachable":
|
||||
next_hop['special-next-hop'] = "unreachable"
|
||||
if entry['type'] == "unicast":
|
||||
if entry.get("dev"):
|
||||
next_hop['outgoing-interface'] = entry['dev']
|
||||
if entry.get("gateway"):
|
||||
next_hop[f'ietf-{proto}-unicast-routing:next-hop-address'] = entry['gateway']
|
||||
if next_hops:
|
||||
new['next-hop'] = {'next-hop-list': {'next-hop': next_hops}}
|
||||
else:
|
||||
next_hop = {}
|
||||
protocol = route.get('protocol', 'unicast')
|
||||
if protocol == "blackhole":
|
||||
next_hop['special-next-hop'] = "blackhole"
|
||||
elif protocol == "unreachable":
|
||||
next_hop['special-next-hop'] = "unreachable"
|
||||
else:
|
||||
if route.get('interfaceName'):
|
||||
next_hop['outgoing-interface'] = route['interfaceName']
|
||||
if route.get('nexthop'):
|
||||
next_hop[f'ietf-{proto}-unicast-routing:next-hop-address'] = route['nexthop']
|
||||
|
||||
new['next-hop'] = next_hop
|
||||
new['next-hop'] = next_hop
|
||||
|
||||
out['route'].append(new)
|
||||
out['route'].append(new)
|
||||
|
||||
insert(routes, 'routes', out)
|
||||
|
||||
|
||||
def add_ipv4_route(routes):
|
||||
"""Fetch IPv4 routes from kernel and populate tree"""
|
||||
data = run_json_cmd(['ip', '-4', '-s', '-d', '-j', 'route'], "ip-4-route.json")
|
||||
data = run_json_cmd(['sudo', 'vtysh', '-c', "show ip route json"],
|
||||
"vtysh-ip4-route.json", check=False, default={})
|
||||
get_routes(routes, "ipv4", data)
|
||||
|
||||
|
||||
def add_ipv6_route(routes):
|
||||
"""Fetch IPv6 routes from kernel and populate tree"""
|
||||
data = run_json_cmd(['ip', '-6', '-s', '-d', '-j', 'route'], "ip-6-route.json")
|
||||
data = run_json_cmd(['sudo', 'vtysh', '-c', "show ipv6 route json"],
|
||||
"vtysh-ip6-route.json", check=False, default={})
|
||||
get_routes(routes, "ipv6", data)
|
||||
|
||||
|
||||
@@ -307,9 +346,11 @@ def frr_to_ietf_neighbor_state(state):
|
||||
def add_ospf_routes(ospf):
|
||||
"""Fetch OSPF routes from Frr"""
|
||||
cmd = ['vtysh', '-c', "show ip ospf rout json"]
|
||||
data = run_json_cmd(cmd, "")
|
||||
routes = []
|
||||
data = run_json_cmd(cmd, "", check=False, default=[])
|
||||
if data == []:
|
||||
return # No OSPF routes available
|
||||
|
||||
routes = []
|
||||
for prefix, info in data.items():
|
||||
if prefix.find("/") == -1: # Ignore router IDs
|
||||
continue
|
||||
@@ -348,8 +389,7 @@ def add_ospf_routes(ospf):
|
||||
def add_ospf(control_protocols):
|
||||
"""Populate OSPF status"""
|
||||
cmd = ['/usr/libexec/statd/ospf-status']
|
||||
data = run_json_cmd(cmd, "")
|
||||
|
||||
data = run_json_cmd(cmd, "", check=False, default={})
|
||||
if data == {}:
|
||||
return # No OSPF data available
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
[7mPREFIX NEXT-HOP PREF PROTOCOL [0m
|
||||
10.0.0.1/32 10.0.13.1 20 ospf
|
||||
10.0.0.2/32 10.0.23.1 20 ospf
|
||||
10.0.0.4/32 10.0.13.1 20 ospf
|
||||
10.0.23.1
|
||||
10.0.12.0/30 10.0.13.1 20 ospf
|
||||
10.0.23.1
|
||||
10.0.13.0/30 eth3 0 kernel
|
||||
10.0.23.0/30 eth4 0 kernel
|
||||
10.0.24.0/30 10.0.23.1 20 ospf
|
||||
10.0.41.0/30 10.0.13.1 20 ospf
|
||||
192.168.4.0/24 10.0.13.1 20 ospf
|
||||
10.0.23.1
|
||||
[7m DESTINATION PREF NEXT-HOP PROTO UPTIME[0m
|
||||
>* 0.0.0.0/0 110/2 10.0.23.1 ospfv2 0h0m0s
|
||||
>* 10.0.0.1/32 110/4000 10.0.13.1 ospfv2 0h0m0s
|
||||
10.0.0.3/32 110/0 lo ospfv2 0h0m0s
|
||||
>* 10.0.0.3/32 0/0 lo direct 0h0m0s
|
||||
10.0.13.0/30 110/2000 e5 ospfv2 0h0m0s
|
||||
>* 10.0.13.0/30 0/0 e5 direct 0h0m0s
|
||||
10.0.23.0/30 110/1 e6 ospfv2 0h0m0s
|
||||
>* 10.0.23.0/30 0/0 e6 direct 0h0m0s
|
||||
192.168.3.0/24 110/1 e2 ospfv2 0h0m0s
|
||||
>* 192.168.3.0/24 0/0 e2 direct 0h0m0s
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
[7mPREFIX NEXT-HOP PREF PROTOCOL [0m
|
||||
2001:db8:3c4d:50::/64 eth4 256 kernel
|
||||
2001:db8:3c4d:200::1/128 lo 256 kernel
|
||||
fe80::/64 eth5 256 kernel
|
||||
fe80::/64 eth3 256 kernel
|
||||
fe80::/64 eth2 256 kernel
|
||||
fe80::/64 eth1 256 kernel
|
||||
fe80::/64 eth0 256 kernel
|
||||
fe80::/64 eth4 256 kernel
|
||||
::/0 2001:db8:3c4d:50::1 20 static
|
||||
[7m DESTINATION PREF NEXT-HOP PROTO UPTIME[0m
|
||||
>* ::/0 1/0 2001:db8:3c4d:50::1 static 0h0m0s
|
||||
>* 2001:db8:3c4d:50::/64 0/0 e6 direct 0h0m0s
|
||||
>* 2001:db8:3c4d:200::1/128 0/0 lo direct 0h0m0s
|
||||
* fe80::/64 0/0 e7 direct 0h0m0s
|
||||
* fe80::/64 0/0 e6 direct 0h0m0s
|
||||
* fe80::/64 0/0 e5 direct 0h0m0s
|
||||
* fe80::/64 0/0 e4 direct 0h0m0s
|
||||
* fe80::/64 0/0 e3 direct 0h0m0s
|
||||
* fe80::/64 0/0 e2 direct 0h0m0s
|
||||
>* fe80::/64 0/0 e1 direct 0h0m0s
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
[
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.0.1",
|
||||
"nhid": 33,
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 33,
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"scope": "link",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.0.2",
|
||||
"nhid": 34,
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 34,
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"scope": "link",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.0.4",
|
||||
"nhid": 35,
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 35,
|
||||
"group": [
|
||||
{
|
||||
"id": 33
|
||||
},
|
||||
{
|
||||
"id": 34
|
||||
}
|
||||
],
|
||||
"scope": "global",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
},
|
||||
"nexthops": [
|
||||
{
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"weight": 1,
|
||||
"flags": []
|
||||
},
|
||||
{
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"weight": 1,
|
||||
"flags": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.12.0/30",
|
||||
"nhid": 35,
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 35,
|
||||
"group": [
|
||||
{
|
||||
"id": 33
|
||||
},
|
||||
{
|
||||
"id": 34
|
||||
}
|
||||
],
|
||||
"scope": "global",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
},
|
||||
"nexthops": [
|
||||
{
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"weight": 1,
|
||||
"flags": []
|
||||
},
|
||||
{
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"weight": 1,
|
||||
"flags": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.13.0/30",
|
||||
"dev": "eth3",
|
||||
"protocol": "kernel",
|
||||
"scope": "link",
|
||||
"prefsrc": "10.0.13.2",
|
||||
"flags": []
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.23.0/30",
|
||||
"dev": "eth4",
|
||||
"protocol": "kernel",
|
||||
"scope": "link",
|
||||
"prefsrc": "10.0.23.2",
|
||||
"flags": []
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.24.0/30",
|
||||
"nhid": 34,
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 34,
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"scope": "link",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "10.0.41.0/30",
|
||||
"nhid": 33,
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 33,
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"scope": "link",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "unicast",
|
||||
"dst": "192.168.4.0/24",
|
||||
"nhid": 35,
|
||||
"protocol": "ospf",
|
||||
"scope": "global",
|
||||
"metric": 20,
|
||||
"flags": [],
|
||||
"nh_info": {
|
||||
"id": 35,
|
||||
"group": [
|
||||
{
|
||||
"id": 33
|
||||
},
|
||||
{
|
||||
"id": 34
|
||||
}
|
||||
],
|
||||
"scope": "global",
|
||||
"protocol": "zebra",
|
||||
"flags": []
|
||||
},
|
||||
"nexthops": [
|
||||
{
|
||||
"gateway": "10.0.13.1",
|
||||
"dev": "eth3",
|
||||
"weight": 1,
|
||||
"flags": []
|
||||
},
|
||||
{
|
||||
"gateway": "10.0.23.1",
|
||||
"dev": "eth4",
|
||||
"weight": 1,
|
||||
"flags": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
[{"type":"unicast","dst":"2001:db8:3c4d:50::/64","dev":"eth4","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"2001:db8:3c4d:200::1","dev":"lo","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"fe80::/64","dev":"eth5","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"fe80::/64","dev":"eth3","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"fe80::/64","dev":"eth2","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"fe80::/64","dev":"eth1","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"fe80::/64","dev":"eth0","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"fe80::/64","dev":"eth4","protocol":"kernel","scope":"global","metric":256,"flags":[],"pref":"medium"},{"type":"unicast","dst":"default","nhid":23,"gateway":"2001:db8:3c4d:50::1","dev":"eth4","protocol":"static","scope":"global","metric":20,"flags":[],"pref":"medium","nh_info":{"id":23,"gateway":"2001:db8:3c4d:50::1","dev":"eth4","scope":"link","protocol":"zebra","flags":[]}}]
|
||||
@@ -0,0 +1,302 @@
|
||||
{
|
||||
"0.0.0.0/0":[
|
||||
{
|
||||
"prefix":"0.0.0.0/0",
|
||||
"prefixLen":0,
|
||||
"protocol":"ospf",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":110,
|
||||
"metric":2,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":42,
|
||||
"installedNexthopGroupId":42,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"ip":"10.0.23.1",
|
||||
"afi":"ipv4",
|
||||
"interfaceIndex":7,
|
||||
"interfaceName":"e6",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.0.0.1/32":[
|
||||
{
|
||||
"prefix":"10.0.0.1/32",
|
||||
"prefixLen":32,
|
||||
"protocol":"ospf",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":110,
|
||||
"metric":4000,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":43,
|
||||
"installedNexthopGroupId":43,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"ip":"10.0.13.1",
|
||||
"afi":"ipv4",
|
||||
"interfaceIndex":6,
|
||||
"interfaceName":"e5",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.0.0.3/32":[
|
||||
{
|
||||
"prefix":"10.0.0.3/32",
|
||||
"prefixLen":32,
|
||||
"protocol":"ospf",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":110,
|
||||
"metric":0,
|
||||
"table":254,
|
||||
"internalStatus":0,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":30,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":1,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":1,
|
||||
"interfaceName":"lo",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.0.3/32",
|
||||
"prefixLen":32,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":25,
|
||||
"installedNexthopGroupId":25,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":1,
|
||||
"interfaceName":"lo",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.0.13.0/30":[
|
||||
{
|
||||
"prefix":"10.0.13.0/30",
|
||||
"prefixLen":30,
|
||||
"protocol":"ospf",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":110,
|
||||
"metric":2000,
|
||||
"table":254,
|
||||
"internalStatus":0,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":31,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":1,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":6,
|
||||
"interfaceName":"e5",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.13.0/30",
|
||||
"prefixLen":30,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":20,
|
||||
"installedNexthopGroupId":20,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":6,
|
||||
"interfaceName":"e5",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.0.23.0/30":[
|
||||
{
|
||||
"prefix":"10.0.23.0/30",
|
||||
"prefixLen":30,
|
||||
"protocol":"ospf",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":110,
|
||||
"metric":1,
|
||||
"table":254,
|
||||
"internalStatus":0,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":32,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":1,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":7,
|
||||
"interfaceName":"e6",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.23.0/30",
|
||||
"prefixLen":30,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":23,
|
||||
"installedNexthopGroupId":23,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":7,
|
||||
"interfaceName":"e6",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"192.168.3.0/24":[
|
||||
{
|
||||
"prefix":"192.168.3.0/24",
|
||||
"prefixLen":24,
|
||||
"protocol":"ospf",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":110,
|
||||
"metric":1,
|
||||
"table":254,
|
||||
"internalStatus":0,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":33,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":1,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":3,
|
||||
"interfaceName":"e2",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"192.168.3.0/24",
|
||||
"prefixLen":24,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":17,
|
||||
"installedNexthopGroupId":17,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":3,
|
||||
"interfaceName":"e2",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
{
|
||||
"::/0":[
|
||||
{
|
||||
"prefix":"::/0",
|
||||
"prefixLen":0,
|
||||
"protocol":"static",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":1,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":73,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":24,
|
||||
"installedNexthopGroupId":24,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"ip":"2001:db8:3c4d:50::1",
|
||||
"afi":"ipv6",
|
||||
"interfaceIndex":7,
|
||||
"interfaceName":"e6",
|
||||
"active":true,
|
||||
"weight":1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"2001:db8:3c4d:50::/64":[
|
||||
{
|
||||
"prefix":"2001:db8:3c4d:50::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":15,
|
||||
"installedNexthopGroupId":15,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":7,
|
||||
"interfaceName":"e6",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"2001:db8:3c4d:200::1/128":[
|
||||
{
|
||||
"prefix":"2001:db8:3c4d:200::1/128",
|
||||
"prefixLen":128,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":13,
|
||||
"installedNexthopGroupId":13,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":1,
|
||||
"interfaceName":"lo",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"fe80::/64":[
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":14,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":8,
|
||||
"interfaceName":"e7",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":15,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":7,
|
||||
"interfaceName":"e6",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":16,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":6,
|
||||
"interfaceName":"e5",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":17,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":5,
|
||||
"interfaceName":"e4",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":18,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":4,
|
||||
"interfaceName":"e3",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":0,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":19,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":3,
|
||||
"interfaceName":"e2",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"prefix":"fe80::/64",
|
||||
"prefixLen":64,
|
||||
"protocol":"connected",
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"selected":true,
|
||||
"destSelected":true,
|
||||
"distance":0,
|
||||
"metric":0,
|
||||
"installed":true,
|
||||
"table":254,
|
||||
"internalStatus":16,
|
||||
"internalFlags":8,
|
||||
"internalNextHopNum":1,
|
||||
"internalNextHopActiveNum":1,
|
||||
"nexthopGroupId":20,
|
||||
"installedNexthopGroupId":20,
|
||||
"uptime":"00:00:00",
|
||||
"nexthops":[
|
||||
{
|
||||
"flags":3,
|
||||
"fib":true,
|
||||
"directlyConnected":true,
|
||||
"interfaceIndex":2,
|
||||
"interfaceName":"e1",
|
||||
"active":true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
=== IPv4 linklocal
|
||||
=== IPv4 link-local
|
||||
==== Description
|
||||
Verifies that linklocal (ZeroConf) work as expected
|
||||
Verifies that link-local (IPv4LL/ZeroConf) address assignment work as
|
||||
expected. Checks random address, the request-address setting, and
|
||||
address removal on autoconf disable.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::../../test/case/ietf_interfaces/ipv4_autoconf/topology.png[IPv4 linklocal topology]
|
||||
image::../../test/case/ietf_interfaces/ipv4_autoconf/topology.png[IPv4 link-local topology]
|
||||
endif::topdoc[]
|
||||
ifndef::topdoc[]
|
||||
ifdef::testgroup[]
|
||||
image::ipv4_autoconf/topology.png[IPv4 linklocal topology]
|
||||
image::ipv4_autoconf/topology.png[IPv4 link-local topology]
|
||||
endif::testgroup[]
|
||||
ifndef::testgroup[]
|
||||
image::topology.png[IPv4 linklocal topology]
|
||||
image::topology.png[IPv4 link-local topology]
|
||||
endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Initialize
|
||||
. Configure an interface with IPv4 ZeroConf IP
|
||||
. Wait for linklocal address on interface
|
||||
. Verify link local address exist on target:mgmt
|
||||
. Configure target:mgmt with a specific IPv4 ZeroConf IP
|
||||
. Verify target:mgmt has link local address 169.254.42.42
|
||||
. Remove IPv4 link-local addresses from target:mgmt
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -3,24 +3,46 @@
|
||||
# PC ---- e0: 10.0.0.2
|
||||
#
|
||||
"""
|
||||
IPv4 linklocal
|
||||
IPv4 link-local
|
||||
|
||||
Verifies that linklocal (ZeroConf) work as expected
|
||||
Verifies that link-local (IPv4LL/ZeroConf) address assignment work as
|
||||
expected. Checks random address, the request-address setting, and
|
||||
address removal on autoconf disable.
|
||||
"""
|
||||
import ipaddress
|
||||
import infamy
|
||||
import time
|
||||
import infamy.iface
|
||||
|
||||
from infamy.util import until
|
||||
|
||||
def has_linklocal(target, iface):
|
||||
"""Check if interface as a linklocal address"""
|
||||
|
||||
def has_linklocal(target, iface, request=None):
|
||||
"""Check if interface as a link-local address"""
|
||||
addrs = infamy.iface.get_ipv4_address(target, iface)
|
||||
if not addrs:
|
||||
return False
|
||||
for addr in addrs:
|
||||
if addr['origin'] == "random":
|
||||
return True
|
||||
if request is None:
|
||||
print(f"Got IP address {addr['ip']}")
|
||||
return True
|
||||
if addr['ip'] == request:
|
||||
print(f"Got requested IP address {addr['ip']}")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def no_linklocal(target, iface):
|
||||
"""Check if interface has no link-local address in the 169.254/16 range"""
|
||||
addrs = infamy.iface.get_ipv4_address(target, iface)
|
||||
if not addrs:
|
||||
return True
|
||||
for addr in addrs:
|
||||
ip = ipaddress.IPv4Address(addr['ip'])
|
||||
if ip in ipaddress.IPv4Network('169.254.0.0/16'):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Initialize"):
|
||||
@@ -46,7 +68,37 @@ with infamy.Test() as test:
|
||||
}
|
||||
})
|
||||
|
||||
with test.step("Wait for linklocal address on interface"):
|
||||
until(lambda: has_linklocal(target, tport), attempts=30)
|
||||
with test.step("Verify link local address exist on target:mgmt"):
|
||||
until(lambda: has_linklocal(target, tport), attempts=30)
|
||||
|
||||
with test.step("Configure target:mgmt with a specific IPv4 ZeroConf IP"):
|
||||
_, tport = env.ltop.xlate("target", "data")
|
||||
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": tport,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"autoconf": {
|
||||
"enabled": True,
|
||||
"request-address": "169.254.42.42"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
with test.step("Verify target:mgmt has link local address 169.254.42.42"):
|
||||
until(lambda: has_linklocal(target, tport, request="169.254.42.42"),
|
||||
attempts=30)
|
||||
|
||||
with test.step("Remove IPv4 link-local addresses from target:mgmt"):
|
||||
xpath = f"/ietf-interfaces:interfaces/interface[name='{tport}']" \
|
||||
"/ietf-ip:ipv4/infix-ip:autoconf"
|
||||
target.delete_xpath(xpath)
|
||||
until(lambda: no_linklocal(target, tport), attempts=30)
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -171,8 +171,8 @@ with infamy.Test() as test:
|
||||
config_target2(R2, R2link))
|
||||
with test.step("Wait for OSPF routes"):
|
||||
print("Waiting for OSPF routes..")
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
|
||||
with test.step("Test connectivity from PC:data to 192.168.200.1"):
|
||||
_, hport0 = env.ltop.xlate("PC", "data")
|
||||
|
||||
@@ -197,30 +197,30 @@ def config_target2(target, ring1, ring2, cross):
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "10.0.0.2",
|
||||
"prefix-length": 32
|
||||
},{
|
||||
"ip": "11.0.9.1",
|
||||
"prefix-length": 24
|
||||
},{
|
||||
"ip": "11.0.10.1",
|
||||
"prefix-length": 24
|
||||
},{
|
||||
"ip": "11.0.11.1",
|
||||
"prefix-length": 24
|
||||
},{
|
||||
"ip": "11.0.12.1",
|
||||
"prefix-length": 24
|
||||
},{
|
||||
"ip": "11.0.13.1",
|
||||
"prefix-length": 24
|
||||
},{
|
||||
"ip": "11.0.14.1",
|
||||
"prefix-length": 24
|
||||
},{
|
||||
"ip": "11.0.15.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
"ip": "10.0.0.2",
|
||||
"prefix-length": 32
|
||||
}, {
|
||||
"ip": "11.0.9.1",
|
||||
"prefix-length": 24
|
||||
}, {
|
||||
"ip": "11.0.10.1",
|
||||
"prefix-length": 24
|
||||
}, {
|
||||
"ip": "11.0.11.1",
|
||||
"prefix-length": 24
|
||||
}, {
|
||||
"ip": "11.0.12.1",
|
||||
"prefix-length": 24
|
||||
}, {
|
||||
"ip": "11.0.13.1",
|
||||
"prefix-length": 24
|
||||
}, {
|
||||
"ip": "11.0.14.1",
|
||||
"prefix-length": 24
|
||||
}, {
|
||||
"ip": "11.0.15.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -243,7 +243,7 @@ def config_target2(target, ring1, ring2, cross):
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.0",
|
||||
"interfaces":{
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
@@ -251,32 +251,30 @@ def config_target2(target, ring1, ring2, cross):
|
||||
"name": ring2,
|
||||
"hello-interval": 1,
|
||||
"enabled": True
|
||||
},{
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"area-id": "0.0.0.1",
|
||||
"area-type": "nssa-area",
|
||||
"summary": False,
|
||||
"interfaces":{
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
},
|
||||
"name": ring1,
|
||||
"hello-interval": 1,
|
||||
},{
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"area-id": "0.0.0.2",
|
||||
"interfaces":{
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
@@ -284,7 +282,7 @@ def config_target2(target, ring1, ring2, cross):
|
||||
"name": cross,
|
||||
"hello-interval": 1,
|
||||
"cost": 2000
|
||||
},{
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True
|
||||
}]
|
||||
@@ -297,6 +295,7 @@ def config_target2(target, ring1, ring2, cross):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_target3(target, ring2, cross, link):
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
@@ -366,7 +365,7 @@ def config_target3(target, ring2, cross, link):
|
||||
"area-id": "0.0.0.1",
|
||||
"area-type": "nssa-area",
|
||||
"summary": False,
|
||||
"interfaces":{
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
@@ -375,18 +374,18 @@ def config_target3(target, ring2, cross, link):
|
||||
"hello-interval": 1,
|
||||
"enabled": True,
|
||||
"cost": 2000
|
||||
},{
|
||||
}, {
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
},
|
||||
"name": ring2,
|
||||
"hello-interval": 1,
|
||||
"enabled": True
|
||||
},{
|
||||
}, {
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"passive": True
|
||||
},{
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True
|
||||
}]
|
||||
@@ -399,6 +398,7 @@ def config_target3(target, ring2, cross, link):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_target4(target, ring1, cross, link):
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
@@ -429,8 +429,8 @@ def config_target4(target, ring1, cross, link):
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.4.1",
|
||||
"prefix-length": 24
|
||||
"ip": "192.168.4.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
},
|
||||
@@ -439,8 +439,8 @@ def config_target4(target, ring1, cross, link):
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "10.0.0.4",
|
||||
"prefix-length": 32
|
||||
"ip": "10.0.0.4",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -459,48 +459,48 @@ def config_target4(target, ring1, cross, link):
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [
|
||||
{
|
||||
"type": "infix-routing:ospfv2",
|
||||
"name": "default",
|
||||
"ospf": {
|
||||
"redistribute": {
|
||||
"redistribute": [
|
||||
{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.2",
|
||||
"interfaces":{
|
||||
"interface": [{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
},
|
||||
"name": ring1,
|
||||
"hello-interval": 1,
|
||||
"enabled": True
|
||||
},{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
},
|
||||
"name": cross,
|
||||
"hello-interval": 1,
|
||||
"enabled": True,
|
||||
"cost": 5000
|
||||
},{
|
||||
"name": "lo",
|
||||
"enabled": True
|
||||
"type": "infix-routing:ospfv2",
|
||||
"name": "default",
|
||||
"ospf": {
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
}
|
||||
}]
|
||||
},
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.2",
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
},
|
||||
"name": ring1,
|
||||
"hello-interval": 1,
|
||||
"enabled": True
|
||||
}, {
|
||||
"bfd": {
|
||||
"enabled": True
|
||||
},
|
||||
"name": cross,
|
||||
"hello-interval": 1,
|
||||
"enabled": True,
|
||||
"cost": 5000
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def disable_link(target, link):
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
@@ -512,6 +512,7 @@ def disable_link(target, link):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Configure targets"):
|
||||
env = infamy.Env()
|
||||
@@ -520,7 +521,6 @@ with infamy.Test() as test:
|
||||
R3 = env.attach("R3", "mgmt")
|
||||
R4 = env.attach("R4", "mgmt")
|
||||
|
||||
|
||||
_, R1ring1 = env.ltop.xlate("R1", "ring1")
|
||||
_, R1ring2 = env.ltop.xlate("R1", "ring2")
|
||||
_, R2ring1 = env.ltop.xlate("R2", "ring1")
|
||||
@@ -528,17 +528,18 @@ with infamy.Test() as test:
|
||||
_, R3ring2 = env.ltop.xlate("R3", "ring2")
|
||||
_, R4ring1 = env.ltop.xlate("R4", "ring1")
|
||||
|
||||
_, R3data = env.ltop.xlate("R3", "data")
|
||||
_, R4data = env.ltop.xlate("R4", "data")
|
||||
_, R3data = env.ltop.xlate("R3", "data")
|
||||
_, R4data = env.ltop.xlate("R4", "data")
|
||||
|
||||
_, R1cross = env.ltop.xlate("R1", "cross")
|
||||
_, R2cross = env.ltop.xlate("R2", "cross")
|
||||
_, R3cross = env.ltop.xlate("R3", "cross")
|
||||
_, R4cross = env.ltop.xlate("R4", "cross")
|
||||
_, R1cross = env.ltop.xlate("R1", "cross")
|
||||
_, R2cross = env.ltop.xlate("R2", "cross")
|
||||
_, R3cross = env.ltop.xlate("R3", "cross")
|
||||
_, R4cross = env.ltop.xlate("R4", "cross")
|
||||
parallel(config_target1(R1, R1ring1, R1ring2, R1cross),
|
||||
config_target2(R2, R2ring1, R2ring2, R2cross),
|
||||
config_target3(R3, R3ring2, R3cross, R3data),
|
||||
config_target4(R4, R4ring1, R4cross, R4data))
|
||||
|
||||
with test.step("Wait for all neighbor to peer"):
|
||||
print("Waiting for neighbors to peer")
|
||||
until(lambda: route.ospf_get_neighbor(R1, "0.0.0.0", R1ring1, "10.0.0.2"), attempts=200)
|
||||
@@ -549,33 +550,34 @@ with infamy.Test() as test:
|
||||
|
||||
with test.step("Wait for routes from OSPF on all routers"):
|
||||
print("Waiting for routes from OSPF")
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.0.2/32", nexthop="10.0.12.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.0.3/32", nexthop="10.0.13.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.0.4/32", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.4.0/24", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.24.0/30", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.0.1/32", nexthop="10.0.23.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.0.3/32", nexthop="10.0.23.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.0.4/32", nexthop="10.0.24.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R3, "0.0.0.0/0", nexthop="10.0.23.1", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.41.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.13.0/30", nexthop="10.0.23.2", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.0.2/32", nexthop="10.0.12.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.0.3/32", nexthop="10.0.13.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.0.4/32", nexthop="10.0.41.1", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.4.0/24", nexthop="10.0.41.1", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "10.0.24.0/30", nexthop="10.0.41.1", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.0.1/32", nexthop="10.0.23.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.0.3/32", nexthop="10.0.23.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.0.4/32", nexthop="10.0.24.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R3, "0.0.0.0/0", nexthop="10.0.23.1", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.41.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "10.0.13.0/30", nexthop="10.0.23.2", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
|
||||
with test.step("Verify Area 0.0.0.1 on R3 is NSSA area"):
|
||||
assert(route.ospf_is_area_nssa(R3, "0.0.0.1"))
|
||||
|
||||
with test.step("Verify on R3, there are no routes beyond 10.0.23.1, just a default route"): # Should be only default route out of the area.
|
||||
parallel(until(lambda: route.ipv4_route_exist(R3, "0.0.0.0/0"), attempts=200),
|
||||
until(lambda: route.ipv4_route_exist(R3, "10.0.12.0/30") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "10.0.12.0/30") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.8.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.9.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.10.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.11.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.12.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.13.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.14.0/24") == False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.15.0/24") == False, attempts=5))
|
||||
with test.step("Verify on R3, there are no routes beyond 10.0.23.1, just a default route"):
|
||||
# Should be only default route out of the area.
|
||||
parallel(until(lambda: route.ipv4_route_exist(R3, "0.0.0.0/0"), attempts=200),
|
||||
until(lambda: route.ipv4_route_exist(R3, "10.0.12.0/30") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "10.0.12.0/30") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.8.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.9.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.10.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.11.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.12.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.13.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.14.0/24") is False, attempts=5),
|
||||
until(lambda: route.ipv4_route_exist(R3, "11.0.15.0/24") is False, attempts=5))
|
||||
|
||||
_, hport0 = env.ltop.xlate("PC", "data3")
|
||||
with infamy.IsolatedMacVlan(hport0) as ns0:
|
||||
@@ -589,21 +591,23 @@ with infamy.Test() as test:
|
||||
ns0.addip("192.168.4.2")
|
||||
ns0.addroute("0.0.0.0/0", "192.168.4.1")
|
||||
with test.step("Verify that the route to 10.0.0.3 from PC:data4, go through 10.0.41.2"):
|
||||
trace=ns0.traceroute("10.0.0.3")
|
||||
assert(len(trace) == 3)
|
||||
assert(trace[1][1] == "10.0.41.2")
|
||||
assert(trace[2][1] == "10.0.0.3")
|
||||
trace = ns0.traceroute("10.0.0.3")
|
||||
assert len(trace) == 3
|
||||
assert trace[1][1] == "10.0.41.2"
|
||||
assert trace[2][1] == "10.0.0.3"
|
||||
|
||||
with test.step("Break link R1:ring2 --- R4:ring1"):
|
||||
disable_link(R1, R1ring2) # Here we should test with link breakers, to test BFD recouppling, for now disable the link
|
||||
# Here we should test with link breakers, to test BFD
|
||||
# recouppling, for now disable the link
|
||||
disable_link(R1, R1ring2)
|
||||
|
||||
with test.step("Verify that the route to 10.0.0.3 from PC:data4, go through 10.0.24.1"):
|
||||
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.24.1", source_protocol = "infix-routing:ospf"), attempts=100)
|
||||
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.41.2") == False, attempts = 10)
|
||||
trace=ns0.traceroute("10.0.0.3")
|
||||
assert(len(trace) == 3)
|
||||
assert(trace[1][1] == "10.0.24.1")
|
||||
assert(trace[2][1] == "10.0.0.3")
|
||||
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.24.1", proto="ietf-ospf:ospfv2"), attempts=100)
|
||||
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.41.2") is False, attempts=10)
|
||||
trace = ns0.traceroute("10.0.0.3")
|
||||
assert len(trace) == 3
|
||||
assert trace[1][1] == "10.0.24.1"
|
||||
assert trace[2][1] == "10.0.0.3"
|
||||
ns0.must_reach("10.0.0.3")
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -37,8 +37,8 @@ def config_target1(target, data, link):
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "10.0.0.1",
|
||||
"prefix-length": 32
|
||||
"ip": "10.0.0.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
},
|
||||
@@ -47,8 +47,8 @@ def config_target1(target, data, link):
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.100.1",
|
||||
"prefix-length": 32
|
||||
"ip": "192.168.100.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -73,12 +73,10 @@ def config_target1(target, data, link):
|
||||
"hello-interval": 1,
|
||||
"dead-interval": 3,
|
||||
"interface-type": "point-to-point"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"name": data,
|
||||
"passive": True
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"name": "lo",
|
||||
"passive": True
|
||||
}]
|
||||
@@ -91,6 +89,7 @@ def config_target1(target, data, link):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_target2(target, link):
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
@@ -112,8 +111,8 @@ def config_target2(target, link):
|
||||
"forwarding": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.200.1",
|
||||
"prefix-length": 32
|
||||
"ip": "192.168.200.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -126,33 +125,33 @@ def config_target2(target, link):
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [
|
||||
{
|
||||
"type": "infix-routing:ospfv2",
|
||||
"name": "default",
|
||||
"ospf": {
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.0",
|
||||
"interfaces":{
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"hello-interval": 1,
|
||||
"dead-interval": 3,
|
||||
"interface-type": "point-to-point"
|
||||
},
|
||||
{
|
||||
"name": "lo",
|
||||
"passive": True
|
||||
}]
|
||||
}
|
||||
}]
|
||||
"type": "infix-routing:ospfv2",
|
||||
"name": "default",
|
||||
"ospf": {
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.0",
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"hello-interval": 1,
|
||||
"dead-interval": 3,
|
||||
"interface-type": "point-to-point"
|
||||
}, {
|
||||
"name": "lo",
|
||||
"passive": True
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Configure targets"):
|
||||
env = infamy.Env()
|
||||
@@ -167,9 +166,9 @@ with infamy.Test() as test:
|
||||
lambda: config_target2(R2, R2link))
|
||||
with test.step("Wait for OSPF routes"):
|
||||
print("Waiting for OSPF routes..")
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.10.0/24", source_protocol = "infix-routing:ospf"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.10.0/24", proto="ietf-ospf:ospfv2"), attempts=200)
|
||||
|
||||
with test.step("Check interface type"):
|
||||
assert(route.ospf_get_interface_type(R1, "0.0.0.0", R1link) == "point-to-point")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
=== Static routing
|
||||
==== Description
|
||||
Test that it is possible to use static routes (IPv4 and IPv6)
|
||||
works as expected
|
||||
Verify that it is possible to add static routes, both IPv4 and IPv6, and
|
||||
that data forwarding works as expected via an intermediate device.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
@@ -19,7 +19,7 @@ endif::topdoc[]
|
||||
. Configure targets
|
||||
. Wait for routes
|
||||
. Configure host addresses and routes
|
||||
. Verify that dut2 is reachable on 192.168.200.1 from PC:data
|
||||
. Verify that R2 is reachable on 192.168.200.1 from PC:data
|
||||
. Verify that R2 is reachable on 2001:db8:3c4d:200::1 from PC:data
|
||||
. Remove all static routes from R1
|
||||
. Verify R2 is no longer reachable on either IPv4 or IPv6 from PC:data
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# |
|
||||
# +--------------+ +-------------+
|
||||
# | |192.168.50.0/24 | |
|
||||
# | DUT1 .1 +----------------+ .2 DUT2 |-192.168.200.1/32 (lo)
|
||||
# | R1 .1 +----------------+ .2 R2 |-192.168.200.1/32 (lo)
|
||||
# | .1 | | .1 |
|
||||
# +--+------+----+ +---+-----+---+
|
||||
# | | |
|
||||
@@ -18,75 +18,73 @@
|
||||
"""
|
||||
Static routing
|
||||
|
||||
Test that it is possible to use static routes (IPv4 and IPv6)
|
||||
works as expected
|
||||
Verify that it is possible to add static routes, both IPv4 and IPv6, and
|
||||
that data forwarding works as expected via an intermediate device.
|
||||
"""
|
||||
import copy
|
||||
import infamy
|
||||
import time
|
||||
import infamy.iface as iface
|
||||
import infamy.route as route
|
||||
from infamy.util import until, parallel
|
||||
|
||||
|
||||
def config_target1(target, data, link):
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.10.1",
|
||||
"prefix-length": 24
|
||||
}]},
|
||||
"ipv6": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "2001:db8:3c4d:10::1",
|
||||
"prefix-length": 64
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.10.1",
|
||||
"prefix-length": 24
|
||||
}]},
|
||||
"ipv6": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "2001:db8:3c4d:10::1",
|
||||
"prefix-length": 64
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
},
|
||||
"ipv6": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "2001:db8:3c4d:50::1",
|
||||
"prefix-length": 64
|
||||
}]
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ipv6": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "2001:db8:3c4d:50::1",
|
||||
"prefix-length": 64
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.100.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
},
|
||||
"ipv6": {
|
||||
"address": [{
|
||||
"ip": "2001:db8:3c4d:100::1",
|
||||
"prefix-length": 128
|
||||
}]
|
||||
}
|
||||
}]
|
||||
},
|
||||
"ipv6": {
|
||||
"address": [{
|
||||
"ip": "2001:db8:3c4d:100::1",
|
||||
"prefix-length": 128
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
||||
target.put_config_dict("ietf-routing", {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
@@ -104,10 +102,10 @@ def config_target1(target, data, link):
|
||||
},
|
||||
"ipv6": {
|
||||
"route": [{
|
||||
"destination-prefix": "2001:db8:3c4d:200::1/128",
|
||||
"next-hop": {
|
||||
"next-hop-address": "2001:db8:3c4d:50::2"
|
||||
}
|
||||
"destination-prefix": "2001:db8:3c4d:200::1/128",
|
||||
"next-hop": {
|
||||
"next-hop-address": "2001:db8:3c4d:50::2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -115,6 +113,8 @@ def config_target1(target, data, link):
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_target2(target, link):
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
@@ -143,8 +143,8 @@ def config_target2(target, link):
|
||||
"forwarding": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.200.1",
|
||||
"prefix-length": 32
|
||||
"ip": "192.168.200.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
},
|
||||
"ipv6": {
|
||||
@@ -168,18 +168,18 @@ def config_target2(target, link):
|
||||
"static-routes": {
|
||||
"ipv4": {
|
||||
"route": [{
|
||||
"destination-prefix": "0.0.0.0/0",
|
||||
"next-hop": {
|
||||
"next-hop-address": "192.168.50.1"
|
||||
}
|
||||
}]
|
||||
"destination-prefix": "0.0.0.0/0",
|
||||
"next-hop": {
|
||||
"next-hop-address": "192.168.50.1"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"ipv6": {
|
||||
"route": [{
|
||||
"destination-prefix": "::/0",
|
||||
"next-hop": {
|
||||
"next-hop-address": "2001:db8:3c4d:50::1"
|
||||
}
|
||||
"destination-prefix": "::/0",
|
||||
"next-hop": {
|
||||
"next-hop-address": "2001:db8:3c4d:50::1"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -188,6 +188,7 @@ def config_target2(target, link):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Configure targets"):
|
||||
env = infamy.Env()
|
||||
@@ -210,21 +211,21 @@ with infamy.Test() as test:
|
||||
_, hport0 = env.ltop.xlate("PC", "data")
|
||||
with infamy.IsolatedMacVlan(hport0) as ns0:
|
||||
with test.step("Configure host addresses and routes"):
|
||||
ns0.addip("2001:db8:3c4d:10::2", prefix_length=64, proto="ipv6")
|
||||
ns0.addroute("2001:db8:3c4d:200::1/128", "2001:db8:3c4d:10::1", proto="ipv6")
|
||||
ns0.addip("192.168.10.2")
|
||||
ns0.addroute("192.168.200.1/32", "192.168.10.1")
|
||||
ns0.addip("2001:db8:3c4d:10::2", prefix_length=64, proto="ipv6")
|
||||
ns0.addroute("2001:db8:3c4d:200::1/128", "2001:db8:3c4d:10::1", proto="ipv6")
|
||||
ns0.addip("192.168.10.2")
|
||||
ns0.addroute("192.168.200.1/32", "192.168.10.1")
|
||||
|
||||
with test.step("Verify that dut2 is reachable on 192.168.200.1 from PC:data"):
|
||||
ns0.must_reach("192.168.200.1")
|
||||
with test.step("Verify that R2 is reachable on 192.168.200.1 from PC:data"):
|
||||
ns0.must_reach("192.168.200.1")
|
||||
|
||||
with test.step("Verify that R2 is reachable on 2001:db8:3c4d:200::1 from PC:data"):
|
||||
ns0.must_reach("2001:db8:3c4d:200::1")
|
||||
ns0.must_reach("2001:db8:3c4d:200::1")
|
||||
|
||||
with test.step("Remove all static routes from R1"):
|
||||
R1.delete_xpath("/ietf-routing:routing/control-plane-protocols")
|
||||
parallel(until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32") == False),
|
||||
until(lambda: route.ipv6_route_exist(R1, "2001:db8:3c4d:200::1/128") == False))
|
||||
parallel(until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32") is False),
|
||||
until(lambda: route.ipv6_route_exist(R1, "2001:db8:3c4d:200::1/128") is False))
|
||||
|
||||
with test.step("Verify R2 is no longer reachable on either IPv4 or IPv6 from PC:data"):
|
||||
infamy.parallel(ns0.must_not_reach("192.168.200.1"),
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
=== DHCP router
|
||||
=== DHCP option 121 vs option 3
|
||||
==== Description
|
||||
Verify DHCP option 121 (staticroutes) is used over option 3 and that the routes exist in
|
||||
the operational datastore
|
||||
Verify DHCP option 121 (staticroutes) is used over option 3 and that the
|
||||
routes exist in the operational datastore.
|
||||
|
||||
Installing unrelated routes from a DHCP server should not affect already
|
||||
existing routes. To verify this a canary route is set up in the client
|
||||
before initiating DHCP. This canary route does not need to be reachable
|
||||
before a DHCP lease has been acquired.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::../../test/case/infix_dhcp/dhcp_routes/topology.png[DHCP router topology]
|
||||
image::../../test/case/infix_dhcp/dhcp_routes/topology.png[DHCP option 121 vs option 3 topology]
|
||||
endif::topdoc[]
|
||||
ifndef::topdoc[]
|
||||
ifdef::testgroup[]
|
||||
image::dhcp_routes/topology.png[DHCP router topology]
|
||||
image::dhcp_routes/topology.png[DHCP option 121 vs option 3 topology]
|
||||
endif::testgroup[]
|
||||
ifndef::testgroup[]
|
||||
image::topology.png[DHCP router topology]
|
||||
image::topology.png[DHCP option 121 vs option 3 topology]
|
||||
endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Initialize
|
||||
. Setting up client
|
||||
. Verify client use classless routes, option 121
|
||||
. Verify client did *not* use option 3
|
||||
. Verify client has canary route, 20.0.0.0/24
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -1,47 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
# Verify DHCP option 121 (staticroutes) is used over option 3
|
||||
"""
|
||||
DHCP router
|
||||
DHCP option 121 vs option 3
|
||||
|
||||
Verify DHCP option 121 (staticroutes) is used over option 3 and that the routes exist in
|
||||
the operational datastore
|
||||
Verify DHCP option 121 (staticroutes) is used over option 3 and that the
|
||||
routes exist in the operational datastore.
|
||||
|
||||
Installing unrelated routes from a DHCP server should not affect already
|
||||
existing routes. To verify this a canary route is set up in the client
|
||||
before initiating DHCP. This canary route does not need to be reachable
|
||||
before a DHCP lease has been acquired.
|
||||
"""
|
||||
import time
|
||||
import infamy, infamy.dhcp
|
||||
import infamy.iface as iface
|
||||
import infamy
|
||||
import infamy.dhcp
|
||||
import infamy.route as route
|
||||
from infamy.util import until
|
||||
|
||||
with infamy.Test() as test:
|
||||
PREFIX = '10.0.0.0/24'
|
||||
ROUTER = '192.168.0.254'
|
||||
with test.step("Initialize"):
|
||||
PREFIX = '10.0.0.0/24'
|
||||
ROUTER = '192.168.0.254'
|
||||
CANARY = '20.0.0.0/24'
|
||||
CANHOP = '192.168.0.2'
|
||||
|
||||
with test.step("Setting up client"):
|
||||
env = infamy.Env()
|
||||
client = env.attach("client", "mgmt")
|
||||
_, host = env.ltop.xlate("host", "data")
|
||||
_, port = env.ltop.xlate("client", "data")
|
||||
|
||||
# Install canary route to smoke out any regressions in
|
||||
# how the DHCP client installs routes in the kernel FIB
|
||||
client.put_config_dict("ietf-routing", {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:static",
|
||||
"name": "default",
|
||||
"static-routes": {
|
||||
"ipv4": {
|
||||
"route": [{
|
||||
"destination-prefix": CANARY,
|
||||
"next-hop": {
|
||||
"next-hop-address": CANHOP
|
||||
},
|
||||
"infix-routing:route-preference": 250
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
client.put_config_dict("infix-dhcp-client", {
|
||||
"dhcp-client": {
|
||||
"client-if": [{
|
||||
"if-name": f"{port}",
|
||||
"option": [
|
||||
{"name": "router"},
|
||||
{"name": "staticroutes"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
with infamy.IsolatedMacVlan(host) as netns:
|
||||
netns.addip("192.168.0.1")
|
||||
with infamy.dhcp.Server(netns, prefix=PREFIX, router=ROUTER):
|
||||
_, port = env.ltop.xlate("client", "data")
|
||||
config = {
|
||||
"dhcp-client": {
|
||||
"client-if": [{
|
||||
"if-name": f"{port}",
|
||||
"option": [
|
||||
{ "name": "router" },
|
||||
{ "name": "staticroutes" }
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
client.put_config_dict("infix-dhcp-client", config)
|
||||
|
||||
with test.step(f"Verify client sets up correct route via {ROUTER}"):
|
||||
# Wait for client to set the classless static route, option 121
|
||||
with test.step("Verify client use classless routes, option 121"):
|
||||
until(lambda: route.ipv4_route_exist(client, PREFIX, ROUTER))
|
||||
# Ensure client did *not* use option 3 (option 121 takes precedence)
|
||||
|
||||
with test.step("Verify client did *not* use option 3"):
|
||||
if route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER):
|
||||
test.fail()
|
||||
|
||||
with test.step("Verify client has canary route, 20.0.0.0/24"):
|
||||
until(lambda: route.ipv4_route_exist(client, CANARY,
|
||||
CANHOP, pref=250))
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
Verify that basic services like mDNS and LLDP can be enabled and
|
||||
disabled. We verify operation and non-operation by using tcpdump.
|
||||
|
||||
XXX: with socat in the Docker container we could speed up the LLDP
|
||||
detection considerably by sending a probe:
|
||||
|
||||
echo -ne '\x01\x80\xc2\x00\x00\x0e\x01\x80\xc2\x00\x00\x0e\x88\xcc\x02\x07\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | socat - UDP4-DATAGRAM:255.255.255.255:7010,broadcast
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::../../test/case/infix_services/services_basic/topology.png[Services basic topology]
|
||||
@@ -24,7 +19,7 @@ endif::topdoc[]
|
||||
. Initialize
|
||||
. Set static IPv4 address and disable services
|
||||
. Start sniffer and enable services on target ...
|
||||
. Disable services on target, verify they're not running anymore ...
|
||||
. Disable services on target, verify no longer running ...
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -1,32 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Verify that basic services like mDNS and LLDP can be enabled and
|
||||
# disabled. We verify operation and non-operation by using tcpdump.
|
||||
#
|
||||
# XXX: with socat in the Docker container we could speed up the LLDP
|
||||
# detection considerably by sending a probe:
|
||||
#
|
||||
# echo -ne "\x01\x80\xc2\x00\x00\x0e\x01\x80\xc2\x00\x00\x0e\x88\xcc\x02\x07\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | socat - UDP4-DATAGRAM:255.255.255.255:7010,broadcast
|
||||
#
|
||||
"""
|
||||
Services basic
|
||||
|
||||
Verify that basic services like mDNS and LLDP can be enabled and
|
||||
disabled. We verify operation and non-operation by using tcpdump.
|
||||
|
||||
XXX: with socat in the Docker container we could speed up the LLDP
|
||||
detection considerably by sending a probe:
|
||||
|
||||
echo -ne '\\x01\\x80\\xc2\\x00\\x00\\x0e\\x01\\x80\\xc2\\x00\\x00\\x0e\\x88\\xcc\\x02\\x07\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00' | socat - UDP4-DATAGRAM:255.255.255.255:7010,broadcast
|
||||
|
||||
"""
|
||||
import time
|
||||
import infamy
|
||||
|
||||
|
||||
def toggle(updown):
|
||||
"""Toggle port down/up to kick services"""
|
||||
_, port = env.ltop.xlate("target", "data")
|
||||
act = "UP" if updown else "DOWN"
|
||||
|
||||
print(f"target: taking interface {port} {act} ...")
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
@@ -38,38 +26,46 @@ def toggle(updown):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def verify(enabled, sec):
|
||||
"""Verify service traffic, or no traffic in case service not enabled"""
|
||||
_, hport = env.ltop.xlate("host", "data")
|
||||
|
||||
with infamy.IsolatedMacVlan(hport) as netns:
|
||||
snif = infamy.Sniffer(netns, "port 5353 or ether proto 0x88cc")
|
||||
act = "enabling" if enabled else "disabling"
|
||||
|
||||
netns.addip("10.0.0.1")
|
||||
netns.addroute("0.0.0.0/0", "10.0.0.1")
|
||||
|
||||
# Put service enable/disable before starting tcpdump, because
|
||||
# LLDP lingers and will send a final shutdown message that
|
||||
# otherwise would get in the capture for disable.
|
||||
target.put_config_dict("infix-services", {
|
||||
"mdns": {
|
||||
"enabled": enabled
|
||||
}
|
||||
})
|
||||
target.put_config_dict("ieee802-dot1ab-lldp", {
|
||||
"lldp": {
|
||||
"enabled": enabled
|
||||
}
|
||||
})
|
||||
|
||||
# Prevent old-state traffic from being captured before reconf
|
||||
toggle(False)
|
||||
|
||||
with snif:
|
||||
time.sleep(1)
|
||||
print("host: collecting network traffic ...")
|
||||
|
||||
# Put service enable/disable before starting tcpdump,
|
||||
# because LLDP lingers and will send a final shutdown
|
||||
# message that otherwise would get in the capture for
|
||||
# disable.
|
||||
print(f"target: {act} LLDP and mDNS services ...")
|
||||
target.put_config_dict("infix-services", {
|
||||
"mdns": {
|
||||
"enabled": enabled
|
||||
}
|
||||
})
|
||||
target.put_config_dict("ieee802-dot1ab-lldp", {
|
||||
"lldp": {
|
||||
"enabled": enabled
|
||||
}
|
||||
})
|
||||
|
||||
toggle(True)
|
||||
time.sleep(sec - 1)
|
||||
time.sleep(sec)
|
||||
|
||||
return snif.output()
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Initialize"):
|
||||
env = infamy.Env()
|
||||
@@ -109,16 +105,16 @@ with infamy.Test() as test:
|
||||
|
||||
with test.step("Start sniffer and enable services on target ..."):
|
||||
rc = verify(True, 10)
|
||||
#print(rc.stdout)
|
||||
print(rc.stdout)
|
||||
# breakpoint()
|
||||
if "10.0.0.10.5353" not in rc.stdout:
|
||||
test.fail()
|
||||
if "LLDP" not in rc.stdout:
|
||||
test.fail()
|
||||
|
||||
with test.step("Disable services on target, verify they're not running anymore ..."):
|
||||
with test.step("Disable services on target, verify no longer running ..."):
|
||||
rc = verify(False, 10)
|
||||
#print(rc.stdout)
|
||||
print(rc.stdout)
|
||||
if "10.0.0.10.5353" in rc.stdout:
|
||||
test.fail()
|
||||
if "LLDP" in rc.stdout:
|
||||
|
||||
+12
-4
@@ -287,8 +287,8 @@ class Device(Transport):
|
||||
def get_data(self, xpath=None, parse=True):
|
||||
"""RPC <get-data> to fetch operational data"""
|
||||
|
||||
if parse==False:
|
||||
return self._get_data(xpath,parse)
|
||||
if parse is False:
|
||||
return self._get_data(xpath, parse)
|
||||
|
||||
data = self._get_data(xpath)
|
||||
if not data:
|
||||
@@ -391,28 +391,36 @@ class Device(Transport):
|
||||
def get_iface(self, name):
|
||||
"""Fetch target dict for iface and extract param from JSON"""
|
||||
content = self.get_data(iface.get_iface_xpath(name))
|
||||
interface=content.get("interfaces", {}).get("interface", None)
|
||||
interface = content.get("interfaces", {}).get("interface", None)
|
||||
|
||||
if interface is None:
|
||||
return None
|
||||
|
||||
# Restconf (rousette) address by id and netconf (netopper2) address by name
|
||||
# Restconf (rousette) address by id, otherwise (netopper2) by name
|
||||
return interface[name]
|
||||
|
||||
def delete_xpath(self, xpath):
|
||||
# Split out the model and the container from xpath'
|
||||
pattern = r"^/(?P<module>[^:]+):(?P<path>[^/]+)"
|
||||
match = re.search(pattern, xpath)
|
||||
if not match:
|
||||
raise ValueError(f"Failed parsing xpath:{xpath}")
|
||||
|
||||
module = match.group('module')
|
||||
modpath = f"/{match.group('module')}:{match.group('path')}"
|
||||
|
||||
old = self.get_config_dict(modpath)
|
||||
new = copy.deepcopy(old)
|
||||
|
||||
libyang.xpath_del(new, xpath)
|
||||
|
||||
mod = self.ly.get_module(module)
|
||||
oldd = mod.parse_data_dict(old, no_state=True)
|
||||
newd = mod.parse_data_dict(new, no_state=True)
|
||||
|
||||
lyd = oldd.diff(newd)
|
||||
if lyd is None:
|
||||
raise ValueError(f"Failed generating diff for xpath:{xpath}")
|
||||
|
||||
return self.put_config(lyd.print_mem("xml", with_siblings=True,
|
||||
pretty=False))
|
||||
|
||||
+63
-33
@@ -1,92 +1,122 @@
|
||||
"""
|
||||
IETF Routing helper methods
|
||||
"""
|
||||
|
||||
|
||||
def _get_routes(target, protocol):
|
||||
xpath="/ietf-routing:routing/ribs"
|
||||
xpath = "/ietf-routing:routing/ribs"
|
||||
rib = target.get_data(xpath)["routing"]["ribs"]["rib"]
|
||||
for r in rib:
|
||||
if r["name"] != protocol:
|
||||
continue
|
||||
return r.get("routes", {}).get("route",{})
|
||||
return r.get("routes", {}).get("route", {})
|
||||
return {}
|
||||
|
||||
def _exist_route(target, prefix, nexthop, version, source_protocol):
|
||||
routes = _get_routes(target, version)
|
||||
route_found = False
|
||||
|
||||
def _exist_route(target, dest, nexthop=None, ip=None, proto=None, pref=None):
|
||||
routes = _get_routes(target, ip)
|
||||
for r in routes:
|
||||
# netconf presents destination-prefix, restconf prefix with model
|
||||
p=r.get("destination-prefix") or (version == "ipv4" and r.get("ietf-ipv4-unicast-routing:destination-prefix")) or (version == "ipv6" and r.get("ietf-ipv6-unicast-routing:destination-prefix"))
|
||||
if p != prefix:
|
||||
dst = r.get("destination-prefix") or \
|
||||
r.get(f"ietf-{ip}-unicast-routing:destination-prefix")
|
||||
if dst != dest:
|
||||
continue
|
||||
|
||||
if source_protocol and r.get("source-protocol") != source_protocol:
|
||||
if proto is not None and r.get("source-protocol") != proto:
|
||||
return False
|
||||
|
||||
route_found = True
|
||||
nh = r["next-hop"]
|
||||
next_hop_list = nh.get("next-hop-list")
|
||||
if nexthop:
|
||||
if pref is not None and r.get("route-preference") != pref:
|
||||
return False
|
||||
|
||||
if nexthop is not None:
|
||||
nh = r["next-hop"]
|
||||
if not nh:
|
||||
return False
|
||||
|
||||
next_hop_list = nh.get("next-hop-list")
|
||||
if next_hop_list:
|
||||
for nhl in next_hop_list["next-hop"]:
|
||||
address = nhl.get("address")
|
||||
# netconf presents address, restconf prefix with
|
||||
# model ietf-ipv4-unicast-routing:address
|
||||
address = nhl.get("address") or \
|
||||
nhl.get(f"ietf-{ip}-unicast-routing:address")
|
||||
if address == nexthop:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
# netconf presents next-hop-address, restconf prefix with model ietf-ipv4-unicast-routing:next-hop-address
|
||||
nh_addr=nh.get("next-hop-address", None) or (version == "ipv4" and nh.get("ietf-ipv4-unicast-routing:next-hop-address", None)) or (version == "ipv6" and nh.get("ietf-ipv6-unicast-routing:next-hop-address", None))
|
||||
# netconf presents next-hop-address, restconf prefix
|
||||
# with model ietf-ipv4-unicast-routing:next-hop-address
|
||||
nh_addr = nh.get("next-hop-address") or \
|
||||
nh.get(f"ietf-{ip}-unicast-routing:next-hop-address")
|
||||
if nh_addr == nexthop:
|
||||
return True
|
||||
return False
|
||||
return route_found
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def ipv4_route_exist(target, prefix, nexthop=None,source_protocol=None):
|
||||
return _exist_route(target, prefix, nexthop, "ipv4",source_protocol)
|
||||
|
||||
def ipv6_route_exist(target, prefix, nexthop=None,source_protocol=None):
|
||||
return _exist_route(target, prefix, nexthop, "ipv6",source_protocol)
|
||||
def ipv4_route_exist(target, dest, nexthop=None, proto=None, pref=None):
|
||||
return _exist_route(target, dest, nexthop=nexthop, ip="ipv4", proto=proto, pref=pref)
|
||||
|
||||
|
||||
def ipv6_route_exist(target, dest, nexthop=None, proto=None, pref=None):
|
||||
return _exist_route(target, dest, nexthop=nexthop, ip="ipv6", proto=proto, pref=pref)
|
||||
|
||||
|
||||
def _get_ospf_status(target):
|
||||
xpath="/ietf-routing:routing/control-plane-protocols"
|
||||
rib = target.get_data(xpath)["routing"]["control-plane-protocols"].get("control-plane-protocol", {})
|
||||
xpath = "/ietf-routing:routing/control-plane-protocols"
|
||||
protos = target.get_data(xpath)["routing"]["control-plane-protocols"]
|
||||
rib = protos.get("control-plane-protocol", {})
|
||||
for p in rib:
|
||||
if p["type"] == "infix-routing:ospfv2":
|
||||
return p.get("ospf") or p.get("ietf-ospf:ospf")
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def _get_ospf_status_area(target, area_id):
|
||||
ospf=_get_ospf_status(target)
|
||||
ospf = _get_ospf_status(target)
|
||||
for area in ospf.get("areas", {}).get("area", {}):
|
||||
if area["area-id"] == area_id:
|
||||
return area
|
||||
|
||||
return {}
|
||||
|
||||
def _get_ospf_status_area_interface(target, area_id, ifname):
|
||||
area=_get_ospf_status_area(target,area_id)
|
||||
|
||||
def _get_ospf_status_area_interface(target, area_id, ifname):
|
||||
area = _get_ospf_status_area(target, area_id)
|
||||
for interface in area.get("interfaces", {}).get("interface", {}):
|
||||
if interface.get("name") == ifname:
|
||||
return interface
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def ospf_get_neighbor(target, area_id, ifname, neighbour_id, full=True):
|
||||
ospf_interface=_get_ospf_status_area_interface(target,area_id, ifname)
|
||||
ospf_interface = _get_ospf_status_area_interface(target, area_id, ifname)
|
||||
for neighbor in ospf_interface.get("neighbors", {}).get("neighbor", {}):
|
||||
if neighbor.get("neighbor-router-id") == neighbour_id:
|
||||
if full == False:
|
||||
if full is False:
|
||||
return True
|
||||
if(neighbor.get("state") == "full"):
|
||||
if neighbor.get("state") == "full":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def ospf_get_interface_type(target, area_id, ifname):
|
||||
ospf_interface=_get_ospf_status_area_interface(target,area_id,ifname)
|
||||
ospf_interface = _get_ospf_status_area_interface(target, area_id, ifname)
|
||||
return ospf_interface.get("interface-type", None)
|
||||
|
||||
|
||||
def ospf_get_interface_passive(target, area_id, ifname):
|
||||
ospf_interface=_get_ospf_status_area_interface(target,area_id,ifname)
|
||||
ospf_interface = _get_ospf_status_area_interface(target, area_id, ifname)
|
||||
return ospf_interface.get("passive", False)
|
||||
|
||||
def ospf_is_area_nssa(target, area_id):
|
||||
area=_get_ospf_status_area(target, area_id)
|
||||
|
||||
def ospf_is_area_nssa(target, area_id):
|
||||
area = _get_ospf_status_area(target, area_id)
|
||||
if area.get("area-type", "") == "ietf-ospf:nssa-area":
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user