]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
ifupdown2 2.0.0-1
authorJulien Fortin <julien@cumulusnetworks.com>
Tue, 17 Dec 2019 00:04:54 +0000 (01:04 +0100)
committerJulien Fortin <julien@cumulusnetworks.com>
Tue, 17 Dec 2019 12:46:18 +0000 (13:46 +0100)
Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
69 files changed:
.gitignore
debian/changelog
debian/control
debian/ifup@.service [new file with mode: 0644]
debian/ifupdown2.install
debian/ifupdown2.networking.service
debian/networking.init [deleted file]
debian/rules
debian/watch [new file with mode: 0644]
etc/default/networking [deleted file]
etc/init.d/networking [new file with mode: 0644]
etc/network/ifupdown2/addons.conf
etc/network/ifupdown2/ifupdown2.conf
ifupdown2/__init__.py
ifupdown2/__main__.py
ifupdown2/addons/address.py
ifupdown2/addons/addressvirtual.py
ifupdown2/addons/batman_adv.py
ifupdown2/addons/bond.py
ifupdown2/addons/bridge.py
ifupdown2/addons/bridgevlan.py
ifupdown2/addons/dhcp.py
ifupdown2/addons/ethtool.py
ifupdown2/addons/link.py
ifupdown2/addons/mstpctl.py
ifupdown2/addons/ppp.py
ifupdown2/addons/tunnel.py
ifupdown2/addons/vlan.py
ifupdown2/addons/vrf.py
ifupdown2/addons/vxlan.py
ifupdown2/addons/xfrm.py
ifupdown2/ifupdown/argv.py
ifupdown2/ifupdown/client.py [new file with mode: 0644]
ifupdown2/ifupdown/exceptions.py
ifupdown2/ifupdown/iface.py
ifupdown2/ifupdown/iff.py [deleted file]
ifupdown2/ifupdown/ifupdownbase.py [deleted file]
ifupdown2/ifupdown/ifupdownmain.py
ifupdown2/ifupdown/log.py [deleted file]
ifupdown2/ifupdown/main.py
ifupdown2/ifupdown/netlink.py [deleted file]
ifupdown2/ifupdown/networkinterfaces.py
ifupdown2/ifupdown/policymanager.py
ifupdown2/ifupdown/scheduler.py
ifupdown2/ifupdown/statemanager.py
ifupdown2/ifupdown/utils.py
ifupdown2/ifupdownaddons/LinkUtils.py [deleted file]
ifupdown2/ifupdownaddons/cache.py
ifupdown2/ifupdownaddons/dhclient.py
ifupdown2/ifupdownaddons/mstpctlutil.py
ifupdown2/lib/__init__.py [new file with mode: 0644]
ifupdown2/lib/addon.py [new file with mode: 0644]
ifupdown2/lib/base_objects.py [new file with mode: 0644]
ifupdown2/lib/dry_run.py [new file with mode: 0644]
ifupdown2/lib/exceptions.py [new file with mode: 0644]
ifupdown2/lib/io.py [new file with mode: 0644]
ifupdown2/lib/iproute2.py [new file with mode: 0644]
ifupdown2/lib/log.py [new file with mode: 0644]
ifupdown2/lib/nlcache.py [new file with mode: 0644]
ifupdown2/lib/status.py [new file with mode: 0644]
ifupdown2/lib/sysfs.py [new file with mode: 0644]
ifupdown2/man/ifdown.8.rst
ifupdown2/man/ifup.8.rst
ifupdown2/nlmanager/nllistener.py
ifupdown2/nlmanager/nlmanager.py
ifupdown2/nlmanager/nlpacket.py
ifupdown2/sbin/ifupdown2d [deleted file]
ifupdown2/sbin/start-networking [deleted file]
setup.py

index bd4b76624a484d61634a8b9af8f9441d25e326d0..60a10f6c15763cdb9af4d8d697d191929b3d232e 100644 (file)
@@ -16,7 +16,7 @@ dist/
 downloads/
 eggs/
 .eggs/
-lib/
+/lib/
 lib64/
 parts/
 sdist/
index 3ff53e750ffc4dc370a4f2e2962dff3493e1705c..b64932ed61f3874b22e7fbb21174ead6e793bdf9 100644 (file)
@@ -1,8 +1,22 @@
-ifupdown2 (1.2.9-1) unstable; urgency=medium
-
-  *
-
- -- Julien Fortin <julien@cumulusnetworks.com>  Thu, 11 Jul 2019 23:42:42 +0200
+ifupdown2 (2.0.0-1) unstable; urgency=medium
+
+  * Introduction of the live netlink cache
+  * Refactoring and PEP8 fixes
+  * Install systemd ifupdown2.netowrking.service and ifup.service
+  * Addons: bond: bond-primary attributes (closes: #9)
+  * Addons: address-virtual: vrrp support
+  * Addons: address: add arp-accept option
+  * Addons: tunnel: adding "tunnel-" prefix to every attributes
+  * Loopback interface won't go down (unless link-down yes)
+  * Macvlans (address-virtual) can now be configured without ips
+  * Add support for vxlan multicast group (vxlan-mcastgrp)
+  * New sets of poliicies:
+       - bridge polcy for vxlan port: bridge-vxlan-arp-nd-suppres (ON/off)
+       - bridge policy for vxlan port: bridge_vxlan_port_learning (ON/off)
+       - bridge policy for vxlan port:
+               vxlan_bridge_igmp_snooping_enable_port_mcrouter (1/0)
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Tue, 01 Oct 2019 23:42:42 +0200
 
 ifupdown2 (1.2.8-1) unstable; urgency=medium
 
index dd1a0b2c13d6d58a7c8455362a225ebbd29d5f17..7809864ca0c32c5805fb354c041296cf4719aabd 100644 (file)
@@ -2,13 +2,15 @@ Source: ifupdown2
 Section: admin
 Priority: optional
 Maintainer: Julien Fortin <julien@cumulusnetworks.com>
-Build-Depends: debhelper (>= 9.20160709),
+Build-Depends: debhelper (>=9),
+               dh-systemd,
                dh-python,
                python-all,
                python-setuptools,
                python-docutils
 Standards-Version: 4.2.1
 Homepage: https://github.com/cumulusnetworks/ifupdown2
+X-Python-Version: >= 2.7
 
 Package: ifupdown2
 Architecture: all
@@ -16,7 +18,7 @@ Provides: ifupdown
 Conflicts: ifupdown
 Replaces: ifupdown
 Depends: ${python:Depends}, ${misc:Depends}, iproute2, python-argcomplete, python-ipaddr
-Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako, python-pkg-resources
+Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako
 Description: Network Interface Management tool similar to ifupdown
  ifupdown2 is ifupdown re-written in Python. It replaces ifupdown and provides
  the same user interface as ifupdown for network interface configuration.
diff --git a/debian/ifup@.service b/debian/ifup@.service
new file mode 100644 (file)
index 0000000..da1891b
--- /dev/null
@@ -0,0 +1,17 @@
+[Unit]
+Description=ifup for %I
+After=local-fs.target network-pre.target networking.service systemd-sysctl.service
+Before=network.target shutdown.target network-online.target
+Conflicts=shutdown.target
+BindsTo=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+DefaultDependencies=no
+IgnoreOnIsolate=yes
+
+[Service]
+# avoid stopping on shutdown via stopping system-ifup.slice
+Slice=system.slice
+ExecStart=/sbin/ifup --allow=hotplug %I
+ExecStop=/sbin/ifdown %I
+RemainAfterExit=true
+TimeoutStartSec=2min
index 7444031d3f511623654bf58112e13ee7e847c181..0ecf4b9863c9ce6502efb712e3e8497b34f1082b 100644 (file)
@@ -1,3 +1,2 @@
-etc/default/networking /etc/default/
 etc/network/ifupdown2/addons.conf /etc/network/ifupdown2/
 etc/network/ifupdown2/ifupdown2.conf /etc/network/ifupdown2/
index 06e1545b15e794b86bca4b628b0330b4fd0e6a9c..b2acd97aca9bec72241c530d7ca0a3c9b5f84df3 100644 (file)
@@ -1,5 +1,5 @@
 [Unit]
-Description=ifupdown2 networking initialization
+Description=Network initialization
 Documentation=man:interfaces(5) man:ifup(8) man:ifdown(8)
 DefaultDependencies=no
 Before=shutdown.target
@@ -10,9 +10,9 @@ Type=oneshot
 RemainAfterExit=yes
 SyslogIdentifier=networking
 TimeoutStopSec=30s
-ExecStart=/usr/share/ifupdown2/sbin/start-networking start
-ExecStop=/usr/share/ifupdown2/sbin/start-networking stop
-ExecReload=/usr/share/ifupdown2/sbin/start-networking reload
+ExecStart=/sbin/ifup -a
+ExecStop=/sbin/ifdown -a
+ExecReload=/sbin/ifreload -a
 
 [Install]
 WantedBy=basic.target network.target shutdown.target
diff --git a/debian/networking.init b/debian/networking.init
deleted file mode 100644 (file)
index b52bfdc..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/bin/bash
-### BEGIN INIT INFO
-# Provides:          networking ifupdown
-# Required-Start:    mountkernfs $local_fs urandom
-# Required-Stop:     $local_fs
-# Default-Start:     S
-# Default-Stop:      0 6
-# Short-Description: Raise network interfaces.
-# Description:       Prepare /run/network directory, ifstate file and raise network interfaces, or take them down.
-### END INIT INFO
-
-RUN_DIR="/run/network"
-IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
-
-STATE_DIR="/var/tmp/network"
-IFSTATE_FILE="${STATE_DIR}/ifstatenew"
-
-NAME=networking
-SCRIPTNAME=/etc/init.d/$NAME
-
-[ -x /sbin/ifup ] || exit 0
-[ -x /sbin/ifdown ] || exit 0
-
-. /lib/lsb/init-functions
-
-CONFIGURE_INTERFACES=yes
-
-EXTRA_ARGS=
-
-[ -f /etc/default/networking ] && . /etc/default/networking
-
-[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
-[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
-[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
-
-gen_examples() {
-    # Generate sample interfaces file. The interfaces files are
-    # generated under /usr/share/doc/ifupdown2/examples/
-    #
-
-    # generate files only at boot
-    [ -f ${IFSTATE_LOCKFILE} ] && return
-
-    python_ifupdown2_docdir="/usr/share/doc/ifupdown2"
-    swpfile=${python_ifupdown2_docdir}"/examples/swp_defaults"
-    bridgedefaultfile=${python_ifupdown2_docdir}"/examples/bridge_untagged_default"
-    interfaces_gen_script=${python_ifupdown2_docdir}"/examples/generate_interfaces.py"
-
-    [ ! -e $interfaces_gen_script ] && return
-    ret=$($interfaces_gen_script -s 2>&1 >$swpfile)
-    ret=$($interfaces_gen_script -b 2>&1 >$bridgedefaultfile)
-    return
-}
-
-perf_options() {
-    # At bootup lets set perfmode
-    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
-
-    echo -n "--perfmode"
-}
-
-process_exclusions() {
-    set -- $EXCLUDE_INTERFACES
-    exclusions=""
-    for d
-    do
-       exclusions="-X $d $exclusions"
-    done
-    echo $exclusions
-}
-
-check_network_file_systems() {
-    [ -e /proc/mounts ] || return 0
-
-    if [ -e /etc/iscsi/iscsi.initramfs ]; then
-       log_warning_msg "not deconfiguring network interfaces: iSCSI root is mounted."
-       exit 0
-    fi
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           log_warning_msg "not deconfiguring network interfaces: network devices still mounted."
-           exit 0
-           ;;
-       esac
-       case $FSTYPE in
-       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
-           log_warning_msg "not deconfiguring network interfaces: network file systems still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/mounts
-}
-
-check_network_swap() {
-    [ -e /proc/swaps ] || return 0
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           log_warning_msg "not deconfiguring network interfaces: network swap still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/swaps
-}
-
-ifup_hotplug () {
-    if [ -d /sys/class/net ]
-    then
-           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
-                           do
-                                   link=${iface##:*}
-                                   link=${link##.*}
-                                   if [ -e "/sys/class/net/$link" ] && [ "$(cat /sys/class/net/$link/operstate)" = up ]
-                                   then
-                                           echo "$iface"
-                                   fi
-                           done)
-           if [ -n "$ifaces" ]
-           then
-               ifup $ifaces "$@" || true
-           fi
-    fi
-}
-
-ifupdown_init() {
-       # remove state file at boot
-       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
-       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
-       [ ! -e /etc/network/run ] && \
-               ln -sf /run/network /etc/network/run &>/dev/null
-}
-
-case "$1" in
-start)
-       gen_examples
-       ifupdown_init
-       if [ "$CONFIGURE_INTERFACES" = no ]
-       then
-           log_action_msg "Not configuring network interfaces, see /etc/default/networking"
-           exit 0
-       fi
-       set -f
-       exclusions=$(process_exclusions)
-       perfoptions=$(perf_options)
-       log_action_begin_msg "Configuring network interfaces"
-       ifup -a $EXTRA_ARGS $exclusions $perfoptions
-       log_action_end_msg $?
-       ;;
-
-stop)
-       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
-               shutdown_or_reboot=$(runlevel 2>/dev/null | \
-                                    /usr/bin/tr -s " " | \
-                                    /usr/bin/cut -d " " \
-                                    -f1- --output-delimiter=$'\n' | \
-                                    /bin/grep -e "0" -e "6")
-               if [ -n "$shutdown_or_reboot" ]; then
-                       log_action_begin_msg "Deconfiguring network interfaces..skip"
-                       log_action_end_msg 0
-                       exit 0
-               fi
-       fi
-       ifupdown_init
-       check_network_file_systems
-       check_network_swap
-       exclusions=$(process_exclusions)
-
-       log_action_begin_msg "Deconfiguring network interfaces"
-       ifdown -a $EXTRA_ARGS $exclusions
-       log_action_end_msg $?
-       ;;
-
-reload)
-
-       ifupdown_init
-       log_action_begin_msg "Reloading network interfaces configuration"
-
-       ifreload -a $EXTRA_ARGS
-       log_action_end_msg $?
-       ;;
-
-reload-currently-up)
-
-       ifupdown_init
-       log_action_begin_msg "Reloading currently up network interfaces configuration"
-
-       ifreload --currently-up $EXTRA_ARGS
-       log_action_end_msg $?
-       ;;
-
-force-reload)
-
-       ifupdown_init
-
-       log_action_begin_msg "Reloading network interfaces configuration"
-       ifreload -f -a $EXTRA_ARGS
-       log_action_end_msg $?
-       ;;
-
-restart)
-       ifupdown_init
-
-       set -f
-       exclusions=$(process_exclusions)
-       log_action_begin_msg "Reconfiguring network interfaces"
-       ifdown -a $EXTRA_ARGS $exclusions || true
-       ifup -a $EXTRA_ARGS $exclusions
-       log_action_end_msg $?
-       ;;
-
-*)
-       echo "Usage: /etc/init.d/networking {start|stop|reload|restart|force-reload}"
-       exit 1
-       ;;
-esac
-
-exit 0
-
-# vim: noet ts=8
index 363560029d4892b91126c1e137f3542e66fe91f6..6274b3a5e6d05a0dded7669b16d246e62ee7904a 100755 (executable)
@@ -11,14 +11,17 @@ override_dh_installman:
        ./ifupdown2/man/genmanpages.sh ./ifupdown2/man ./man
        dh_installman
 
+override_dh_install:
+       dh_install
+       mkdir -p debian/ifupdown2/lib/systemd/system/
+       install --mode=644 debian/ifup@.service debian/ifupdown2/lib/systemd/system/
+
+
 override_dh_systemd_start:
        dh_systemd_start --name=networking --no-start
 
 override_dh_systemd_enable:
        dh_systemd_enable --name=networking
 
-override_dh_installinit:
-       dh_installinit --name=networking --no-start
-
 override_dh_compress:
        dh_compress -X.py
diff --git a/debian/watch b/debian/watch
new file mode 100644 (file)
index 0000000..9e7c0da
--- /dev/null
@@ -0,0 +1 @@
+version=3
diff --git a/etc/default/networking b/etc/default/networking
deleted file mode 100644 (file)
index cc3d3ef..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-#
-# Parameters for the /etc/init.d/networking script
-#
-#
-
-# Change the below to yes if you want verbose logging to be enabled
-VERBOSE="no"
-
-# Change the below to yes if you want debug logging to be enabled
-DEBUG="no"
-
-# Change the below to yes if you want logging to go to syslog
-SYSLOG="no"
-
-# Exclude interfaces
-EXCLUDE_INTERFACES=
-
-# Set to 'yes' if you want to skip ifdown during system reboot
-# and shutdown. This is of interest in large scale interface
-# deployments where you dont want to wait for interface
-# deconfiguration to speed up shutdown/reboot
-SKIP_DOWN_AT_SYSRESET="yes"
diff --git a/etc/init.d/networking b/etc/init.d/networking
new file mode 100644 (file)
index 0000000..b52bfdc
--- /dev/null
@@ -0,0 +1,222 @@
+#!/bin/bash
+### BEGIN INIT INFO
+# Provides:          networking ifupdown
+# Required-Start:    mountkernfs $local_fs urandom
+# Required-Stop:     $local_fs
+# Default-Start:     S
+# Default-Stop:      0 6
+# Short-Description: Raise network interfaces.
+# Description:       Prepare /run/network directory, ifstate file and raise network interfaces, or take them down.
+### END INIT INFO
+
+RUN_DIR="/run/network"
+IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
+
+STATE_DIR="/var/tmp/network"
+IFSTATE_FILE="${STATE_DIR}/ifstatenew"
+
+NAME=networking
+SCRIPTNAME=/etc/init.d/$NAME
+
+[ -x /sbin/ifup ] || exit 0
+[ -x /sbin/ifdown ] || exit 0
+
+. /lib/lsb/init-functions
+
+CONFIGURE_INTERFACES=yes
+
+EXTRA_ARGS=
+
+[ -f /etc/default/networking ] && . /etc/default/networking
+
+[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
+[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
+[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
+
+gen_examples() {
+    # Generate sample interfaces file. The interfaces files are
+    # generated under /usr/share/doc/ifupdown2/examples/
+    #
+
+    # generate files only at boot
+    [ -f ${IFSTATE_LOCKFILE} ] && return
+
+    python_ifupdown2_docdir="/usr/share/doc/ifupdown2"
+    swpfile=${python_ifupdown2_docdir}"/examples/swp_defaults"
+    bridgedefaultfile=${python_ifupdown2_docdir}"/examples/bridge_untagged_default"
+    interfaces_gen_script=${python_ifupdown2_docdir}"/examples/generate_interfaces.py"
+
+    [ ! -e $interfaces_gen_script ] && return
+    ret=$($interfaces_gen_script -s 2>&1 >$swpfile)
+    ret=$($interfaces_gen_script -b 2>&1 >$bridgedefaultfile)
+    return
+}
+
+perf_options() {
+    # At bootup lets set perfmode
+    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
+
+    echo -n "--perfmode"
+}
+
+process_exclusions() {
+    set -- $EXCLUDE_INTERFACES
+    exclusions=""
+    for d
+    do
+       exclusions="-X $d $exclusions"
+    done
+    echo $exclusions
+}
+
+check_network_file_systems() {
+    [ -e /proc/mounts ] || return 0
+
+    if [ -e /etc/iscsi/iscsi.initramfs ]; then
+       log_warning_msg "not deconfiguring network interfaces: iSCSI root is mounted."
+       exit 0
+    fi
+
+    while read DEV MTPT FSTYPE REST; do
+       case $DEV in
+       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
+           log_warning_msg "not deconfiguring network interfaces: network devices still mounted."
+           exit 0
+           ;;
+       esac
+       case $FSTYPE in
+       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
+           log_warning_msg "not deconfiguring network interfaces: network file systems still mounted."
+           exit 0
+           ;;
+       esac
+    done < /proc/mounts
+}
+
+check_network_swap() {
+    [ -e /proc/swaps ] || return 0
+
+    while read DEV MTPT FSTYPE REST; do
+       case $DEV in
+       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
+           log_warning_msg "not deconfiguring network interfaces: network swap still mounted."
+           exit 0
+           ;;
+       esac
+    done < /proc/swaps
+}
+
+ifup_hotplug () {
+    if [ -d /sys/class/net ]
+    then
+           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
+                           do
+                                   link=${iface##:*}
+                                   link=${link##.*}
+                                   if [ -e "/sys/class/net/$link" ] && [ "$(cat /sys/class/net/$link/operstate)" = up ]
+                                   then
+                                           echo "$iface"
+                                   fi
+                           done)
+           if [ -n "$ifaces" ]
+           then
+               ifup $ifaces "$@" || true
+           fi
+    fi
+}
+
+ifupdown_init() {
+       # remove state file at boot
+       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
+       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
+       [ ! -e /etc/network/run ] && \
+               ln -sf /run/network /etc/network/run &>/dev/null
+}
+
+case "$1" in
+start)
+       gen_examples
+       ifupdown_init
+       if [ "$CONFIGURE_INTERFACES" = no ]
+       then
+           log_action_msg "Not configuring network interfaces, see /etc/default/networking"
+           exit 0
+       fi
+       set -f
+       exclusions=$(process_exclusions)
+       perfoptions=$(perf_options)
+       log_action_begin_msg "Configuring network interfaces"
+       ifup -a $EXTRA_ARGS $exclusions $perfoptions
+       log_action_end_msg $?
+       ;;
+
+stop)
+       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
+               shutdown_or_reboot=$(runlevel 2>/dev/null | \
+                                    /usr/bin/tr -s " " | \
+                                    /usr/bin/cut -d " " \
+                                    -f1- --output-delimiter=$'\n' | \
+                                    /bin/grep -e "0" -e "6")
+               if [ -n "$shutdown_or_reboot" ]; then
+                       log_action_begin_msg "Deconfiguring network interfaces..skip"
+                       log_action_end_msg 0
+                       exit 0
+               fi
+       fi
+       ifupdown_init
+       check_network_file_systems
+       check_network_swap
+       exclusions=$(process_exclusions)
+
+       log_action_begin_msg "Deconfiguring network interfaces"
+       ifdown -a $EXTRA_ARGS $exclusions
+       log_action_end_msg $?
+       ;;
+
+reload)
+
+       ifupdown_init
+       log_action_begin_msg "Reloading network interfaces configuration"
+
+       ifreload -a $EXTRA_ARGS
+       log_action_end_msg $?
+       ;;
+
+reload-currently-up)
+
+       ifupdown_init
+       log_action_begin_msg "Reloading currently up network interfaces configuration"
+
+       ifreload --currently-up $EXTRA_ARGS
+       log_action_end_msg $?
+       ;;
+
+force-reload)
+
+       ifupdown_init
+
+       log_action_begin_msg "Reloading network interfaces configuration"
+       ifreload -f -a $EXTRA_ARGS
+       log_action_end_msg $?
+       ;;
+
+restart)
+       ifupdown_init
+
+       set -f
+       exclusions=$(process_exclusions)
+       log_action_begin_msg "Reconfiguring network interfaces"
+       ifdown -a $EXTRA_ARGS $exclusions || true
+       ifup -a $EXTRA_ARGS $exclusions
+       log_action_end_msg $?
+       ;;
+
+*)
+       echo "Usage: /etc/init.d/networking {start|stop|reload|restart|force-reload}"
+       exit 1
+       ;;
+esac
+
+exit 0
+
+# vim: noet ts=8
index dd3d2237e33edcc03c62727fbb4a5832946e419b..c43d3776f3420c44e267813bf78ddf51a92b57b5 100644 (file)
@@ -12,7 +12,9 @@ pre-up,bridgevlan
 pre-up,mstpctl
 pre-up,tunnel
 pre-up,vrf
+pre-up,tunnel
 pre-up,ethtool
+pre-up,address
 up,dhcp
 up,address
 up,addressvirtual
@@ -40,4 +42,4 @@ post-down,batman_adv
 post-down,usercmds
 post-down,link
 post-down,tunnel
-post-down,xfrm
\ No newline at end of file
+post-down,xfrm
index e05c35f689c9e87563088348ebdd037eab5153ae..ecdd94f3dc5e38492fc3a93ffb5f45c627b49175 100644 (file)
@@ -4,6 +4,9 @@
 # This file contains default settings for ifupdown
 #
 
+# use ifupdown2d
+use_daemon=no
+
 # enable templates
 template_enable=1
 
@@ -84,5 +87,5 @@ adjust_logical_dev_mtu=1
 # directory where the state file is stored
 # if this directory doesn't exists ifupdown2 will create it
 # if directory creation fails or state_dir variable is empty
-# state_dir will default to /run/network/
-state_dir=/run/network/
+# state_dir will default to /var/tmp/network/
+state_dir=/var/tmp/network/
index 151f650fce04e32a2fd8d47cb60516957a99ea8b..e60c8d929ffc0d30d6f972fd858ca30801f85918 100644 (file)
@@ -1,9 +1,9 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-__version__ = '1.2.9'
+__version__ = '2.0.0'
 
-# Copyright (C) 2014,2015,2016,2017,2018,219 Cumulus Networks, Inc. All rights reserved
+# Copyright (C) 2014,2015,2016,2017,2018,2019 Cumulus Networks, Inc. All rights reserved
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
index 4dd4270b35423369a32c3f167bfc828aa91a634b..4477b82230e9cf2d977bb2c2192172119773920a 100755 (executable)
 #!/usr/bin/python
+# Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
 #
-# Copyright 2016 Cumulus Networks, Inc. All rights reserved.
-# Author: Julien Fortin, julien@cumulusnetworks.com
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
 #
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown2 - Network Manager
 #
 
 import os
-import re
 import sys
-import json
-import errno
-import struct
-import select
-import socket
-import signal
 
 try:
-    import ifupdown2.ifupdown.config as core_config
-    from ifupdown2.ifupdown.log import log
+    from ifupdown2.lib.log import LogManager, root_logger
+    from ifupdown2.lib.status import Status
+except:
+    from lib.log import LogManager, root_logger
+    from lib.status import Status
+
+# first thing first, setup the logging infra
+LogManager.get_instance()
+
+try:
+    import ifupdown2.ifupdown.config as config
+
     from ifupdown2 import __version__
 
-    core_config.__version__ = __version__
-except:
-    import ifupdown.config as core_config
-    from ifupdown.log import log
+    config.__version__ = __version__
 
-    core_config.__version__ = __import__('__init__').__version__
+    from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError
 
+    from ifupdown2.ifupdown.client import Client
+    from ifupdown2.ifupdown.exceptions import ArgvParseHelp
+except:
+    import ifupdown.config as config
 
-class Ifupdown2Complete(Exception):
-    def __init__(self, status):
-        self.status = status
+    config.__version__ = __import__("__init__").__version__
 
+    from lib.exceptions import ExitWithStatus, ExitWithStatusAndError
 
-class Ifupdown2Client:
-    def __init__(self, argv):
+    from ifupdown.client import Client
+    from ifupdown.exceptions import ArgvParseHelp
 
-        self.stdin = None
-        self.argv = argv
-        self.data = ''
-        self.HEADER_SIZE = 4
-        self.daemon_pid = -1
 
-        self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        try:
-            self.socket.connect('/var/run/ifupdown2d/uds')
-
-            signal.signal(signal.SIGINT, self.signal_handler)
-            signal.signal(signal.SIGTERM, self.signal_handler)
-            signal.signal(signal.SIGQUIT, self.signal_handler)
-
-            try:
-                self.SO_PEERCRED = socket.SO_PEERCRED
-            except AttributeError:
-                # powerpc is the only non-generic we care about. alpha, mips,
-                # sparc, and parisc also have non-generic values.
-                machine = os.uname()[4]
-                if re.search(r'^(ppc|powerpc)', machine):
-                    self.SO_PASSCRED = 20
-                    self.SO_PEERCRED = 21
-                else:
-                    self.SO_PASSCRED = 16
-                    self.SO_PEERCRED = 17
-            try:
-                self.socket.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
-            except Exception as e:
-                raise Exception('setsockopt: %s' % str(e))
-
-        except socket.error:
-            self.socket.close()
-            self.socket = None
-            sys.stderr.write("""
-    ERROR: %s could not connect to ifupdown2d
-
-    Try starting ifupdown2d with:
-    sudo systemctl start ifupdown2d
-
-    To configure ifupdown2d to start when the box boots:
-    sudo systemctl enable ifupdown2d
-    """ % argv[0])
-
-    def __del__(self):
-        if self.socket:
-            self.socket.close()
-
-    def signal_handler(self, sig, frame):
-        if self.daemon_pid > 0:
-            os.kill(self.daemon_pid, sig)
-
-    def read_data(self):
-        ready = select.select([self.socket], [], [])
-        if ready and ready[0] and ready[0][0] == self.socket:
-            d = self.socket.recv(65536)
-            if self.data:
-                self.data += d
-            else:
-                self.data = d
-            return True
+def daemon_mode():
+    """ Check ifupdown2 config to see if we should start the client """
+    try:
+        with open(config.IFUPDOWN2_CONF_PATH) as f:
+            return "use_daemon=yes" in f.read()
+    except:
         return False
 
-    def get_packets(self):
-        """
-            ifupdown2 output is divided into "json packets"
-            the first 4 bytes is the size of the next json
-            object to etract
-
-        """
-        data_size = len(self.data)
-        if not data_size:
-            raise Ifupdown2Complete(status=1)
 
-        packets = []
-        try:
-            while data_size > 0:
-                packet_len = struct.unpack('=I', self.data[:self.HEADER_SIZE])[0]
-                packet_data = self.data[self.HEADER_SIZE:packet_len + self.HEADER_SIZE]
-
-                fmt = "=%ds" % packet_len
-
-                packets.append(json.loads(struct.unpack(fmt, packet_data)[0]))
-
-                self.data = self.data[self.HEADER_SIZE + packet_len:]
-                data_size -= self.HEADER_SIZE + packet_len
-        except:
-            pass
-        return packets
-
-    def process_packets(self, packets):
-        for packet in packets:
-            if 'pid' in packet:
-                self.daemon_pid = packet['pid']
-            if 'stdout' in packet:
-                sys.stdout.write(packet['stdout'])
-            if 'stderr' in packet:
-                sys.stderr.write(packet['stderr'])
-            if 'status' in packet:
-                raise Ifupdown2Complete(packet['status'])
-
-    def run(self):
-        status = 1
-        if self.socket:
-            for arg in ['-i', '--interfaces']:
-                try:
-                    if self.argv[self.argv.index(arg) + 1] == '-':
-                        self.stdin = sys.stdin.read()
-                        continue
-                except (ValueError, IndexError):
-                    pass
-
-            self.socket.send(json.dumps({
-                'argv': self.argv,
-                'stdin': self.stdin
-            }))
-
-            try:
-                while True:
-                    try:
-                        self.read_data()
-                        self.process_packets(self.get_packets())
-                    except Ifupdown2Complete as e:
-                        status = e.status
-                        break
-                    except Exception as e:
-                        if ((isinstance(e.args, tuple) and e[0] == 4)
-                            or (hasattr(e, 'errno') and e.errno == errno.EINTR)):
-                            pass
-                        else:
-                            raise
-            except Exception as e:
-                sys.stderr.write(str(e))
-            finally:
-                self.socket.close()
-        return status if status != None else 1
-
-
-def ifupdown2_standalone():
+def client():
+    try:
+        status = Client(sys.argv).run()
+    except ExitWithStatusAndError as e:
+        root_logger.error(e.message)
+        status = e.status
+    except ExitWithStatus as e:
+        status = e.status
+    return status
+
+
+def stand_alone():
+    if not sys.argv[0].endswith("query") and os.geteuid() != 0:
+        sys.stderr.write('must be root to run this command\n')
+        return 1
     try:
-        import ifupdown2.ifupdown.main as main_ifupdown2
+        from ifupdown2.ifupdown.main import Ifupdown2
+        from ifupdown2.lib.nlcache import NetlinkListenerWithCache, NetlinkListenerWithCacheErrorNotInitialized
     except:
-        import ifupdown.main as main_ifupdown2
-    ifupdown2 = main_ifupdown2.Ifupdown2(daemon=False, uid=os.geteuid())
-    ifupdown2.parse_argv(sys.argv)
-    ifupdown2.update_logger()
-    return ifupdown2.main()
+        from ifupdown.main import Ifupdown2
+        from lib.nlcache import NetlinkListenerWithCache, NetlinkListenerWithCacheErrorNotInitialized
+    ifupdown2 = Ifupdown2(daemon=False, uid=os.geteuid())
+    try:
+        ifupdown2.parse_argv(sys.argv)
+        LogManager.get_instance().start_standalone_logging(ifupdown2.args)
+    except ArgvParseHelp:
+        # on --help parse_args raises SystemExit, we catch it and raise a
+        # custom exception ArgvParseHelp to return 0
+        return 0
+    try:
+        status = ifupdown2.main()
+    finally:
+        try:
+            NetlinkListenerWithCache.get_instance().cleanup()
+        except NetlinkListenerWithCacheErrorNotInitialized:
+            status = Status.Client.STATUS_NLERROR
+    return status
 
 
 def main():
     try:
-        if 'use_daemon=yes' in open(core_config.IFUPDOWN2_CONF_PATH).read():
-            return Ifupdown2Client(sys.argv).run()
+        if daemon_mode():
+            return client()
         else:
-            return ifupdown2_standalone()
+            return stand_alone()
+    except ArgvParseHelp:
+        return Status.Client.STATUS_SUCCESS
     except KeyboardInterrupt:
-        return 1
+        return Status.Client.STATUS_KEYBOARD_INTERRUPT
     except Exception as e:
-        log.error(str(e))
-        return 1
+        root_logger.exception("main: %s" % str(e))
+        return Status.Client.STATUS_EXCEPTION_MAIN
 
 
 if __name__ == '__main__':
     try:
         sys.exit(main())
     except KeyboardInterrupt:
-        sys.exit(1)
+        sys.exit(Status.Client.STATUS_KEYBOARD_INTERRUPT)
index a8ef1e25fe38a5132bc2443c54fb5392ce7295a9..83974d791e57006b9b43046e751b410083eca257 100644 (file)
@@ -6,15 +6,16 @@
 
 import socket
 
-from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6
+from ipaddr import IPNetwork, IPv4Network, IPv6Network
 
 try:
+    from ifupdown2.lib.addon import Addon
+    from ifupdown2.nlmanager.nlmanager import Link
+
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
 
     from ifupdown2.ifupdownaddons.dhclient import dhclient
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     import ifupdown2.ifupdown.statemanager as statemanager
@@ -22,12 +23,13 @@ try:
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
 except ImportError:
+    from lib.addon import Addon
+    from nlmanager.nlmanager import Link
+
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
 
     from ifupdownaddons.dhclient import dhclient
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.statemanager as statemanager
@@ -36,103 +38,144 @@ except ImportError:
     import ifupdown.ifupdownconfig as ifupdownconfig
 
 
-class address(moduleBase):
+class address(Addon, moduleBase):
     """  ifupdown2 addon module to configure address, mtu, hwaddress, alias
     (description) on an interface """
 
-    _modinfo = {'mhelp' : 'address configuration module for interfaces',
-                'attrs': {
-                      'address' :
-                            {'help' : 'ipv4 or ipv6 addresses',
-                             'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
-                             'multiline' : True,
-                             'example' : ['address 10.0.12.3/24',
-                             'address 2000:1000:1000:1000:3::5/128']},
-                      'netmask' :
-                            {'help': 'netmask',
-                             'example' : ['netmask 255.255.255.0'],
-                             'compat' : True},
-                      'broadcast' :
-                            {'help': 'broadcast address',
-                             'validvals' : ['<ipv4>', ],
-                             'example' : ['broadcast 10.0.1.255']},
-                      'scope' :
-                            {'help': 'scope',
-                             'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'],
-                             'example' : ['scope host']},
-                      'preferred-lifetime' :
-                            {'help': 'preferred lifetime',
-                              'validrange' : ['0', '65535'],
-                             'example' : ['preferred-lifetime forever',
-                                          'preferred-lifetime 10']},
-                      'gateway' :
-                            {'help': 'default gateway',
-                             'validvals' : ['<ipv4>', '<ipv6>'],
-                             'multiline' : True,
-                             'example' : ['gateway 255.255.255.0']},
-                      'mtu' :
-                            { 'help': 'interface mtu',
-                              'validrange' : ['552', '9216'],
-                              'example' : ['mtu 1600'],
-                              'default' : '1500'},
-                      'hwaddress' :
-                            {'help' : 'hw address',
-                             'validvals' : ['<mac>',],
-                             'example': ['hwaddress 44:38:39:00:27:b8']},
-                      'alias' :
-                            { 'help': 'description/alias',
-                              'example' : ['alias testnetwork']},
-                      'address-purge' :
-                            { 'help': 'purge existing addresses. By default ' +
-                              'any existing ip addresses on an interface are ' +
-                              'purged to match persistant addresses in the ' +
-                              'interfaces file. Set this attribute to \'no\'' +
-                              'if you want to preserve existing addresses',
-                              'validvals' : ['yes', 'no'],
-                              'default' : 'yes',
-                              'example' : ['address-purge yes/no']},
-                      'clagd-vxlan-anycast-ip' :
-                            { 'help'     : 'Anycast local IP address for ' +
-                              'dual connected VxLANs',
-                              'validvals' : ['<ipv4>', ],
-                              'example'  : ['clagd-vxlan-anycast-ip 36.0.0.11']},
-                      'arp-accept' :
-                            { 'help': 'Allow gratuitous arp to update arp table',
-                              'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
-                              'default' : 'off',
-                              'example' : ['arp-accept on']},
-                      'ip-forward' :
-                            { 'help': 'ip forwarding flag',
-                              'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
-                              'default' : 'off',
-                              'example' : ['ip-forward off']},
-                      'ip6-forward' :
-                            { 'help': 'ipv6 forwarding flag',
-                              'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
-                              'default' : 'off',
-                              'example' : ['ip6-forward off']},
-                      'mpls-enable' :
-                            { 'help': 'mpls enable flag',
-                              'validvals': ['yes', 'no'],
-                              'default' : 'no',
-                              'example' : ['mpls-enable yes']},
-                    'ipv6-addrgen': {
-                        'help': 'enable disable ipv6 link addrgenmode',
-                        'validvals': ['on', 'off'],
-                        'default': 'on',
-                        'example': [
-                            'ipv6-addrgen on',
-                            'ipv6-addrgen off'
-                        ]
-                    }
-                }}
+    _modinfo = {
+        'mhelp': 'address configuration module for interfaces',
+        'attrs': {
+            'address': {
+                'help': 'The address of the interface. The format of the '
+                        'address depends on the protocol. It is a dotted '
+                        'quad for IP and a sequence of hexadecimal halfwords '
+                        'separated by colons for IPv6. The ADDRESS may be '
+                        'followed by a slash and a decimal number which '
+                        'encodes the network prefix length.',
+                'validvals': ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
+                'multiline': True,
+                'example': [
+                    'address 10.0.12.3/24',
+                    'address 2000:1000:1000:1000:3::5/128'
+                ]
+            },
+            'netmask': {
+                'help': 'Address netmask',
+                'example': ['netmask 255.255.255.0'],
+                'compat': True
+            },
+            'broadcast': {
+                'help': 'The broadcast address on the interface.',
+                'validvals': ['<ipv4>'],
+                'example': ['broadcast 10.0.1.255']
+            },
+            'scope': {
+                'help': 'The scope of the area where this address is valid. '
+                        'The available scopes are listed in file /etc/iproute2/rt_scopes. '
+                        'Predefined scope values are: '
+                        'global - the address is globally valid. '
+                        'site - (IPv6 only, deprecated) the address is site local, i.e. it is valid inside this site. '
+                        'link - the address is link local, i.e. it is valid only on this device. '
+                        'host - the address is valid only inside this host.',
+                'validvals': ['universe', 'site', 'link', 'host', 'nowhere'],
+                'example': ['scope host']
+            },
+            'preferred-lifetime': {
+                'help': 'The preferred lifetime of this address; see section '
+                        '5.5.4 of RFC 4862. When it expires, the address is '
+                        'no longer used for new outgoing connections. '
+                        'Defaults to forever.',
+                'validrange': ['0', '65535'],
+                'example': [
+                    'preferred-lifetime forever',
+                    'preferred-lifetime 10'
+                ]
+            },
+            'pointopoint': {
+                'help': 'Set the remote IP address for a point-to-point link',
+                'validvals': ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
+                'example': [
+                    'pointopoint 10.10.10.42/32'
+                ]
+            },
+            'gateway': {
+                'help': 'Default gateway',
+                'validvals': ['<ipv4>', '<ipv6>'],
+                'multiline': True,
+                'example': ['gateway 255.255.255.0']
+            },
+            'mtu': {
+                'help': 'Interface MTU (maximum transmission unit)',
+                'validrange': ['552', '9216'],
+                'example': ['mtu 1600'],
+                'default': '1500'
+            },
+            'hwaddress': {
+                'help': 'Hardware address (mac)',
+                'validvals': ['<mac>'],
+                'example': ['hwaddress 44:38:39:00:27:b8']
+            },
+            'alias': {
+                'help': 'description/alias: give the device a symbolic name for easy reference.',
+                'example': ['alias testnetwork']
+            },
+            'address-purge': {
+                'help': 'Purge existing addresses. By default any existing '
+                        'ip addresses on an interface are purged to match '
+                        'persistant addresses in the interfaces file. Set '
+                        'this attribute to \'no\' if you want to preserve '
+                        'existing addresses',
+                'validvals': ['yes', 'no'],
+                'default': 'yes',
+                'example': ['address-purge yes/no']
+            },
+            'clagd-vxlan-anycast-ip': {
+                'help': 'Anycast local IP address for dual connected VxLANs',
+                'validvals': ['<ipv4>'],
+                'example': ['clagd-vxlan-anycast-ip 36.0.0.11']
+            },
+            'ip-forward': {
+                'help': 'ip forwarding flag',
+                'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
+                'default': 'off',
+                'example': ['ip-forward off']
+            },
+            'ip6-forward': {
+                'help': 'ipv6 forwarding flag',
+                'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
+                'default': 'off',
+                'example': ['ip6-forward off']
+            },
+            'mpls-enable': {
+                'help': 'mpls enable flag',
+                'validvals': ['yes', 'no'],
+                'default': 'no',
+                'example': ['mpls-enable yes']
+            },
+            'ipv6-addrgen': {
+                'help': 'enable disable ipv6 link addrgenmode',
+                'validvals': ['on', 'off'],
+                'default': 'on',
+                'example': [
+                    'ipv6-addrgen on',
+                    'ipv6-addrgen off'
+                ]
+            },
+            'arp-accept': {
+                'help': 'Allow gratuitous arp to update arp table',
+                'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
+                'default': 'off',
+                'example': ['arp-accept on']
+            },
+        }
+    }
+
+    DEFAULT_MTU_STRING = "1500"
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
         self._bridge_fdb_query_cache = {}
-        self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu')
-        self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu')
         self.ipforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip-forward')
         self.ip6forward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-forward')
         self.ifaces_defaults = policymanager.policymanager_api.get_iface_defaults(module_name=self.__class__.__name__)
@@ -143,15 +186,10 @@ class address(moduleBase):
             )
         )
 
-        if not self.default_mtu:
-            self.default_mtu = '1500'
-
-        self.logger.info('address: using default mtu %s' %self.default_mtu)
+        self.default_mtu = self.__policy_get_default_mtu()
+        self.max_mtu     = self.__policy_get_max_mtu()
 
-        if self.max_mtu:
-            self.logger.info('address: using max mtu %s' %self.max_mtu)
-
-        self.lower_iface_mtu_checked_list = list()
+        self.default_loopback_addresses = (IPNetwork('127.0.0.1/8'), IPNetwork('::1/128'))
 
         self.l3_intf_arp_accept = utils.get_boolean_from_string(
             policymanager.policymanager_api.get_module_globals(
@@ -169,6 +207,39 @@ class address(moduleBase):
             default=True
         )
 
+    def __policy_get_default_mtu(self):
+        default_mtu = policymanager.policymanager_api.get_attr_default(
+            module_name=self.__class__.__name__,
+            attr="mtu"
+        )
+
+        if not default_mtu:
+            default_mtu = self.DEFAULT_MTU_STRING
+
+        try:
+            self.default_mtu_int = int(default_mtu)
+        except ValueError as e:
+            self.logger.error("address: invalid default mtu \"%s\" set via policy: %s" % (default_mtu, str(e)))
+            default_mtu = self.DEFAULT_MTU_STRING
+            self.default_mtu_int = int(self.DEFAULT_MTU_STRING)
+
+        self.logger.info("address: using default mtu %s" % default_mtu)
+
+        return default_mtu
+
+    def __policy_get_max_mtu(self):
+        max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr="max_mtu")
+        if max_mtu:
+            try:
+                max_mtu_int = int(max_mtu)
+                self.logger.info("address: using max mtu %s" % self.max_mtu)
+                return max_mtu_int
+            except ValueError as e:
+                self.logger.warning("address: policy max_mtu: %s" % str(e))
+        else:
+            self.logger.info("address: max_mtu undefined")
+        return 0
+
     def syntax_check(self, ifaceobj, ifaceobj_getfunc=None):
         return (self.syntax_check_multiple_gateway(ifaceobj)
                 and self.syntax_check_addr_allowed_on(ifaceobj, True)
@@ -226,10 +297,14 @@ class address(moduleBase):
         return result
 
     def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc):
-        mtu = ifaceobj.get_attr_value_first('mtu')
-        if mtu:
-            return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc,
-                                          syntaxcheck=True)
+        mtu_str = ifaceobj.get_attr_value_first('mtu')
+        if mtu_str:
+            try:
+                mtu_int = int(mtu_str)
+            except ValueError as e:
+                self.logger.warning("%s: invalid mtu %s: %s" % (ifaceobj.name, mtu_str, str(e)))
+                return False
+            return self._check_mtu_config(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntaxcheck=True)
         return True
 
     def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False):
@@ -280,13 +355,13 @@ class address(moduleBase):
         arp_accept = ifaceobj.get_attr_value_first('arp-accept')
         arp_accept = utils.boolean_support_binary(arp_accept)
         is_vlan_dev_on_vlan_aware_bridge = False
-        is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
+        is_bridge = self.cache.get_link_kind(ifaceobj.name) == 'bridge'
         if not is_bridge:
             if ifaceobj.link_kind & ifaceLinkKind.VLAN:
                 bridgename = ifaceobj.lowerifaces[0]
                 vlan = self._get_vlan_id(ifaceobj)
-                is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
-        if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
+                is_vlan_dev_on_vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridgename)
+        if ((is_bridge and not self.cache.bridge_is_vlan_aware(ifaceobj.name))
                         or is_vlan_dev_on_vlan_aware_bridge):
             if self._address_valid(addrs):
                 if self.l3_intf_arp_accept:
@@ -304,152 +379,169 @@ class address(moduleBase):
                 try:
                     for old_obj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []:
                         old_hwaddress = old_obj.get_attr_value_first("hwaddress")
-                        if old_hwaddress and self.ipcmd.mac_str_to_int(old_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress):
-                            self.ipcmd.bridge_fdb_del(bridgename, old_hwaddress, vlan)
+                        if old_hwaddress and utils.mac_str_to_int(old_hwaddress) != utils.mac_str_to_int(hwaddress):
+                            self.iproute2.bridge_fdb_del(bridgename, old_hwaddress, vlan)
                             break
                 except:
                     pass
-                self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
+                self.iproute2.bridge_fdb_add(bridgename, hwaddress, vlan)
             else:
-                self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
+                self.iproute2.bridge_fdb_del(bridgename, hwaddress, vlan)
 
-    def _get_anycast_addr(self, ifaceobjlist):
-        for ifaceobj in ifaceobjlist:
-            anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')
-            if anycast_addr:
-                anycast_addr = anycast_addr+'/32'
-                return anycast_addr
-        return None
+    def __get_ip_addr_with_attributes(self, ifaceobj_list, ifname):
+        user_config_ip_addrs_list = list()
 
-    def _inet_address_convert_to_cidr(self, ifaceobjlist):
-        newaddrs = []
-        newaddr_attrs = {}
+        try:
+            for ifaceobj in ifaceobj_list:
 
-        for ifaceobj in ifaceobjlist:
-            addrs = ifaceobj.get_attr_value('address')
-            if not addrs:
-                continue
+                user_addrs = ifaceobj.get_attr_value("address")
 
-            if not self.syntax_check_addr_allowed_on(ifaceobj,
-                                                     syntax_check=False):
-                return (False, newaddrs, newaddr_attrs)
-            # If user address is not in CIDR notation, convert them to CIDR
-            for addr_index in range(0, len(addrs)):
-                addr = addrs[addr_index]
-                newaddr = addr
-                if '/' in addr:
-                    newaddrs.append(addr)
-                else:
-                    netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
-                    if netmask:
-                        prefixlen = IPNetwork('%s' %addr +
-                                    '/%s' %netmask).prefixlen
-                        newaddr = addr + '/%s' %prefixlen
+                if not user_addrs:
+                    continue
+
+                if not self.syntax_check_addr_allowed_on(ifaceobj, syntax_check=False):
+                    return False, None
+
+                for index, addr in enumerate(user_addrs):
+                    addr_attributes = {}
+                    addr_obj = None
+
+                    # convert the ip from string to IPNetwork object
+                    if "/" in addr:
+                        addr_obj = IPNetwork(addr)
                     else:
-                        # we are here because there is no slash (/xx) and no netmask
-                        # just let IPNetwork handle the ipv4 or ipv6 address mask
-                        prefixlen = IPNetwork(addr).prefixlen
-                        newaddr = addr + '/%s' %prefixlen
-                    newaddrs.append(newaddr)
-
-                attrs = {}
-                for a in ['broadcast', 'pointopoint', 'scope',
-                        'preferred-lifetime']:
-                    aval = ifaceobj.get_attr_value_n(a, addr_index)
-                    if aval:
-                        attrs[a] = aval
-
-                if attrs:
-                    newaddr_attrs[newaddr]= attrs
-        return (True, newaddrs, newaddr_attrs)
-
-    def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs):
-        for addr_index in range(0, len(newaddrs)):
-            try:
-                if newaddr_attrs:
-                    self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('broadcast'),
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('pointopoint'),
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('scope'),
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('preferred-lifetime'))
+                        netmask = ifaceobj.get_attr_value_n("netmask", index)
+
+                        if netmask:
+                            addr_obj = IPNetwork("%s/%s" % (addr, netmask))
+                        else:
+                            addr_obj = IPNetwork(addr)
+
+                    for attr_name in ("broadcast", "scope", "preferred-lifetime"):
+                        attr_value = ifaceobj.get_attr_value_n(attr_name, index)
+                        if attr_value:
+                            addr_attributes[attr_name] = attr_value
+
+                    pointopoint = ifaceobj.get_attr_value_n("pointopoint", index)
+                    try:
+                        if pointopoint:
+                            addr_attributes["pointopoint"] = IPNetwork(pointopoint)
+                    except Exception as e:
+                        self.logger.warning("%s: pointopoint %s: %s" % (ifaceobj.name, pointopoint, str(e)))
+
+                    user_config_ip_addrs_list.append((addr_obj, addr_attributes))
+        except Exception as e:
+            self.logger.warning("%s: convert string ip address into IPNetwork object: %s" % (ifname, str(e)))
+            return False, None
+
+        return True, user_config_ip_addrs_list
+
+    def __add_ip_addresses_with_attributes(self, ifaceobj, ifname, user_config_ip_addrs):
+        try:
+            for ip, attributes in user_config_ip_addrs:
+                if attributes:
+                    self.netlink.addr_add(
+                        ifname, ip,
+                        scope=attributes.get("scope"),
+                        peer=attributes.get("pointopoint"),
+                        broadcast=attributes.get("broadcast"),
+                        preferred_lifetime=attributes.get("preferred-lifetime")
+                    )
                 else:
-                    self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index])
-            except Exception, e:
-                self.log_error(str(e), ifaceobj)
+                    self.netlink.addr_add(ifname, ip)
+        except Exception as e:
+            self.log_error(str(e), ifaceobj)
 
-    def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None,
-                             force_reapply=False):
-        squash_addr_config = (True if \
-                                  ifupdownconfig.config.get('addr_config_squash', \
-                              '0') == '1' else False)
+    @staticmethod
+    def __add_loopback_anycast_ip_to_running_ip_addr_list(ifaceobjlist):
+        """
+        if anycast address is configured on 'lo' and is in running
+        config add it to newaddrs so that ifreload doesn't wipe it out
+        :param ifaceobjlist:
+        :param running_ip_addrs:
+        """
+        anycast_ip_addr = None
 
-        if (squash_addr_config and
-            not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)):
+        for ifaceobj in ifaceobjlist:
+            anycast_addr = ifaceobj.get_attr_value_first("clagd-vxlan-anycast-ip")
+            if anycast_addr:
+                anycast_ip_addr = IPNetwork(anycast_addr)
+
+        return str(anycast_ip_addr) if anycast_ip_addr else None
+
+    def process_addresses(self, ifaceobj, ifaceobj_getfunc=None, force_reapply=False):
+        squash_addr_config = ifupdownconfig.config.get("addr_config_squash", "0") == "1"
+
+        if squash_addr_config and not ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING:
             return
 
-        purge_addresses = ifaceobj.get_attr_value_first('address-purge')
-        if not purge_addresses:
-           purge_addresses = 'yes'
+        ifname = ifaceobj.name
+        purge_addresses = utils.get_boolean_from_string(ifaceobj.get_attr_value_first("address-purge"), default=True)
+
+        if not squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS:
+            # if youngest sibling and squash addr is not set
+            # print a warning that addresses will not be purged
+            if ifaceobj.flags & iface.YOUNGEST_SIBLING:
+                self.logger.warning("%s: interface has multiple iface stanzas, skip purging existing addresses" % ifname)
+            purge_addresses = False
 
         if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS:
-            ifaceobjlist = ifaceobj_getfunc(ifaceobj.name)
+            ifaceobj_list = ifaceobj_getfunc(ifname)
         else:
-            ifaceobjlist = [ifaceobj]
-
-        module_name = self.__class__.__name__
-        ifname = ifaceobj.name
+            ifaceobj_list = [ifaceobj]
 
-        (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist)
-        newaddrs = utils.get_ip_objs(module_name, ifname, newaddrs)
+        addr_supported, user_config_ip_addrs_list = self.__get_ip_addr_with_attributes(ifaceobj_list, ifname)
 
         if not addr_supported:
             return
-        if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)):
-            # if youngest sibling and squash addr is not set
-            # print a warning that addresses will not be purged
-            if (ifaceobj.flags & iface.YOUNGEST_SIBLING):
-                self.logger.warn('%s: interface has multiple ' %ifaceobj.name +
-                               'iface stanzas, skip purging existing addresses')
-            purge_addresses = 'no'
 
-        if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes':
-            # if perfmode is not set and purge addresses is not set to 'no'
+        if not ifupdownflags.flags.PERFMODE and purge_addresses:
+            # if perfmode is not set and purge addresses is set to True
             # lets purge addresses not in the config
-            runningaddrs = self.ipcmd.get_running_addrs(ifaceobj, details=False)
+            anycast_ip = None
+
+            running_ip_addrs = self.cache.get_ifupdown2_addresses_list(ifaceobj_list, ifname)
 
-            # if anycast address is configured on 'lo' and is in running config
-            # add it to newaddrs so that ifreload doesn't wipe it out
-            anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist))
+            if ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
+                anycast_ip = self.__add_loopback_anycast_ip_to_running_ip_addr_list(ifaceobj_list)
 
-            if runningaddrs and anycast_addr and anycast_addr in runningaddrs:
-                newaddrs.append(anycast_addr)
+            # user_ip4, user_ip6 and ordered_user_configured_ips  IP addresses are now represented
+            # in string format. Comparaisons between IPNetwork object are not reliable, i.e.:
+            #    IPNetwork("2001:aa::2/64") == IPNetwork("2001:aa::150/64")
+            user_ip4, user_ip6, ordered_user_configured_ips = self.order_user_configured_addrs(user_config_ip_addrs_list)
 
-            user_ip4, user_ip6, newaddrs = self.order_user_configured_addrs(newaddrs)
+            running_ip_addrs_str = self.get_ipnetwork_object_list_in_string_format(running_ip_addrs)
 
-            if newaddrs == runningaddrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, runningaddrs):
+            if ordered_user_configured_ips == running_ip_addrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, running_ip_addrs):
                 if force_reapply:
-                    self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
+                    self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list)
                 return
             try:
-                # if primary address is not same, there is no need to keep any.
-                # reset all addresses
-                if newaddrs and runningaddrs and newaddrs[0] != runningaddrs[0]:
+                # if primary address is not same, there is no need to keep any, reset all addresses.
+                if ordered_user_configured_ips and running_ip_addrs_str and ordered_user_configured_ips[0] != running_ip_addrs_str[0]:
+                    self.logger.info("%s: primary ip changed (from %s to %s) we need to purge all ip addresses and re-add them"
+                                     % (ifname, ordered_user_configured_ips[0], running_ip_addrs_str[0]))
                     skip_addrs = []
                 else:
-                    skip_addrs = newaddrs or []
-                for addr in runningaddrs or []:
+                    skip_addrs = ordered_user_configured_ips
+
+                if anycast_ip:
+                    skip_addrs.append(anycast_ip)
+
+                for index, addr in enumerate(running_ip_addrs_str):
                     if addr in skip_addrs:
                         continue
-                    self.ipcmd.addr_del(ifaceobj.name, addr)
+                    # we still have to send the IPNetwork object
+                    # to the netlink "addr_del" API
+                    self.netlink.addr_del(ifname, running_ip_addrs[index])
             except Exception, e:
                 self.log_warn(str(e))
-        if not newaddrs:
+        if not user_config_ip_addrs_list:
             return
-        self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
+        self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list)
+
+    def get_ipnetwork_object_list_in_string_format(self, obj_list):
+        return [str(obj) for obj in obj_list]
 
     def compare_running_ips_and_user_config(self, user_ip4, user_ip6, running_addrs):
         """
@@ -496,12 +588,13 @@ class address(moduleBase):
 
         return i == len_ip6
 
-    def order_user_configured_addrs(self, user_config_addrs):
+    @staticmethod
+    def order_user_configured_addrs(user_config_addrs):
         ip4 = []
         ip6 = []
 
-        for a in user_config_addrs:
-            if isinstance(a, _BaseV6):
+        for a, _ in user_config_addrs:
+            if a.version == 6:
                 ip6.append(str(a))
             else:
                 ip4.append(str(a))
@@ -511,7 +604,7 @@ class address(moduleBase):
     def _delete_gateway(self, ifaceobj, gateways, vrf, metric):
         for del_gw in gateways:
             try:
-                self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric)
+                self.iproute2.route_del_gateway(ifaceobj.name, del_gw, vrf, metric)
             except Exception as e:
                 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
 
@@ -522,7 +615,7 @@ class address(moduleBase):
                              vrf, metric)
         for add_gw in gateways:
             try:
-                self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink)
+                self.iproute2.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink)
             except Exception as e:
                 self.log_error('%s: %s' % (ifaceobj.name, str(e)))
 
@@ -536,7 +629,7 @@ class address(moduleBase):
             return ipv
         return prev_gateways
 
-    def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False):
+    def _check_mtu_config(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntaxcheck=False):
         retval = True
         if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE):
             if syntaxcheck:
@@ -550,39 +643,41 @@ class address(moduleBase):
                 masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0])
                 if masterobj:
                     master_mtu = masterobj[0].get_attr_value_first('mtu')
-                    if master_mtu and master_mtu != mtu:
+                    if master_mtu and master_mtu != mtu_str:
+                        log_msg = ("%s: bond slave mtu %s is different from bond master %s mtu %s. "
+                                  "There is no need to configure mtu on a bond slave." %
+                                   (ifaceobj.name, mtu_str, masterobj[0].name, master_mtu))
                         if syntaxcheck:
-                            self.logger.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
+                            self.logger.warn(log_msg)
                             retval = False
                         else:
-                            self.logger.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
+                            self.logger.info(log_msg)
             elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and
                   ifaceobj.lowerifaces):
                 lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0])
                 if lowerobj:
                     if syntaxcheck:
-                        lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu')
+                        lowerdev_mtu = int(lowerobj[0].get_attr_value_first('mtu') or 0)
                     else:
-                        lowerdev_mtu = self.ipcmd.link_get_mtu_sysfs(lowerobj[0].name)
-                    if lowerdev_mtu and int(mtu) > int(lowerdev_mtu):
+                        lowerdev_mtu = self.cache.get_link_mtu(lowerobj[0].name)  # return type: int
+                    if lowerdev_mtu and mtu_int > lowerdev_mtu:
                         self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
-                                         %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu))
+                                         %(ifaceobj.name, mtu_str, lowerobj[0].name, lowerdev_mtu))
                         retval = False
                     elif (not lowerobj[0].link_kind and
                           not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
-                          not lowerdev_mtu and self.default_mtu and
-                          (int(mtu) > int(self.default_mtu))):
+                          not lowerdev_mtu and self.default_mtu and (mtu_int > self.default_mtu_int)):
                         # only check default mtu on lower device which is a physical interface
                         self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
-                                         %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu))
+                                         %(ifaceobj.name, mtu_str, lowerobj[0].name, self.default_mtu))
                         retval = False
-            if self.max_mtu and mtu > self.max_mtu:
+            if self.max_mtu and mtu_int > self.max_mtu:
                 self.logger.warn('%s: specified mtu %s is greater than max mtu %s'
-                                 %(ifaceobj.name, mtu, self.max_mtu))
+                                 %(ifaceobj.name, mtu_str, self.max_mtu))
                 retval = False
         return retval
 
-    def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc):
+    def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc):
         if (not ifaceobj.upperifaces or
             (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or
             (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or
@@ -596,53 +691,48 @@ class address(moduleBase):
             # only adjust mtu for vlan devices on ifaceobj
             umtu = upperobjs[0].get_attr_value_first('mtu')
             if not umtu:
-                running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name)
-                if not running_mtu or (running_mtu != mtu):
-                    self.ipcmd.link_set(u, 'mtu', mtu)
+                running_mtu = self.cache.get_link_mtu(upperobjs[0].name)
+                if not running_mtu or running_mtu != mtu_int:
+                    self.sysfs.link_set_mtu(u, mtu_str=mtu_str, mtu_int=mtu_int)
 
-    def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc, mtu):
-        if mtu:
-            if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc):
-                return
-            cached_running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
-            running_mtu = self.ipcmd.link_get_mtu_sysfs(ifaceobj.name)
-            if not running_mtu or (running_mtu and running_mtu != mtu):
-                force = cached_running_mtu != running_mtu
-                self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu, force=force)
-                if (not ifupdownflags.flags.ALL and
+    def _process_mtu_config_mtu_valid(self, ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int):
+        if not self._check_mtu_config(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc):
+            return
+
+        if mtu_int != self.cache.get_link_mtu(ifaceobj.name):
+            self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=mtu_str, mtu_int=mtu_int)
+
+            if (not ifupdownflags.flags.ALL and
                     not ifaceobj.link_kind and
                     ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'):
-                    # This is additional cost to us, so do it only when
-                    # ifupdown2 is called on a particular interface and
-                    # it is a physical interface
-                    self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc)
-            return
+                # This is additional cost to us, so do it only when
+                # ifupdown2 is called on a particular interface and
+                # it is a physical interface
+                self._propagate_mtu_to_upper_devs(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc)
+        return
+
+    def _process_mtu_config_mtu_none(self, ifaceobj):
+        cached_link_mtu = self.cache.get_link_mtu(ifaceobj.name)
 
         if ifaceobj.link_kind:
             # bonds, vxlan and custom devices (like dummy) need an explicit set of mtu.
             # bridges don't need mtu set
-            if (ifaceobj.link_kind & ifaceLinkKind.BOND or
-                ifaceobj.link_kind & ifaceLinkKind.VXLAN or
-                ifaceobj.link_kind & ifaceLinkKind.OTHER
-            ):
-                running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
-                if (self.default_mtu and running_mtu != self.default_mtu):
-                    self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
+            if ifaceobj.link_kind & ifaceLinkKind.BOND \
+                    or ifaceobj.link_kind & ifaceLinkKind.VXLAN \
+                    or ifaceobj.link_kind & ifaceLinkKind.OTHER:
+                if cached_link_mtu != self.default_mtu_int:
+                    self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int)
                 return
+
             if (ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'
                 and ifaceobj.lowerifaces):
                 # set vlan interface mtu to lower device mtu
                 if (ifaceobj.link_kind & ifaceLinkKind.VLAN):
                     lower_iface = ifaceobj.lowerifaces[0]
-                    if lower_iface not in self.lower_iface_mtu_checked_list:
-                        lower_iface_mtu = self.ipcmd.link_get_mtu_sysfs(lower_iface)
-                        self.ipcmd.cache_update([lower_iface, 'mtu'], lower_iface_mtu)
-                        self.lower_iface_mtu_checked_list.append(lower_iface)
-                    else:
-                        lower_iface_mtu = self.ipcmd.link_get_mtu(lower_iface)
+                    lower_iface_mtu_int = self.cache.get_link_mtu(lower_iface)
 
-                    if lower_iface_mtu != self.ipcmd.link_get_mtu_sysfs(ifaceobj.name):
-                        self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu)
+                    if lower_iface_mtu_int != cached_link_mtu:
+                        self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=str(lower_iface_mtu_int), mtu_int=lower_iface_mtu_int)
 
         elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and
               not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
@@ -655,28 +745,28 @@ class address(moduleBase):
             # config by the kernel in play, we try to be cautious here
             # on which devices we want to reset mtu to default.
             # essentially only physical interfaces which are not bond slaves
-            running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
-            if running_mtu != self.default_mtu:
-                self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
+            if cached_link_mtu != self.default_mtu_int:
+                self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int)
 
     def _set_bridge_forwarding(self, ifaceobj):
         """ set ip forwarding to 0 if bridge interface does not have a
         ip nor svi """
         ifname = ifaceobj.name
+
+        netconf_ipv4_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET, ifname)
+        netconf_ipv6_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET6, ifname)
+
         if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address'):
-            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '1':
+            if netconf_ipv4_forwarding:
                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0)
-            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '1':
+            if netconf_ipv6_forwarding:
                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 0)
         else:
-            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '0':
+            if not netconf_ipv4_forwarding:
                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 1)
-            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '0':
+            if not netconf_ipv6_forwarding:
                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 1)
 
-    def sysctl_get_forwarding_value_from_proc(self, ifname, family):
-        return self.read_file_oneline("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname))
-
     def sysctl_write_forwarding_value_to_proc(self, ifname, family, value):
         self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value)
 
@@ -692,10 +782,7 @@ class address(moduleBase):
         if ifupdownflags.flags.PERFMODE:
             running_mpls_enable = '0'
         else:
-            running_mpls_enable = self.read_file_oneline(
-                '/proc/sys/net/mpls/conf/%s/input'
-                % ifaceobj.name
-            )
+            running_mpls_enable = str(self.cache.get_netconf_mpls_input(ifaceobj.name))
 
         if mpls_enable != running_mpls_enable:
             try:
@@ -733,73 +820,65 @@ class address(moduleBase):
                 self.log_error('%s: \'ip6-forward\' is not supported for '
                                'bridge port' %ifaceobj.name)
             return
-
         setting_default_value = False
         if not ipforward:
             setting_default_value = True
-            ipforward = self.ipforward
-
-        if ipforward:
-            ipforward = utils.boolean_support_binary(ipforward)
-            # File read has been used for better performance
-            # instead of using sysctl command
-            running_ipforward = self.read_file_oneline(
-                                    '/proc/sys/net/ipv4/conf/%s/forwarding'
-                                   %ifaceobj.name)
-
-            if ipforward != running_ipforward:
-                try:
-
-                    self.sysctl_set('net.ipv4.conf.%s.forwarding'
-                                    %('/'.join(ifaceobj.name.split("."))),
-                                    ipforward)
-                except Exception as e:
-                    if not setting_default_value:
-                        ifaceobj.status = ifaceStatus.ERROR
-                        self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
-
+            ipforward = (self.ipforward or
+                         self.get_mod_subattr('ip-forward', 'default'))
+        ipforward = int(utils.get_boolean_from_string(ipforward))
+        running_ipforward = self.cache.get_netconf_forwarding(socket.AF_INET, ifaceobj.name)
+        if ipforward != running_ipforward:
+            try:
+                self.sysctl_set('net.ipv4.conf.%s.forwarding'
+                                %('/'.join(ifaceobj.name.split("."))),
+                                ipforward)
+            except Exception as e:
+                if not setting_default_value:
+                    ifaceobj.status = ifaceStatus.ERROR
+                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
 
         setting_default_value = False
         if not ip6forward:
             setting_default_value = True
-            ip6forward = self.ip6forward
-
-        if ip6forward:
-            ip6forward = utils.boolean_support_binary(ip6forward)
-            # File read has been used for better performance
-            # instead of using sysctl command
-            running_ip6forward = self.read_file_oneline(
-                                    '/proc/sys/net/ipv6/conf/%s/forwarding'
-                                    %ifaceobj.name)
-            if ip6forward != running_ip6forward:
-                try:
-                    self.sysctl_set('net.ipv6.conf.%s.forwarding'
-                                    %('/'.join(ifaceobj.name.split("."))),
-                                    ip6forward)
-                except Exception as e:
-                    # There is chance of ipv6 being removed because of,
-                    # for example, setting mtu < 1280
-                    # In such cases, log error only if user has configured
-                    # ip6-forward
-                    if not setting_default_value:
-                        ifaceobj.status = ifaceStatus.ERROR
-                        self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+            ip6forward = (self.ip6forward or
+                          self.get_mod_subattr('ip6-forward', 'default'))
+        ip6forward = int(utils.get_boolean_from_string(ip6forward))
+        running_ip6forward = self.cache.get_netconf_forwarding(socket.AF_INET6, ifaceobj.name)
+        if ip6forward != running_ip6forward:
+            try:
+                self.sysctl_set('net.ipv6.conf.%s.forwarding'
+                                %('/'.join(ifaceobj.name.split("."))),
+                                ip6forward)
+            except Exception as e:
+                # There is chance of ipv6 being removed because of,
+                # for example, setting mtu < 1280
+                # In such cases, log error only if user has configured
+                # ip6-forward
+                if not setting_default_value:
+                    ifaceobj.status = ifaceStatus.ERROR
+                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
 
     def process_mtu(self, ifaceobj, ifaceobj_getfunc):
-        mtu = ifaceobj.get_attr_value_first('mtu')
+        mtu_str = ifaceobj.get_attr_value_first('mtu')
+        mtu_from_policy = False
 
-        if not mtu:
-            default_iface_mtu = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu')
+        if not mtu_str:
+            mtu_str = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu')
+            mtu_from_policy = True
 
-            if default_iface_mtu:
-                try:
-                    mtu = default_iface_mtu
-                    int(default_iface_mtu)
-                except Exception as e:
-                    self.logger.warning('%s: MTU value from policy file: %s' % (ifaceobj.name, str(e)))
-                    return
+        if mtu_str:
+            try:
+                mtu_int = int(mtu_str)
+            except Exception as e:
+                if mtu_from_policy:
+                    self.logger.warning("%s: invalid MTU value from policy file (iface_defaults): %s" % (ifaceobj.name, str(e)))
+                else:
+                    self.logger.warning("%s: invalid MTU value: %s" % (ifaceobj.name, str(e)))
+                return
 
-        self._process_mtu_config(ifaceobj, ifaceobj_getfunc, mtu)
+            self._process_mtu_config_mtu_valid(ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int)
+        else:
+            self._process_mtu_config_mtu_none(ifaceobj)
 
     def up_ipv6_addrgen(self, ifaceobj):
         user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen')
@@ -808,7 +887,7 @@ class address(moduleBase):
             # no need to go further during perfmode (boot)
             return
 
-        if not user_configured_ipv6_addrgen and ifaceobj.addr_method == 'dhcp':
+        if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp"]:
             return
 
         if not user_configured_ipv6_addrgen:
@@ -825,24 +904,24 @@ class address(moduleBase):
         }.get(user_configured_ipv6_addrgen.lower(), None)
 
         if ipv6_addrgen_nl is not None:
-            self.ipcmd.ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True)
+            self.iproute2.batch_start()
+            self.iproute2.link_set_ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True)
+            self.iproute2.batch_commit()
             # link_create=False will flush the addr cache of that intf
         else:
             self.logger.warning('%s: invalid value "%s" for attribute ipv6-addrgen' % (ifaceobj.name, user_configured_ipv6_addrgen))
 
-    def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        if not self.ipcmd.link_exists(ifaceobj.name):
+    def _pre_up(self, ifaceobj, ifaceobj_getfunc=None):
+        if not self.cache.link_exists(ifaceobj.name):
             return
 
         if not self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc):
             return
 
-        alias = ifaceobj.get_attr_value_first('alias')
-        current_alias = self.ipcmd.link_get_alias(ifaceobj.name)
-        if alias and alias != current_alias:
-            self.ipcmd.link_set_alias(ifaceobj.name, alias)
-        elif not alias and current_alias:
-            self.ipcmd.link_set_alias(ifaceobj.name, '')
+        #
+        # alias
+        #
+        self.sysfs.link_set_alias(ifaceobj.name, ifaceobj.get_attr_value_first("alias"))
 
         self._sysctl_config(ifaceobj)
 
@@ -850,7 +929,7 @@ class address(moduleBase):
         force_reapply = False
         try:
             # release any stale dhcp addresses if present
-            if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and
+            if (addr_method not in ["dhcp", "ppp"]  and not ifupdownflags.flags.PERFMODE and
                     not (ifaceobj.flags & iface.HAS_SIBLINGS)):
                 # if not running in perf mode and ifaceobj does not have
                 # any sibling iface objects, kill any stale dhclient
@@ -859,100 +938,122 @@ class address(moduleBase):
                 if dhclientcmd.is_running(ifaceobj.name):
                     # release any dhcp leases
                     dhclientcmd.release(ifaceobj.name)
+                    self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET)
                     force_reapply = True
                 elif dhclientcmd.is_running6(ifaceobj.name):
                     dhclientcmd.release6(ifaceobj.name)
+                    self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6)
                     force_reapply = True
         except:
             pass
 
-        self.ipcmd.batch_start()
         self.up_ipv6_addrgen(ifaceobj)
 
         if addr_method not in ["dhcp", "ppp"]:
-            self._inet_address_config(ifaceobj, ifaceobj_getfunc,
-                                      force_reapply)
+            self.process_addresses(ifaceobj, ifaceobj_getfunc, force_reapply)
         else:
             # remove old addresses added by ifupdown2
             # (if intf was moved from static config to dhcp)
             for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []:
                 for addr in old_ifaceobj.get_attr_value("address") or []:
-                    self.ipcmd.addr_del(ifaceobj.name, addr)
+                    self.netlink.addr_del(ifaceobj.name, addr)
 
         self.process_mtu(ifaceobj, ifaceobj_getfunc)
 
         try:
-            self.ipcmd.batch_commit()
-        except Exception as e:
-            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False)
+            self.process_hwaddress(ifaceobj)
 
-        self.up_hwaddress(ifaceobj)
+            # Handle special things on a bridge
+            self._process_bridge(ifaceobj, True)
+        except Exception, e:
+            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
 
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
         gateways = ifaceobj.get_attr_value('gateway')
         if not gateways:
             gateways = []
         prev_gw = self._get_prev_gateway(ifaceobj, gateways)
         self._add_delete_gateway(ifaceobj, gateways, prev_gw)
 
-    def up_hwaddress(self, ifaceobj):
-        try:
-            hwaddress = self._get_hwaddress(ifaceobj)
+    def process_hwaddress(self, ifaceobj):
+        hwaddress = self._get_hwaddress(ifaceobj)
 
-            if hwaddress:
-                if not ifupdownflags.flags.PERFMODE:  # system is clean
-                    running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
-                else:
-                    running_hwaddress = None
-
-                if self.ipcmd.mac_str_to_int(running_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress):
-                    slave_down = False
-                    netlink.link_set_updown(ifaceobj.name, "down")
-                    if ifaceobj.link_kind & ifaceLinkKind.BOND:
-                        # if bond, down all the slaves
-                        if ifaceobj.lowerifaces:
-                            for l in ifaceobj.lowerifaces:
-                                netlink.link_set_updown(l, "down")
-                            slave_down = True
-                    try:
-                        self.ipcmd.link_set_hwaddress(ifaceobj.name, hwaddress, force=True)
-                    finally:
-                        netlink.link_set_updown(ifaceobj.name, "up")
-                        if slave_down:
-                            for l in ifaceobj.lowerifaces:
-                                netlink.link_set_updown(l, "up")
+        if not hwaddress:
+            if ifaceobj.link_kind & ifaceLinkKind.VLAN:
+                # When hwaddress is removed from vlan config
+                # we should go back to system or bridge mac
+                for lower in ifaceobj.lowerifaces:
+                    if self.cache.get_link_kind(lower) == "bridge":
+                        hwaddress = self.cache.get_link_address(lower)
+                        break
+                if not hwaddress:
+                    return
+            else:
+                return
 
-            # Handle special things on a bridge
-            self._process_bridge(ifaceobj, True)
-        except Exception, e:
-            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
+        if not ifupdownflags.flags.PERFMODE:  # system is clean
+            running_hwaddress = self.cache.get_link_address(ifaceobj.name)
+        else:
+            running_hwaddress = None
+
+        if utils.mac_str_to_int(hwaddress) != utils.mac_str_to_int(running_hwaddress):
+            slave_down = False
+            if ifaceobj.link_kind & ifaceLinkKind.BOND:
+                # if bond, down all the slaves
+                if ifaceobj.lowerifaces:
+                    for l in ifaceobj.lowerifaces:
+                        self.netlink.link_down(l)
+                    slave_down = True
+            try:
+                self.netlink.link_set_address(ifaceobj.name, hwaddress)
+            finally:
+                if slave_down:
+                    for l in ifaceobj.lowerifaces:
+                        self.netlink.link_up(l)
 
     def _down(self, ifaceobj, ifaceobj_getfunc=None):
         try:
-            if not self.ipcmd.link_exists(ifaceobj.name):
+            if not self.cache.link_exists(ifaceobj.name):
                 return
             addr_method = ifaceobj.addr_method
             if addr_method not in ["dhcp", "ppp"]:
                 if ifaceobj.get_attr_value_first('address-purge')=='no':
                     addrlist = ifaceobj.get_attr_value('address')
-                    for addr in addrlist:
-                        self.ipcmd.addr_del(ifaceobj.name, addr)
-                    #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0])
+                    for addr in addrlist or []:
+                        self.netlink.addr_del(ifaceobj.name, addr)
                 elif not ifaceobj.link_kind:
                     # for logical interfaces we don't need to remove the ip addresses
                     # kernel will do it for us on 'ip link del'
-                    self.ipcmd.del_addr_all(ifaceobj.name)
+                    if ifaceobj_getfunc:
+                        ifaceobj_list = ifaceobj_getfunc(ifaceobj.name) or [ifaceobj]
+                    else:
+                        ifaceobj_list = [ifaceobj]
+
+                    for addr in self.cache.get_ifupdown2_addresses_list(ifaceobj_list, ifaceobj.name):
+                        self.netlink.addr_del(ifaceobj.name, addr)
+
             gateways = ifaceobj.get_attr_value('gateway')
             if gateways:
                 self._delete_gateway(ifaceobj, gateways,
                                      ifaceobj.get_attr_value_first('vrf'),
                                      ifaceobj.get_attr_value_first('metric'))
-            mtu = ifaceobj.get_attr_value_first('mtu')
-            if (not ifaceobj.link_kind and mtu and
-                self.default_mtu and (mtu != self.default_mtu)):
-                self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
-            alias = ifaceobj.get_attr_value_first('alias')
-            if alias:
-                self.write_file('/sys/class/net/%s/ifalias' % ifaceobj.name, '\n')
+
+            #
+            # mtu --
+            # If device is not a logical intf and has its MTU configured by
+            # ifupdown2. If this MTU is different from our default mtu,
+            # if so we need to reset it back to default.
+            if not ifaceobj.link_kind and self.default_mtu and ifaceobj.get_attr_value_first('mtu') and self.cache.get_link_mtu(ifaceobj.name) != self.default_mtu_int:
+                self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int)
+
+            #
+            # alias
+            # only reset alias on non-logical device
+            if not ifaceobj.link_kind:
+                alias = ifaceobj.get_attr_value_first("alias")
+                if alias:
+                    self.sysfs.link_set_alias(ifaceobj.name, None)  # None to reset alias.
+
             # XXX hwaddress reset cannot happen because we dont know last
             # address.
 
@@ -980,7 +1081,7 @@ class address(moduleBase):
     def _get_bridge_fdbs(self, bridgename, vlan):
         fdbs = self._bridge_fdb_query_cache.get(bridgename)
         if not fdbs:
-           fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
+           fdbs = self.iproute2.bridge_fdb_show_dev(bridgename)
            if not fdbs:
               return
            self._bridge_fdb_query_cache[bridgename] = fdbs
@@ -992,11 +1093,11 @@ class address(moduleBase):
         if ifaceobj.link_kind & ifaceLinkKind.VLAN:
             bridgename = ifaceobj.lowerifaces[0]
             vlan = self._get_vlan_id(ifaceobj)
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                fdb_addrs = [self.ipcmd.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))]
+            if self.cache.bridge_is_vlan_aware(bridgename):
+                fdb_addrs = [utils.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))]
                 if not fdb_addrs:
-                    return False
-                hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress)
+                   return False
+                hwaddress_int = utils.mac_str_to_int(hwaddress)
                 if hwaddress_int not in fdb_addrs:
                     return False
         return True
@@ -1011,10 +1112,7 @@ class address(moduleBase):
                                            'for bridge port')
                 ifaceobjcurr.update_config_with_status('ip-forward', 1, None)
             else:
-                running_ipforward = self.read_file_oneline(
-                                        '/proc/sys/net/ipv4/conf/%s/forwarding'
-                                        %ifaceobj.name)
-                running_ipforward = utils.get_boolean_from_string(running_ipforward)
+                running_ipforward = self.cache.get_netconf_forwarding(socket.AF_INET, ifaceobj.name)
                 config_ipforward = utils.get_boolean_from_string(ipforward)
                 ifaceobjcurr.update_config_with_status(
                     'ip-forward',
@@ -1030,10 +1128,7 @@ class address(moduleBase):
                                            'for bridge port')
                 ifaceobjcurr.update_config_with_status('ip6-forward', 1, None)
             else:
-                running_ip6forward = self.read_file_oneline(
-                                        '/proc/sys/net/ipv6/conf/%s/forwarding'
-                                        %ifaceobj.name)
-                running_ip6forward = utils.get_boolean_from_string(running_ip6forward)
+                running_ip6forward = self.cache.get_netconf_forwarding(socket.AF_INET6, ifaceobj.name)
                 config_ip6forward = utils.get_boolean_from_string(ip6forward)
                 ifaceobjcurr.update_config_with_status(
                     'ip6-forward',
@@ -1042,11 +1137,7 @@ class address(moduleBase):
                 )
         mpls_enable = ifaceobj.get_attr_value_first('mpls-enable');
         if mpls_enable:
-            running_mpls_enable = self.read_file_oneline(
-                                    '/proc/sys/net/mpls/conf/%s/input'
-                                    %ifaceobj.name)
-            running_mpls_enable = utils.get_yesno_from_onezero(
-                                            running_mpls_enable)
+            running_mpls_enable = utils.get_yesno_from_onezero(str(self.cache.get_netconf_mpls_input(ifaceobj.name)))
             ifaceobjcurr.update_config_with_status('mpls-enable',
                                                    running_mpls_enable,
                                             mpls_enable != running_mpls_enable)
@@ -1062,14 +1153,17 @@ class address(moduleBase):
             ifaceobjcurr.update_config_with_status(
                 'ipv6-addrgen',
                 ipv6_addrgen,
-                utils.get_boolean_from_string(ipv6_addrgen) == self.ipcmd.get_ipv6_addrgen_mode(ifaceobj.name)
+                utils.get_boolean_from_string(ipv6_addrgen) == self.cache.get_link_ipv6_addrgen_mode(ifaceobj.name)
             )
         else:
             ifaceobjcurr.update_config_with_status('ipv6-addrgen', ipv6_addrgen, 1)
 
     def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+        """
+        TODO: Check broadcast address, scope, etc
+        """
         runningaddrsdict = None
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             self.logger.debug('iface %s not found' %ifaceobj.name)
             return
 
@@ -1077,11 +1171,11 @@ class address(moduleBase):
 
         addr_method = ifaceobj.addr_method
         self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
-                'mtu', self.ipcmd.link_get_mtu)
+                'mtu', self.cache.get_link_mtu_str)
         hwaddress = self._get_hwaddress(ifaceobj)
         if hwaddress:
-            rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
-            if not rhwaddress or self.ipcmd.mac_str_to_int(rhwaddress) != self.ipcmd.mac_str_to_int(hwaddress):
+            rhwaddress = self.cache.get_link_address(ifaceobj.name)
+            if not rhwaddress or utils.mac_str_to_int(rhwaddress) != utils.mac_str_to_int(hwaddress):
                ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
                        1)
             elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
@@ -1093,69 +1187,71 @@ class address(moduleBase):
                ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
                        0)
         self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
-                    'alias', self.ipcmd.link_get_alias)
+                    'alias', self.cache.get_link_alias)
         self._query_sysctl(ifaceobj, ifaceobjcurr)
         # compare addresses
         if addr_method in ["dhcp", "ppp"]:
            return
-        addrs = utils.get_normalized_ip_addr(ifaceobj.name,
-                                             self._get_iface_addresses(ifaceobj))
-        runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobj)
-        # if anycast address is configured on 'lo' and is in running config
-        # add it to addrs so that query_check doesn't fail
-        anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip'))
-        if anycast_addr:
-            anycast_addr = anycast_addr+'/32'
-        if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr):
-            addrs.append(anycast_addr)
+
+        if ifaceobj_getfunc:
+            ifaceobj_list = ifaceobj_getfunc(ifaceobj.name)
+        else:
+            ifaceobj_list = [ifaceobj]
+
+        intf_running_addrs = self.cache.get_ifupdown2_addresses_list(ifaceobj_list, ifaceobj.name)
+        user_config_addrs = self.cache.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format([ifaceobj], details=False)
+
+        try:
+            clagd_vxlan_anycast_ip = IPNetwork(ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip'))
+
+            if clagd_vxlan_anycast_ip in intf_running_addrs:
+                user_config_addrs.append(clagd_vxlan_anycast_ip)
+        except:
+            pass
 
         # Set ifaceobjcurr method and family
         ifaceobjcurr.addr_method = ifaceobj.addr_method
         ifaceobjcurr.addr_family = ifaceobj.addr_family
-        if not runningaddrsdict and not addrs:
+
+        if not intf_running_addrs and not user_config_addrs:
+            # The device doesn't have any ips configured and the
+            # the user didn't specify any ip in the configuration file
             return
-        runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
-        # Add /32 netmask to configured address without netmask.
-        # This may happen on interfaces where pointopoint is used.
-        runningaddrs = [ addr if '/' in addr else addr + '/32' for addr in runningaddrs]
-        if runningaddrs != addrs:
-            runningaddrsset = set(runningaddrs) if runningaddrs else set([])
-            addrsset = set(addrs) if addrs else set([])
-            if (ifaceobj.flags & iface.HAS_SIBLINGS):
-                if not addrsset:
-                    return
-                # only check for addresses present in running config
-                addrsdiff = addrsset.difference(runningaddrsset)
-                for addr in addrs:
-                    if addr in addrsdiff:
-                        ifaceobjcurr.update_config_with_status('address',
-                                    addr, 1)
-                    else:
-                        ifaceobjcurr.update_config_with_status('address',
-                                    addr, 0)
-            else:
-                addrsdiff = addrsset.symmetric_difference(runningaddrsset)
-                for addr in addrsset.union(runningaddrsset):
-                    if addr in addrsdiff:
-                        ifaceobjcurr.update_config_with_status('address',
-                                                               addr, 1)
-                    else:
-                        ifaceobjcurr.update_config_with_status('address',
-                                                               addr, 0)
-        elif addrs:
-            [ifaceobjcurr.update_config_with_status('address',
-                       addr, 0) for addr in addrs]
-        #XXXX Check broadcast address, scope, etc
+
+        for address in user_config_addrs:
+            ifaceobjcurr.update_config_with_status('address', str(address), address not in intf_running_addrs)
+            try:
+                intf_running_addrs.remove(address)
+            except:
+                pass
+
+        # if any ip address is left in 'intf_running_addrs' it means
+        # that they used to be configured by ifupdown2 but not anymore
+        # but are still on the intf, so we need to mark them as fail
+        # we will only mark them as failure on the first sibling
+        if ifaceobj.flags & iface.HAS_SIBLINGS:
+            if not ifaceobj.flags & iface.YOUNGEST_SIBLING:
+                return
+
+        all_stanza_user_config_ip = self.cache.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format(
+            ifaceobj_list,
+            details=False
+        )
+
+        for address in intf_running_addrs:
+            if address not in all_stanza_user_config_ip:
+                ifaceobjcurr.update_config_with_status('address', str(address), 1)
+
         return
 
     def query_running_ipv6_addrgen(self, ifaceobjrunning):
-        ipv6_addrgen = self.ipcmd.get_ipv6_addrgen_mode(ifaceobjrunning.name)
+        ipv6_addrgen = self.cache.get_link_ipv6_addrgen_mode(ifaceobjrunning.name)
 
         if ipv6_addrgen:
             ifaceobjrunning.update_config('ipv6-addrgen', 'off')
 
     def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+        if not self.cache.link_exists(ifaceobjrunning.name):
             self.logger.debug('iface %s not found' %ifaceobjrunning.name)
             return
 
@@ -1166,46 +1262,44 @@ class address(moduleBase):
                 dhclientcmd.is_running6(ifaceobjrunning.name)):
             # If dhcp is configured on the interface, we skip it
             return
-        isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
-        if isloopback:
-            default_addrs = ['127.0.0.1/8', '::1/128']
+
+        intf_running_addrs = self.cache.get_addresses_list(ifaceobjrunning.name) or []
+
+        if self.cache.link_is_loopback(ifaceobjrunning.name):
+            for default_addr in self.default_loopback_addresses:
+                try:
+                    intf_running_addrs.remove(default_addr)
+                except:
+                    pass
             ifaceobjrunning.addr_family.append('inet')
             ifaceobjrunning.addr_method = 'loopback'
-        else:
-            default_addrs = []
-        runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobjrunning)
-        if runningaddrsdict:
-            [ifaceobjrunning.update_config('address', addr)
-                for addr, addrattrs in runningaddrsdict.items()
-                if addr not in default_addrs]
-        mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
+
+        for addr in intf_running_addrs:
+            ifaceobjrunning.update_config('address', str(addr))
+
+        mtu = self.cache.get_link_mtu_str(ifaceobjrunning.name)
         if (mtu and
                 (ifaceobjrunning.name == 'lo' and mtu != '16436') or
                 (ifaceobjrunning.name != 'lo' and
                     mtu != self.get_mod_subattr('mtu', 'default'))):
                 ifaceobjrunning.update_config('mtu', mtu)
-        alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
+
+        alias = self.cache.get_link_alias(ifaceobjrunning.name)
         if alias:
             ifaceobjrunning.update_config('alias', alias)
 
-        ipforward = self.read_file_oneline(
-                        '/proc/sys/net/ipv4/conf/%s/forwarding'
-                        %ifaceobjrunning.name)
-
-
-    _run_ops = {'up' : _up,
-               'down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running }
+    _run_ops = {
+        'pre-up': _pre_up,
+        'up': _up,
+        'down': _down,
+        'query-checkcurr': _query_check,
+        'query-running': _query_running
+    }
 
     def get_ops(self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
         """ run address configuration on the interface object passed as argument
 
@@ -1227,7 +1321,6 @@ class address(moduleBase):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj,
                        ifaceobj_getfunc=ifaceobj_getfunc)
index be16c8118125330f45b183ce54aa0ee327ddb6ea..f15d5c2ab8debd40beaa4a53efd4f8d17f37a1ff 100644 (file)
@@ -6,19 +6,18 @@
 
 import os
 import glob
+import subprocess
 
 from collections import deque
 from ipaddr import IPNetwork, IPv6Network
 
 try:
+    from ifupdown2.lib.addon import Addon
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
 
     from ifupdown2.nlmanager.nlpacket import Link
 
-    from ifupdown2.ifupdownaddons.cache import *
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     import ifupdown2.ifupdown.statemanager as statemanager
@@ -26,14 +25,12 @@ try:
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
 except ImportError:
+    from lib.addon import Addon
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
 
     from nlmanager.nlpacket import Link
 
-    from ifupdownaddons.cache import *
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.statemanager as statemanager
@@ -42,42 +39,45 @@ except ImportError:
     import ifupdown.ifupdownconfig as ifupdownconfig
 
 
-class addressvirtual(moduleBase):
+class addressvirtual(Addon, moduleBase):
     """  ifupdown2 addon module to configure virtual addresses """
 
-    _modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
-                          'interfaces. It creates a macvlan interface for ' +
-                          'every mac ip address-virtual line',
-                'attrs' : {
-                    'address-virtual' :
-                        { 'help' : 'bridge router virtual mac and ips',
-                          'multivalue' : True,
-                          'validvals' : ['<mac-ip/prefixlen-list>',],
-                          'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']
-                          },
-                    'address-virtual-ipv6-addrgen': {
-                        'help': 'enable disable ipv6 link addrgenmode',
-                        'validvals': ['on', 'off'],
-                        'default': 'on',
-                        'example': [
-                            'address-virtual-ipv6-addrgen on',
-                            'address-virtual-ipv6-addrgen off'
-                        ]
-                    },
-                    "vrrp": {
-                        "help": "VRRP support",
-                        "multivalue": True,
-                        "example": [
-                            "vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
-                            "vrrp 42 10.0.0.42/24"
-                        ]
-                    }
-                }}
+    _modinfo = {
+        "mhelp": "address module configures virtual addresses for interfaces. "
+                 "It creates a macvlan interface for every mac ip address-virtual line",
+        "attrs": {
+            "address-virtual": {
+                "help": "bridge router virtual mac and ips",
+                "multivalue": True,
+                "validvals": ["<mac-ip/prefixlen-list>", ],
+                "example": ["address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24"]
+            },
+            "address-virtual-ipv6-addrgen": {
+                "help": "enable disable ipv6 link addrgenmode",
+                "validvals": ["on", "off"],
+                "default": "on",
+                "example": [
+                    "address-virtual-ipv6-addrgen on",
+                    "address-virtual-ipv6-addrgen off"
+                ]
+            },
+            "vrrp": {
+                "help": "VRRP support",
+                "multivalue": True,
+                "example": [
+                    "vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
+                    "vrrp 42 10.0.0.42/24"
+                ]
+            }
+        }
+    }
 
+    DEFAULT_IP_METRIC = 1024
+    ADDR_METRIC_SUPPORT = None
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
         self._bridge_fdb_query_cache = {}
         self.addressvirtual_with_route_metric = utils.get_boolean_from_string(
             policymanager.policymanager_api.get_module_globals(
@@ -89,6 +89,27 @@ class addressvirtual(moduleBase):
 
         self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
 
+        if addressvirtual.ADDR_METRIC_SUPPORT is None:
+            try:
+                cmd = [utils.ip_cmd, 'addr', 'help']
+                self.logger.info('executing %s addr help' % utils.ip_cmd)
+
+                process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                stdout, stderr = process.communicate()
+                addressvirtual.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
+                self.logger.info('address metric support: %s' % ('OK' if addressvirtual.ADDR_METRIC_SUPPORT else 'KO'))
+            except Exception:
+                addressvirtual.ADDR_METRIC_SUPPORT = False
+                self.logger.info('address metric support: KO')
+
+    @classmethod
+    def addr_metric_support(cls):
+        return cls.ADDR_METRIC_SUPPORT
+
+    @classmethod
+    def get_default_ip_metric(cls):
+        return cls.DEFAULT_IP_METRIC
+
     def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
         if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
             ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
@@ -96,20 +117,19 @@ class addressvirtual(moduleBase):
     def _get_macvlan_prefix(self, ifaceobj):
         return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
 
-    @staticmethod
-    def get_vrrp_prefix(ifname, family):
-        return "vrrp%s-%s-" % (family, netlink.get_iface_index(ifname))
+    def get_vrrp_prefix(self, ifname, family):
+        return "vrrp%s-%s-" % (family, self.cache.get_ifindex(ifname))
 
     def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
         # XXX: batch the addresses
         if ifaceobj.link_kind & ifaceLinkKind.VLAN:
             bridgename = ifaceobj.lowerifaces[0]
             vlan = self._get_vlan_id(ifaceobj)
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                [self.ipcmd.bridge_fdb_add(bridgename, addr,
+            if self.cache.bridge_is_vlan_aware(bridgename):
+                [self.iproute2.bridge_fdb_add(bridgename, addr,
                     vlan) for addr in hwaddress]
-        elif self.ipcmd.is_bridge(ifaceobj.name):
-            [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
+        elif self.cache.link_is_bridge(ifaceobj.name):
+            [self.iproute2.bridge_fdb_add(ifaceobj.name, addr)
                     for addr in hwaddress]
 
     def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
@@ -117,17 +137,17 @@ class addressvirtual(moduleBase):
         if ifaceobj.link_kind & ifaceLinkKind.VLAN:
             bridgename = ifaceobj.lowerifaces[0]
             vlan = self._get_vlan_id(ifaceobj)
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
+            if self.cache.bridge_is_vlan_aware(bridgename):
                 for addr in hwaddress:
                     try:
-                        self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
+                        self.iproute2.bridge_fdb_del(bridgename, addr, vlan)
                     except Exception, e:
                         self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
                         pass
-        elif self.ipcmd.is_bridge(ifaceobj.name):
+        elif self.cache.link_is_bridge(ifaceobj.name):
             for addr in hwaddress:
                 try:
-                    self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
+                    self.iproute2.bridge_fdb_del(ifaceobj.name, addr)
                 except Exception, e:
                     self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
                     pass
@@ -135,7 +155,7 @@ class addressvirtual(moduleBase):
     def _get_bridge_fdbs(self, bridgename, vlan):
         fdbs = self._bridge_fdb_query_cache.get(bridgename)
         if not fdbs:
-           fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
+           fdbs = self.iproute2.bridge_fdb_show_dev(bridgename)
            if not fdbs:
               return
            self._bridge_fdb_query_cache[bridgename] = fdbs
@@ -147,13 +167,13 @@ class addressvirtual(moduleBase):
         if ifaceobj.link_kind & ifaceLinkKind.VLAN:
             bridgename = ifaceobj.lowerifaces[0]
             vlan = self._get_vlan_id(ifaceobj)
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
+            if self.cache.bridge_is_vlan_aware(bridgename):
                 fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
                 if not fdb_addrs:
                    return False
-                hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress)
+                hwaddress_int = utils.mac_str_to_int(hwaddress)
                 for mac in fdb_addrs:
-                    if self.ipcmd.mac_str_to_int(mac) == hwaddress_int:
+                    if utils.mac_str_to_int(mac) == hwaddress_int:
                         return True
                 return False
         return True
@@ -182,29 +202,24 @@ class addressvirtual(moduleBase):
             route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
 
             if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
-                vrf_master = self.ipcmd.link_get_master(ifaceobj.name)
+                vrf_master = self.cache.get_master(ifaceobj.name)
             else:
                 vrf_master = None
 
-            dev = self.ipcmd.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
+            dev = self.iproute2.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
 
             if dev and dev != ifaceobj.name:
                 self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
                                  'seems to be of the macvlan dev %s'
                                  %vifacename +
                                  ' .. flapping macvlan dev to fix entry.')
-                self.ipcmd.link_down(vifacename)
-                self.ipcmd.link_up(vifacename)
+                self.iproute2.link_down(vifacename)
+                self.iproute2.link_up(vifacename)
         except Exception, e:
             self.logger.debug('%s: fixing route entry failed (%s)'
                               % (ifaceobj.name, str(e)))
             pass
 
-    def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
-        vrfname = self.ipcmd.link_get_master(ifaceobj.name)
-        if vrfname:
-            self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
-
     def _get_macs_from_old_config(self, ifaceobj=None):
         """ This method returns a list of the mac addresses
         in the address-virtual attribute for the bridge. """
@@ -251,10 +266,10 @@ class addressvirtual(moduleBase):
         return False, None
 
     def _remove_running_address_config(self, ifaceobj):
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
         hwaddress = []
-        self.ipcmd.batch_start()
+
         for macvlan_prefix in [
             self._get_macvlan_prefix(ifaceobj),
             self.get_vrrp_prefix(ifaceobj.name, "4"),
@@ -262,13 +277,13 @@ class addressvirtual(moduleBase):
         ]:
             for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % macvlan_prefix):
                 macvlan_ifacename = os.path.basename(macvlan_ifacename)
-                if not self.ipcmd.link_exists(macvlan_ifacename):
+                if not self.cache.link_exists(macvlan_ifacename):
                     continue
-                hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
-                self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+                hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
+                self.netlink.link_del(os.path.basename(macvlan_ifacename))
                 # XXX: Also delete any fdb addresses. This requires, checking mac address
                 # on individual macvlan interfaces and deleting the vlan from that.
-        self.ipcmd.batch_commit()
+
         if any(hwaddress):
             self._remove_addresses_from_bridge(ifaceobj, hwaddress)
 
@@ -277,28 +292,20 @@ class addressvirtual(moduleBase):
             self._remove_running_address_config(ifaceobj)
             return
 
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
         hwaddress = []
-        self.ipcmd.batch_start()
         av_idx = 0
         macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
         for av in address_virtual_list:
             av_attrs = av.split()
-            if len(av_attrs) < 2:
-                self.log_error("%s: incorrect address-virtual attrs '%s'"
-                               %(ifaceobj.name,  av), ifaceobj,
-                               raise_error=False)
-                av_idx += 1
-                continue
 
             # Delete the macvlan device on this device
             macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
-            self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+            self.netlink.link_del(os.path.basename(macvlan_ifacename))
             if av_attrs[0] != 'None':
                 hwaddress.append(av_attrs[0])
             av_idx += 1
-        self.ipcmd.batch_commit()
         self._remove_addresses_from_bridge(ifaceobj, hwaddress)
 
     def check_mac_address(self, ifaceobj, mac):
@@ -325,12 +332,12 @@ class addressvirtual(moduleBase):
         if not ifaceobj_getfunc:
             return
         if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
-            self.ipcmd.link_exists(ifaceobj.name)):
+            self.cache.link_exists(ifaceobj.name)):
             # if I am a vrf device and I have slaves
             # that have address virtual config,
             # enslave the slaves 'address virtual
             # interfaces (macvlans)' to myself:
-            running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
+            running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
             if running_slaves:
                 # pick up any existing slaves of a vrf device and
                 # look for their upperdevices and enslave them to the
@@ -341,7 +348,7 @@ class addressvirtual(moduleBase):
                         (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
                         # enslave all its upper devices to
                         # the vrf device
-                        upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
+                        upperdevs = self.sysfs.link_get_uppers(sobjs[0].name)
                         if not upperdevs:
                             continue
                         for u in upperdevs:
@@ -350,18 +357,18 @@ class addressvirtual(moduleBase):
                             # upper device list
                             if u == ifaceobj.name:
                                 continue
-                            self.ipcmd.link_set(u, 'master', ifaceobj.name,
-                                                state='up')
+                            self.netlink.link_set_master(u, ifaceobj.name)
+                            self.netlink.link_up(u)
         elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
               (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
-              self.ipcmd.link_exists(ifaceobj.name)):
+              self.cache.link_exists(ifaceobj.name)):
             # If I am a vrf slave and I have 'address virtual'
             # config, make sure my addrress virtual interfaces
             # (macvlans) are also enslaved to the vrf device
             vrfname = ifaceobj.get_attr_value_first('vrf')
-            if not vrfname or not self.ipcmd.link_exists(vrfname):
+            if not vrfname or not self.cache.link_exists(vrfname):
                 return
-            running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
+            running_uppers = self.sysfs.link_get_uppers(ifaceobj.name)
             if not running_uppers:
                 return
             macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
@@ -371,10 +378,11 @@ class addressvirtual(moduleBase):
                 if u == vrfname:
                     continue
                 if u.startswith(macvlan_prefix):
-                    self.ipcmd.link_set(u, 'master', vrfname,
-                                        state='up')
+                    self.netlink.link_set_master(u, vrfname)
+                    self.netlink.link_up(u)
 
     def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False):
+
         """
         intf_config_list = [
             {
@@ -384,17 +392,25 @@ class addressvirtual(moduleBase):
             },
         ]
         """
+        hw_address_list = []
+
+        if not intf_config_list:
+            return hw_address_list
+
         user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj)
         purge_existing = False if ifupdownflags.flags.PERFMODE else True
-        hw_address_list = []
         ifname = ifaceobj.name
 
-        lower_iface_mtu = update_mtu = None
+        update_mtu = lower_iface_mtu = lower_iface_mtu_str = None
         if ifupdownconfig.config.get("adjust_logical_dev_mtu", "1") != "0":
             if ifaceobj.lowerifaces and intf_config_list:
                 update_mtu = True
 
-        self.ipcmd.batch_start()
+        if update_mtu:
+            lower_iface_mtu = self.cache.get_link_mtu(ifaceobj.name)
+            lower_iface_mtu_str = str(lower_iface_mtu)
+
+        self.iproute2.batch_start()  # TODO: make sure we only do 1 ip link set down and set up (only one flap in the batch)
 
         for intf_config_dict in intf_config_list:
             link_created = False
@@ -403,19 +419,27 @@ class addressvirtual(moduleBase):
             macvlan_mode = intf_config_dict.get("mode")
             ips = intf_config_dict.get("ips")
 
-            if not self.ipcmd.link_exists(macvlan_ifname):
-                self.ipcmd.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
+            if not self.cache.link_exists(macvlan_ifname):
+                # When creating VRRP macvlan with bridge mode, the kernel
+                # return an error: 'Invalid argument' (22)
+                # so for now we should only use the iproute2 API.
+                # try:
+                #    self.netlink.link_add_macvlan(ifname, macvlan_ifname)
+                # except:
+                self.iproute2.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
                 link_created = True
 
             # first thing we need to handle vrf enslavement
             if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
-                self._handle_vrf_slaves(macvlan_ifname, ifaceobj)
+                vrf_ifname = self.cache.get_master(ifaceobj.name)
+                if vrf_ifname:
+                    self.iproute2.link_set_master(macvlan_ifname, vrf_ifname)
 
             # if we are dealing with a VRRP macvlan we need to set addrgenmode
             # to RANDOM, and protodown on
             if vrrp:
                 try:
-                    self.ipcmd.ipv6_addrgen(
+                    self.iproute2.link_set_ipv6_addrgen(
                         macvlan_ifname,
                         Link.IN6_ADDR_GEN_MODE_RANDOM,
                         link_created
@@ -425,22 +449,26 @@ class addressvirtual(moduleBase):
                                      "operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
                 try:
                     if link_created:
-                        netlink.link_set_protodown(macvlan_ifname, "on")
+                        self.netlink.link_set_protodown_on(macvlan_ifname)
                 except Exception as e:
                     self.logger.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
             elif user_configured_ipv6_addrgenmode:
-                self.ipcmd.ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
+                self.iproute2.link_set_ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
 
             if macvlan_hwaddr:
-                self.ipcmd.link_set_hwaddress(macvlan_ifname, macvlan_hwaddr, keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN)
+                self.iproute2.link_set_address_and_keep_down(
+                    macvlan_ifname,
+                    macvlan_hwaddr,
+                    keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN
+                )
                 hw_address_list.append(macvlan_hwaddr)
 
-            if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
-                metric = self.ipcmd.get_default_ip_metric()
+            if self.addressvirtual_with_route_metric and self.addr_metric_support():
+                metric = self.get_default_ip_metric()
             else:
                 metric = None
 
-            self.ipcmd.addr_add_multiple(
+            self.iproute2.add_addresses(
                 ifaceobj,
                 macvlan_ifname,
                 ips,
@@ -450,53 +478,58 @@ class addressvirtual(moduleBase):
 
             if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
                 self.logger.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname, ifname))
-                netlink.link_set_updown(macvlan_ifname, "down")
+                self.netlink.link_down(macvlan_ifname)
 
             # If link existed before, flap the link
             if not link_created:
 
-                if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
+                if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
                     # if the system doesn't support ip addr set METRIC
                     # we need to do manually check the ordering of the ip4 routes
                     self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
 
                 if update_mtu:
-                    lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
                     update_mtu = False
 
-                if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
                     try:
-                        self.ipcmd.link_set_mtu(macvlan_ifname,
-                                                lower_iface_mtu)
+                        self.sysfs.link_set_mtu(macvlan_ifname, mtu_str=lower_iface_mtu_str, mtu_int=lower_iface_mtu)
                     except Exception as e:
-                        self.logger.info('%s: failed to set mtu %s: %s' %
-                                         (macvlan_ifname, lower_iface_mtu, e))
+                        self.logger.info('%s: failed to set mtu %s: %s' % (macvlan_ifname, lower_iface_mtu, e))
 
                 # set macvlan device to up in anycase.
                 # since we auto create them here..we are responsible
                 # to bring them up here in the case they were brought down
                 # by some other entity in the system.
                 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
-                    netlink.link_set_updown(macvlan_ifname, "up")
+                    self.netlink.link_up(macvlan_ifname)
             else:
                 try:
-                    if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
+                    if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
                         # if the system doesn't support ip addr set METRIC
                         # we need to do manually check the ordering of the ip6 routes
-                        self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
+                        self.iproute2.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
                 except Exception as e:
                     self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
 
             # Disable IPv6 duplicate address detection on VRR interfaces
+            sysctl_prefix = "net.ipv6.conf.%s" % macvlan_ifname
+
+            try:
+                syskey = "%s.%s" % (sysctl_prefix, "enhanced_dad")
+                if self.sysctl_get(syskey) != "0":
+                    self.sysctl_set(syskey, "0")
+            except Exception as e:
+                self.logger.info("sysctl failure: operation not supported: %s" % str(e))
+
             for key, sysval in {
                 "accept_dad": "0",
                 "dad_transmits": "0"
             }.iteritems():
-                syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
+                syskey = "%s.%s" % (sysctl_prefix, key)
                 if self.sysctl_get(syskey) != sysval:
                     self.sysctl_set(syskey, sysval)
 
-        self.ipcmd.batch_commit()
+        self.iproute2.batch_commit()
         return hw_address_list
 
     def _up(self, ifaceobj, ifaceobj_getfunc=None):
@@ -518,7 +551,7 @@ class addressvirtual(moduleBase):
                            "with no upper interfaces or parent interfaces)"
                            % ifaceobj.name, ifaceobj)
 
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
 
         addr_virtual_macs = self.create_macvlan_and_apply_config(
@@ -597,7 +630,7 @@ class addressvirtual(moduleBase):
             if ip4 or ifquery:
                 merged_with_existing_obj = False
                 macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
-                macvlan_ip4_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip4_mac)
+                macvlan_ip4_mac_int = utils.mac_str_to_int(macvlan_ip4_mac)
                 # if the vrr config is defined in different lines for the same ID
                 # we need to save the ip4 and ip6 in the objects we previously
                 # created, example:
@@ -622,13 +655,13 @@ class addressvirtual(moduleBase):
             elif not ip4 and not ifquery:
                 # special check to see if all ipv4 were removed from the vrrp
                 # configuration, if so we need to remove the associated macvlan
-                if self.ipcmd.link_exists(macvlan_ip4_ifname):
-                    netlink.link_del(macvlan_ip4_ifname)
+                if self.cache.link_exists(macvlan_ip4_ifname):
+                    self.netlink.link_del(macvlan_ip4_ifname)
 
             if ip6 or ifquery:
                 merged_with_existing_obj = False
                 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
-                macvlan_ip6_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip6_mac)
+                macvlan_ip6_mac_int = utils.mac_str_to_int(macvlan_ip6_mac)
                 # if the vrr config is defined in different lines for the same ID
                 # we need to save the ip4 and ip6 in the objects we previously
                 # created, example:
@@ -654,8 +687,8 @@ class addressvirtual(moduleBase):
             elif not ip6 and not ifquery:
                 # special check to see if all ipv6 were removed from the vrrp
                 # configuration, if so we need to remove the associated macvlan
-                if self.ipcmd.link_exists(macvlan_ip6_ifname):
-                    netlink.link_del(macvlan_ip6_ifname)
+                if self.cache.link_exists(macvlan_ip6_ifname):
+                    self.netlink.link_del(macvlan_ip6_ifname)
 
         if not ifquery:
             # check if vrrp attribute was removed/re-assigned
@@ -681,11 +714,11 @@ class addressvirtual(moduleBase):
                         macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), id_to_remove)
                         macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), id_to_remove)
 
-                        if self.ipcmd.link_exists(macvlan_ip4_ifname):
-                            netlink.link_del(macvlan_ip4_ifname)
+                        if self.cache.link_exists(macvlan_ip4_ifname):
+                            self.netlink.link_del(macvlan_ip4_ifname)
 
-                        if self.ipcmd.link_exists(macvlan_ip6_ifname):
-                            netlink.link_del(macvlan_ip6_ifname)
+                        if self.cache.link_exists(macvlan_ip6_ifname):
+                            self.netlink.link_del(macvlan_ip6_ifname)
 
             except Exception as e:
                 self.logger.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname)
@@ -714,13 +747,6 @@ class addressvirtual(moduleBase):
 
         for index, addr_virtual in enumerate(address_virtual_list):
             av_attrs = addr_virtual.split()
-
-            if len(av_attrs) < 2:
-                self.log_error("%s: incorrect address-virtual attrs '%s'"
-                               % (ifaceobj.name, addr_virtual), ifaceobj,
-                               raise_error=False)
-                continue
-
             mac = av_attrs[0]
             if mac:
                 mac = mac.lower()
@@ -735,7 +761,7 @@ class addressvirtual(moduleBase):
 
             if mac != "none":
                 config["hwaddress"] = mac
-                config["hwaddress_int"] = self.ipcmd.mac_str_to_int(mac)
+                config["hwaddress_int"] = utils.mac_str_to_int(mac)
 
             ip_network_obj_list = []
             for ip in av_attrs[1:]:
@@ -746,9 +772,6 @@ class addressvirtual(moduleBase):
 
         return user_config_list
 
-    def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
-        return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
-
     def _down(self, ifaceobj, ifaceobj_getfunc=None):
         try:
             self._remove_address_config(ifaceobj,
@@ -756,17 +779,15 @@ class addressvirtual(moduleBase):
 
             #### VRR
             hwaddress = []
-            self.ipcmd.batch_start()
             for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
                 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
                     macvlan_ifacename = os.path.basename(macvlan_ifacename)
-                    if not self.ipcmd.link_exists(macvlan_ifacename):
+                    if not self.cache.link_exists(macvlan_ifacename):
                         continue
-                    hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
-                    self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+                    hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
+                    self.netlink.link_del(macvlan_ifacename)
                     # XXX: Also delete any fdb addresses. This requires, checking mac address
                     # on individual macvlan interfaces and deleting the vlan from that.
-            self.ipcmd.batch_commit()
             if any(hwaddress):
                 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
         except Exception, e:
@@ -776,7 +797,7 @@ class addressvirtual(moduleBase):
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
 
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
 
         user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
@@ -822,6 +843,25 @@ class addressvirtual(moduleBase):
                     return
             ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
 
+    @staticmethod
+    def compare_user_config_vs_running_state(running_addrs, user_addrs):
+        ip4 = []
+        ip6 = []
+
+        for ip in user_addrs or []:
+            obj = IPNetwork(ip)
+
+            if type(obj) == IPv6Network:
+                ip6.append(obj)
+            else:
+                ip4.append(obj)
+
+        running_ipobj = []
+        for ip in running_addrs or []:
+            running_ipobj.append(IPNetwork(ip))
+
+        return running_ipobj == (ip4 + ip6)
+
     def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
         """
         macvlan_config_list = [
@@ -853,7 +893,7 @@ class addressvirtual(moduleBase):
 
             macvlan_ifacename = config.get("ifname")
 
-            if not self.ipcmd.link_exists(macvlan_ifacename):
+            if not self.cache.link_exists(macvlan_ifacename):
                 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
                 continue
 
@@ -861,26 +901,26 @@ class addressvirtual(moduleBase):
             macvlan_hwaddress_int = config.get("hwaddress_int")
 
             if user_config_address_virtual_ipv6_addr:
-                macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
+                macvlans_running_ipv6_addr.append(self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename))
 
             # Check mac and ip address
-            rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
-            raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
+            rhwaddress = ip4_macvlan_hwaddress = self.cache.get_link_address(macvlan_ifacename)
+            raddrs = ip4_running_addrs =[str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
                 ifname=macvlan_ifacename,
-                details=False,
-                addr_virtual_ifaceobj=ifaceobj
-            )
+                ifaceobj_list=[ifaceobj],
+                with_address_virtual=True
+            )]
 
             if not is_vrr:
                 ips = config.get("ips")
 
-                if not raddrs or not rhwaddress:
+                if not rhwaddress:
                     ifaceobjcurr.update_config_with_status(attr_name, "", 1)
                     continue
 
                 try:
-                    if self.ipcmd.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
-                            and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
+                    if utils.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
+                            and self.compare_user_config_vs_running_state(raddrs, ips) \
                             and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
                         ifaceobjcurr.update_config_with_status(
                             attr_name,
@@ -909,24 +949,25 @@ class addressvirtual(moduleBase):
                     ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
 
                     # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
-                    if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
+                    if not ip6_config.get("ips") or self.cache.get_link_address_raw(ip6_macvlan_ifname) == ip6_config.get("hwaddress_int"):
 
                         # check all ip4
-                        if self.ipcmd.compare_user_config_vs_running_state(
+                        if self.compare_user_config_vs_running_state(
                                 ip4_running_addrs,
                                 ip4_config.get("ips")
                         ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
-                            ip6_running_addrs = self.ipcmd.get_running_addrs(
+                            ip6_running_addrs = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
                                 ifname=ip6_macvlan_ifname,
                                 details=False,
-                                addr_virtual_ifaceobj=ifaceobj
-                            )
+                                ifaceobj_list=[ifaceobj],
+                                with_address_virtual=True
+                            )]
 
                             # check all ip6
-                            if self.ipcmd.compare_user_config_vs_running_state(
+                            if self.compare_user_config_vs_running_state(
                                     ip6_running_addrs,
                                     ip6_config.get("ips")
-                            ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
+                            ) and self._check_addresses_in_bridge(ifaceobj, ip6_macvlan_hwaddress):
                                 ifaceobjcurr.update_config_with_status(
                                     attr_name,
                                     "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
@@ -945,19 +986,16 @@ class addressvirtual(moduleBase):
 
     def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
         macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
-        address_virtuals = []
-        for av in linkCache.links:
-            if av.startswith(macvlan_prefix):
-                address_virtuals.append(av)
-
+        address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
         macvlans_ipv6_addrgen_list = []
         for av in address_virtuals:
             macvlan_ifacename = os.path.basename(av)
-            rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
-
-            raddress = []
-            for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
-                raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
+            rhwaddress = self.cache.get_link_address(macvlan_ifacename)
+            raddress = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
+                ifaceobj_list=ifaceobj_getfunc(ifaceobjrunning.name) or [],
+                ifname=ifaceobjrunning.name,
+                with_address_virtual=True
+            )]
 
             raddress = list(set(raddress))
 
@@ -968,7 +1006,7 @@ class addressvirtual(moduleBase):
             ifaceobjrunning.update_config('address-virtual',
                             '%s %s' %(rhwaddress, ' '.join(raddress)))
 
-            macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
+            macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename)))
 
         macvlan_count = len(address_virtuals)
         if not macvlan_count:
@@ -983,19 +1021,17 @@ class addressvirtual(moduleBase):
                 return
         ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
 
-
-    _run_ops = {'up' : _up,
-               'down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
+    _run_ops = {
+        'up': _up,
+        'down': _down,
+        'query-checkcurr': _query_check,
+        'query-running': _query_running
+    }
 
     def get_ops(self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
 
     def run(self, ifaceobj, operation, query_ifaceobj=None,
             ifaceobj_getfunc=None, **extra_args):
@@ -1019,7 +1055,7 @@ class addressvirtual(moduleBase):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
             return
-        self._init_command_handlers()
+
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 23ec0e121ec2562c6a70288bea3c0d84fbfed46e..13a9d7af02ad103ab648086f10f62cafd3d3b023 100644 (file)
@@ -5,20 +5,20 @@
 #
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
-    from ifupdown2.ifupdown.netlink import netlink
     from ifupdown2.ifupdown.exceptions import moduleNotSupported
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 
 except:
+    from lib.addon import Addon
+
     from ifupdown.iface import *
     from ifupdown.utils import utils
     from ifupdownaddons.modulebase import moduleBase
-    from ifupdownaddons.LinkUtils import LinkUtils
-    from ifupdown.netlink import netlink
     from ifupdown.exceptions import moduleNotSupported
     import ifupdown.ifupdownflags as ifupdownflags
 
@@ -27,62 +27,56 @@ import re
 import subprocess
 import os
 
-class batman_adv (moduleBase):
+
+class batman_adv(Addon, moduleBase):
     """  ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
 
     _modinfo = {
-        'mhelp' : 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
-                  'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
-                  'interface to be creatable. You can specify a space separated list' +
-                  'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
-                  'is set for an interfaces this module will do the magic.',
-
-        'attrs' : {
-            'batman-ifaces' : {
-                'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
-               'validvals' : [ '<interface-list>' ],
-                'required' : True,
+        'mhelp': 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
+                 'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
+                 'interface to be creatable. You can specify a space separated list' +
+                 'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
+                 'is set for an interfaces this module will do the magic.',
+        'attrs': {
+            'batman-ifaces': {
+                'help': 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
+                'validvals': ['<interface-list>'],
+                'required': True,
             },
-
-            'batman-ifaces-ignore-regex' : {
-                'help' : 'Interfaces to ignore when verifying configuration (regexp)',
-                'required' : False,
+            'batman-ifaces-ignore-regex': {
+                'help': 'Interfaces to ignore when verifying configuration (regexp)',
+                'required': False,
             },
-
-            'batman-distributed-arp-table' : {
-                'help' : 'B.A.T.M.A.N. distributed ARP table',
-                'validvals' : [ 'enabled', 'disabled' ],
-                'required' : False,
-                'batman-attr' : True,
+            'batman-distributed-arp-table': {
+                'help': 'B.A.T.M.A.N. distributed ARP table',
+                'validvals': ['enabled', 'disabled'],
+                'required': False,
+                'batman-attr': True,
             },
-
-            'batman-gw-mode' : {
-                'help' : 'B.A.T.M.A.N. gateway mode',
-                'validvals' : [ 'off', 'client', 'server' ],
-                'required' : False,
-                'example' : [ 'batman-gw-mode client' ],
-                'batman-attr' : True,
+            'batman-gw-mode': {
+                'help': 'B.A.T.M.A.N. gateway mode',
+                'validvals': ['off', 'client', 'server'],
+                'required': False,
+                'example': ['batman-gw-mode client'],
+                'batman-attr': True,
             },
-
-            'batman-hop-penalty' : {
-                'help' : 'B.A.T.M.A.N. hop penalty',
-                'validvals' : [ '<number>' ],
-                'required' : False,
-                'batman-attr' : True,
+            'batman-hop-penalty': {
+                'help': 'B.A.T.M.A.N. hop penalty',
+                'validvals': ['<number>'],
+                'required': False,
+                'batman-attr': True,
             },
-
-            'batman-multicast-mode' : {
-                'help' : 'B.A.T.M.A.N. multicast mode',
-                'validvals' : [ 'enabled', 'disabled' ],
-                'required' : False,
-                'batman-attr' : True,
+            'batman-multicast-mode': {
+                'help': 'B.A.T.M.A.N. multicast mode',
+                'validvals': ['enabled', 'disabled'],
+                'required': False,
+                'batman-attr': True,
             },
-
-           'batman-routing-algo' : {
-                'help' : 'B.A.T.M.A.N. routing algo',
-                'validvals' : [ 'BATMAN_IV', 'BATMAN_V' ],
-                'required' : False,
-                'batman-attr' : False,
+            'batman-routing-algo': {
+                'help': 'B.A.T.M.A.N. routing algo',
+                'validvals': ['BATMAN_IV', 'BATMAN_V'],
+                'required': False,
+                'batman-attr': False,
             },
         }
     }
@@ -90,12 +84,11 @@ class batman_adv (moduleBase):
     _batman_attrs = {
     }
 
-
     def __init__ (self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__ (self, *args, **kargs)
         if not os.path.exists('/usr/sbin/batctl'):
             raise moduleNotSupported('module init failed: no /usr/sbin/batctl found')
-        self.ipcmd = None
 
         for longname, entry in self._modinfo['attrs'].items ():
             if entry.get ('batman-attr', False) == False:
@@ -106,27 +99,23 @@ class batman_adv (moduleBase):
                  'filename' : attr.replace ("-", "_"),
             }
 
-
     def _is_batman_device (self, ifaceobj):
         if ifaceobj.get_attr_value_first ('batman-ifaces'):
             return True
         return False
 
-
     def _get_batman_ifaces (self, ifaceobj ):
         batman_ifaces = ifaceobj.get_attr_value_first ('batman-ifaces')
         if batman_ifaces:
             return sorted (batman_ifaces.split ())
         return None
 
-
     def _get_batman_ifaces_ignore_regex (self, ifaceobj):
         ifaces_ignore_regex = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
         if ifaces_ignore_regex:
             return re.compile (r"%s" % ifaces_ignore_regex)
         return None
 
-
     def _get_batman_attr (self, ifaceobj, attr):
         if attr not in self._batman_attrs:
             raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
@@ -137,7 +126,6 @@ class batman_adv (moduleBase):
 
         return None
 
-
     def _read_current_batman_attr (self, ifaceobj, attr, dont_map = False):
        # 'routing_algo' needs special handling, D'oh.
         if dont_map:
@@ -156,7 +144,6 @@ class batman_adv (moduleBase):
         except ValueError:
             raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value)
 
-
     def _set_batman_attr (self, ifaceobj, attr, value):
         if attr not in self._batman_attrs:
             raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
@@ -168,7 +155,6 @@ class batman_adv (moduleBase):
         except IOError as i:
             raise Exception ("_set_batman_attr (%s): %s" % (attr, i))
 
-
     def _batctl_if (self, bat_iface, mesh_iface, op):
         if op not in [ 'add', 'del' ]:
             raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op)
@@ -193,7 +179,6 @@ class batman_adv (moduleBase):
         except Exception as e:
             raise Exception ("_set_routing_algo: %s" % e)
 
-
     def _find_member_ifaces (self, ifaceobj, ignore = True):
         members = []
         iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
@@ -208,7 +193,6 @@ class batman_adv (moduleBase):
 
         return sorted (members)
 
-
     def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
         if not self._is_batman_device (ifaceobj):
             return None
@@ -220,7 +204,6 @@ class batman_adv (moduleBase):
 
         return [None]
 
-
     def _up (self, ifaceobj):
         if self._get_batman_ifaces (ifaceobj) == None:
             raise Exception ('could not determine batman interfacaes')
@@ -228,7 +211,7 @@ class batman_adv (moduleBase):
         # Verify existance of batman interfaces (should be present already)
         batman_ifaces = []
         for iface in self._get_batman_ifaces (ifaceobj):
-            if not self.ipcmd.link_exists (iface):
+            if not self.cache.link_exists (iface):
                 self.logger.warn ('batman iface %s not present' % iface)
                 continue
 
@@ -241,10 +224,9 @@ class batman_adv (moduleBase):
         if routing_algo:
             self._set_routing_algo (routing_algo)
 
-
         if_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
         # Is the batman main interface already present?
-        if self.ipcmd.link_exists (ifaceobj.name):
+        if self.cache.link_exists (ifaceobj.name):
             # Verify which member interfaces are present
             members = self._find_member_ifaces (ifaceobj)
             for iface in members:
@@ -269,9 +251,8 @@ class batman_adv (moduleBase):
             netlink.link_set_updown(ifaceobj.name, "up")
 
 
-
     def _down (self, ifaceobj):
-        if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
+        if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists (ifaceobj.name):
            return
 
         members = self._find_member_ifaces (ifaceobj)
@@ -283,7 +264,7 @@ class batman_adv (moduleBase):
 
 
     def _query_check (self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists (ifaceobj.name):
+        if not self.cache.link_exists (ifaceobj.name):
             return
 
         batman_ifaces_cfg = self._get_batman_ifaces (ifaceobj)
@@ -342,30 +323,16 @@ class batman_adv (moduleBase):
 
             ifaceobjcurr.update_config_with_status ('batman-routing-algo', value_curr, value_ok)
 
-
-    def _query_running (self, ifaceobjrunning):
-        if not self.ipcmd.link_exists (ifaceobjrunning.name):
-            return
-
-        # XXX Now what?
-
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check}
-# XXX              'query-running' : _query_running}
-
+    _run_ops = {
+        'pre-up': _up,
+        'post-down': _down,
+        'query-checkcurr': _query_check
+    }
 
     def get_ops (self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys ()
 
-
-    def _init_command_handlers (self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
-
     def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
         """ run B.A.T.M.A.N. configuration on the interface object passed as argument
 
@@ -389,8 +356,6 @@ class batman_adv (moduleBase):
         if (operation != 'query-running' and not self._is_batman_device (ifaceobj)):
             return
 
-        self._init_command_handlers ()
-
         if operation == 'query-checkcurr':
             op_handler (self, ifaceobj, query_ifaceobj)
         else:
index 72096b3617093d0e2775cda33149113f83b9f507..641b520ade6e56ccfed55d8402ea3057c776e93e 100644 (file)
@@ -11,136 +11,162 @@ import os
 from sets import Set
 
 try:
+    from ifupdown2.lib.addon import Addon
     from ifupdown2.nlmanager.nlmanager import Link
 
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
     from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
 
     import ifupdown2.ifupdown.policymanager as policymanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 except ImportError:
+    from lib.addon import Addon
     from nlmanager.nlmanager import Link
 
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
     from ifupdown.statemanager import statemanager_api as statemanager
 
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.policymanager as policymanager
     import ifupdown.ifupdownflags as ifupdownflags
 
-class bond(moduleBase):
+class bond(Addon, moduleBase):
     """  ifupdown2 addon module to configure bond interfaces """
 
     overrides_ifupdown_scripts = ['ifenslave', ]
 
-    _modinfo = { 'mhelp' : 'bond configuration module',
-                    'attrs' : {
-                    'bond-use-carrier':
-                         {'help' : 'bond use carrier',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'yes',
-                          'example': ['bond-use-carrier yes']},
-                     'bond-num-grat-arp':
-                         {'help' : 'bond use carrier',
-                          'validrange' : ['0', '255'],
-                          'default' : '1',
-                          'example' : ['bond-num-grat-arp 1']},
-                     'bond-num-unsol-na' :
-                         {'help' : 'bond slave devices',
-                          'validrange' : ['0', '255'],
-                          'default' : '1',
-                          'example' : ['bond-num-unsol-na 1']},
-                     'bond-xmit-hash-policy' :
-                         {'help' : 'bond slave devices',
-                          'validvals' : ['0', 'layer2',
-                                         '1', 'layer3+4',
-                                         '2', 'layer2+3',
-                                         '3', 'encap2+3',
-                                         '4', 'encap3+4'],
-                          'default' : 'layer2',
-                          'example' : ['bond-xmit-hash-policy layer2']},
-                     'bond-miimon' :
-                         {'help' : 'bond miimon',
-                          'validrange' : ['0', '255'],
-                          'default' : '0',
-                          'example' : ['bond-miimon 0']},
-                     'bond-mode' :
-                         {'help': 'bond mode',
-                          'validvals': ['0', 'balance-rr',
-                                        '1', 'active-backup',
-                                        '2', 'balance-xor',
-                                        '3', 'broadcast',
-                                        '4', '802.3ad',
-                                        '5', 'balance-tlb',
-                                        '6', 'balance-alb'],
-                          'default': 'balance-rr',
-                          'example': ['bond-mode 802.3ad']},
-                     'bond-lacp-rate':
-                         {'help' : 'bond lacp rate',
-                          'validvals' : ['0', 'slow', '1', 'fast'],
-                          'default' : '0',
-                          'example' : ['bond-lacp-rate 0']},
-                     'bond-min-links':
-                         {'help' : 'bond min links',
-                          'default' : '0',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bond-min-links 0']},
-                     'bond-ad-sys-priority':
-                         {'help' : '802.3ad system priority',
-                          'default' : '65535',
-                          'validrange' : ['0', '65535'],
-                          'example' : ['bond-ad-sys-priority 65535'],
-                          'deprecated' : True,
-                          'new-attribute' : 'bond-ad-actor-sys-prio'},
-                     'bond-ad-actor-sys-prio':
-                         {'help' : '802.3ad system priority',
-                          'default' : '65535',
-                          'validrange' : ['0', '65535'],
-                          'example' : ['bond-ad-actor-sys-prio 65535']},
-                     'bond-ad-sys-mac-addr':
-                         {'help' : '802.3ad system mac address',
-                          'validvals': ['<mac>', ],
-                         'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
-                         'deprecated' : True,
-                         'new-attribute' : 'bond-ad-actor-system'},
-                     'bond-ad-actor-system':
-                         {'help' : '802.3ad system mac address',
-                          'validvals': ['<mac>', ],
-                         'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
-                     'bond-lacp-bypass-allow':
-                         {'help' : 'allow lacp bypass',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['bond-lacp-bypass-allow no']},
-                     'bond-slaves' :
-                        {'help' : 'bond slaves',
-                         'required' : True,
-                         'multivalue' : True,
-                         'validvals': ['<interface-list>'],
-                         'example' : ['bond-slaves swp1 swp2',
-                                      'bond-slaves glob swp1-2',
-                                      'bond-slaves regex (swp[1|2)'],
-                         'aliases': ['bond-ports']},
-                     'bond-updelay' :
-                        {'help' : 'bond updelay',
-                         'default' : '0',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bond-updelay 100']},
-                     'bond-downdelay':
-                        {'help' : 'bond downdelay',
-                         'default' : '0',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bond-downdelay 100']}
-                    }}
+    _modinfo = {
+        "mhelp": "bond configuration module",
+        "attrs": {
+            "bond-use-carrier": {
+                "help": "bond use carrier",
+                "validvals": ["yes", "no", "0", "1"],
+                "default": "yes",
+                "example": ["bond-use-carrier yes"]},
+            "bond-num-grat-arp": {
+                "help": "bond use carrier",
+                "validrange": ["0", "255"],
+                "default": "1",
+                "example": ["bond-num-grat-arp 1"]
+            },
+            "bond-num-unsol-na": {
+                "help": "bond slave devices",
+                "validrange": ["0", "255"],
+                "default": "1",
+                "example": ["bond-num-unsol-na 1"]
+            },
+            "bond-xmit-hash-policy": {
+                "help": "bond slave devices",
+                "validvals": [
+                    "0", "layer2",
+                    "1", "layer3+4",
+                    "2", "layer2+3",
+                    "3", "encap2+3",
+                    "4", "encap3+4"
+                ],
+                "default": "layer2",
+                "example": ["bond-xmit-hash-policy layer2"]
+            },
+            "bond-miimon": {
+                "help": "bond miimon",
+                "validrange": ["0", "255"],
+                "default": "0",
+                "example": ["bond-miimon 0"]
+            },
+            "bond-mode": {
+                "help": "bond mode",
+                "validvals": [
+                    "0", "balance-rr",
+                    "1", "active-backup",
+                    "2", "balance-xor",
+                    "3", "broadcast",
+                    "4", "802.3ad",
+                    "5", "balance-tlb",
+                    "6", "balance-alb"
+                ],
+                "default": "balance-rr",
+                "example": ["bond-mode 802.3ad"]
+            },
+            "bond-lacp-rate": {
+                "help": "bond lacp rate",
+                "validvals": ["0", "slow", "1", "fast"],
+                "default": "0",
+                "example": ["bond-lacp-rate 0"]
+            },
+            "bond-min-links": {
+                "help": "bond min links",
+                "default": "0",
+                "validrange": ["0", "255"],
+                "example": ["bond-min-links 0"]
+            },
+            "bond-ad-sys-priority": {
+                "help": "802.3ad system priority",
+                "default": "65535",
+                "validrange": ["0", "65535"],
+                "example": ["bond-ad-sys-priority 65535"],
+                "deprecated": True,
+                "new-attribute": "bond-ad-actor-sys-prio"
+            },
+            "bond-ad-actor-sys-prio": {
+                "help": "802.3ad system priority",
+                "default": "65535",
+                "validrange": ["0", "65535"],
+                "example": ["bond-ad-actor-sys-prio 65535"]
+            },
+            "bond-ad-sys-mac-addr": {
+                "help": "802.3ad system mac address",
+                "validvals": ["<mac>", ],
+                "example": ["bond-ad-sys-mac-addr 00:00:00:00:00:00"],
+                "deprecated": True,
+                "new-attribute": "bond-ad-actor-system"
+            },
+            "bond-ad-actor-system": {
+                "help": "802.3ad system mac address",
+                "validvals": ["<mac>", ],
+                "example": ["bond-ad-actor-system 00:00:00:00:00:00"],
+            },
+            "bond-lacp-bypass-allow": {
+                "help": "allow lacp bypass",
+                "validvals": ["yes", "no", "0", "1"],
+                "default": "no",
+                "example": ["bond-lacp-bypass-allow no"]
+            },
+            "bond-slaves": {
+                "help": "bond slaves",
+                "required": True,
+                "multivalue": True,
+                "validvals": ["<interface-list>"],
+                "example": [
+                    "bond-slaves swp1 swp2",
+                    "bond-slaves glob swp1-2",
+                    "bond-slaves regex (swp[1|2)"
+                ],
+                "aliases": ["bond-ports"]
+            },
+            "bond-updelay": {
+                "help": "bond updelay",
+                "default": "0",
+                "validrange": ["0", "65535"],
+                "example": ["bond-updelay 100"]
+            },
+            "bond-downdelay": {
+                "help": "bond downdelay",
+                "default": "0",
+                "validrange": ["0", "65535"],
+                "example": ["bond-downdelay 100"]
+            },
+            "bond-primary": {
+                "help": "Control which slave interface is "
+                        "preferred active member",
+                "example": ["bond-primary swp1"]
+            }
+        }
+    }
 
     _bond_attr_netlink_map = {
         'bond-mode': Link.IFLA_BOND_MODE,
@@ -157,7 +183,8 @@ class bond(moduleBase):
         'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
         'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
         'bond-updelay': Link.IFLA_BOND_UPDELAY,
-        'bond-downdelay': Link.IFLA_BOND_DOWNDELAY
+        'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
+        'bond-primary': Link.IFLA_BOND_PRIMARY
     }
 
     # ifquery-check attr dictionary with callable object to translate user data to netlink format
@@ -173,7 +200,8 @@ class bond(moduleBase):
         Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
         Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
         Link.IFLA_BOND_UPDELAY: int,
-        Link.IFLA_BOND_DOWNDELAY: int
+        Link.IFLA_BOND_DOWNDELAY: int,
+        # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
     }
 
     # ifup attr list with callable object to translate user data to netlink format
@@ -194,13 +222,13 @@ class bond(moduleBase):
         ('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
         ('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
         ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
-        ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
+        ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str)
+        # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
     )
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self.bondcmd = None
 
         if not os.path.exists('/sys/class/net/bonding_masters'):
             try:
@@ -208,6 +236,9 @@ class bond(moduleBase):
             except Exception as e:
                 self.logger.info("bond: error while loading bonding module: %s" % str(e))
 
+        self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex
+        self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),)
+
     @staticmethod
     def get_bond_slaves(ifaceobj):
         slaves = ifaceobj.get_attr_value_first('bond-slaves')
@@ -218,9 +249,7 @@ class bond(moduleBase):
     def _is_bond(self, ifaceobj):
         # at first link_kind is not set but once ifupdownmain
         # calls get_dependent_ifacenames link_kind is set to BOND
-        if ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj):
-            return True
-        return False
+        return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
 
     def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
         """ Returns list of interfaces dependent on ifaceobj """
@@ -244,8 +273,7 @@ class bond(moduleBase):
         return self.syntax_check_updown_delay(ifaceobj)
 
     def get_dependent_ifacenames_running(self, ifaceobj):
-        self._init_command_handlers()
-        return self.bondcmd.bond_get_slaves(ifaceobj.name)
+        return self.cache.get_slaves(ifaceobj.name)
 
     def _get_slave_list(self, ifaceobj):
         """ Returns slave list present in ifaceobj config """
@@ -264,8 +292,10 @@ class bond(moduleBase):
         If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
         """
         try:
-            if os.path.exists("/sys/class/net/%s/brport" % ifname):
-                self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
+            for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
+                if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+                    self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
+                    return
         except Exception, e:
             self.logger.info(str(e))
 
@@ -276,46 +306,44 @@ class bond(moduleBase):
                 return True
         return False
 
-    def _add_slaves(self, ifaceobj, ifaceobj_getfunc=None):
-        runningslaves = []
-
+    def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
         slaves = self._get_slave_list(ifaceobj)
         if not slaves:
             self.logger.debug('%s: no slaves found' %ifaceobj.name)
             return
 
-        if not ifupdownflags.flags.PERFMODE:
-            runningslaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
-
         clag_bond = self._is_clag_bond(ifaceobj)
 
         for slave in Set(slaves).difference(Set(runningslaves)):
             if (not ifupdownflags.flags.PERFMODE and
-                not self.ipcmd.link_exists(slave)):
+                not self.cache.link_exists(slave)):
                     self.log_error('%s: skipping slave %s, does not exist'
                                    %(ifaceobj.name, slave), ifaceobj,
                                      raise_error=False)
                     continue
             link_up = False
-            if self.ipcmd.is_link_up(slave):
-                netlink.link_set_updown(slave, "down")
+            if self.cache.link_is_up(slave):
+                self.netlink.link_down_force(slave)
                 link_up = True
             # If clag bond place the slave in a protodown state; clagd
             # will protoup it when it is ready
             if clag_bond:
                 try:
-                    netlink.link_set_protodown(slave, "on")
+                    self.netlink.link_set_protodown_on(slave)
                 except Exception, e:
                     self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
+
             self.enable_ipv6_if_prev_brport(slave)
-            netlink.link_set_master(slave, ifaceobj.name)
+            self.netlink.link_set_master(slave, ifaceobj.name)
+            # TODO: if this fail we should switch to iproute2
+            # start a batch: down - set master - up
             if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
                try:
                     if (ifaceobj_getfunc(slave)[0].link_privflags &
                         ifaceLinkPrivFlags.KEEP_LINK_DOWN):
-                        netlink.link_set_updown(slave, "down")
+                        self.netlink.link_down_force(slave)
                     else:
-                        netlink.link_set_updown(slave, "up")
+                        self.netlink.link_up(slave)
                except Exception, e:
                     self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
                     pass
@@ -323,22 +351,22 @@ class bond(moduleBase):
         if runningslaves:
             for s in runningslaves:
                 if s not in slaves:
-                    self.bondcmd.bond_remove_slave(ifaceobj.name, s)
+                    self.sysfs.bond_remove_slave(ifaceobj.name, s)
                     if clag_bond:
                         try:
-                            netlink.link_set_protodown(s, "off")
+                            self.netlink.link_set_protodown_off(s)
                         except Exception, e:
                             self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
                 else:
                     # apply link-down config changes on running slaves
                     try:
-                        link_up = self.ipcmd.is_link_up(s)
+                        link_up = self.cache.link_is_up(s)
                         config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
                                             ifaceLinkPrivFlags.KEEP_LINK_DOWN)
                         if (config_link_down and link_up):
-                            netlink.link_set_updown(s, "down")
+                            self.netlink.link_down_force(s)
                         elif (not config_link_down and not link_up):
-                            netlink.link_set_updown(s, "up")
+                            self.netlink.link_up_force(s)
                     except Exception, e:
                         self.logger.warn('%s: %s' % (ifaceobj.name, str(e)))
 
@@ -396,7 +424,7 @@ class bond(moduleBase):
         """
         ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
         if link_exists and ifla_bond_miimon is None:
-            ifla_bond_miimon = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', Link.IFLA_BOND_MIIMON])
+            ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
 
         if ifla_bond_miimon == 0:
             for nl_attr, attr_name in self._bond_updown_delay_nl_list:
@@ -417,7 +445,7 @@ class bond(moduleBase):
     def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
         ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
         if ifla_bond_mode is None and link_exists:
-            ifla_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
+            ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
             # in this case the link already exists (we have a cached value):
             # if IFLA_BOND_MODE is not present in ifla_info_data it means:
             #   - that bond-mode was present in the user config and didn't change
@@ -430,7 +458,7 @@ class bond(moduleBase):
         if ifla_bond_mode == 4:  # 802.3ad
             min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
             if min_links is None:
-                min_links = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
+                min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
             # get_min_links_nl may return None so we need to strictly check 0
             if min_links == 0:
                 self.logger.warn('%s: attribute bond-min-links is set to \'0\'' % ifname)
@@ -495,7 +523,7 @@ class bond(moduleBase):
                     nl_value = func_ptr(user_config.lower())
 
                     if link_exists:
-                        cached_value = self.bondcmd.link_cache_get([ifname, 'linkinfo', netlink_attr])
+                        cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
 
                     if link_exists and cached_value is None:
                         # the link already exists but we don't have any value
@@ -539,10 +567,10 @@ class bond(moduleBase):
                 return True
         return False
 
-    def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data):
+    def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
         # if bond-mode was changed the bond needs to be brought
         # down and slaves un-slaved before bond mode is changed.
-        cached_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
+        cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
         ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
 
         # bond-mode was changed or is not specified
@@ -551,24 +579,26 @@ class bond(moduleBase):
                 self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
                                  % (ifname, ifla_bond_mode))
                 if is_link_up:
-                    netlink.link_set_updown(ifname, 'down')
+                    self.netlink.link_down(ifname)
                     is_link_up = False
 
                 for lower_dev in ifaceobj.lowerifaces:
-                    netlink.link_set_nomaster(lower_dev)
+                    self.netlink.link_set_nomaster(lower_dev)
+                    try:
+                        bond_slaves.remove(lower_dev)
+                    except:
+                        pass
 
-                self.bondcmd.cache_delete([ifname, 'linkinfo', 'slaves'])
             else:
                 # bond-mode user config value is the current running(cached) value
                 # no need to reset it again we can ignore this attribute
                 del ifla_info_data[Link.IFLA_BOND_MODE]
 
-        return is_link_up
+        return is_link_up, bond_slaves
 
     def create_or_set_bond_config(self, ifaceobj):
         ifname          = ifaceobj.name
-        link_exists     = self.ipcmd.link_exists(ifname)
-        is_link_up      = self.ipcmd.is_link_up(ifname) if link_exists else False
+        link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
         ifla_info_data  = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
 
         remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
@@ -576,13 +606,21 @@ class bond(moduleBase):
         # if link exists: down link if specific attributes are specified
         if link_exists:
             # did bond-mode changed?
-            is_link_up = self.should_update_bond_mode(ifaceobj, ifname, is_link_up, ifla_info_data)
+            is_link_up, bond_slaves = self.should_update_bond_mode(
+                ifaceobj,
+                ifname,
+                is_link_up,
+                ifla_info_data,
+                self.cache.get_slaves(ifname)
+            )
 
             # if specific attributes need to be set we need to down the bond first
             if ifla_info_data and is_link_up:
                 if self._should_down_bond(ifla_info_data):
-                    netlink.link_set_updown(ifname, 'down')
+                    self.netlink.link_down_force(ifname)
                     is_link_up = False
+        else:
+            bond_slaves = []
 
         if link_exists and not ifla_info_data:
             # if the bond already exists and no attrs need to be set
@@ -590,7 +628,7 @@ class bond(moduleBase):
             self.logger.info('%s: already exists, no change detected' % ifname)
         else:
             try:
-                netlink.link_add_set(kind='bond', ifname=ifname, ifla_info_data=ifla_info_data)
+                self.netlink.link_add_bond_with_info_data(ifname, ifla_info_data)
             except Exception as e:
                 # defensive code
                 # if anything happens, we try to set up the bond with the sysfs api
@@ -603,30 +641,30 @@ class bond(moduleBase):
                 ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
                 ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
 
-            # if link_add doesn't raise we can update the cache, the future
-            # netlink listener will update the cache based on the kernel response
-            for key, value in ifla_info_data.items():
-                self.bondcmd.cache_update([ifname, 'linkinfo', key], value)
-
         if link_exists and ifla_info_data and not is_link_up:
-            netlink.link_set_updown(ifname, 'up')
+            self.netlink.link_up_force(ifname)
+
+        return bond_slaves
 
     def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            self.bondcmd.create_bond(ifaceobj.name)
-        self.bondcmd.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
+        if not self.cache.link_exists(ifaceobj.name):
+            self.sysfs.bond_create(ifaceobj.name)
+        self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
 
     def _up(self, ifaceobj, ifaceobj_getfunc=None):
         try:
-            self.create_or_set_bond_config(ifaceobj)
-            self._add_slaves(ifaceobj, ifaceobj_getfunc)
+            bond_slaves = self.create_or_set_bond_config(ifaceobj)
+            self._add_slaves(
+                ifaceobj,
+                bond_slaves,
+                ifaceobj_getfunc,
+            )
         except Exception, e:
             self.log_error(str(e), ifaceobj)
 
     def _down(self, ifaceobj, ifaceobj_getfunc=None):
         try:
-            netlink.link_del(ifaceobj.name)
-            self.bondcmd.cache_delete([ifaceobj.name])
+            self.netlink.link_del(ifaceobj.name)
         except Exception as e:
             self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
 
@@ -651,7 +689,7 @@ class bond(moduleBase):
         ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
 
     def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
-        if not self.bondcmd.bond_exists(ifaceobj.name):
+        if not self.cache.bond_exists(ifaceobj.name):
             self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
             return
 
@@ -673,7 +711,7 @@ class bond(moduleBase):
             query_slaves = True
             if not user_bond_slaves:
                 user_bond_slaves = self._get_slave_list(ifaceobj)
-                running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
+                running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
 
             self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
         except:
@@ -684,7 +722,7 @@ class bond(moduleBase):
             # if user specified bond-ports we need to display it
             if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
                 user_bond_slaves = self._get_slave_list(ifaceobj)
-                running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
+                running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
 
             self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
         except:
@@ -693,7 +731,7 @@ class bond(moduleBase):
         for attr in iface_attrs:
             nl_attr         = self._bond_attr_netlink_map[attr]
             translate_func  = self._bond_attr_ifquery_check_translate_func[nl_attr]
-            current_config  = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', nl_attr])
+            current_config  = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
             user_config     = ifaceobj.get_attr_value_first(attr)
 
             if current_config == translate_func(user_config):
@@ -710,28 +748,35 @@ class bond(moduleBase):
         return 'fast' if value else 'slow'
 
     def _query_running_attrs(self, bondname):
+        cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
+
         bond_attrs = {
-            'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])),
-            'bond-miimon': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON]),
-            'bond-use-carrier': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])),
-            'bond-lacp-rate': self.translate_nl_value_slowfast(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])),
-            'bond-min-links': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS]),
-            'bond-ad-actor-system': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM]),
-            'bond-ad-actor-sys-prio': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO]),
-            'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])),
-            'bond-lacp-bypass-allow': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])),
-            'bond-num-unsol-na': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
-            'bond-num-grat-arp': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
-            'bond-updelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY]),
-            'bond-downdelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
+            'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
+            'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
+            'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
+            'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
+            'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
+            'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
+            'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
+            'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_XMIT_HASH_POLICY)),
+            'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
+            'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
+            'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
+            'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
+            'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
         }
-        slaves = self.bondcmd.bond_get_slaves(bondname)
+
+        cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
+        if cached_bond_primary:
+            bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
+
+        slaves = self.cache.get_slaves(bondname)
         if slaves:
             bond_attrs['bond-slaves'] = slaves
         return bond_attrs
 
     def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
-        if not self.bondcmd.bond_exists(ifaceobjrunning.name):
+        if not self.cache.bond_exists(ifaceobjrunning.name):
             return
         bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
         if bond_attrs.get('bond-slaves'):
@@ -752,10 +797,6 @@ class bond(moduleBase):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = self.bondcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None,
             ifaceobj_getfunc=None):
         """ run bond configuration on the interface object passed as argument
@@ -779,7 +820,6 @@ class bond(moduleBase):
             return
         if operation != 'query-running' and not self._is_bond(ifaceobj):
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj,
                        ifaceobj_getfunc=ifaceobj_getfunc)
index 3e5fd788fb1078476b8218d5f790d795cc532d07..00b1eaafec98fba56abdf78ca3d20e07e8dbd19e 100644 (file)
@@ -12,6 +12,8 @@ from sets import Set
 from collections import Counter
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     import ifupdown2.ifupdown.exceptions as exceptions
     import ifupdown2.ifupdown.policymanager as policymanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
@@ -20,14 +22,12 @@ try:
 
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
 
     from ifupdown2.ifupdownaddons.cache import *
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
-
-    import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
 except ImportError:
+    from lib.addon import Addon
+
     import ifupdown.exceptions as exceptions
     import ifupdown.policymanager as policymanager
     import ifupdown.ifupdownflags as ifupdownflags
@@ -36,367 +36,430 @@ except ImportError:
 
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
 
     from ifupdownaddons.cache import *
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
-    import ifupdown.ifupdownconfig as ifupdownconfig
-
 
 class bridgeFlags:
     PORT_PROCESSED = 0x1
     PORT_PROCESSED_OVERRIDE = 0x2
 
 
-class bridge(moduleBase):
+class bridge(Addon, moduleBase):
     """  ifupdown2 addon module to configure linux bridges """
 
-    _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' +
-                    'vlan aware and non vlan aware bridges. For the vlan ' +
-                    'aware bridge, the port specific attributes must be ' +
-                    'specified under the port. And for vlan unaware bridge ' +
-                    'port specific attributes must be specified under the ' +
-                    'bridge.',
-                 'attrs' : {
-                   'bridge-vlan-aware' :
-                        {'help' : 'vlan aware bridge. Setting this ' +
-                                  'attribute to yes enables vlan filtering' +
-                                  ' on the bridge',
-                         'validvals' : ['yes', 'no'],
-                         'example' : ['bridge-vlan-aware yes/no'],
-                         'default': 'no'
-                         },
-                   'bridge-ports' :
-                        {'help' : 'bridge ports',
-                         'multivalue' : True,
-                         'required' : True,
-                         'validvals': ['<interface-list>', 'none'],
-                         'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
-                                      'bridge-ports glob swp1-3.100',
-                                      'bridge-ports regex (swp[1|2|3].100)']},
-                   'bridge-ports-condone-regex' :
-                        {'help' : 'bridge ports to ignore/condone when reloading config / removing interfaces',
-                         'required' : False,
-                         'example' : [ 'bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$']},
-                   'bridge-stp' :
-                        {'help': 'bridge-stp yes/no',
-                         'example' : ['bridge-stp no'],
-                         'validvals' : ['yes', 'on', 'off', 'no'],
-                         'default' : 'no'},
-                   'bridge-bridgeprio' :
-                        {'help': 'bridge priority',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bridge-bridgeprio 32768'],
-                         'default' : '32768'},
-                   'bridge-ageing' :
-                       {'help': 'bridge ageing',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bridge-ageing 300'],
-                         'default' : '300'},
-                   'bridge-fd' :
-                        { 'help' : 'bridge forward delay',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-fd 15'],
-                          'default' : '15'},
-                   'bridge-gcint' :
-                        # XXX: recheck values
-                        { 'help' : 'bridge garbage collection interval in secs',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-gcint 4'],
-                          'default' : '4',
-                          'compat' : True,
-                          'deprecated': True},
-                   'bridge-hello' :
-                        { 'help' : 'bridge set hello time',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-hello 2'],
-                          'default' : '2'},
-                   'bridge-maxage' :
-                        { 'help' : 'bridge set maxage',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-maxage 20'],
-                          'default' : '20'},
-                   'bridge-pathcosts' :
-                        { 'help' : 'bridge set port path costs',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '65535'],
-                          'example' : ['under the port (for vlan aware bridge): bridge-pathcosts 100',
-                                       'under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100'],
-                          'default' : '100'},
-                   'bridge-portprios' :
-                        { 'help' : 'bridge port prios',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '65535'],
-                          'example' : ['under the port (for vlan aware bridge): bridge-portprios 32',
-                                       'under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32'],
-                          'default' : '32'},
-                   'bridge-mclmc' :
-                        { 'help' : 'set multicast last member count',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-mclmc 2'],
-                          'default' : '2'},
-                    'bridge-mcrouter' :
-                        { 'help': 'Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes',
-                          'validvals' : ['yes', 'no', '0', '1', '2'],
-                          'example' : ['bridge-mcrouter 1'],
-                          'default': 'yes'
-                          },
-                    'bridge-mcsnoop' :
-                        { 'help' : 'set multicast snooping',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'yes',
-                          'example' : ['bridge-mcsnoop yes']},
-                    'bridge-mcsqc' :
-                        { 'help' : 'set multicast startup query count',
-                          'validrange' : ['0', '255'],
-                          'default' : '2',
-                          'example' : ['bridge-mcsqc 2']},
-                    'bridge-mcqifaddr' :
-                        { 'help' : 'set multicast query to use ifaddr',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['bridge-mcqifaddr no']},
-                    'bridge-mcquerier' :
-                        { 'help' : 'set multicast querier',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['bridge-mcquerier no']},
-                    'bridge-hashel' :
-                        { 'help' : 'set hash elasticity',
-                          'validrange' : ['0', '4096'],
-                          'default' : '4',
-                          'example' : ['bridge-hashel 4096']},
-                    'bridge-hashmax' :
-                        { 'help' : 'set hash max',
-                          'validrange' : ['0', '4096'],
-                          'default' : '512',
-                          'example' : ['bridge-hashmax 4096']},
-                    'bridge-mclmi' :
-                        { 'help' : 'set multicast last member interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '1',
-                          'example' : ['bridge-mclmi 1']},
-                    'bridge-mcmi' :
-                        { 'help' : 'set multicast membership interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '260',
-                          'example' : ['bridge-mcmi 260']},
-                    'bridge-mcqpi' :
-                        { 'help' : 'set multicast querier interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '255',
-                          'example' : ['bridge-mcqpi 255']},
-                    'bridge-mcqi' :
-                        { 'help' : 'set multicast query interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '125',
-                          'example' : ['bridge-mcqi 125']},
-                    'bridge-mcqri' :
-                        { 'help' : 'set multicast query response interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '10',
-                          'example' : ['bridge-mcqri 10']},
-                    'bridge-mcsqi' :
-                        { 'help' : 'set multicast startup query interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '31',
-                          'example' : ['bridge-mcsqi 31']},
-                    'bridge-mcqv4src' :
-                        { 'help' : 'set per VLAN v4 multicast querier source address',
-                          'validvals' : ['<number-ipv4-list>', ],
-                          'multivalue' : True,
-                          'compat' : True,
-                          'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
-                     'bridge-portmcrouter':
-                         {
-                             'help': 'Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled',
-                             'validvals': ['<interface-disabled-automatic-enabled>'],
-                             'example': [
-                                 'under the port (for vlan aware bridge): bridge-portmcrouter 0',
-                                 'under the port (for vlan aware bridge): bridge-portmcrouter 1',
-                                 'under the port (for vlan aware bridge): bridge-portmcrouter 2',
-                                 'under the port (for vlan aware bridge): bridge-portmcrouter disabled',
-                                 'under the port (for vlan aware bridge): bridge-portmcrouter automatic',
-                                 'under the port (for vlan aware bridge): bridge-portmcrouter enabled',
-                                 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2',
-                                 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled',
-                                 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1',
-                             ]
-                         },
-                    'bridge-portmcfl' :
-                        { 'help' : 'port multicast fast leave.',
-                          'validvals': ['<interface-yes-no-0-1-list>'],
-                          'default' : 'no',
-                          'example' : ['under the port (for vlan aware bridge): bridge-portmcfl no',
-                                       'under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no']},
-                    'bridge-waitport' :
-                        { 'help' : 'wait for a max of time secs for the' +
-                                ' specified ports to become available,' +
-                                'if no ports are specified then those' +
-                                ' specified on bridge-ports will be' +
-                                ' used here. Specifying no ports here ' +
-                                'should not be used if we are using ' +
-                                'regex or \"all\" on bridge_ports,' +
-                                'as it wouldnt work.',
-                          'default' : '0',
-                          'validvals': ['<number-interface-list>'],
-                          'example' : ['bridge-waitport 4 swp1 swp2']},
-                    'bridge-maxwait' :
-                        { 'help' : 'forces to time seconds the maximum time ' +
-                                'that the Debian bridge setup scripts will ' +
-                                'wait for the bridge ports to get to the ' +
-                                'forwarding status, doesn\'t allow factional ' +
-                                'part. If it is equal to 0 then no waiting' +
-                                ' is done',
-                          'validrange' : ['0', '255'],
-                          'default' : '0',
-                          'example' : ['bridge-maxwait 3']},
-                    'bridge-vids' :
-                        { 'help' : 'bridge port vids. Can be specified ' +
-                                   'under the bridge or under the port. ' +
-                                   'If specified under the bridge the ports ' +
-                                   'inherit it unless overridden by a ' +
-                                   'bridge-vids attribute under the port',
-                          'multivalue' : True,
-                          'validvals': ['<number-comma-range-list>'],
-                          'example' : ['bridge-vids 4000',
-                                       'bridge-vids 2000 2200-3000'],
-                          'aliases': ['bridge-trunk']},
-                    'bridge-pvid' :
-                        { 'help' : 'bridge port pvid. Must be specified under' +
-                                   ' the bridge port',
-                          'validrange' : ['0', '4096'],
-                          'example' : ['bridge-pvid 1']},
-                    'bridge-access' :
-                        { 'help' : 'bridge port access vlan. Must be ' +
-                                   'specified under the bridge port',
-                          'validrange' : ['1', '4094'],
-                          'example' : ['bridge-access 300']},
-                    'bridge-allow-untagged' :
-                        { 'help' : 'indicate if the bridge port accepts ' +
-                                   'untagged packets or not.  Must be ' +
-                                   'specified under the bridge port. ' +
-                                   'Default is \'yes\'',
-                          'validvals' : ['yes', 'no'],
-                          'example' : ['bridge-allow-untagged yes'],
-                          'default' : 'yes'},
-                    'bridge-port-vids' :
-                        { 'help' : 'bridge vlans',
-                          'compat': True,
-                          'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
-                    'bridge-port-pvids' :
-                        { 'help' : 'bridge port vlans',
-                          'compat': True,
-                          'example' : ['bridge-port-pvids bond0=100 bond1=200']},
-                    'bridge-learning' :
-                        { 'help' : 'bridge port learning flag',
-                          'validvals': ['on', 'off', '<interface-on-off-list>'],
-                          'default': 'on',
-                          'example' : ['bridge-learning off']},
-                    'bridge-igmp-version' :
-                        { 'help' : 'mcast igmp version',
-                          'validvals': ['2', '3'],
-                          'default' : '2',
-                          'example' : ['bridge-igmp-version 2']},
-                    'bridge-mld-version':
-                        { 'help' : 'mcast mld version',
-                          'validvals': ['1', '2'],
-                          'default' : '1',
-                          'example' : ['bridge-mld-version 1']},
-                    'bridge-unicast-flood' :
-                        { 'help' : 'bridge port unicast flood flag',
-                          'validvals': ['on', 'off', '<interface-on-off-list>'],
-                          'default': 'on',
-                          'example' : ['under the port (for vlan aware bridge): bridge-unicast-flood on',
-                                       'under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on']},
-                    'bridge-multicast-flood' :
-                        { 'help' : 'bridge port multicast flood flag',
-                          'validvals': ['on', 'off', '<interface-on-off-list>'],
-                          'default': 'on',
-                          'example' : ['under the port (for vlan aware bridge): bridge-multicast-flood on',
-                                       'under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on']},
-                    'bridge-vlan-protocol' :
-                        { 'help' : 'bridge vlan protocol',
-                          'default' : '802.1q',
-                          'validvals': ['802.1q', '802.1ad'],
-                          'example' : ['bridge-vlan-protocol 802.1q']},
-                    'bridge-vlan-stats' :
-                        { 'help' : 'bridge vlan stats',
-                          'default' : 'off',
-                          'validvals': ['on', 'off'],
-                          'example' : ['bridge-vlan-stats off']},
-                    'bridge-arp-nd-suppress' :
-                        { 'help' : 'bridge port arp nd suppress flag',
-                          'validvals': ['on', 'off', '<interface-on-off-list>'],
-                          'default': 'off',
-                          'example' : ['under the port (for vlan aware bridge): bridge-arp-nd-suppress on',
-                                       'under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on']},
-                    'bridge-mcstats' :
-                        { 'help' : 'bridge multicast stats',
-                          'default' : 'off',
-                          'validvals': ['on', 'off'],
-                          'example' : ['bridge-mcstats off']},
-                     'bridge-l2protocol-tunnel': {
-                         'help': 'layer 2 protocol tunneling',
-                         'validvals': [ # XXX: lists all combinations, should move to
-                                        # a better representation
-                                        'all',
-                                        'cdp',
-                                        'cdp lacp',
-                                        'cdp lacp lldp',
-                                        'cdp lacp lldp pvst',
-                                        'cdp lacp lldp stp',
-                                        'cdp lacp pvst',
-                                        'cdp lacp pvst stp',
-                                        'cdp lacp stp',
-                                        'cdp lldp',
-                                        'cdp lldp pvst',
-                                        'cdp lldp pvst stp',
-                                        'cdp lldp stp',
-                                        'cdp pvst',
-                                        'cdp pvst stp',
-                                        'cdp stp',
-                                        'lacp',
-                                        'lacp lldp',
-                                        'lacp lldp pvst',
-                                        'lacp lldp pvst stp',
-                                        'lacp lldp stp',
-                                        'lacp pvst',
-                                        'lacp pvst stp',
-                                        'lacp stp',
-                                        'lldp',
-                                        'lldp pvst',
-                                        'lldp pvst stp',
-                                        'lldp stp',
-                                        'pvst',
-                                        'pvst stp',
-                                        'stp',
-                                        '<interface-l2protocol-tunnel-list>'],
-                         'example': [
-                             'under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all',
-                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst',
-                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst',
-                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp',
-                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel all'
-                         ]
-                     }
-                     }}
+    _modinfo = {
+        "mhelp": "Bridge configuration module. Supports both vlan aware and non "
+                 "vlan aware bridges. For the vlan aware bridge, the port "
+                 "specific attributes must be specified under the port. And for "
+                 "vlan unaware bridge port specific attributes must be specified "
+                 "under the bridge.",
+        "attrs": {
+            "bridge-vlan-aware": {
+                "help": "vlan aware bridge. Setting this "
+                        "attribute to yes enables vlan filtering"
+                        " on the bridge",
+                "validvals": ["yes", "no"],
+                "example": ["bridge-vlan-aware yes/no"],
+                "default": "no"
+            },
+            "bridge-ports": {
+                "help": "bridge ports",
+                "multivalue": True,
+                "required": True,
+                "validvals": ["<interface-list>"],
+                "example": [
+                    "bridge-ports swp1.100 swp2.100 swp3.100",
+                    "bridge-ports glob swp1-3.100",
+                    "bridge-ports regex (swp[1|2|3].100)"
+                ]
+            },
+            "bridge-stp": {
+                "help": "bridge-stp yes/no",
+                "example": ["bridge-stp no"],
+                "validvals": ["yes", "on", "off", "no"],
+                "default": "no"
+            },
+            "bridge-bridgeprio": {
+                "help": "bridge priority",
+                "validrange": ["0", "65535"],
+                "example": ["bridge-bridgeprio 32768"],
+                "default": "32768"
+            },
+            "bridge-ageing": {
+                "help": "bridge ageing",
+                "validrange": ["0", "65535"],
+                "example": ["bridge-ageing 300"],
+                "default": "300"
+            },
+            "bridge-fd": {
+                "help": "bridge forward delay",
+                "validrange": ["0", "255"],
+                "example": ["bridge-fd 15"],
+                "default": "15"
+            },
+            # XXX: recheck values
+            "bridge-gcint": {
+                "help": "bridge garbage collection interval in secs",
+                "validrange": ["0", "255"],
+                "example": ["bridge-gcint 4"],
+                "default": "4",
+                "compat": True,
+                "deprecated": True
+            },
+            "bridge-hello": {
+                "help": "bridge set hello time",
+                "validrange": ["0", "255"],
+                "example": ["bridge-hello 2"],
+                "default": "2"
+            },
+            "bridge-maxage": {
+                "help": "bridge set maxage",
+                "validrange": ["0", "255"],
+                "example": ["bridge-maxage 20"],
+                "default": "20"
+            },
+            "bridge-pathcosts": {
+                "help": "bridge set port path costs",
+                "validvals": ["<interface-range-list>"],
+                "validrange": ["0", "65535"],
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-pathcosts 100",
+                    "under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100"
+                ],
+                "default": "100"
+            },
+            "bridge-portprios": {
+                "help": "bridge port prios",
+                "validvals": ["<interface-range-list>"],
+                "validrange": ["0", "65535"],
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-portprios 32",
+                    "under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32"
+                ],
+            },
+            "bridge-mclmc": {
+                "help": "set multicast last member count",
+                "validrange": ["0", "255"],
+                "example": ["bridge-mclmc 2"],
+                "default": "2"
+            },
+            "bridge-mcrouter": {
+                "help": "Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes",
+                "validvals": ["yes", "no", "0", "1", "2"],
+                "example": ["bridge-mcrouter 1"],
+                "default": "yes"
+            },
+            "bridge-mcsnoop": {
+                "help": "set multicast snooping",
+                "validvals": ["yes", "no", "0", "1"],
+                "default": "yes",
+                "example": ["bridge-mcsnoop yes"]
+            },
+            "bridge-mcsqc": {
+                "help": "set multicast startup query count",
+                "validrange": ["0", "255"],
+                "default": "2",
+                "example": ["bridge-mcsqc 2"]
+            },
+            "bridge-mcqifaddr": {
+                "help": "set multicast query to use ifaddr",
+                "validvals": ["yes", "no", "0", "1"],
+                "default": "no",
+                "example": ["bridge-mcqifaddr no"]
+            },
+            "bridge-mcquerier": {
+                "help": "set multicast querier",
+                "validvals": ["yes", "no", "0", "1"],
+                "default": "no",
+                "example": ["bridge-mcquerier no"]
+            },
+            "bridge-hashel": {
+                "help": "set hash elasticity",
+                "validrange": ["0", "4096"],
+                "default": "4",
+                "example": ["bridge-hashel 4096"]
+            },
+            "bridge-hashmax": {
+                "help": "set hash max",
+                "validrange": ["0", "4096"],
+                "default": "512",
+                "example": ["bridge-hashmax 4096"]
+            },
+            "bridge-mclmi": {
+                "help": "set multicast last member interval (in secs)",
+                "validrange": ["0", "255"],
+                "default": "1",
+                "example": ["bridge-mclmi 1"]
+            },
+            "bridge-mcmi": {
+                "help": "set multicast membership interval (in secs)",
+                "validrange": ["0", "255"],
+                "default": "260",
+                "example": ["bridge-mcmi 260"]
+            },
+            "bridge-mcqpi": {
+                "help": "set multicast querier interval (in secs)",
+                "validrange": ["0", "255"],
+                "default": "255",
+                "example": ["bridge-mcqpi 255"]
+            },
+            "bridge-mcqi": {
+                "help": "set multicast query interval (in secs)",
+                "validrange": ["0", "255"],
+                "default": "125",
+                "example": ["bridge-mcqi 125"]
+            },
+            "bridge-mcqri": {
+                "help": "set multicast query response interval (in secs)",
+                "validrange": ["0", "255"],
+                "default": "10",
+                "example": ["bridge-mcqri 10"]
+            },
+            "bridge-mcsqi": {
+                "help": "set multicast startup query interval (in secs)",
+                "validrange": ["0", "255"],
+                "default": "31",
+                "example": ["bridge-mcsqi 31"]
+            },
+            "bridge-mcqv4src": {
+                "help": "set per VLAN v4 multicast querier source address",
+                "validvals": ["<number-ipv4-list>", ],
+                "multivalue": True,
+                "compat": True,
+                "example": ["bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1"]
+            },
+            "bridge-portmcrouter": {
+                "help": "Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled",
+                "validvals": ["<interface-disabled-automatic-enabled>"],
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-portmcrouter 0",
+                    "under the port (for vlan aware bridge): bridge-portmcrouter 1",
+                    "under the port (for vlan aware bridge): bridge-portmcrouter 2",
+                    "under the port (for vlan aware bridge): bridge-portmcrouter disabled",
+                    "under the port (for vlan aware bridge): bridge-portmcrouter automatic",
+                    "under the port (for vlan aware bridge): bridge-portmcrouter enabled",
+                    "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2",
+                    "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled",
+                    "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1",
+                ]
+            },
+            "bridge-portmcfl": {
+                "help": "port multicast fast leave.",
+                "validvals": ["<interface-yes-no-0-1-list>"],
+                "default": "no",
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-portmcfl no",
+                    "under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no"
+                ]
+            },
+            "bridge-waitport": {
+                "help": "wait for a max of time secs for the"
+                        " specified ports to become available,"
+                        "if no ports are specified then those"
+                        " specified on bridge-ports will be"
+                        " used here. Specifying no ports here "
+                        "should not be used if we are using "
+                        "regex or \"all\" on bridge_ports,"
+                        "as it wouldnt work.",
+                "default": "0",
+                "validvals": ["<number-interface-list>"],
+                "example": ["bridge-waitport 4 swp1 swp2"]
+            },
+            "bridge-maxwait": {
+                "help": "forces to time seconds the maximum time "
+                        "that the Debian bridge setup scripts will "
+                        "wait for the bridge ports to get to the "
+                        "forwarding status, doesn\"t allow factional "
+                        "part. If it is equal to 0 then no waiting"
+                        " is done",
+                "validrange": ["0", "255"],
+                "default": "0",
+                "example": ["bridge-maxwait 3"]
+            },
+            "bridge-vids": {
+                "help": "bridge port vids. Can be specified "
+                        "under the bridge or under the port. "
+                        "If specified under the bridge the ports "
+                        "inherit it unless overridden by a "
+                        "bridge-vids attribute under the port",
+                "multivalue": True,
+                "validvals": ["<number-comma-range-list>"],
+                "example": [
+                    "bridge-vids 4000",
+                    "bridge-vids 2000 2200-3000"
+                ],
+                "aliases": ["bridge-trunk"]
+            },
+            "bridge-pvid": {
+                "help": "bridge port pvid. Must be specified under"
+                        " the bridge port",
+                "validrange": ["0", "4096"],
+                "example": ["bridge-pvid 1"]
+            },
+            "bridge-access": {
+                "help": "bridge port access vlan. Must be "
+                        "specified under the bridge port",
+                "validrange": ["1", "4094"],
+                "example": ["bridge-access 300"]
+            },
+            "bridge-allow-untagged": {
+                "help": "indicate if the bridge port accepts "
+                        "untagged packets or not.  Must be "
+                        "specified under the bridge port. "
+                        "Default is \"yes\"",
+                "validvals": ["yes", "no"],
+                "example": ["bridge-allow-untagged yes"],
+                "default": "yes"
+            },
+            "bridge-port-vids": {
+                "help": "bridge vlans",
+                "compat": True,
+                "example": ["bridge-port-vids bond0=1-1000,1010-1020"]
+            },
+            "bridge-port-pvids": {
+                "help": "bridge port vlans",
+                "compat": True,
+                "example": ["bridge-port-pvids bond0=100 bond1=200"]
+            },
+            "bridge-learning": {
+                "help": "bridge port learning flag",
+                "validvals": ["on", "off", "<interface-on-off-list>"],
+                "default": "on",
+                "example": ["bridge-learning off"]
+            },
+            "bridge-igmp-version": {
+                "help": "mcast igmp version",
+                "validvals": ["2", "3"],
+                "default": "2",
+                "example": ["bridge-igmp-version 2"]
+            },
+            "bridge-mld-version": {
+                "help": "mcast mld version",
+                "validvals": ["1", "2"],
+                "default": "1",
+                "example": ["bridge-mld-version 1"]
+            },
+            "bridge-unicast-flood": {
+                "help": "bridge port unicast flood flag",
+                "validvals": ["on", "off", "<interface-on-off-list>"],
+                "default": "on",
+                "example": ["under the port (for vlan aware bridge): bridge-unicast-flood on",
+                            "under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on"]
+            },
+            "bridge-multicast-flood": {
+                "help": "bridge port multicast flood flag",
+                "validvals": ["on", "off", "<interface-on-off-list>"],
+                "default": "on",
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-multicast-flood on",
+                    "under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on"
+                ]
+            },
+            "bridge-broadcast-flood": {
+                "help": "bridge port broadcast flood flag",
+                "validvals": ["on", "off", "<interface-on-off-list>"],
+                "default": "on",
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-broadcast-flood on",
+                    "under the bridge (for vlan unaware bridge): bridge-broadcast-flood swp1=on swp2=on"
+                ]
+            },
+            "bridge-vlan-protocol": {
+                "help": "bridge vlan protocol",
+                "default": "802.1q",
+                "validvals": ["802.1q", "802.1ad"],
+                "example": ["bridge-vlan-protocol 802.1q"]
+            },
+            "bridge-vlan-stats": {
+                "help": "bridge vlan stats",
+                "default": "off",
+                "validvals": ["on", "off"],
+                "example": ["bridge-vlan-stats off"]
+            },
+            "bridge-arp-nd-suppress": {
+                "help": "bridge port arp nd suppress flag",
+                "validvals": ["on", "off", "<interface-on-off-list>"],
+                "default": "off",
+                "example": [
+                    "under the port (for vlan aware bridge): bridge-arp-nd-suppress on",
+                    "under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on"
+                ]
+            },
+            "bridge-mcstats": {
+                "help": "bridge multicast stats",
+                "default": "off",
+                "validvals": ["on", "off", "1", "0", "yes", "no"],
+                "example": ["bridge-mcstats off"]
+            },
+            "bridge-l2protocol-tunnel": {
+                "help": "layer 2 protocol tunneling",
+                "validvals": [  # XXX: lists all combinations, should move to
+                    # a better representation
+                    "all",
+                    "cdp",
+                    "cdp lacp",
+                    "cdp lacp lldp",
+                    "cdp lacp lldp pvst",
+                    "cdp lacp lldp stp",
+                    "cdp lacp pvst",
+                    "cdp lacp pvst stp",
+                    "cdp lacp stp",
+                    "cdp lldp",
+                    "cdp lldp pvst",
+                    "cdp lldp pvst stp",
+                    "cdp lldp stp",
+                    "cdp pvst",
+                    "cdp pvst stp",
+                    "cdp stp",
+                    "lacp",
+                    "lacp lldp",
+                    "lacp lldp pvst",
+                    "lacp lldp pvst stp",
+                    "lacp lldp stp",
+                    "lacp pvst",
+                    "lacp pvst stp",
+                    "lacp stp",
+                    "lldp",
+                    "lldp pvst",
+                    "lldp pvst stp",
+                    "lldp stp",
+                    "pvst",
+                    "pvst stp",
+                    "stp",
+                    "<interface-l2protocol-tunnel-list>"],
+                "example": [
+                    "under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all",
+                    "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst",
+                    "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst",
+                    "under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp",
+                    "under the port (for vlan aware bridge): bridge-l2protocol-tunnel all"
+                ]
+            },
+            "bridge-ports-condone-regex": {
+                    "help": "bridge ports to ignore/condone when reloading config / removing interfaces",
+                    "required": False,
+                    "example": ["bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$"]
+            },
+        }
+    }
+
+    bridge_utils_missing_warning = True
 
     # Netlink attributes not associated with ifupdown2
     # attributes are left commented-out for a future use
     # and kept in order :)
-    _ifla_br_attributes_map = (
+    _ifla_br_attributes_map = {
         # Link.IFLA_BR_UNSPEC,
-        ('bridge-fd', Link.IFLA_BR_FORWARD_DELAY),
-        ('bridge-hello', Link.IFLA_BR_HELLO_TIME),
-        ('bridge-maxage', Link.IFLA_BR_MAX_AGE),
-        ('bridge-ageing', Link.IFLA_BR_AGEING_TIME),
-        ('bridge-stp', Link.IFLA_BR_STP_STATE),
-        ('bridge-bridgeprio', Link.IFLA_BR_PRIORITY),
-        ('bridge-vlan-aware', Link.IFLA_BR_VLAN_FILTERING),
-        ('bridge-vlan-protocol', Link.IFLA_BR_VLAN_PROTOCOL),
+        'bridge-fd': Link.IFLA_BR_FORWARD_DELAY,
+        'bridge-hello': Link.IFLA_BR_HELLO_TIME,
+        'bridge-maxage': Link.IFLA_BR_MAX_AGE,
+        'bridge-ageing': Link.IFLA_BR_AGEING_TIME,
+        'bridge-stp': Link.IFLA_BR_STP_STATE,
+        'bridge-bridgeprio': Link.IFLA_BR_PRIORITY,
+        'bridge-vlan-aware': Link.IFLA_BR_VLAN_FILTERING,
+        'bridge-vlan-protocol': Link.IFLA_BR_VLAN_PROTOCOL,
         # Link.IFLA_BR_GROUP_FWD_MASK,
         # Link.IFLA_BR_ROOT_ID,
         # Link.IFLA_BR_BRIDGE_ID,
@@ -410,68 +473,35 @@ class bridge(moduleBase):
         # Link.IFLA_BR_GC_TIMER,
         # Link.IFLA_BR_GROUP_ADDR,
         # Link.IFLA_BR_FDB_FLUSH,
-        ('bridge-mcrouter', Link.IFLA_BR_MCAST_ROUTER),
+        'bridge-mcrouter': Link.IFLA_BR_MCAST_ROUTER,
         #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr
-        ('bridge-mcqifaddr', Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
-        ('bridge-mcquerier', Link.IFLA_BR_MCAST_QUERIER),
-        ('bridge-hashel', Link.IFLA_BR_MCAST_HASH_ELASTICITY),
-        ('bridge-hashmax', Link.IFLA_BR_MCAST_HASH_MAX),
-        ('bridge-mclmc', Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
-        ('bridge-mcsqc', Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
-        ('bridge-mclmi', Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
-        ('bridge-mcmi', Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
-        ('bridge-mcqpi', Link.IFLA_BR_MCAST_QUERIER_INTVL),
-        ('bridge-mcqi', Link.IFLA_BR_MCAST_QUERY_INTVL),
-        ('bridge-mcqri', Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
-        ('bridge-mcsqi', Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
+        'bridge-mcqifaddr': Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
+        'bridge-mcquerier': Link.IFLA_BR_MCAST_QUERIER,
+        'bridge-hashel': Link.IFLA_BR_MCAST_HASH_ELASTICITY,
+        'bridge-hashmax': Link.IFLA_BR_MCAST_HASH_MAX,
+        'bridge-mclmc': Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
+        'bridge-mcsqc': Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+        'bridge-mclmi': Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+        'bridge-mcmi': Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+        'bridge-mcqpi': Link.IFLA_BR_MCAST_QUERIER_INTVL,
+        'bridge-mcqi': Link.IFLA_BR_MCAST_QUERY_INTVL,
+        'bridge-mcqri': Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+        'bridge-mcsqi': Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
         # Link.IFLA_BR_NF_CALL_IPTABLES,
         # Link.IFLA_BR_NF_CALL_IP6TABLES,
         # Link.IFLA_BR_NF_CALL_ARPTABLES,
         # Link.IFLA_BR_VLAN_DEFAULT_PVID,
         # Link.IFLA_BR_PAD,
         # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), #  already dealt with, in a separate loop
-        ('bridge-igmp-version', Link.IFLA_BR_MCAST_IGMP_VERSION, ),
-        ('bridge-mcstats', Link.IFLA_BR_MCAST_STATS_ENABLED),
-        ('bridge-mld-version', Link.IFLA_BR_MCAST_MLD_VERSION)
-    )
+        'bridge-igmp-version': Link.IFLA_BR_MCAST_IGMP_VERSION,
+        'bridge-mcstats': Link.IFLA_BR_MCAST_STATS_ENABLED,
+        'bridge-mld-version': Link.IFLA_BR_MCAST_MLD_VERSION
+    }
     # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today
     # they are supported. It is done this way because this dictionary is used
     # in a loop, but these attributes require additional work. Thus they are
     # excluded from this loop without overhead.
 
-    # we are still using the old linkCache we need an easy way
-    # to use this cache with the new full-netlink approach
-    _ifla_br_attributes_old_cache_key_map = dict(
-        (
-            (Link.IFLA_BR_FORWARD_DELAY, 'fd'),
-            (Link.IFLA_BR_HELLO_TIME, 'hello'),
-            (Link.IFLA_BR_MAX_AGE, 'maxage'),
-            (Link.IFLA_BR_AGEING_TIME, 'ageing'),
-            (Link.IFLA_BR_STP_STATE, 'stp'),
-            (Link.IFLA_BR_PRIORITY, 'bridgeprio'),
-            (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering'),
-            (Link.IFLA_BR_VLAN_PROTOCOL, 'vlan-protocol'),
-            (Link.IFLA_BR_MCAST_ROUTER, 'mcrouter'),
-            (Link.IFLA_BR_MCAST_SNOOPING, 'mcsnoop'),
-            (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, 'mcqifaddr'),
-            (Link.IFLA_BR_MCAST_QUERIER, 'mcquerier'),
-            (Link.IFLA_BR_MCAST_HASH_ELASTICITY, 'hashel'),
-            (Link.IFLA_BR_MCAST_HASH_MAX, 'hashmax'),
-            (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, 'mclmc'),
-            (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, 'mcsqc'),
-            (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, 'mclmi'),
-            (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, 'mcmi'),
-            (Link.IFLA_BR_MCAST_QUERIER_INTVL, 'mcqpi'),
-            (Link.IFLA_BR_MCAST_QUERY_INTVL, 'mcqi'),
-            (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, 'mcqri'),
-            (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, 'mcsqi'),
-            (Link.IFLA_BR_VLAN_STATS_ENABLED, 'vlan-stats'),
-            (Link.IFLA_BR_MCAST_STATS_ENABLED, 'mcstats'),
-            (Link.IFLA_BR_MCAST_IGMP_VERSION, 'igmp-version'),
-            (Link.IFLA_BR_MCAST_MLD_VERSION, 'mld-version')
-        )
-    )
-
     _ifla_br_attributes_translate_user_config_to_netlink_map = dict(
         (
             # Link.IFLA_BR_UNSPEC,
@@ -482,7 +512,7 @@ class bridge(moduleBase):
             # Link.IFLA_BR_STP_STATE, #  STP is treated outside the loop
             (Link.IFLA_BR_PRIORITY, int),
             (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string),
-            (Link.IFLA_BR_VLAN_PROTOCOL, str),
+            (Link.IFLA_BR_VLAN_PROTOCOL, str.upper),
             # Link.IFLA_BR_GROUP_FWD_MASK,
             # Link.IFLA_BR_ROOT_ID,
             # Link.IFLA_BR_BRIDGE_ID,
@@ -522,17 +552,17 @@ class bridge(moduleBase):
         )
     )
 
-    _ifla_brport_attributes_map = (
+    _ifla_brport_attributes_map = {
         # Link.IFLA_BRPORT_UNSPEC,
         # Link.IFLA_BRPORT_STATE,
-        ('bridge-portprios', Link.IFLA_BRPORT_PRIORITY),
-        ('bridge-pathcosts', Link.IFLA_BRPORT_COST),
+        'bridge-portprios': Link.IFLA_BRPORT_PRIORITY,
+        'bridge-pathcosts': Link.IFLA_BRPORT_COST,
         # Link.IFLA_BRPORT_MODE,
         # Link.IFLA_BRPORT_GUARD,
         # Link.IFLA_BRPORT_PROTECT,
-        ('bridge-portmcfl', Link.IFLA_BRPORT_FAST_LEAVE),
-        ('bridge-learning', Link.IFLA_BRPORT_LEARNING),
-        ('bridge-unicast-flood', Link.IFLA_BRPORT_UNICAST_FLOOD),
+        'bridge-portmcfl': Link.IFLA_BRPORT_FAST_LEAVE,
+        'bridge-learning': Link.IFLA_BRPORT_LEARNING,
+        'bridge-unicast-flood': Link.IFLA_BRPORT_UNICAST_FLOOD,
         # Link.IFLA_BRPORT_PROXYARP,
         # Link.IFLA_BRPORT_LEARNING_SYNC,
         # Link.IFLA_BRPORT_PROXYARP_WIFI,
@@ -548,17 +578,17 @@ class bridge(moduleBase):
         # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
         # Link.IFLA_BRPORT_HOLD_TIMER,
         # Link.IFLA_BRPORT_FLUSH,
-        ('bridge-portmcrouter', Link.IFLA_BRPORT_MULTICAST_ROUTER),
+        'bridge-portmcrouter': Link.IFLA_BRPORT_MULTICAST_ROUTER,
         # Link.IFLA_BRPORT_PAD,
-        ('bridge-multicast-flood', Link.IFLA_BRPORT_MCAST_FLOOD),
+        'bridge-multicast-flood': Link.IFLA_BRPORT_MCAST_FLOOD,
         # Link.IFLA_BRPORT_MCAST_TO_UCAST,
         # Link.IFLA_BRPORT_VLAN_TUNNEL,
-        # Link.IFLA_BRPORT_BCAST_FLOOD
-        ('bridge-l2protocol-tunnel', Link.IFLA_BRPORT_GROUP_FWD_MASK),
+        'bridge-broadcast-flood': Link.IFLA_BRPORT_BCAST_FLOOD,
+        'bridge-l2protocol-tunnel': Link.IFLA_BRPORT_GROUP_FWD_MASK,
         # Link.IFLA_BRPORT_PEER_LINK,
         # Link.IFLA_BRPORT_DUAL_LINK,
-        ('bridge-arp-nd-suppress', Link.IFLA_BRPORT_ARP_SUPPRESS),
-    )
+        'bridge-arp-nd-suppress': Link.IFLA_BRPORT_NEIGH_SUPPRESS,
+    }
 
     _ifla_brport_multicast_router_dict_to_int = {
         'disabled': 0,
@@ -571,6 +601,12 @@ class bridge(moduleBase):
         '2': 2,
     }
 
+    _ifla_brport_multicast_router_dict_int_to_str = {
+        0: "disabled",
+        1: "automatic",
+        2: "enabled"
+    }
+
     # callable to translate <interface-yes-no-0-1-list> to netlink value
     _ifla_brport_attributes_translate_user_config_to_netlink_map = dict(
         (
@@ -581,18 +617,16 @@ class bridge(moduleBase):
             (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string),
             (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string),
             (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string),
+            (Link.IFLA_BRPORT_BCAST_FLOOD, utils.get_boolean_from_string),
             (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
-            (Link.IFLA_BRPORT_ARP_SUPPRESS, utils.get_boolean_from_string)
+            (Link.IFLA_BRPORT_NEIGH_SUPPRESS, utils.get_boolean_from_string)
         )
     )
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
         self.name = self.__class__.__name__
-        self.brctlcmd = None
-        self._running_vidinfo = {}
-        self._running_vidinfo_valid = False
         self._resv_vlan_range =  self._get_reserved_vlan_range()
         self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range)))
 
@@ -659,6 +693,15 @@ class bridge(moduleBase):
             default=True
         )
 
+        self.bridge_vxlan_arp_nd_suppress = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr="bridge-vxlan-arp-nd-suppress"
+            ),
+            default=False
+        )
+        self.bridge_vxlan_arp_nd_suppress_int = int(self.bridge_vxlan_arp_nd_suppress)
+
         self.l2protocol_tunnel_callback = {
             'all': self._l2protocol_tunnel_set_all,
             'stp': self._l2protocol_tunnel_set_stp,
@@ -677,6 +720,63 @@ class bridge(moduleBase):
             'lacp': self._query_check_l2protocol_tunnel_lacp
         }
 
+        self._bridge_attribute_query_check_handler = {
+            "bridge-maxwait": (self._query_check_br_attr_wait, None),
+            "bridge-waitport": (self._query_check_br_attr_wait, None),
+
+            "bridge-stp": (self._query_check_br_attr_stp, Link.IFLA_BR_STP_STATE),
+
+            "bridge-mcstats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_MCAST_STATS_ENABLED),
+            "bridge-vlan-stats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_VLAN_STATS_ENABLED),
+
+            "bridge-vlan-aware": (self._query_check_br_attr_boolean, Link.IFLA_BR_VLAN_FILTERING),
+            "bridge-mcqifaddr": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
+            "bridge-mcsnoop": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_SNOOPING),
+            "bridge-mcquerier": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERIER),
+            "bridge-mcrouter": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_ROUTER),
+
+            "bridge-vlan-protocol": (self._query_check_br_attr_string, Link.IFLA_BR_VLAN_PROTOCOL),
+
+            "bridge-mcsqc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
+            "bridge-mclmc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
+            "bridge-hashmax": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_MAX),
+            "bridge-hashel": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_ELASTICITY),
+            "bridge-bridgeprio": (self._query_check_br_attr_int, Link.IFLA_BR_PRIORITY),
+            "bridge-igmp-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_IGMP_VERSION),
+            "bridge-mld-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_MLD_VERSION),
+
+            "bridge-maxage": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MAX_AGE),
+            "bridge-fd": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_FORWARD_DELAY),
+            "bridge-hello": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_HELLO_TIME),
+            "bridge-ageing": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_AGEING_TIME),
+            "bridge-mcmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
+            "bridge-mcsqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
+            "bridge-mclmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
+            "bridge-mcqri": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
+            "bridge-mcqpi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERIER_INTVL),
+            "bridge-mcqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_INTVL),
+        }
+
+        self._brport_attribute_query_check_handler = {
+            "bridge-pathcosts": self._query_check_brport_attr_int,
+            "bridge-portprios": self._query_check_brport_attr_int,
+            "bridge-portmcfl": self._query_check_brport_attr_boolean_yes_no,
+            "bridge-learning": self._query_check_brport_attr_boolean_on_off,
+            "bridge-arp-nd-suppress": self._query_check_brport_attr_boolean_on_off,
+            "bridge-unicast-flood": self._query_check_brport_attr_boolean_on_off,
+            "bridge-multicast-flood": self._query_check_brport_attr_boolean_on_off,
+            "bridge-broadcast-flood": self._query_check_brport_attr_boolean_on_off,
+            "bridge-portmcrouter": self._query_check_brport_attr_portmcrouter,
+        }
+
+        self.bridge_vxlan_port_learning = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                self.__class__.__name__,
+                "bridge_vxlan_port_learning"
+            ),
+            default=True
+        )
+
     @staticmethod
     def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
         if not ifla_brport_group_maskhi:
@@ -851,13 +951,8 @@ class bridge(moduleBase):
 
         return ' '.join(bridge_ports)
 
-    def _is_bridge_port(self, ifaceobj):
-        if self.brctlcmd.is_bridge_port(ifaceobj.name):
-            return True
-        return False
-
     def check_valid_bridge(self, ifaceobj, ifname):
-        if LinkUtils.link_exists_nodryrun(ifname) and not LinkUtils.is_bridge(ifname):
+        if self.cache.link_exists(ifname) and not self.cache.link_is_bridge(ifname):
             self.log_error('misconfiguration of bridge attribute(s) on existing non-bridge interface (%s)' % ifname, ifaceobj=ifaceobj)
             return False
         return True
@@ -879,10 +974,9 @@ class bridge(moduleBase):
                                     ifacenames_all)
 
     def get_dependent_ifacenames_running(self, ifaceobj):
-        self._init_command_handlers()
-        if not self.brctlcmd.bridge_exists(ifaceobj.name):
+        if not self.cache.bridge_exists(ifaceobj.name):
             return None
-        return self.brctlcmd.get_bridge_ports(ifaceobj.name)
+        return self.cache.get_slaves(ifaceobj.name)
 
     def _get_bridge_port_list(self, ifaceobj):
 
@@ -944,7 +1038,7 @@ class bridge(moduleBase):
             starttime = time.time()
             while ((time.time() - starttime) < waitporttime):
                 if all([False for p in waitportlist
-                        if not self.ipcmd.link_exists(p)]):
+                        if not self.cache.link_exists(p)]):
                     break;
                 time.sleep(1)
         except Exception, e:
@@ -957,11 +1051,7 @@ class bridge(moduleBase):
         except Exception, e:
             self.logger.info(str(e))
 
-    def handle_ipv6(self, ports, state, ifaceobj=None):
-        if (ifaceobj and
-                (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN) and
-                not ifaceobj.get_attr_value('address') and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE):
-            self._enable_disable_ipv6(ifaceobj.name, state)
+    def handle_ipv6(self, ports, state):
         for p in ports:
             self._enable_disable_ipv6(p, state)
 
@@ -994,32 +1084,28 @@ class bridge(moduleBase):
         bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj)
         runningbridgeports = []
 
-        self.ipcmd.batch_start()
         self._process_bridge_waitport(ifaceobj, bridgeports)
-        self.ipcmd.batch_start()
         # Delete active ports not in the new port list
         if not ifupdownflags.flags.PERFMODE:
-            runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+            runningbridgeports = self.cache.get_slaves(ifaceobj.name)
             if runningbridgeports:
                 for bport in runningbridgeports:
                     if not bridgeports or bport not in bridgeports:
                         if bridgeportscondoneregex and bridgeportscondoneregex.match(bport):
                             self.logger.info("%s: port %s will stay enslaved as it matches with bridge-ports-condone-regex" % (ifaceobj.name, bport))
                             continue
-                        self.ipcmd.link_set(bport, 'nomaster')
+                        self.netlink.link_set_nomaster(bport)
                         # set admin DOWN on all removed ports
                         # that don't have config outside bridge
                         if not ifaceobj_getfunc(bport):
-                            netlink.link_set_updown(bport, "down")
+                            self.netlink.link_down(bport)
                         # enable ipv6 for ports that were removed
                         self.handle_ipv6([bport], '0')
             else:
                 runningbridgeports = []
         if not bridgeports:
-            self.ipcmd.batch_commit()
             return []
         err = 0
-        ports = 0
         newbridgeports = Set(bridgeports).difference(Set(runningbridgeports))
         newly_enslaved_ports = []
 
@@ -1028,38 +1114,32 @@ class bridge(moduleBase):
             if br_port in newbridgeports:
                 newbridgeports_ordered.append(br_port)
 
+        self.iproute2.batch_start()
+
         for bridgeport in newbridgeports_ordered:
             try:
                 if (not ifupdownflags.flags.DRYRUN and
-                    not self.ipcmd.link_exists(bridgeport)):
+                    not self.cache.link_exists(bridgeport)):
                     self.log_error('%s: bridge port %s does not exist'
                                    %(ifaceobj.name, bridgeport), ifaceobj)
                     err += 1
                     continue
-                hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
+                hwaddress = self.cache.get_link_address(bridgeport)
                 if not self._valid_ethaddr(hwaddress):
                     self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
                                   bridgeport) + 'invalid ether addr %s'
                                   %hwaddress)
                     continue
-                self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
+                self.iproute2.link_set_master(bridgeport, ifaceobj.name)
                 newly_enslaved_ports.append(bridgeport)
                 self.handle_ipv6([bridgeport], '1')
-                self.ipcmd.addr_flush(bridgeport)
-                ports += 1
-                if ports == 250:
-                    ports = 0
-                    self.ipcmd.batch_commit()
-                    self.ipcmd.batch_start()
+                self.iproute2.addr_flush(bridgeport)
             except Exception, e:
                 self.logger.error(str(e))
                 pass
-        try:
-            self.ipcmd.batch_commit()
-        except Exception, e:
-            self._pretty_print_add_ports_error(str(e), ifaceobj,
-                                               bridgeports)
-            pass
+
+        self.iproute2.batch_commit()
+        self.cache.force_add_slave_list(ifaceobj.name, newly_enslaved_ports)
 
         if err:
             self.log_error('bridge configuration failed (missing ports)')
@@ -1123,11 +1203,15 @@ class bridge(moduleBase):
     def _diff_vids(self, vids1_ints, vids2_ints):
         return Set(vids2_ints).difference(vids1_ints), Set(vids1_ints).difference(vids2_ints)
 
-    def _compare_vids(self, vids1, vids2, pvid=None):
+    def _compare_vids(self, vids1, vids2, pvid=None, expand_range=True):
         """ Returns true if the vids are same else return false """
 
-        vids1_ints = self._ranges_to_ints(vids1)
-        vids2_ints = self._ranges_to_ints(vids2)
+        if expand_range:
+            vids1_ints = self._ranges_to_ints(vids1)
+            vids2_ints = self._ranges_to_ints(vids2)
+        else:
+            vids1_ints = self._ranges_to_ints(vids1)
+            vids2_ints = vids2
         set_diff = Set(vids1_ints).symmetric_difference(vids2_ints)
         if pvid and int(pvid) in set_diff:
             set_diff.remove(int(pvid))
@@ -1144,7 +1228,7 @@ class bridge(moduleBase):
         if attrval:
             running_mcqv4src = {}
             if not ifupdownflags.flags.PERFMODE:
-                running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobj.name)
+                running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
             mcqs = {}
             srclist = attrval.split()
             for s in srclist:
@@ -1153,25 +1237,14 @@ class bridge(moduleBase):
 
             k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
             for v in k_to_del:
-                self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
+                self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
             for v in mcqs.keys():
-                self.brctlcmd.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
+                self.iproute2.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
         elif not ifupdownflags.flags.PERFMODE:
-            running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobj.name)
+            running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
             if running_mcqv4src:
                 for v in running_mcqv4src.keys():
-                    self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
-
-    def _get_running_vidinfo(self):
-        if self._running_vidinfo_valid:
-            return self._running_vidinfo
-        self._running_vidinfo = {}
-
-        # Removed check for PERFMODE.  Need the get in all cases
-        # including reboot, so that we can configure the pvid correctly.
-        self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all_json()
-        self._running_vidinfo_valid = True
-        return self._running_vidinfo
+                    self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
 
     def _set_bridge_vidinfo_compat(self, ifaceobj):
         #
@@ -1196,13 +1269,13 @@ class bridge(moduleBase):
                 try:
                     (port, pvid) = p.split('=')
                     pvid = int(pvid)
-                    running_pvid = self._get_running_pvid(port)
+                    running_pvid = self.cache.get_pvid(port)
                     if running_pvid:
                         if running_pvid == pvid:
                             continue
                         else:
-                            self.ipcmd.bridge_port_pvid_del(port, running_pvid)
-                    self.ipcmd.bridge_port_pvid_add(port, pvid)
+                            self.iproute2.bridge_vlan_del_pvid(port, running_pvid)
+                    self.iproute2.bridge_vlan_add_pvid(port, pvid)
                 except Exception, e:
                     self.log_warn('%s: failed to set pvid `%s` (%s)'
                             %(ifaceobj.name, p, str(e)))
@@ -1219,18 +1292,18 @@ class bridge(moduleBase):
                     (port, val) = p.split('=')
                     vids = val.split(',')
                     vids_int =  self._ranges_to_ints(vids)
-                    running_vids = self.ipcmd.bridge_vlan_get_vids(port)
+                    _, running_vids = self.cache.get_pvid_and_vids(port)
                     if running_vids:
                         (vids_to_del, vids_to_add) = \
                                 self._diff_vids(vids_int, running_vids)
                         if vids_to_del:
-                            self.ipcmd.bridge_port_vids_del(port,
+                            self.iproute2.bridge_vlan_del_vid_list(port,
                                     self._compress_into_ranges(vids_to_del))
                         if vids_to_add:
-                            self.ipcmd.bridge_port_vids_add(port,
+                            self.iproute2.bridge_vlan_add_vid_list(port,
                                     self._compress_into_ranges(vids_to_add))
                     else:
-                        self.ipcmd.bridge_port_vids_add(port, vids_int)
+                        self.iproute2.bridge_vlan_add_vid_list(port, vids_int)
                 except Exception, e:
                     self.log_warn('%s: failed to set vid `%s` (%s)'
                         %(ifaceobj.name, p, str(e)))
@@ -1271,7 +1344,8 @@ class bridge(moduleBase):
                                                    ifname,
                                                    nl_attr,
                                                    attr_name,
-                                                   user_config):
+                                                   user_config,
+                                                   cached_value):
         try:
             translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr)
 
@@ -1285,21 +1359,13 @@ class bridge(moduleBase):
                     attr=attr_name
                 )
 
-            old_cache_key = self._ifla_br_attributes_old_cache_key_map.get(nl_attr)
-            if old_cache_key and not link_just_created:
-                cached_value = self.brctlcmd.link_cache_get([ifname, 'linkinfo', old_cache_key])
-                if not cached_value or cached_value == "None":
-                    # the link already exists but we don't have any value
-                    # cached for this attr, it probably means that the
-                    # capability is not available on this system (i.e old kernel)
-                    self.logger.debug('%s: ignoring %s %s: capability '
-                                      'probably not supported on this system'
-                                      % (ifname, attr_name, user_config))
-                    return
-                # we need to convert the cache value to "netlink" format
-                cached_value = translate_func(cached_value.lower())
-            else:
-                cached_value = None
+            if not link_just_created and cached_value is None:
+                # the link already exists but we don't have any value
+                # cached for this attr, it probably means that the
+                # capability is not available on this system (i.e old kernel)
+                self.logger.debug("%s: ignoring %s %s: capability probably not supported on this system"
+                                  % (ifname, attr_name, user_config))
+                return
 
             if not user_config and not link_just_created and cached_value is not None:
                 # there is no user configuration for this attribute
@@ -1335,16 +1401,28 @@ class bridge(moduleBase):
         ifla_info_data = dict()
         ifname = ifaceobj.name
 
-        self.logger.info('%s: apply bridge settings' % ifname)
+        self.logger.info('%s: applying bridge settings' % ifname)
 
-        for attr_name, nl_attr in self._ifla_br_attributes_map:
+        cached_ifla_info_data = self.cache.get_link_info_data(ifname)
+
+        try:
+            # we compare the user value (or policy value) with the current running state
+            # we need to divide the cached value by 100 to ignore small difference.
+            # i.e. our default value is 31 but the kernel default seems to be 3125
+            cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] /= 100
+            cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] *= 100
+        except:
+            pass
+
+        for attr_name, nl_attr in self._ifla_br_attributes_map.iteritems():
             self.fill_ifla_info_data_with_ifla_br_attribute(
                 ifla_info_data=ifla_info_data,
                 link_just_created=link_just_created,
                 ifname=ifname,
                 nl_attr=nl_attr,
                 attr_name=attr_name,
-                user_config=ifaceobj.get_attr_value_first(attr_name)
+                user_config=ifaceobj.get_attr_value_first(attr_name),
+                cached_value=cached_ifla_info_data.get(nl_attr)
             )
 
         # bridge-mcsnoop
@@ -1354,7 +1432,9 @@ class bridge(moduleBase):
             ifname=ifname,
             nl_attr=Link.IFLA_BR_MCAST_SNOOPING,
             attr_name='bridge-mcsnoop',
-            user_config=self.get_bridge_mcsnoop_value(ifaceobj)
+            user_config=self.get_bridge_mcsnoop_value(ifaceobj),
+            cached_value=cached_ifla_info_data.get(Link.IFLA_BR_MCAST_SNOOPING)
+
         )
 
         # bridge-vlan-stats
@@ -1365,7 +1445,8 @@ class bridge(moduleBase):
                 ifname=ifname,
                 nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED,
                 attr_name='bridge-vlan-stats',
-                user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats
+                user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats,
+                cached_value=cached_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
             )
 
         try:
@@ -1385,7 +1466,7 @@ class bridge(moduleBase):
             self.logger.warning('%s: bridge stp: %s' % (ifname, str(e)))
 
         if ifla_info_data:
-            netlink.link_add_set(ifname=ifname, kind='bridge', ifla_info_data=ifla_info_data, link_exists=True)
+            self.netlink.link_set_bridge_info_data(ifname, ifla_info_data, link_just_created)
 
     def _check_vids(self, ifaceobj, vids):
         ret = True
@@ -1405,22 +1486,8 @@ class bridge(moduleBase):
                                  %(ifaceobj.name, v))
         return ret
 
-    def _get_running_pvid(self, ifacename):
-        pvid = 0
-
-        running_vidinfo = self._get_running_vidinfo()
-        for vinfo in running_vidinfo.get(ifacename, {}):
-            v = vinfo.get('vlan')
-            pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-            if pvid:
-                return pvid
-        return pvid
-
     def _get_running_vids_n_pvid_str(self, ifacename):
-        vids = []
-        pvid = None
-
-        (vids, pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(ifacename)
+        pvid, vids = self.cache.get_pvid_and_vids(ifacename)
 
         if vids:
             ret_vids = self._compress_into_ranges(vids)
@@ -1465,8 +1532,7 @@ class bridge(moduleBase):
             if not self._check_vids(bportifaceobj, vids):
                return
 
-            (running_vids, running_pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(
-                                                        bportifaceobj.name)
+            running_pvid, running_vids = self.cache.get_pvid_and_vids(bportifaceobj.name)
 
             if not running_vids and not running_pvid:
                 # There cannot be a no running pvid.
@@ -1515,7 +1581,7 @@ class bridge(moduleBase):
             if vids_to_del:
                if pvid_to_add in vids_to_del:
                    vids_to_del.remove(pvid_to_add)
-               self.ipcmd.bridge_vids_del(bportifaceobj.name,
+               self.iproute2.bridge_vlan_del_vid_list_self(bportifaceobj.name,
                                           self._compress_into_ranges(
                                           vids_to_del), isbridge)
         except Exception, e:
@@ -1524,7 +1590,7 @@ class bridge(moduleBase):
 
         try:
             if pvid_to_del:
-               self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
+               self.iproute2.bridge_vlan_del_pvid(bportifaceobj.name,
                                                pvid_to_del)
         except Exception, e:
                 self.log_warn('%s: failed to del pvid `%s` (%s)'
@@ -1532,7 +1598,7 @@ class bridge(moduleBase):
 
         try:
             if vids_to_add:
-               self.ipcmd.bridge_vids_add(bportifaceobj.name,
+               self.iproute2.bridge_vlan_add_vid_list_self(bportifaceobj.name,
                                           self._compress_into_ranges(
                                           vids_to_add), isbridge)
         except Exception, e:
@@ -1542,7 +1608,7 @@ class bridge(moduleBase):
 
         try:
             if pvid_to_add and pvid_to_add != running_pvid:
-                self.ipcmd.bridge_port_pvid_add(bportifaceobj.name,
+                self.iproute2.bridge_vlan_add_pvid(bportifaceobj.name,
                                                 pvid_to_add)
         except Exception, e:
                 self.log_error('%s: failed to set pvid `%s` (%s)'
@@ -1624,11 +1690,11 @@ class bridge(moduleBase):
         if not bridgeports:
            self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
            return
-        self.ipcmd.batch_start()
+        self.iproute2.batch_start()
         for bport in bridgeports:
-            # Use the brctlcmd bulk set method: first build a dictionary
-            # and then call set
-            if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport):
+            # on link_set_master we need to wait until we cache the correct
+            # notification and register the brport as slave
+            if not self.cache.bridge_port_exists(ifaceobj.name, bport):
                 self.logger.info('%s: skipping bridge config' %ifaceobj.name +
                         ' for port %s (missing port)' %bport)
                 continue
@@ -1636,7 +1702,7 @@ class bridge(moduleBase):
                              %(ifaceobj.name, bport))
             bportifaceobjlist = ifaceobj_getfunc(bport)
             if not bportifaceobjlist:
-               continue
+                continue
             for bportifaceobj in bportifaceobjlist:
                 # Dont process bridge port if it already has been processed
                 # and there is no override on port_processed
@@ -1657,7 +1723,7 @@ class bridge(moduleBase):
                     err = True
                     self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
                     pass
-        self.ipcmd.bridge_batch_commit()
+        self.iproute2.batch_commit()
         if err:
            raise Exception('%s: errors applying port settings' %ifaceobj.name)
 
@@ -1670,12 +1736,12 @@ class bridge(moduleBase):
                 self.warn_on_untagged_bridge_absence = False
 
     def bridge_port_get_bridge_name(self, ifaceobj):
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+        bridgename = self.cache.get_bridge_name_from_port(ifaceobj.name)
         if not bridgename:
             # bridge port is not enslaved to a bridge we need to find
             # the bridge in it's upper ifaces then enslave it
             for u in ifaceobj.upperifaces:
-                if self.ipcmd.is_bridge(u):
+                if self.cache.link_is_bridge(u):
                     return True, u
             return False, None
         # return should_enslave port, bridgename
@@ -1683,7 +1749,7 @@ class bridge(moduleBase):
 
     def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
         if should_enslave_port:
-            netlink.link_set_master(ifaceobj.name, bridge_name)
+            self.netlink.link_set_master(ifaceobj.name, bridge_name)
             self.handle_ipv6([ifaceobj.name], '1')
 
         bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
@@ -1701,7 +1767,7 @@ class bridge(moduleBase):
             # bridge doesn't exist
             return
 
-        vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridge_name)
+        vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridge_name)
         if vlan_aware_bridge:
             self.up_bridge_port_vlan_aware_bridge(ifaceobj,
                                                   ifaceobj_getfunc,
@@ -1717,11 +1783,12 @@ class bridge(moduleBase):
 
         ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
 
-    def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_exists):
+    def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_created):
         if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
             if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
                 return False
-            if link_exists:
+            if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name):
+                # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports
                 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
             return True
         return False
@@ -1734,8 +1801,7 @@ class bridge(moduleBase):
             config[ifname] = value
         return config
 
-    def sync_bridge_learning_to_vxlan_brport(self, bridge_name, bridge_vlan_aware, brport_ifaceobj,
-                                             brport_name, brport_ifla_info_slave_data, brport_learning):
+    def sync_bridge_learning_to_vxlan_brport(self, bridge_name, brport_ifaceobj, brport_name, brport_ifla_info_slave_data, user_config_brport_learning_nl, cached_brport_learning):
         """
             brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
             and
@@ -1749,62 +1815,77 @@ class bridge(moduleBase):
         kind = None
         ifla_info_data = {}
 
-        brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first('vxlan-learning')
-        # if the user explicitly defined vxlan-learning we need to honor his config
-        # and not sync vxlan-learning with bridge-learning
+        if user_config_brport_learning_nl is None:
+            user_config_brport_learning_nl = self.bridge_vxlan_port_learning
+            # bridge-learning is not configured by the user or by a policy
+            # use "bridge-vxlan-port-learning" policy to set bridge-learning (default on)
 
-        brport_vxlan_learning = self.ipcmd.get_vxlandev_learning(brport_name)
+        if user_config_brport_learning_nl != cached_brport_learning:
+            brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] \
+                = cached_brport_learning \
+                = user_config_brport_learning_nl
 
-        # if BRIDGE_LEARNING is in the desired configuration
-        # and differs from the running vxlan configuration
-        if brport_learning is not None and brport_learning != brport_vxlan_learning and not brport_vxlan_learning_config:
-            kind = 'vxlan'
-            ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_learning}
-            self.logger.info('%s: %s: vxlan learning and bridge learning out of sync: set %s'
-                             % (bridge_name, brport_name, brport_learning))
+            self.logger.info(
+                "%s: %s: set bridge-learning %s"
+                % (bridge_name, brport_name, "on" if user_config_brport_learning_nl else "off")
+            )
+        else:
+            # in this case, the current bridge-learning value is properly configured and
+            # doesn't need to be reset. We need to make sure that BRPORT_LEARNING is not
+            # part of ifla_info_slave_data.
+            try:
+                del brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING]
+            except:
+                pass
+
+        #
+        # vxlan-learning sync:
+        #
 
-        elif brport_learning is None and bridge_vlan_aware:
-            # is bridge-learning is not configured but the bridge is vlan-aware
+        brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first("vxlan-learning")
+        # if vxlan-learning is defined by the user or via policy file we need
+        # to honor his config and not sync vxlan-learning with bridge-learning
 
-            running_value = self.ipcmd.get_brport_learning_bool(brport_name)
-            default_value = utils.get_boolean_from_string(self.get_mod_subattr('bridge-learning', 'default'))
+        if not brport_vxlan_learning_config:
+            # check policy file
+            brport_vxlan_learning_config = policymanager.policymanager_api.get_attr_default("vxlan", "vxlan-learning")
 
-            if default_value != running_value:
-                brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] = default_value
+        # convert vxlan-learning string to netlink value (if None use brport-learning value instead)
+        brport_vxlan_learning_config_nl = utils.get_boolean_from_string(brport_vxlan_learning_config) \
+            if brport_vxlan_learning_config \
+            else cached_brport_learning
 
-                if not brport_vxlan_learning_config:
-                    kind = 'vxlan'
-                    ifla_info_data = {Link.IFLA_VXLAN_LEARNING: default_value}
-                    self.logger.info('%s: %s: reset brport learning to %s and sync vxlan learning'
-                                     % (bridge_name, brport_name, default_value))
+        if brport_vxlan_learning_config_nl != self.cache.get_link_info_data_attribute(brport_name, Link.IFLA_VXLAN_LEARNING):
+            self.logger.info(
+                "%s: %s: vxlan learning and bridge learning out of sync: set vxlan-learning %s"
+                % (bridge_name, brport_name, "on" if brport_vxlan_learning_config_nl else "off")
+            )
+            ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_vxlan_learning_config_nl}
+            kind = "vxlan"
 
         # if kind and ifla_info_data are set they will be added to the
         # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
         return kind, ifla_info_data
 
-    def check_vxlan_brport_arp_suppress(self, ifaceobj, bridge_vlan_aware, brport_ifaceobj, brport_name, user_config):
-        if user_config:
-            if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
-                self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
-                                    'is not supported on a non-vxlan port'
-                                    % (ifaceobj.name, brport_name))
-                raise Exception()
-        elif (bridge_vlan_aware and
-                  (not self.arp_nd_suppress_only_on_vxlan or
-                       (self.arp_nd_suppress_only_on_vxlan and
-                                brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN))):
-            return self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
-        return None
-
     def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
         ifname = ifaceobj.name
 
         try:
             brports_ifla_info_slave_data    = dict()
             brport_ifaceobj_dict            = dict()
+            brport_name_list                = []
+
+            cache_brports_ifla_info_slave_data = {}
+
+            port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE
 
-            running_brports = self.brctlcmd.get_bridge_ports(ifname)
+            running_brports = self.cache.get_slaves(ifname)
 
+            # If target_ports is specified we want to configure only this
+            # sub-list of port, we need to check if these ports are already
+            # enslaved, if not they will be ignored.
+            # If target_ports is not populated we will apply the brport
+            # attributes on all running brport.
             if target_ports:
                 new_targets = []
                 for brport_name in target_ports:
@@ -1814,24 +1895,46 @@ class bridge(moduleBase):
                         new_targets.append(brport_name)
                 running_brports = new_targets
 
-            self.logger.info('%s: applying bridge port configuration: %s' % (ifname, running_brports))
-
-            # If target_ports is specified we want to configure only this
-            # sub-list of port we need to check if these ports are already
-            # enslaved, if not they will be ignored.
-            # If target_ports is not populated we will apply the brport
-            # attributes on all running brport.
-
             for port in running_brports:
                 brport_list = ifaceobj_getfunc(port)
                 if brport_list:
-                    brport_ifaceobj_dict[port] = brport_list[0]
-                    brports_ifla_info_slave_data[port] = dict()
+                    port_already_processed = False
+
+                    # ports just added to the bridge have to be processed
+                    if port not in newly_enslaved_ports:
+                        # check if brport was already processed
+                        for brportifaceobj in brport_list:
+                            if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED:
+                                # skip port if already processed (probably by `up_bridge_port`)
+                                port_already_processed = True
+                                self.logger.info("%s: port %s: already processed" % (ifname, port))
+                                break
+
+                    if not port_already_processed:
+                        brport_name_list.append(port)
+                        brport_ifaceobj_dict[port] = brport_list[0]
+                        brports_ifla_info_slave_data[port] = dict()
+
+                        if not ifupdownflags.flags.PERFMODE and port not in newly_enslaved_ports:
+                            # if the port has just been enslaved, info_slave_data is not cached yet
+                            cache_brports_ifla_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
+                        else:
+                            cache_brports_ifla_info_slave_data[port] = {}
+
+            if not brport_name_list:
+                self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
+                return
+
+            self.logger.info('%s: applying bridge port configuration: %s' % (ifname, brport_name_list))
+
+            cached_bridge_mcsnoop = self.cache.get_bridge_multicast_snooping(ifname)
 
             bridge_ports_learning = {}
+            bridge_ports_vxlan_arp_suppress = {}
+            cached_bridge_ports_learning = {}
 
             # we iterate through all IFLA_BRPORT supported attributes
-            for attr_name, nl_attr in self._ifla_brport_attributes_map:
+            for attr_name, nl_attr in self._ifla_brport_attributes_map.iteritems():
                 br_config = ifaceobj.get_attr_value_first(attr_name)
                 translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
 
@@ -1871,9 +1974,8 @@ class bridge(moduleBase):
                     brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
                     brport_name = brport_ifaceobj.name
 
-                    if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
-                        # if the port has just been enslaved, info_slave_data is not cached yet
-                        cached_value = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', nl_attr])
+                    if not ifupdownflags.flags.PERFMODE:
+                        cached_value = cache_brports_ifla_info_slave_data.get(brport_name, {}).get(nl_attr, None)
                     else:
                         cached_value = None
 
@@ -1901,15 +2003,21 @@ class bridge(moduleBase):
                     # attribute specific work
                     # This shouldn't be here but we don't really have a choice otherwise this
                     # will require too much code duplication and will make the code very complex
-                    if nl_attr == Link.IFLA_BRPORT_ARP_SUPPRESS:
+                    if nl_attr == Link.IFLA_BRPORT_NEIGH_SUPPRESS:
+                        bridge_ports_vxlan_arp_suppress[brport_name] = user_config
                         try:
-                            arp_suppress = self.check_vxlan_brport_arp_suppress(ifaceobj,
-                                                                                bridge_vlan_aware,
-                                                                                brport_ifaceobj,
-                                                                                brport_name,
-                                                                                user_config)
-                            if arp_suppress:
-                                user_config = arp_suppress
+                            if user_config:
+                                if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+                                    self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
+                                                        'is not supported on a non-vxlan port'
+                                                        % (ifaceobj.name, brport_name))
+                                    continue
+                            elif bridge_vlan_aware:
+                                if not self.arp_nd_suppress_only_on_vxlan:
+                                    user_config = self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
+                                elif self.arp_nd_suppress_only_on_vxlan and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+                                    # ignore the case of VXLAN brport - handled later in the code
+                                    continue
                         except:
                             continue
                     elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
@@ -1941,7 +2049,9 @@ class bridge(moduleBase):
                             # IFLA_BRPORT_LEARNING if the user value is already configured and running
                             # nevertheless we still need to check if the vxlan-learning is rightly synced with
                             # the brport since it might go out of sync for X and Y reasons.
+                            # we also store the cached value to avoid an extra cache lookup.
                             bridge_ports_learning[brport_name] = user_config_nl
+                            cached_bridge_ports_learning[brport_name] = cached_value
 
                     elif cached_value is not None:
                         # no config found, do we need to reset to default?
@@ -1949,18 +2059,30 @@ class bridge(moduleBase):
                         if default:
                             default_netlink = translate_func(default)
 
-                            if (nl_attr == Link.IFLA_BRPORT_LEARNING
-                                and not ifupdownflags.flags.PERFMODE
-                                    and brport_name not in newly_enslaved_ports):
-                                try:
-                                    if self.ipcmd.get_brport_peer_link(brport_name):
-                                        if default_netlink != cached_value:
-                                            self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
-                                                              % (ifname, brport_name))
-                                        continue
-                                    bridge_ports_learning[brport_name] = default_netlink
-                                except Exception as e:
-                                    self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
+                            if nl_attr == Link.IFLA_BRPORT_LEARNING:
+                                # for vxlan-learning sync purposes we need to save the user config for each brports.
+                                # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
+                                # IFLA_BRPORT_LEARNING if the user value is already configured and running
+                                # nevertheless we still need to check if the vxlan-learning is rightly synced with
+                                # the brport since it might go out of sync for X and Y reasons.
+                                # we also store the cached value to avoid an extra cache lookup.
+                                cached_bridge_ports_learning[brport_name] = cached_value
+                                bridge_ports_learning[brport_name] = self.bridge_vxlan_port_learning
+
+                                if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+                                    # bridge-learning for vxlan device is handled separatly in sync_bridge_learning_to_vxlan_brport
+                                    continue
+
+                                if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
+                                    # We don't query new slaves and not during boot
+                                    try:
+                                        if self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_PEER_LINK):
+                                            if default_netlink != cached_value:
+                                                self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
+                                                                  % (ifname, brport_name))
+                                            continue
+                                    except Exception as e:
+                                        self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
 
                             if default_netlink != cached_value:
                                 self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
@@ -1968,6 +2090,12 @@ class bridge(moduleBase):
                                 self.logger.debug('(cache %s)' % cached_value)
                                 brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
 
+            # is the current bridge (ifaceobj) a QinQ bridge?
+            # This variable is initialized to None and will be
+            # change to True/False, so that the check is only
+            # performed once
+            qinq_bridge = None
+
             # applying bridge port configuration via netlink
             for brport_name, brport_ifla_info_slave_data in brports_ifla_info_slave_data.items():
 
@@ -1978,13 +2106,11 @@ class bridge(moduleBase):
                     # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
                     # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
                     kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
-                                                                                     bridge_vlan_aware,
                                                                                      brport_ifaceobj,
                                                                                      brport_name,
                                                                                      brport_ifla_info_slave_data,
-                                                                                     bridge_ports_learning.get(brport_name))
-
-                    cached_bridge_mcsnoop = self.brctlcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BR_MCAST_SNOOPING])
+                                                                                     bridge_ports_learning.get(brport_name),
+                                                                                     cached_bridge_ports_learning.get(brport_name))
 
                     if (self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string(
                             self.get_bridge_mcsnoop_value(ifaceobj)
@@ -1992,30 +2118,118 @@ class bridge(moduleBase):
                         # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter"
                         # is on and mcsnoop is on (or mcsnoop is already enabled on the
                         # bridge, set 'bridge-portmcrouter 2' on vxlan ports (if not set by the user)
-                        if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER):
+                        if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER) \
+                                and self.cache.get_bridge_port_multicast_router(brport_name) != 2:
                             brport_ifla_info_slave_data[Link.IFLA_BRPORT_MULTICAST_ROUTER] = 2
                             self.logger.info("%s: %s: vxlan bridge igmp snooping: enable port multicast router" % (ifname, brport_name))
+
+                    #
+                    # handling attribute: bridge-arp-nd-suppress
+                    # defaults to bridge-vxlan-arp-nd-suppress policy (default False)
+                    #
+                    user_config_neigh_suppress = bridge_ports_vxlan_arp_suppress.get(brport_name)
+
+                    if user_config_neigh_suppress is None:
+
+                        if qinq_bridge is None:
+                            # QinQ bridge hasn't been checked yet
+                            qinq_bridge = self.is_qinq_bridge(
+                                ifaceobj,
+                                brport_name,
+                                running_brports,
+                                brport_ifaceobj_dict,
+                                ifaceobj_getfunc
+                            )
+
+                        if qinq_bridge:
+                            # exclude QinQ bridge from arp-nd-suppress default policy on
+                            config_neigh_suppress = 0
+                            self.logger.info("%s: QinQ bridge detected: %s: set bridge-arp-nd-suppress off" % (ifname, brport_name))
+                        else:
+                            config_neigh_suppress = self.bridge_vxlan_arp_nd_suppress_int
+                    else:
+                        config_neigh_suppress = int(utils.get_boolean_from_string(user_config_neigh_suppress))
+
+                    brport_neigh_suppress_cached_value = self.cache.get_link_info_slave_data_attribute(
+                        brport_name,
+                        Link.IFLA_BRPORT_NEIGH_SUPPRESS
+                    )
+
+                    if config_neigh_suppress != brport_neigh_suppress_cached_value:
+                        brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] = config_neigh_suppress
+
+                        if not user_config_neigh_suppress:
+                            # if the configuration is not explicitely defined by the user
+                            # we need report that the default behavior is enabled by policy
+                            self.logger.info(
+                                "%s: set bridge-arp-nd-suppress %s by default on vxlan port (%s)"
+                                % (ifname, "on" if self.bridge_vxlan_arp_nd_suppress else "off", brport_name)
+                            )
+                    else:
+                        # the user configuration (or policy) is already configured and running
+                        # we need to remove this attribute from the request dictionary
+                        try:
+                            del brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS]
+                        except:
+                            pass
+
+                    #
+                    #
+                    #
+
                 else:
                     kind = None
                     ifla_info_data = {}
 
                 if brport_ifla_info_slave_data or ifla_info_data:
                     try:
-                        netlink.link_add_set(ifname=brport_name,
-                                             kind=kind,
-                                             ifla_info_data=ifla_info_data,
-                                             slave_kind='bridge',
-                                             ifla_info_slave_data=brport_ifla_info_slave_data)
+                        self.netlink.link_set_brport_with_info_slave_data(
+                            ifname=brport_name,
+                            kind=kind,
+                            ifla_info_data=ifla_info_data,
+                            ifla_info_slave_data=brport_ifla_info_slave_data
+                        )
                     except Exception as e:
                         self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
 
-            self._set_bridge_vidinfo_compat(ifaceobj)
-            self._set_bridge_mcqv4src_compat(ifaceobj)
-            self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
+            self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
 
         except Exception as e:
             self.log_error(str(e), ifaceobj)
 
+    def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc):
+        """ Detect QinQ bridge
+        Potential improvement: We could add a ifaceobj.link_privflags called
+        BRIDGE_QINQ but for now it is not necessary.
+        """
+
+        # bridge-vlan-aware case
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+            return (ifaceobj.get_attr_value_first("bridge-vlan-protocol") or "").lower() == "802.1ad"
+
+        # old-bridge
+        else:
+            for qinq_running_brport in running_brports:
+                if qinq_running_brport == brport_name:
+                    continue
+
+                qinq_running_brport_ifaceobj = brport_ifaceobj_dict.get(qinq_running_brport)
+
+                if not qinq_running_brport_ifaceobj:
+                    continue
+
+                if qinq_running_brport_ifaceobj.link_kind & ifaceLinkKind.VLAN:
+                    for lower_iface in qinq_running_brport_ifaceobj.lowerifaces or []:
+                        for lower_ifaceobj in ifaceobj_getfunc(lower_iface) or []:
+                            if (lower_ifaceobj.get_attr_value_first("vlan-protocol") or "").lower() == "802.1ad":
+                                return True
+        return False
+
+    def bridge_process_vidinfo_mcqv4src_maxwait(self, ifaceobj):
+        self._set_bridge_vidinfo_compat(ifaceobj)
+        self._set_bridge_mcqv4src_compat(ifaceobj)
+        self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
+
     def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
         """
             Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
@@ -2037,7 +2251,7 @@ class bridge(moduleBase):
                     ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
 
         # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
-        cached_ifla_brport_group_fwd_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+        cached_ifla_brport_group_fwd_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
 
         log_mask_change = True
         # if user specify bridge-l2protocol-tunnel stp cdp
@@ -2086,18 +2300,18 @@ class bridge(moduleBase):
         ifname = ifaceobj.name
 
         if ifupdownflags.flags.PERFMODE:
-            link_just_created = True
             link_exists = False
         else:
-            link_exists = self.ipcmd.link_exists(ifaceobj.name)
-            link_just_created = not link_exists
+            link_exists = self.cache.link_exists(ifaceobj.name)
 
         if not link_exists:
-            netlink.link_add_bridge(ifname, self.get_bridge_mtu(ifaceobj))
+            self.netlink.link_add_bridge(ifname, mtu=self.get_bridge_mtu(ifaceobj))
+            link_just_created = True
         else:
+            link_just_created = False
             self.logger.info('%s: bridge already exists' % ifname)
 
-        bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, not link_just_created)
+        bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created)
 
         self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
 
@@ -2110,10 +2324,9 @@ class bridge(moduleBase):
 
         running_ports = ''
         try:
-            running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+            running_ports = self.cache.get_slaves(ifaceobj.name)
             if not running_ports:
                 return
-            self.handle_ipv6([], '1', ifaceobj=ifaceobj)
             self._apply_bridge_port_settings_all(ifaceobj,
                                                  ifaceobj_getfunc=ifaceobj_getfunc,
                                                  bridge_vlan_aware=bridge_vlan_aware)
@@ -2123,17 +2336,18 @@ class bridge(moduleBase):
             self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
         finally:
             if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+                self.iproute2.batch_start()
                 for p in running_ports:
                     ifaceobj_list = ifaceobj_getfunc(p)
                     if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN):
-                        netlink.link_set_updown(p, "down")
+                        self.iproute2.link_down(p)
                         continue
-                    try:
-                        netlink.link_set_updown(p, "up")
-                    except Exception, e:
-                        self.logger.debug('%s: %s: link set up (%s)'
-                                          % (ifaceobj.name, p, str(e)))
-                        pass
+                    self.iproute2.link_up(p)
+                try:
+                    self.iproute2.batch_commit()
+                except Exception as e:
+                    # link set up on bridge ports failed - ignore and log debug
+                    self.logger.debug("%s: %s" % (ifname, str(e)))
 
         try:
             self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
@@ -2158,11 +2372,11 @@ class bridge(moduleBase):
                         if iface_user_configured_hwaddress:
                             iface_mac = iface_user_configured_hwaddress
 
-                if not iface_mac and not self.ipcmd.link_exists(bridge_mac_intf):
+                if not iface_mac and not self.cache.link_exists(bridge_mac_intf):
                     continue
 
                 if not iface_mac:
-                    iface_mac = self.ipcmd.cache_get('link', [bridge_mac_intf, 'hwaddress'])
+                    iface_mac = self.cache.get_link_address(bridge_mac_intf)
                     # if hwaddress attribute is not configured we use the running mac addr
 
                 self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
@@ -2183,7 +2397,7 @@ class bridge(moduleBase):
                             # what we have in the cache (data retrieved via a netlink dump by
                             # nlmanager). nlmanager return all macs in lower-case
                         else:
-                            iface_mac = self.ipcmd.link_get_hwaddress(port)
+                            iface_mac = self.cache.get_link_address(port)
 
                         if iface_mac:
                             self.bridge_mac_iface = (port, iface_mac)
@@ -2193,7 +2407,7 @@ class bridge(moduleBase):
 
     def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
         if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
-            self.ipcmd.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
+            self.iproute2.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
 
     def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
         """
@@ -2224,20 +2438,20 @@ class bridge(moduleBase):
             # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
             # from the kernel. The only way to counter that is to convert all mac to int
             # and compare the ints, it will increase perfs and be safer.
-            cached_value = self.ipcmd.cache_get('link', [ifname, 'hwaddress'])
+            cached_value = self.cache.get_link_address(ifname)
             self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
-            if cached_value and self.ipcmd.mac_str_to_int(cached_value) == self.ipcmd.mac_str_to_int(bridge_mac):
+            if cached_value and utils.mac_str_to_int(cached_value) == utils.mac_str_to_int(bridge_mac):
                 # the bridge mac is already set to the bridge_mac_intf's mac
                 return
 
             self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
             try:
-                self.ipcmd.link_set(ifname, 'address', value=bridge_mac, force=True)
+                self.netlink.link_set_address(ifname, bridge_mac)  # force=True
             except Exception as e:
                 self.logger.info('%s: %s' % (ifname, str(e)))
                 # log info this error because the user didn't explicitly configured this
         else:
-            self._add_bridge_mac_to_fdb(ifaceobj, self.ipcmd.link_get_hwaddress(ifname))
+            self._add_bridge_mac_to_fdb(ifaceobj, self.cache.get_link_address(ifname))
 
     def _up(self, ifaceobj, ifaceobj_getfunc=None):
         if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
@@ -2258,26 +2472,18 @@ class bridge(moduleBase):
         if not self._is_bridge(ifaceobj):
             return
         ifname = ifaceobj.name
-        if not self.ipcmd.link_exists(ifname):
+        if not self.cache.link_exists(ifname):
             return
         try:
-            running_ports = self.brctlcmd.get_bridge_ports(ifname)
+            running_ports = self.cache.get_slaves(ifname)
             if running_ports:
                 self.handle_ipv6(running_ports, '0')
                 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
-                    map(lambda p: netlink.link_set_updown(p, 'down'), running_ports)
+                    map(lambda p: self.netlink.link_down(p), running_ports)
         except Exception as e:
             self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
         try:
-            netlink.link_del(ifname)
-
-            if utils.get_boolean_from_string(ifupdownconfig.config.get("ifreload_down_changed")):
-                self.ipcmd.del_cache_entry(ifname)
-                for upper in ifaceobj.upperifaces or []:
-                    try:
-                        self.ipcmd.del_cache_entry(upper)
-                    except:
-                        pass
+            self.netlink.link_del(ifname)
         except Exception as e:
             ifaceobj.set_status(ifaceStatus.ERROR)
             self.logger.error(str(e))
@@ -2289,7 +2495,7 @@ class bridge(moduleBase):
             running_bridge_port_vids = ''
             for p in ports:
                 try:
-                    running_vids = self._get_runing_vids(p)
+                    _, running_vids = self.cache.get_pvid_and_vids(p)
                     if running_vids:
                         running_bridge_port_vids += ' %s=%s' %(p,
                                                       ','.join(running_vids))
@@ -2300,7 +2506,7 @@ class bridge(moduleBase):
             running_bridge_port_pvid = ''
             for p in ports:
                 try:
-                    running_pvid = self._get_runing_pvid(p)
+                    running_pvid = self.cache.get_pvid(p)
                     if running_pvid:
                         running_bridge_port_pvid += ' %s=%s' %(p,
                                                         running_pvid)
@@ -2308,7 +2514,7 @@ class bridge(moduleBase):
                     pass
             running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
 
-        running_bridge_vids = self.ipcmd.bridge_vlan_get_vids(ifaceobjrunning.name)
+        _, running_bridge_vids = self.cache.get_pvid_and_vids(ifaceobjrunning.name)
         if running_bridge_vids:
             running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
         return running_attrs
@@ -2375,7 +2581,7 @@ class bridge(moduleBase):
         return running_attrs
 
     def _query_running_mcqv4src(self, ifaceobjrunning):
-        running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobjrunning.name)
+        running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobjrunning.name)
         mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
         mcqs.sort()
         mcq = ' '.join(mcqs)
@@ -2383,58 +2589,109 @@ class bridge(moduleBase):
 
     def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
                              bridge_vlan_aware=False):
+
+        ifname = ifaceobjrunning.name
         bridgeattrdict = {}
         userspace_stp = 0
         ports = None
-        skip_kernel_stp_attrs = 0
-
         try:
             if self.systcl_get_net_bridge_stp_user_space() == '1':
                 userspace_stp = 1
         except Exception as e:
             self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
 
-        tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
-        if not tmpbridgeattrdict:
-            self.logger.warn('%s: unable to get bridge attrs'
-                    %ifaceobjrunning.name)
-            return bridgeattrdict
+        bridge_ifla_info_data = self.cache.get_link_info_data(ifname)
+
 
         # Fill bridge_ports and bridge stp attributes first
-        ports = tmpbridgeattrdict.get('ports')
-        if ports:
-            bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
-        stp = tmpbridgeattrdict.get('stp', 'no')
-        if stp != self.get_mod_subattr('bridge-stp', 'default'):
-            bridgeattrdict['bridge-stp'] = [stp]
-
-        if  stp == 'yes' and userspace_stp:
-            skip_kernel_stp_attrs = 1
-
-        vlan_stats = utils.get_onff_from_onezero(
-                            tmpbridgeattrdict.get('vlan-stats', None))
-        if (vlan_stats and
-            vlan_stats != self.get_mod_subattr('bridge-vlan-stats', 'default')):
-            bridgeattrdict['bridge-vlan-stats'] = [vlan_stats]
-
-        bool2str = {'0': 'no', '1': 'yes'}
-        # pick all other attributes
-        for k,v in tmpbridgeattrdict.items():
-            if not v:
-                continue
-            if k == 'ports' or k == 'stp':
-                continue
+        #
+        # bridge-ports
+        #
+        bridgeattrdict["bridge-ports"] = [" ".join(self.cache.get_slaves(ifname))]
+
+        #
+        # bridge-stp
+        #
+        cached_stp = bool(bridge_ifla_info_data.get(Link.IFLA_BR_STP_STATE))
+
+        if cached_stp != utils.get_boolean_from_string(
+                self.get_mod_subattr("bridge-stp", "default")
+        ):
+            bridgeattrdict['bridge-stp'] = ["yes" if cached_stp else "no"]
+
+        skip_kernel_stp_attrs = cached_stp and userspace_stp
+
+        if skip_kernel_stp_attrs:
+            bridge_attributes_map = {
+                "bridge-mcqifaddr": Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
+                "bridge-mcquerier": Link.IFLA_BR_MCAST_QUERIER,
+                "bridge-mcrouter": Link.IFLA_BR_MCAST_ROUTER,
+                "bridge-mcstats": Link.IFLA_BR_MCAST_STATS_ENABLED,
+                "bridge-mcsnoop": Link.IFLA_BR_MCAST_SNOOPING,
+                "bridge-mclmc": Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
+                "bridge-mclmi": Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+                "bridge-mcqri": Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+                "bridge-mcqpi": Link.IFLA_BR_MCAST_QUERIER_INTVL,
+                "bridge-mcsqc": Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+                "bridge-mcsqi": Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
+                "bridge-mcmi": Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+                "bridge-mcqi": Link.IFLA_BR_MCAST_QUERY_INTVL,
+            }
+        else:
+            bridge_attributes_map = dict(self._ifla_br_attributes_map)
+            try:
+                del bridge_attributes_map[Link.IFLA_BR_STP_STATE]
+            except:
+                pass
+
+        #
+        # bridge-vlan-stats
+        #
+        cached_vlan_stats = bridge_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
+
+        if cached_vlan_stats != utils.get_boolean_from_string(
+                self.get_mod_subattr("bridge-vlan-stats", "default")
+        ):
+            bridgeattrdict['bridge-vlan-stats'] = ["on" if cached_vlan_stats else "off"]
+
+        try:
+            del bridge_attributes_map[Link.IFLA_BR_VLAN_STATS_ENABLED]
+        except:
+            pass
 
-            if skip_kernel_stp_attrs and k[:2] != 'mc':
-                # only include igmp attributes if kernel stp is off
+        lambda_nl_value_int_divide100 = lambda x: str(x / 100)
+        lambda_nl_value_to_yes_no_boolean = lambda x: "yes" if x else "no"
+
+        bridge_attr_value_netlink_to_string_dict = {
+            Link.IFLA_BR_VLAN_PROTOCOL: lambda x: x.lower(),  # return lower case vlan protocol
+            Link.IFLA_BR_AGEING_TIME: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MAX_AGE: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_FORWARD_DELAY: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_HELLO_TIME: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MCAST_QUERIER_INTVL: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_MCAST_QUERY_INTVL: lambda_nl_value_int_divide100,
+            Link.IFLA_BR_VLAN_FILTERING: lambda_nl_value_to_yes_no_boolean,
+            Link.IFLA_BR_MCAST_QUERY_USE_IFADDR: lambda_nl_value_to_yes_no_boolean,
+            Link.IFLA_BR_MCAST_SNOOPING: lambda_nl_value_to_yes_no_boolean,
+            Link.IFLA_BR_MCAST_QUERIER: lambda_nl_value_to_yes_no_boolean,
+            Link.IFLA_BR_MCAST_ROUTER: lambda_nl_value_to_yes_no_boolean,
+        }
+
+        for attr_name, attr_nl in bridge_attributes_map.iteritems():
+            default_value = self.get_mod_subattr(attr_name, "default")
+            cached_value = bridge_ifla_info_data.get(attr_nl)
+
+            if cached_value is None:
                 continue
-            attrname = 'bridge-' + k
-            mod_default = self.get_mod_subattr(attrname, 'default')
-            if v != mod_default:
-                # convert '0|1' running values to 'no|yes'
-                if v in bool2str.keys() and bool2str[v] == mod_default:
-                    continue
-                bridgeattrdict[attrname] = [v]
+
+            cached_value_string = bridge_attr_value_netlink_to_string_dict.get(attr_nl, str)(cached_value)
+
+            if default_value != cached_value_string:
+                bridgeattrdict[attr_name] = [cached_value_string]
 
         if bridge_vlan_aware:
             if not ports:
@@ -2463,45 +2720,44 @@ class bridge(moduleBase):
                           'bridge-learning' : '',
                           'bridge-unicast-flood' : '',
                           'bridge-multicast-flood' : '',
+                          'bridge-broadcast-flood' : '',
                           'bridge-arp-nd-suppress' : '',
                          }
             for p, v in ports.items():
-                v = self.brctlcmd.bridge_get_pathcost(ifaceobjrunning.name, p)
+                v = str(self.cache.get_brport_cost(p))
                 if v and v != self.get_mod_subattr('bridge-pathcosts',
                                                    'default'):
                     portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
 
-                v = self.brctlcmd.bridge_get_portprio(ifaceobjrunning.name, p)
+                v = str(self.cache.get_brport_priority(p))
                 if v and v != self.get_mod_subattr('bridge-portprios',
                                                    'default'):
                     portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
 
-                v = utils.get_onff_from_onezero(
-                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
-                                                          p, 'learning'))
+                v = utils.get_onff_from_onezero(self.cache.get_brport_learning(p))
                 if (v and
                     v != self.get_mod_subattr('bridge-learning', 'default')):
                     portconfig['bridge-learning'] += ' %s=%s' %(p, v)
 
-                v = utils.get_onff_from_onezero(
-                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
-                                                          p, 'unicast-flood'))
+                v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(p))
                 if (v and
                     v != self.get_mod_subattr('bridge-unicast-flood',
                                               'default')):
                     portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
 
-                v = utils.get_onff_from_onezero(
-                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
-                                                          p, 'multicast-flood'))
+                v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(p))
                 if (v and
                     v != self.get_mod_subattr('bridge-multicast-flood',
                                               'default')):
                     portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
 
-                v = utils.get_onff_from_onezero(
-                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
-                                                          p, 'arp-nd-suppress'))
+                v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(p))
+                if (v and
+                    v != self.get_mod_subattr('bridge-broadcast-flood',
+                                              'default')):
+                    portconfig['bridge-broadcast-flood'] += ' %s=%s' %(p, v)
+
+                v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(p))
                 if (v and
                     v != self.get_mod_subattr('bridge-arp-nd-suppress',
                                               'default')):
@@ -2522,69 +2778,64 @@ class bridge(moduleBase):
             ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
                          running_mcqs, 1 if running_mcqs != mcqsout else 0)
 
-    def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
-        err = 0
-        attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
-        if attrval:
-            running_bridge_port_vids = ''
-            portlist = self.parse_port_list(ifaceobj.name, attrval)
-            if not portlist:
-                self.log_warn('%s: could not parse \'bridge-port-vids %s\''
-                          %(ifaceobj.name, attrval))
+    def _query_check_bridge_vidinfo(self, ifname, ifaceobj, ifaceobjcurr):
+        #
+        # bridge-port-vids
+        #
+        bridge_port_vids_user_config = ifaceobj.get_attr_value_first("bridge-port-vids")
+        if bridge_port_vids_user_config:
+
+            port_list = self.parse_port_list(ifname, bridge_port_vids_user_config)
+
+            if not port_list:
+                self.log_warn("%s: could not parse 'bridge-port-vids %s'"
+                              % (ifname, bridge_port_vids_user_config))
+                ifaceobjcurr.update_config_with_status("bridge-port-vids", "ERROR", 1)
                 return
-            err = 0
-            for p in portlist:
+
+            error = False
+            for port_config in port_list:
                 try:
-                    (port, val) = p.split('=')
-                    vids = val.split(',')
-                    running_vids = self.ipcmd.bridge_vlan_get_vids(port)
-                    if running_vids:
-                        if not self._compare_vids(vids, running_vids):
-                            err += 1
-                            running_bridge_port_vids += ' %s=%s' %(port,
-                                                      ','.join(running_vids))
-                        else:
-                            running_bridge_port_vids += ' %s' %p
-                    else:
-                        err += 1
-                except Exception, e:
-                    self.log_warn('%s: failure checking vid %s (%s)'
-                        %(ifaceobj.name, p, str(e)))
-            if err:
-                ifaceobjcurr.update_config_with_status('bridge-port-vids',
-                                                 running_bridge_port_vids, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('bridge-port-vids',
-                                                 attrval, 0)
+                    port, vids_raw = port_config.split("=")
+                    packed_vids = vids_raw.split(",")
+
+                    running_pvid, running_vids = self.cache.get_pvid_and_vids(port)
+
+                    if not self._compare_vids(packed_vids, running_vids, pvid=running_pvid, expand_range=False):
+                        error = True
 
+                except Exception as e:
+                    self.log_warn("%s: failure checking vid %s (%s)" % (ifname, port_config, str(e)))
+
+            ifaceobjcurr.update_config_with_status("bridge-port-vids", bridge_port_vids_user_config, error)
+
+        #
+        # bridge-port-pvids
+        #
         attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
         if attrval:
             portlist = self.parse_port_list(ifaceobj.name, attrval)
             if not portlist:
                 self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
-                              %(ifaceobj.name, attrval))
+                              % (ifname, attrval))
                 return
-            running_bridge_port_pvids = ''
-            err = 0
+
+            error = False
+            running_pvid_config = []
             for p in portlist:
-                try:
-                    (port, pvid) = p.split('=')
-                    running_pvid = self.ipcmd.bridge_vlan_get_vids(port)
-                    if running_pvid and running_pvid == pvid:
-                        running_bridge_port_pvids += ' %s' %p
-                    else:
-                        err += 1
-                        running_bridge_port_pvids += ' %s=%s' %(port,
-                                                            running_pvid)
-                except Exception, e:
-                    self.log_warn('%s: failure checking pvid %s (%s)'
-                            %(ifaceobj.name, pvid, str(e)))
-            if err:
-                ifaceobjcurr.update_config_with_status('bridge-port-pvids',
-                                                 running_bridge_port_pvids, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('bridge-port-pvids',
-                                                 running_bridge_port_pvids, 0)
+                (port, pvid) = p.split('=')
+                running_pvid, _ = self.cache.get_pvid_and_vids(port)
+
+                running_pvid_config.append("%s=%s" % (port, running_pvid))
+
+                if running_pvid != int(pvid):
+                    error = True
+
+            ifaceobjcurr.update_config_with_status(
+                "bridge-port-pvids",
+                " ".join(running_pvid_config),
+                int(error)
+            )
 
         vids = self.get_ifaceobj_bridge_vids(ifaceobj)
         if vids[1]:
@@ -2596,170 +2847,198 @@ class bridge(moduleBase):
                 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
             ifaceobj.replace_config('bridge-mcsnoop', 'no')
 
-    def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
-                            ifaceobj_getfunc=None):
+    def _query_check_bridge(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
         if not self._is_bridge(ifaceobj):
             return
-        if not self.brctlcmd.bridge_exists(ifaceobj.name):
-            self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
-            return
 
-        self._query_check_snooping_wdefault(ifaceobj)
+        ifname = ifaceobj.name
 
-        ifaceattrs = self.dict_key_subset(ifaceobj.config,
-                                          self.get_mod_attrs())
-        #Add default attributes if --with-defaults is set
-        if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in ifaceattrs:
-            ifaceattrs.append('bridge-stp')
-        if not ifaceattrs:
+        if not self.cache.bridge_exists(ifname):
+            self.logger.info("%s: bridge: does not exist" % (ifname))
             return
-        try:
-            runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
-            if not runningattrs:
-               self.logger.debug('%s: bridge: unable to get bridge attrs'
-                                 %ifaceobj.name)
-               runningattrs = {}
-        except Exception, e:
-            self.logger.warn(str(e))
-            runningattrs = {}
 
-        self._query_check_support_yesno_attrs(runningattrs, ifaceobj)
+        self._query_check_snooping_wdefault(ifaceobj)
 
-        filterattrs = ['bridge-vids', 'bridge-trunk', 'bridge-port-vids',
-                       'bridge-port-pvids']
+        user_config_attributes = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
 
-        diff = Set(ifaceattrs).difference(filterattrs)
+        # add default attributes if --with-defaults is set
+        if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in user_config_attributes:
+            user_config_attributes.append('bridge-stp')
 
-        if 'bridge-l2protocol-tunnel' in diff:
-            diff.remove('bridge-l2protocol-tunnel')
-            # bridge-l2protocol-tunnel requires separate handling
+        if not user_config_attributes:
+            return
 
-        if 'bridge-ports' in diff:
-            self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, runningattrs.get('ports', {}).keys(), ifaceobj_getfunc)
-            diff.remove('bridge-ports')
+        if "bridge-ports" in user_config_attributes:
+            self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, self.cache.get_slaves(ifname), ifaceobj_getfunc)
 
-        if "bridge-ports-condone-regex" in diff:
+        if "bridge-ports-condone-regex" in user_config_attributes:
             ifaceobjcurr.update_config_with_status(
                 "bridge-ports-condone-regex",
                 self._get_bridge_port_condone_regex(ifaceobj, True),
                 0
             )
-            diff.remove("bridge-ports-condone-regex")
-
-        for k in diff:
-            # get the corresponding ifaceobj attr
-            v = ifaceobj.get_attr_value_first(k)
-            if not v:
-                if ifupdownflags.flags.WITHDEFAULTS and k == 'bridge-stp':
-                    v = 'on' if self.default_stp_on else 'off'
-                else:
-                    continue
-            rv = runningattrs.get(k[7:])
-            if k == 'bridge-mcqv4src':
-               continue
-            if k == 'bridge-maxwait' or k == 'bridge-waitport':
-                ifaceobjcurr.update_config_with_status(k, v, 0)
+
+        # Those attributes require separate handling
+        filter_attributes = [
+            "bridge-trunk",
+            "bridge-ports",
+            "bridge-vids",
+            "bridge-trunk",
+            "bridge-mcqv4src",
+            "bridge-port-vids",
+            "bridge-port-pvids",
+            "bridge-l2protocol-tunnel",
+            "bridge-ports-condone-regex"
+        ]
+
+        ignore_attributes = (
+            # bridge-pvid and bridge-vids on a bridge does not correspond
+            # directly to a running config on the bridge. They correspond to
+            # default values for the bridge ports. And they are already checked
+            # against running config of the bridge port and reported against a
+            # bridge port. So, ignore these attributes under the bridge. Use '2'
+            # for ignore today. XXX: '2' will be mapped to a defined value in
+            # subsequent patches.
+            "bridge-pvid",
+            "bridge-allow-untagged",
+        )
+        for attr in ignore_attributes:
+            if attr in user_config_attributes:
+                ifaceobjcurr.update_config_with_status(attr, ifaceobj.get_attr_value_first(attr), 2)
+                filter_attributes.append(attr)
+
+        bridge_config = Set(user_config_attributes).difference(filter_attributes)
+        cached_ifla_info_data = self.cache.get_link_info_data(ifname)
+
+        self._query_check_bridge_attributes(ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data)
+        self._query_check_brport_attributes_on_bridge(ifname, ifaceobj, ifaceobjcurr, bridge_config)
+        self._query_check_bridge_vidinfo(ifname, ifaceobj, ifaceobjcurr)
+        self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
+        self._query_check_l2protocol_tunnel_on_bridge(ifname, ifaceobj, ifaceobjcurr)
+
+    def _query_check_bridge_attributes(self, ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data):
+        for attr in list(bridge_config):
+            query_check_handler, netlink_attr = self._bridge_attribute_query_check_handler.get(attr, (None, None))
+
+            if callable(query_check_handler):
+                query_check_handler(attr, ifaceobj.get_attr_value_first(attr), ifaceobjcurr, cached_ifla_info_data.get(netlink_attr))
+                bridge_config.remove(attr)
+
+    def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
+        brports_info_slave_data = {}
+        # bridge_config should only have bridge-port-list attributes
+        for attr in bridge_config:
+            attr_nl = self._ifla_brport_attributes_map.get(attr)
+            brport_query_check_handler = self._brport_attribute_query_check_handler.get(attr)
+
+            if not attr_nl or not brport_query_check_handler:
+                self.logger.warning("%s: query-check: missing handler for attribute: %s (%s)" % (ifname, attr, attr_nl))
                 continue
-            if k == 'bridge-vlan-aware':
-                rv = self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)
-                if (rv and v == 'yes') or (not rv and v == 'no'):
-                    ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
-                               v, 0)
-                else:
-                    ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
-                               v, 1)
-            elif k == 'bridge-stp':
-               # special case stp compare because it may
-               # contain more than one valid values
-               stp_on_vals = ['on', 'yes']
-               stp_off_vals = ['off', 'no']
-               if ((v in stp_on_vals and rv in stp_on_vals) or
-                   (v in stp_off_vals and rv in stp_off_vals)):
-                    ifaceobjcurr.update_config_with_status('bridge-stp',
-                               rv, 0)
-               else:
-                    ifaceobjcurr.update_config_with_status('bridge-stp',
-                               rv, 1)
-            elif k in ['bridge-pathcosts',
-                       'bridge-portprios',
-                       'bridge-portmcrouter',
-                       'bridge-portmcfl',
-                       'bridge-learning',
-                       'bridge-unicast-flood',
-                       'bridge-multicast-flood',
-                       'bridge-arp-nd-suppress',
-                      ]:
-               if k == 'bridge-arp-nd-suppress':
-                  brctlcmdattrname = k[7:]
-               else:
-                  brctlcmdattrname = k[7:].rstrip('s')
-               # for port attributes, the attributes are in a list
-               # <portname>=<portattrvalue>
-               status = 0
-               currstr = ''
-               vlist = self.parse_port_list(ifaceobj.name, v)
-               if not vlist:
-                  continue
-               for vlistitem in vlist:
-                   try:
-                      (p, v) = vlistitem.split('=')
-                      if k in ['bridge-learning',
-                               'bridge-unicast-flood',
-                               'bridge-multicast-flood',
-                               'bridge-arp-nd-suppress',
-                              ]:
-                         currv = utils.get_onoff_bool(
-                                    self.brctlcmd.get_bridgeport_attr(
-                                         ifaceobj.name, p,
-                                         brctlcmdattrname))
-                      else:
-                         currv = self.brctlcmd.get_bridgeport_attr(
-                                         ifaceobj.name, p,
-                                         brctlcmdattrname)
-                      if currv:
-                          currstr += ' %s=%s' %(p, currv)
-                      else:
-                          currstr += ' %s=%s' %(p, 'None')
-
-                      if k == 'bridge-portmcrouter':
-                          if self._ifla_brport_multicast_router_dict_to_int.get(v) != int(currv):
-                              status = 1
-                      elif currv != v:
-                          status = 1
-                   except Exception, e:
-                      self.log_warn(str(e))
-                   pass
-               ifaceobjcurr.update_config_with_status(k, currstr, status)
-            elif k == 'bridge-vlan-stats' or k == 'bridge-mcstats':
-                rv = utils.get_onff_from_onezero(rv)
-                if v != rv:
-                    ifaceobjcurr.update_config_with_status(k, rv, 1)
+
+            running_config = []
+            status = 0
+
+            for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []:
+                port, config = port_config.split("=")
+
+                if not port in brports_info_slave_data:
+                    info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
                 else:
-                    ifaceobjcurr.update_config_with_status(k, rv, 0)
-            elif not rv:
-               if k == 'bridge-pvid' or k == 'bridge-vids' or k == 'bridge-trunk' or k == 'bridge-allow-untagged':
-                   # bridge-pvid and bridge-vids on a bridge does
-                   # not correspond directly to a running config
-                   # on the bridge. They correspond to default
-                   # values for the bridge ports. And they are
-                   # already checked against running config of the
-                   # bridge port and reported against a bridge port.
-                   # So, ignore these attributes under the bridge.
-                   # Use '2' for ignore today. XXX: '2' will be
-                   # mapped to a defined value in subsequent patches.
-                   ifaceobjcurr.update_config_with_status(k, v, 2)
-               else:
-                   ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
-               continue
-            elif v.upper() != rv.upper():
-               ifaceobjcurr.update_config_with_status(k, rv, 1)
+                    info_slave_data = brports_info_slave_data[port]
+
+                port_config, port_status = brport_query_check_handler(port, config, info_slave_data.get(attr_nl))
+
+                running_config.append(port_config)
+
+                if port_status:
+                    status = 1
+
+            ifaceobjcurr.update_config_with_status(
+                attr,
+                " ".join(running_config),
+                status
+            )
+
+    @staticmethod
+    def _query_check_br_attr_wait(attr, wait_value, ifaceobjcurr, __):
+        ifaceobjcurr.update_config_with_status(attr, wait_value, 0)
+
+    def _query_check_br_attr_stp(self, attr, stp_value, ifaceobjcurr, cached_value):
+        if not stp_value:
+            if ifupdownflags.flags.WITHDEFAULTS:
+                stp_value = "on" if self.default_stp_on else "off"
             else:
-               ifaceobjcurr.update_config_with_status(k, rv, 0)
+                return
 
-        self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
+        user_config_to_nl = utils.get_boolean_from_string(stp_value)
 
-        self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
-        self._query_check_l2protocol_tunnel_on_bridge(ifaceobj, ifaceobjcurr, runningattrs)
+        ifaceobjcurr.update_config_with_status(
+            attr,
+            "yes" if cached_value else "no",
+            user_config_to_nl != bool(cached_value)
+        )
+
+    @staticmethod
+    def _query_check_br_attr_int(attr, user_config, ifaceobjcurr, cached_value):
+        ifaceobjcurr.update_config_with_status(
+            attr,
+            str(cached_value),
+            int(user_config) != cached_value
+        )
+
+    @staticmethod
+    def _query_check_br_attr_int_divided100(attr, user_config, ifaceobjcurr, cached_value):
+        value = cached_value / 100
+        ifaceobjcurr.update_config_with_status(
+            attr,
+            str(value),
+            int(user_config) != value
+        )
+
+    @staticmethod
+    def _query_check_br_attr_boolean(attr, user_config, ifaceobjcurr, cached_value):
+        ifaceobjcurr.update_config_with_status(
+            attr,
+            "yes" if cached_value else "no",
+            utils.get_boolean_from_string(user_config) != cached_value
+        )
+
+    @staticmethod
+    def _query_check_br_attr_boolean_on_off(attr, user_config, ifaceobjcurr, cached_value):
+        ifaceobjcurr.update_config_with_status(
+            attr,
+            "on" if cached_value else "off",
+            utils.get_boolean_from_string(user_config) != cached_value
+        )
+
+    @staticmethod
+    def _query_check_br_attr_string(attr, user_config, ifaceobjcurr, cached_value):
+        ifaceobjcurr.update_config_with_status(
+            attr,
+            cached_value,
+            user_config.lower() != cached_value
+        )
+
+    @staticmethod
+    def _query_check_brport_attr_boolean_on_off(port, user_config, cached_value):
+        return "%s=%s" % (port, "on" if cached_value else "off"), utils.get_boolean_from_string(user_config) != cached_value
+
+    @staticmethod
+    def _query_check_brport_attr_boolean_yes_no(port, user_config, cached_value):
+        return "%s=%s" % (port, "yes" if cached_value else "no"), utils.get_boolean_from_string(user_config) != cached_value
+
+    @staticmethod
+    def _query_check_brport_attr_int(port, user_config, cached_value):
+        return "%s=%s" % (port, cached_value), int(user_config) != cached_value
+
+    @classmethod
+    def _query_check_brport_attr_portmcrouter(cls, port, user_config, cached_value):
+        return (
+            "%s=%s" % (port, cls._ifla_brport_multicast_router_dict_int_to_str.get(cached_value)),
+            cls._ifla_brport_multicast_router_dict_to_int.get(user_config) != cached_value
+        )
+
+    ####################################################################################################################
 
     def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):
         bridge_all_ports = []
@@ -2833,55 +3112,67 @@ class bridge(moduleBase):
                 break
         return pvid
 
-    def _get_bridge_name(self, ifaceobj):
-        return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
-
-    def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
-                                         ifaceobj_getfunc, bridgename):
-        attr_name = 'bridge-access'
-        vid = ifaceobj.get_attr_value_first(attr_name)
-        if vid:
-            (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
-                                                        ifaceobj.name)
-            if (not running_pvid or running_pvid != vid or
-                (running_vids and running_vids[0] != vid)):
-               ifaceobjcurr.update_config_with_status(attr_name,
-                                running_pvid, 1)
-            else:
-               ifaceobjcurr.update_config_with_status(attr_name, vid, 0)
+    def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc):
+        running_pvid, running_vids = self.cache.get_pvid_and_vids(ifname)
+
+        #
+        # bridge-access
+        #
+        brport_vid_access_user_config = ifaceobj.get_attr_value_first("bridge-access")
+
+        if brport_vid_access_user_config:
+            try:
+                vid_int = int(brport_vid_access_user_config)
+            except ValueError as e:
+                ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1)
+                raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e)))
+
+            ifaceobjcurr.update_config_with_status(
+                "bridge-access",
+                str(running_pvid),
+                running_pvid != vid_int or running_vids[0] != vid_int
+            )
             return
 
-        (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
-                                                        ifaceobj.name)
-        attr_name = 'bridge-pvid'
-        pvid = ifaceobj.get_attr_value_first('bridge-pvid')
-        if pvid:
-            if running_pvid and running_pvid == pvid:
-                ifaceobjcurr.update_config_with_status(attr_name,
-                                                       running_pvid, 0)
-            else:
-                ifaceobjcurr.update_config_with_status(attr_name,
-                                                       running_pvid, 1)
+        #
+        # bridge-pvid
+        #
+        brport_pvid_user_config = ifaceobj.get_attr_value_first("bridge-pvid")
+
+        if brport_pvid_user_config:
+            try:
+                pvid = int(brport_pvid_user_config)
+            except ValueError as e:
+                ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1)
+                raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e)))
+
+            ifaceobjcurr.update_config_with_status(
+                "bridge-pvid",
+                str(running_pvid),
+                running_pvid != pvid
+            )
         elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
               ((ifaceobj.flags & iface.HAS_SIBLINGS) and
                (ifaceobj.flags & iface.OLDEST_SIBLING))):
             # if the interface has multiple iface sections,
             # we check the below only for the oldest sibling
             # or the last iface section
-            pvid = self._get_bridge_pvid(bridgename, ifaceobj_getfunc)
+            try:
+                pvid = int(self._get_bridge_pvid(bridge_name, ifaceobj_getfunc))
+            except (TypeError, ValueError):
+                pvid = 0
             if pvid:
                 if not running_pvid or running_pvid != pvid:
                     ifaceobjcurr.status = ifaceStatus.ERROR
                     ifaceobjcurr.status_str = 'bridge pvid error'
-            elif not running_pvid or running_pvid != '1':
+            elif not running_pvid or running_pvid != 1:
                 ifaceobjcurr.status = ifaceStatus.ERROR
                 ifaceobjcurr.status_str = 'bridge pvid error'
 
         attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
         if vids:
            vids = re.split(r'[\s\t]\s*', vids)
-           if not running_vids or not self._compare_vids(vids, running_vids,
-                                                         running_pvid):
+           if not running_vids or not self._compare_vids(vids, running_vids, running_pvid, expand_range=False):
                ifaceobjcurr.update_config_with_status(attr_name,
                                             ' '.join(running_vids), 1)
            else:
@@ -2895,80 +3186,130 @@ class bridge(moduleBase):
            # or the last iface section
 
            # check if it matches the bridge vids
-           bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
+           bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
            if (bridge_vids and (not running_vids  or
-                   not self._compare_vids(bridge_vids, running_vids, running_pvid))):
+                   not self._compare_vids(bridge_vids, running_vids, running_pvid, expand_range=False))):
               ifaceobjcurr.status = ifaceStatus.ERROR
               ifaceobjcurr.status_str = 'bridge vid error'
 
+    _query_check_brport_attributes = (
+        "bridge-pvid",
+        "bridge-vids",
+        "bridge-trunk",
+        "bridge-access",
+        "bridge-pathcosts",
+        "bridge-portprios",
+        "bridge-portmcrouter",
+        "bridge-learning",
+        "bridge-portmcfl",
+        "bridge-unicast-flood",
+        "bridge-multicast-flood",
+        "bridge-broadcast-flood",
+        "bridge-arp-nd-suppress",
+        "bridge-l2protocol-tunnel"
+    )
+
     def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
                                  ifaceobj_getfunc):
-        if not self._is_bridge_port(ifaceobj):
-            # Mark all bridge attributes as failed
-            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
-                    ['bridge-vids', 'bridge-trunk', 'bridge-pvid', 'bridge-access',
-                     'bridge-pathcosts', 'bridge-portprios',
-                     'bridge-portmcrouter',
-                     'bridge-learning',
-                     'bridge-portmcfl', 'bridge-unicast-flood',
-                     'bridge-multicast-flood',
-                     'bridge-arp-nd-suppress', 'bridge-l2protocol-tunnel'
-                    ], 1)
+
+        ifname = ifaceobj.name
+
+        if not self.cache.link_is_bridge_port(ifname):
+            # Mark all bridge brport attributes as failed
+            ifaceobjcurr.check_n_update_config_with_status_many(
+                ifaceobj, self._query_check_brport_attributes, 1
+            )
             return
-        bridgename = self._get_bridge_name(ifaceobj)
-        if not bridgename:
-            self.logger.warn('%s: unable to determine bridge name'
-                             %ifaceobj.name)
+
+        bridge_name = self.cache.get_bridge_name_from_port(ifname)
+        if not bridge_name:
+            self.logger.warn("%s: unable to determine bridge name" % ifname)
             return
 
-        if self.ipcmd.bridge_is_vlan_aware(bridgename):
-            self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr,
-                                                  ifaceobj_getfunc,
-                                                  bridgename)
-        for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
-                              'bridge-portprios' : 'portprio',
-                              'bridge-portmcrouter' : 'portmcrouter',
-                              'bridge-portmcfl' : 'portmcfl',
-                              'bridge-learning' : 'learning',
-                              'bridge-unicast-flood' : 'unicast-flood',
-                              'bridge-multicast-flood' : 'multicast-flood',
-                              'bridge-arp-nd-suppress' : 'arp-nd-suppress',
-                             }.items():
-            attrval = ifaceobj.get_attr_value_first(attr)
-            if not attrval:
+        if self.cache.bridge_is_vlan_aware(bridge_name):
+            self._query_check_bridge_port_vidinfo(ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
+
+        brport_info_slave_data = self.cache.get_link_info_slave_data(ifname)
+
+        #
+        # bridge-portmcfl
+        #
+        portmcfl = ifaceobj.get_attr_value_first("bridge-portmcfl")
+
+        if portmcfl:
+            cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE)
+
+            ifaceobjcurr.update_config_with_status(
+                "bridge-portmcfl",
+                "yes" if cached_value else "no",
+                utils.get_boolean_from_string(portmcfl) != cached_value
+            )
+
+        #
+        # bridge-portmcrouter
+        #
+        portmcrouter = ifaceobj.get_attr_value_first("bridge-portmcrouter")
+
+        if portmcrouter:
+            cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER)
+
+            ifaceobjcurr.update_config_with_status(
+                "bridge-portmcrouter",
+                self._ifla_brport_multicast_router_dict_int_to_str.get(cached_value),
+                self._ifla_brport_multicast_router_dict_to_int.get(portmcrouter) != cached_value
+            )
+
+        #
+        # bridge-learning
+        # bridge-unicast-flood
+        # bridge-multicast-flood
+        # bridge-broadcast-flood
+        # bridge-arp-nd-suppress
+        #
+        for attr_name, attr_nl in (
+                ("bridge-learning", Link.IFLA_BRPORT_LEARNING),
+                ("bridge-unicast-flood", Link.IFLA_BRPORT_UNICAST_FLOOD),
+                ("bridge-multicast-flood", Link.IFLA_BRPORT_MCAST_FLOOD),
+                ("bridge-broadcast-flood", Link.IFLA_BRPORT_BCAST_FLOOD),
+                ("bridge-arp-nd-suppress", Link.IFLA_BRPORT_NEIGH_SUPPRESS),
+        ):
+            attribute_value = ifaceobj.get_attr_value_first(attr_name)
+
+            if not attribute_value:
+                continue
+
+            cached_value = brport_info_slave_data.get(attr_nl)
+
+            ifaceobjcurr.update_config_with_status(
+                attr_name,
+                "on" if cached_value else "off",
+                utils.get_boolean_from_string(attribute_value) != cached_value
+                )
+
+        #
+        # bridge-pathcosts
+        # bridge-portprios
+        #
+        for attr_name, attr_nl in (
+                ("bridge-pathcosts", Link.IFLA_BRPORT_COST),
+                ("bridge-portprios", Link.IFLA_BRPORT_PRIORITY),
+        ):
+            attribute_value = ifaceobj.get_attr_value_first(attr_name)
+
+            if not attribute_value:
                 continue
 
+            cached_value = brport_info_slave_data.get(attr_nl)
+
             try:
-                running_attrval = self.brctlcmd.get_bridgeport_attr(
-                                       bridgename, ifaceobj.name, dstattr)
-
-                if dstattr == 'portmcfl':
-                    if not utils.is_binary_bool(attrval) and running_attrval:
-                        running_attrval = utils.get_yesno_boolean(
-                            utils.get_boolean_from_string(running_attrval))
-                elif dstattr == 'portmcrouter':
-                    if self._ifla_brport_multicast_router_dict_to_int.get(attrval) == int(running_attrval):
-                        ifaceobjcurr.update_config_with_status(attr, attrval, 0)
-                    else:
-                        ifaceobjcurr.update_config_with_status(attr, attrval, 1)
-                    continue
-                elif dstattr in ['learning',
-                                 'unicast-flood',
-                                 'multicast-flood',
-                                 'arp-nd-suppress',
-                                ]:
-                    if not utils.is_binary_bool(attrval) and running_attrval:
-                        running_attrval = utils.get_onff_from_onezero(
-                                                running_attrval)
-
-                if running_attrval != attrval:
-                    ifaceobjcurr.update_config_with_status(attr,
-                                            running_attrval, 1)
-                else:
-                    ifaceobjcurr.update_config_with_status(attr,
-                                            running_attrval, 0)
-            except Exception, e:
-                self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
+                ifaceobjcurr.update_config_with_status(
+                    attr_name,
+                    str(cached_value),
+                    int(attribute_value) != cached_value
+                )
+            except ValueError as e:
+                ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1)
+                raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e)))
 
         self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
 
@@ -2984,7 +3325,7 @@ class bridge(moduleBase):
                 result = 1
             ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
 
-    def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridge_running_attrs):
+    def _query_check_l2protocol_tunnel_on_bridge(self, ifname, ifaceobj, ifaceobjcurr):
         """
             In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
             We need to make sure that all ports comply with the mask given under the bridge
@@ -3001,8 +3342,8 @@ class bridge(moduleBase):
                     return
             else:
                 config_per_port_dict = {}
-                brport_list = bridge_running_attrs.get('ports', {}).keys()
-            result = 1
+                brport_list = self.cache.get_slaves(ifname)
+
             try:
                 for brport_name in brport_list:
                     self._query_check_l2protocol_tunnel(
@@ -3016,8 +3357,8 @@ class bridge(moduleBase):
             ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
 
     def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
-        cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
-        cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
+        cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
+        cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
 
         for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
             callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
@@ -3028,8 +3369,8 @@ class bridge(moduleBase):
                                     % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
 
     def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
-        cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
-        cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
+        cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
+        cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
         running_protocols = []
         for protocol_name, callback in self.query_check_l2protocol_tunnel_callback.items():
             if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
@@ -3057,7 +3398,7 @@ class bridge(moduleBase):
                                           ifaceobj_getfunc)
 
     def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
-        if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name):
+        if self.cache.bridge_is_vlan_aware(ifaceobjrunning.name):
             ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
             ifaceobjrunning.update_config_dict(self._query_running_attrs(
                                                ifaceobjrunning,
@@ -3071,18 +3412,18 @@ class bridge(moduleBase):
         if self.systcl_get_net_bridge_stp_user_space() == '1':
             return
 
-        v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
+        v = str(self.cache.get_brport_cost(ifaceobjrunning.name))
         if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
             ifaceobjrunning.update_config('bridge-pathcosts', v)
 
-        v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
+        v = str(self.cache.get_brport_priority(ifaceobjrunning.name))
         if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
             ifaceobjrunning.update_config('bridge-portprios', v)
 
     def _query_running_bridge_port(self, ifaceobjrunning,
                                    ifaceobj_getfunc=None):
 
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(
+        bridgename = self.cache.get_bridge_name_from_port(
                                                 ifaceobjrunning.name)
         bridge_vids = None
         bridge_pvid = None
@@ -3091,7 +3432,7 @@ class bridge(moduleBase):
                              %ifaceobjrunning.name)
             return
 
-        if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+        if not self.cache.bridge_is_vlan_aware(bridgename):
             try:
                 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
             except Exception as e:
@@ -3125,31 +3466,23 @@ class bridge(moduleBase):
                     ifaceobjrunning.update_config('bridge-pvid',
                                         bridge_port_pvid)
 
-        v = utils.get_onff_from_onezero(
-                self.brctlcmd.get_bridgeport_attr(bridgename,
-                                                  ifaceobjrunning.name,
-                                                  'learning'))
+        v = utils.get_onff_from_onezero(self.cache.get_brport_learning(ifaceobjrunning.name))
         if v and v != self.get_mod_subattr('bridge-learning', 'default'):
             ifaceobjrunning.update_config('bridge-learning', v)
 
-        v = utils.get_onff_from_onezero(
-                self.brctlcmd.get_bridgeport_attr(bridgename,
-                                                  ifaceobjrunning.name,
-                                                  'unicast-flood'))
+        v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(ifaceobjrunning.name))
         if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
             ifaceobjrunning.update_config('bridge-unicast-flood', v)
 
-        v = utils.get_onff_from_onezero(
-                self.brctlcmd.get_bridgeport_attr(bridgename,
-                                                  ifaceobjrunning.name,
-                                                  'multicast-flood'))
+        v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(ifaceobjrunning.name))
         if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
             ifaceobjrunning.update_config('bridge-multicast-flood', v)
 
-        v = utils.get_onff_from_onezero(
-                self.brctlcmd.get_bridgeport_attr(bridgename,
-                                                  ifaceobjrunning.name,
-                                                  'arp-nd-suppress'))
+        v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(ifaceobjrunning.name))
+        if v and v != self.get_mod_subattr('bridge-broadcast-flood', 'default'):
+            ifaceobjrunning.update_config('bridge-broadcast-flood', v)
+
+        v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(ifaceobjrunning.name))
         # Display running 'arp-nd-suppress' only on vxlan ports
         # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
         # otherwise, display on all bridge-ports
@@ -3166,9 +3499,9 @@ class bridge(moduleBase):
 
     def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
         try:
-            if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+            if self.cache.bridge_exists(ifaceobjrunning.name):
                 self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
-            elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+            elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
                 self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
         except Exception as e:
             raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
@@ -3181,47 +3514,52 @@ class bridge(moduleBase):
         if self.default_stp_on:
             ifaceobj.update_config('bridge-stp', 'yes')
 
-    def _query_check_support_yesno_attrs(self, runningattrs, ifaceobj):
-        for attrl in [['mcqifaddr', 'bridge-mcqifaddr'],
-                     ['mcquerier', 'bridge-mcquerier'],
-                     ['mcsnoop', 'bridge-mcsnoop']]:
-            value = ifaceobj.get_attr_value_first(attrl[1])
-            if value and not utils.is_binary_bool(value):
-                if attrl[0] in runningattrs:
-                    bool = utils.get_boolean_from_string(runningattrs[attrl[0]])
-                    runningattrs[attrl[0]] = utils.get_yesno_boolean(bool)
-
-        self._query_check_mcrouter(ifaceobj, runningattrs)
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcfl', ifaceobj.get_attr_value_first('bridge-portmcfl'))
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'learning', ifaceobj.get_attr_value_first('bridge-learning'))
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'unicast-flood', ifaceobj.get_attr_value_first('bridge-unicast-flood'))
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'multicast-flood', ifaceobj.get_attr_value_first('bridge-multicast-flood'))
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'arp-nd-suppress', ifaceobj.get_attr_value_first('bridge-arp-nd-suppress'))
-
-    def _query_check_mcrouter(self, ifaceobj, running_attrs):
+    def __re_evaluate_bridge_vxlan(self, ifaceobj, ifaceobj_getfunc=None):
         """
-        bridge-mcrouter and bridge-portmcrouter supports: yes-no-0-1-2
+        Quick fix for BRIDGE_VXLAN
+
+        BRIDGE_VXLAN is not set on the bridge because the VXLAN hasn't been processed yet
+        (because its defined after the bridge in /e/n/i), here is what happens:
+
+        - ifupdownmain:populate_dependency_info()
+        - loops over all the intf from /e/n/i (with the example config:
+            ['lo', 'eth0', 'swp1', 'swp2', 'bridge', 'vni-10', 'bridge.100', 'vlan100'])
+            ----> bridge is first in the list of interface (that we care about)
+
+        - ifupdownmain:query_lowerifaces()
+        - bridge:get_dependent is called (debug: bridge: evaluating port expr '['swp1', 'swp2', 'vni-10']')
+        - ifupdownmain:preprocess_dependency_list()
+        - calls ifupdownmain:_set_iface_role_n_kind() on all the brports:
+
+        in _set_iface_role_n_kind:
+        ifaceobj is the brport
+        upperifaceobj is the bridge
+
+        it tries to see if the bridge has a VXLAN:
+
+        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
+        and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
+        upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
+
+        but because the bridge is first in the /e/n/i ifupdown2 didn't
+        call vxlan:get_dependent_ifacenames so VXLAN is not set on ifaceobj
+
+        :return:
         """
-        if 'mcrouter' in running_attrs:
-            value = ifaceobj.get_attr_value_first('bridge-mcrouter')
-            if value:
-                try:
-                    int(value)
-                except:
-                    running_attrs['mcrouter'] = 'yes' if utils.get_boolean_from_string(running_attrs['mcrouter']) else 'no'
+        if not ifaceobj_getfunc:
+            return
 
-    def _query_check_support_yesno_attr_port(self, runningattrs, ifaceobj, attr, attrval):
-        if attrval:
-            portlist = self.parse_port_list(ifaceobj.name, attrval)
-            if portlist:
-                to_convert = []
-                for p in portlist:
-                    (port, val) = p.split('=')
-                    if not utils.is_binary_bool(val):
-                        to_convert.append(port)
-                for port in to_convert:
-                    runningattrs['ports'][port][attr] = utils.get_yesno_boolean(
-                        utils.get_boolean_from_string(runningattrs['ports'][port][attr]))
+        if ifaceobj.link_kind & ifaceLinkKind.BRIDGE and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
+            for port in self._get_bridge_port_list(ifaceobj) or []:
+                for brport_ifaceobj in ifaceobj_getfunc(port):
+                    if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+                        ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
+                        return
+
+        elif ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+            for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
+                for bridge_ifaceobj in ifaceobj_getfunc(iface) or []:
+                    bridge_ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
 
     _run_ops = {
         'pre-up': _up,
@@ -3235,10 +3573,6 @@ class bridge(moduleBase):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = self.brctlcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
         """ run bridge configuration on the interface object passed as
             argument. Can create bridge interfaces if they dont exist already
@@ -3260,14 +3594,16 @@ class bridge(moduleBase):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
            return
-        self._init_command_handlers()
 
-        if (not LinkUtils.bridge_utils_is_installed
+        if (not self.requirements.bridge_utils_is_installed
                 and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
-                    and LinkUtils.bridge_utils_missing_warning):
+                    and self.bridge_utils_missing_warning):
             self.logger.warning('%s: missing - bridge operation may not work as expected. '
                                 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
-            LinkUtils.bridge_utils_missing_warning = False
+            self.bridge_utils_missing_warning = False
+
+        # make sure BRIDGE_VXLAN is set if we have a vxlan port
+        self.__re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc)
 
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj,
index 3cc4fb8bd0fef0e6eb5133b4cb1f117536741b22..9d47c2e46bf0d992c3659084f36f5cb27dece231 100644 (file)
@@ -5,54 +5,59 @@
 #
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     from ifupdown2.ifupdown.iface import *
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 except ImportError:
+    from lib.addon import Addon
+
     from ifupdown.iface import *
 
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.ifupdownflags as ifupdownflags
 
 
-class bridgevlan(moduleBase):
+class bridgevlan(Addon, moduleBase):
     """  ifupdown2 addon module to configure vlan attributes on a vlan
          aware bridge """
 
-    _modinfo = {'mhelp' : 'bridgevlan module configures vlan attributes ' +
-                        'on a vlan aware bridge. This module only ' +
-                        'understands vlan interface name ' +
-                        'with dot notations. eg br0.100. where br0 is the ' +
-                        'vlan aware bridge this config is for',
-                'attrs' : {
-                        'bridge-igmp-querier-src' :
-                            { 'help' : 'bridge igmp querier src. Must be ' +
-                                   'specified under the vlan interface',
-                              'validvals' : ['<ipv4>', ],
-                              'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
+    _modinfo = {
+        "mhelp": "bridgevlan module configures vlan attributes on a vlan aware "
+                 "bridge. This module only understands vlan interface name "
+                 "with dot notations. eg br0.100. where br0 is the vlan aware "
+                 "bridge this config is for",
+        "attrs": {
+            "bridge-igmp-querier-src": {
+                "help": "bridge igmp querier src. Must be specified under "
+                        "the vlan interface",
+                "validvals": ["<ipv4>"],
+                "example": ["bridge-igmp-querier-src 172.16.101.1"]
+            }
+        }
+    }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.brctlcmd = None
-        self.ipcmd = None
 
-    def _is_bridge_vlan_device(self, ifaceobj):
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return True
-        return False
+    @staticmethod
+    def _is_bridge_vlan_device(ifaceobj):
+        return ifaceobj.type == ifaceType.BRIDGE_VLAN
 
-    def _get_bridge_n_vlan(self, ifaceobj):
+    @staticmethod
+    def _get_bridge_n_vlan(ifaceobj):
         vlist = ifaceobj.name.split('.', 1)
         if len(vlist) == 2:
-            return (vlist[0], vlist[1])
+            return vlist[0], vlist[1]
         return None
 
-    def _get_bridgename(self, ifaceobj):
+    @staticmethod
+    def _get_bridgename(ifaceobj):
         vlist = ifaceobj.name.split('.', 1)
         if len(vlist) == 2:
             return vlist[0]
@@ -68,18 +73,18 @@ class bridgevlan(moduleBase):
             (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
             vlanid = int(vlan, 10)
         except:
-            self.log_error('%s: bridge vlan interface name ' %ifaceobj.name +
-                    'does not correspond to format (eg. br0.100)', ifaceobj)
+            self.log_error("%s: bridge vlan interface name does not correspond "
+                           "to format (eg. br0.100)" % ifaceobj.name, ifaceobj)
             raise
 
-        if not self.ipcmd.link_exists(bridgename):
+        if not self.cache.link_exists(bridgename):
             #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
             #                 bridgename))
             return
 
         running_mcqv4src = {}
         if not ifupdownflags.flags.PERFMODE:
-            running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(bridgename)
+            running_mcqv4src = self.sysfs.bridge_get_mcqv4src(bridgename)
         if running_mcqv4src:
             r_mcqv4src = running_mcqv4src.get(vlan)
         else:
@@ -87,37 +92,37 @@ class bridgevlan(moduleBase):
         mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
         if not mcqv4src:
             if r_mcqv4src:
-                self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
+                self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
             return
 
         if r_mcqv4src and r_mcqv4src != mcqv4src:
-            self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
-            self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
+            self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
+            self.iproute2.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
         else:
-            self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
+            self.iproute2.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
 
     def _down(self, ifaceobj):
         try:
             (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
             vlanid = int(vlan, 10)
         except:
-            self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
-                    'does not correspond to format (eg. br0.100)')
+            self.logger.warn("%s: bridge vlan interface name does not "
+                             "correspond to format (eg. br0.100)" % ifaceobj.name)
             raise
 
-        if not self.ipcmd.link_exists(bridgename):
+        if not self.cache.link_exists(bridgename):
             #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
             #                 bridgename))
             return
         mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
         if mcqv4src:
-           self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
+            self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
 
     def _query_running_bridge_igmp_querier_src(self, ifaceobj):
         (bridgename, vlanid) = ifaceobj.name.split('.')
-        running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(bridgename)
+        running_mcqv4src = self.sysfs.bridge_get_mcqv4src(bridgename)
         if running_mcqv4src:
-           return running_mcqv4src.get(vlanid)
+            return running_mcqv4src.get(vlanid)
         return None
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
@@ -133,32 +138,24 @@ class bridgevlan(moduleBase):
                 ifaceobjcurr.status = ifaceStatus.SUCCESS
         return
 
-    def _query_running(self, ifaceobjrunning):
-        # XXX not supported
-        return
-
     def syntax_check(self, ifaceobj, ifaceobj_getfunc):
         ret = True
         bvlan_intf = self._is_bridge_vlan_device(ifaceobj)
-        if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and
-            not bvlan_intf):
-            self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' %ifaceobj.name)
+        if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and not bvlan_intf):
+            self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' % ifaceobj.name)
             ret = False
         return ret
 
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
+    _run_ops = {
+        "pre-up": _up,
+        "post-down": _down,
+        "query-checkcurr": _query_check,
+    }
 
     def get_ops(self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = self.brctlcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         """ run vlan configuration on the interface object passed as argument
 
@@ -178,18 +175,15 @@ class bridgevlan(moduleBase):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
             return
-        if (operation != 'query-running' and
-                not self._is_bridge_vlan_device(ifaceobj)):
+        if (operation != 'query-running' and not self._is_bridge_vlan_device(ifaceobj)):
             # most common problem is people specify BRIDGE_VLAN
             # attribute on a bridge or a vlan device, which
             # is incorrect. So, catch them here and warn before
             # giving up processing the interface
-            if ((ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
-                ifaceobj.link_kind & ifaceLinkKind.VLAN) and
-                not self.syntax_check(ifaceobj, None)):
+            if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or ifaceobj.link_kind & ifaceLinkKind.VLAN) \
+                    and not self.syntax_check(ifaceobj, None):
                 ifaceobj.status = ifaceStatus.ERROR
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 30886b42460213b603d3278645d136a9f5251209..4d667bc4e2345550bfd3130671d91436418f4065 100644 (file)
@@ -6,8 +6,11 @@
 
 import re
 import time
+import socket
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     import ifupdown2.ifupdown.policymanager as policymanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 
@@ -15,9 +18,10 @@ try:
     from ifupdown2.ifupdown.utils import utils
 
     from ifupdown2.ifupdownaddons.dhclient import dhclient
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 except ImportError:
+    from lib.addon import Addon
+
     import ifupdown.policymanager as policymanager
     import ifupdown.ifupdownflags as ifupdownflags
 
@@ -25,46 +29,16 @@ except ImportError:
     from ifupdown.utils import utils
 
     from ifupdownaddons.dhclient import dhclient
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
 
-class dhcp(moduleBase):
+class dhcp(Addon, moduleBase):
     """ ifupdown2 addon module to configure dhcp on interface """
 
-    _modinfo = {
-        "mhelp": "Configure dhcp",
-        "policies": {
-            "dhcp6-duid": {
-                "help": "Override the default when selecting the type of DUID to use. By default, DHCPv6 dhclient "
-                        "creates an identifier based on the link-layer address (DUID-LL) if it is running in stateless "
-                        "mode (with -S, not requesting an address), or it creates an identifier based on the "
-                        "link-layer address plus a timestamp (DUID-LLT) if it is running in stateful mode (without -S, "
-                        "requesting an  address). When DHCPv4 is configured to use a DUID using -i option the default "
-                        "is to use a DUID-LLT. -D overrides these default, with a value of either LL or LLT.",
-                "validvals": ["LL", "LLT"],
-                "example": ["dhcp6-duid LL"]
-            },
-            "dhcp-wait": {
-                "help": "Wait or not wait and become a daemon immediately (nowait) rather than waiting until an "
-                        "IP address has been acquired. If not specified default value is true, that is to wait.",
-                "validvals": ["true", "false"],
-                "example": ["dhcp-wait false"]
-            },
-            "dhcp6-ll-wait": {
-                "help": "Overrides the default wait time before DHCPv6 client is started. During this wait time, "
-                        "ifupdown2 checks if the interface requesting an address has a valid link-local address. "
-                        "If not specified default value used is 10 seconds.",
-                "validvals": ["whole numbers"],
-                "example": ["dhcp6-ll-wait 0"]
-            }
-        }
-    }
-
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
         self.dhclientcmd = dhclient(**kargs)
-        self.ipcmd = None
 
     def syntax_check(self, ifaceobj, ifaceobj_getfunc):
         return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
@@ -99,10 +73,9 @@ class dhcp(moduleBase):
                 pass
             dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
                 ifname=ifaceobj.name, attr='dhcp6-duid')
-
             vrf = ifaceobj.get_attr_value_first('vrf')
             if (vrf and self.vrf_exec_cmd_prefix and
-                self.ipcmd.link_exists(vrf)):
+                self.cache.link_exists(vrf)):
                 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
 
             if 'inet' in ifaceobj.addr_family:
@@ -140,8 +113,7 @@ class dhcp(moduleBase):
                     #add delay before starting IPv6 dhclient to
                     #make sure the configured interface/link is up.
                     if timeout > 1:
-                       time.sleep(1)
-
+                        time.sleep(1)
                     while timeout:
                         addr_output = utils.exec_command('%s -6 addr show %s'
                                                          %(utils.ip_cmd, ifaceobj.name))
@@ -173,18 +145,20 @@ class dhcp(moduleBase):
         dhclient_cmd_prefix = None
         vrf = ifaceobj.get_attr_value_first('vrf')
         if (vrf and self.vrf_exec_cmd_prefix and
-            self.ipcmd.link_exists(vrf)):
+            self.cache.link_exists(vrf)):
             dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
         dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
-            ifname=ifaceobj.name, attr='dhcp6-duid')
+                                                                       ifname=ifaceobj.name, attr='dhcp6-duid')
         if 'inet6' in ifaceobj.addr_family:
             self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix, duid=dhcp6_duid)
+            self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6)
         if 'inet' in ifaceobj.addr_family:
             self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
+            self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET)
 
     def _down(self, ifaceobj):
         self._dhcp_down(ifaceobj)
-        self.ipcmd.link_down(ifaceobj.name)
+        self.netlink.link_down(ifaceobj.name)
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
         status = ifaceStatus.SUCCESS
@@ -210,7 +184,7 @@ class dhcp(moduleBase):
         ifaceobjcurr.status = status
 
     def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+        if not self.cache.link_exists(ifaceobjrunning.name):
             return
         if self.dhclientcmd.is_running(ifaceobjrunning.name):
             ifaceobjrunning.addr_family.append('inet')
@@ -229,10 +203,6 @@ class dhcp(moduleBase):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         """ run dhcp configuration on the interface object passed as argument
 
@@ -262,7 +232,6 @@ class dhcp(moduleBase):
             return
         if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index c4f9aa1772c13be7e5b85443d2014541c3f63c6b..09ea519f8dcc3989f8ba2a22f7407245a78ae299 100644 (file)
@@ -7,6 +7,8 @@
 import os
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.policymanager as policymanager
 
@@ -15,9 +17,10 @@ try:
     from ifupdown2.ifupdown.exceptions import moduleNotSupported
 
     from ifupdown2.ifupdownaddons.utilsbase import *
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 except ImportError:
+    from lib.addon import Addon
+
     import ifupdown.ifupdownflags as ifupdownflags
     import ifupdown.policymanager as policymanager
 
@@ -26,51 +29,64 @@ except ImportError:
     from ifupdown.exceptions import moduleNotSupported
 
     from ifupdownaddons.utilsbase import *
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
 
-class ethtool(moduleBase,utilsBase):
+class ethtool(Addon, moduleBase):
     """  ifupdown2 addon module to configure ethtool attributes """
 
-    _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
-                'attrs': {
-                      'link-speed' :
-                            {'help' : 'set link speed',
-                             'validvals' : ['10',
-                                            '100',
-                                            '1000',
-                                            '10000',
-                                            '25000',
-                                            '40000',
-                                            '50000',
-                                            '100000'],
-                             'example' : ['link-speed 1000'],
-                             'default' : 'varies by platform and port'},
-                      'link-duplex' :
-                            {'help': 'set link duplex',
-                             'example' : ['link-duplex full'],
-                             'validvals' : ['half', 'full'],
-                             'default' : 'full'},
-                      'link-autoneg' :
-                            {'help': 'set autonegotiation',
-                             'example' : ['link-autoneg on'],
-                             'validvals' : ['yes', 'no', 'on', 'off'],
-                             'default' : 'varies by platform and port'},
-                      'link-fec' :
-                            {'help': 'set forward error correction mode',
-                             'example' : ['link-fec rs'],
-                             'validvals' : ['rs', 'baser', 'auto', 'off'],
-                             'default' : 'varies by platform and port'}}}
+    _modinfo = {
+        "mhelp": "ethtool configuration module for interfaces",
+        "attrs": {
+            "link-speed": {
+                "help": "set link speed",
+                "validvals": [
+                    "10",
+                    "100",
+                    "1000",
+                    "10000",
+                    "25000",
+                    "40000",
+                    "50000",
+                    "100000"
+                ],
+                "example": ["link-speed 1000"],
+                "default": "varies by platform and port"
+            },
+            "link-duplex": {
+                "help": "set link duplex",
+                "example": ["link-duplex full"],
+                "validvals": ["half", "full"],
+                "default": "full"
+            },
+            "link-autoneg": {
+                "help": "set autonegotiation",
+                "example": ["link-autoneg on"],
+                "validvals": ["yes", "no", "on", "off"],
+                "default": "varies by platform and port"
+            },
+            "link-fec": {
+                "help": "set forward error correction mode",
+                "example": ["link-fec rs"],
+                "validvals": ["rs", "baser", "auto", "off"],
+                "default": "varies by platform and port"
+            }
+        }
+    }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
         if not os.path.exists(utils.ethtool_cmd):
             raise moduleNotSupported('module init failed: %s: not found' % utils.ethtool_cmd)
-        self.ipcmd = None
         # keep a list of iface objects who have modified link attributes
         self.ifaceobjs_modified_configs = []
 
+        self.ethtool_ignore_errors = policymanager.policymanager_api.get_module_globals(
+            module_name=self.__class__.__name__,
+            attr='ethtool_ignore_errors'
+        )
+
     def do_fec_settings(self, ifaceobj):
         feccmd = ''
 
@@ -94,10 +110,6 @@ class ethtool(moduleBase,utilsBase):
         if default_val:
             default_val = default_val.lower()
 
-        if running_val in ["none", "notsupported"]:
-            # None and NotSupported ethtool FEC values mean "off"
-            running_val = "off"
-
         # check running values
         if config_val and config_val == running_val:
             return
@@ -120,7 +132,8 @@ class ethtool(moduleBase,utilsBase):
                            (utils.ethtool_cmd, ifaceobj.name, feccmd))
                 utils.exec_command(feccmd)
             except Exception, e:
-                self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
+                if not self.ethtool_ignore_errors:
+                    self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
         else:
             pass
 
@@ -214,14 +227,15 @@ class ethtool(moduleBase,utilsBase):
                 cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd))
                 utils.exec_command(cmd)
             except Exception, e:
-                self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
+                if not self.ethtool_ignore_errors:
+                    self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
 
     def _pre_up(self, ifaceobj, operation='post_up'):
         """
         _pre_up and _pre_down will reset the layer 2 attributes to default policy
         settings.
         """
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
 
         self.do_speed_settings(ifaceobj)
@@ -306,9 +320,9 @@ class ethtool(moduleBase,utilsBase):
         """
         try:
             for attr in ethtool_output.splitlines():
-                if attr.startswith('FEC encodings'):
+                if attr.startswith('Configured FEC encodings:'):
                     fec_attrs = attr.split()
-                    return(fec_attrs[fec_attrs.index(':')+1])
+                    return(fec_attrs[fec_attrs.index('encodings:')+1])
         except Exception as e:
             self.logger.debug('ethtool: problems in ethtool set-fec output'
                                ' %s: %s' %(ethtool_output.splitlines(), str(e)))
@@ -328,13 +342,14 @@ class ethtool(moduleBase,utilsBase):
                                             (utils.ethtool_cmd, ifaceobj.name))
                 running_attr = self.get_fec_encoding(ethtool_output=output)
             else:
-                running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \
+                running_attr = self.io.read_file_oneline('/sys/class/net/%s/%s' % \
                                                       (ifaceobj.name, attr))
         except Exception as e:
-            # for nonexistent interfaces, we get an error (rc = 256 or 19200)
-            self.logger.debug('ethtool: problems calling ethtool or reading'
-                              ' /sys/class on iface %s for attr %s: %s' %
-                              (ifaceobj.name, attr, str(e)))
+            if not self.ethtool_ignore_errors:
+                # for nonexistent interfaces, we get an error (rc = 256 or 19200)
+                self.logger.debug('ethtool: problems calling ethtool or reading'
+                                  ' /sys/class on iface %s for attr %s: %s' %
+                                  (ifaceobj.name, attr, str(e)))
         return running_attr
 
 
@@ -347,7 +362,7 @@ class ethtool(moduleBase,utilsBase):
         """
         # do not bother showing swp ifaces that are not up for the speed
         # duplex and autoneg are not reliable.
-        if not self.ipcmd.is_link_up(ifaceobj.name):
+        if not self.cache.link_is_up(ifaceobj.name):
             return
         for attr in ['speed', 'duplex', 'autoneg']:
             default_val = policymanager.policymanager_api.get_iface_default(
@@ -396,10 +411,6 @@ class ethtool(moduleBase,utilsBase):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         """ run ethtool configuration on the interface object passed as
             argument
@@ -423,7 +434,6 @@ class ethtool(moduleBase,utilsBase):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 3aaa969a6ffeac034e5bb09c2b29345311760821..2c5f33e957b87958ea596404a3d27a8d4634ed63 100644 (file)
 # loopback or dummy.
 
 try:
+    from ifupdown2.lib.addon import Addon
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.policymanager as policymanager
 except ImportError:
+    from lib.addon import Addon
     from ifupdown.iface import *
     from ifupdown.utils import utils
 
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.ifupdownflags as ifupdownflags
     import ifupdown.policymanager as policymanager
 
 
-class link(moduleBase):
-    _modinfo = {'mhelp' : 'create/configure link types. similar to ip-link',
-                'attrs' : {
-                   'link-type' :
-                        {'help' : 'type of link as in \'ip link\' command.',
-                         'validvals' : ['dummy', 'veth'],
-                         'example' : ['link-type <dummy|veth>']},
-                   'link-down' :
-                        {'help': 'keep link down',
-                         'example' : ['link-down yes/no'],
-                         'default' : 'no',
-                         'validvals' : ['yes', 'no']}}}
+class link(Addon, moduleBase):
+    _modinfo = {
+        "mhelp": "create/configure link types. similar to ip-link",
+        "attrs": {
+            "link-type": {
+                "help": "type of link as in 'ip link' command.",
+                "validvals": ["dummy", "veth"],
+                "example": ["link-type <dummy|veth>"]
+            },
+            "link-down": {
+                "help": "keep link down",
+                "example": ["link-down yes/no"],
+                "default": "no",
+                "validvals": ["yes", "no"]
+            }
+        }
+    }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
 
-        self.check_physical_port_existance = utils.get_boolean_from_string(policymanager.policymanager_api.get_module_globals(
-            self.__class__.__name__,
-            'warn_on_physdev_not_present'
-        ))
+        self.check_physical_port_existance = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                self.__class__.__name__,
+                'warn_on_physdev_not_present'
+            )
+        )
 
     def syntax_check(self, ifaceobj, ifaceobj_getfunc):
         if self.check_physical_port_existance:
-            if not ifaceobj.link_kind and not LinkUtils.link_exists(ifaceobj.name):
+            if not ifaceobj.link_kind and not self.cache.link_exists(ifaceobj.name):
                 self.logger.warning('%s: interface does not exist' % ifaceobj.name)
                 return False
         return True
 
-    def _is_my_interface(self, ifaceobj):
-        if (ifaceobj.get_attr_value_first('link-type')
-                or ifaceobj.get_attr_value_first('link-down')):
-            return True
-        return False
+    @staticmethod
+    def _is_my_interface(ifaceobj):
+        return ifaceobj.get_attr_value_first('link-type') or ifaceobj.get_attr_value_first('link-down')
 
     def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
         if ifaceobj.get_attr_value_first('link-down') == 'yes':
@@ -73,36 +78,32 @@ class link(moduleBase):
     def _up(self, ifaceobj):
         link_type = ifaceobj.get_attr_value_first('link-type')
         if link_type:
-            self.ipcmd.link_create(ifaceobj.name,
-                                   ifaceobj.get_attr_value_first('link-type'))
+            self.netlink.link_add(ifname=ifaceobj.name, kind=link_type)
 
     def _down(self, ifaceobj):
         if not ifaceobj.get_attr_value_first('link-type'):
             return
-        if (not ifupdownflags.flags.PERFMODE and
-            not self.ipcmd.link_exists(ifaceobj.name)):
-           return
+        if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
+            return
         try:
-            self.ipcmd.link_delete(ifaceobj.name)
+            self.netlink.link_del(ifaceobj.name)
         except Exception, e:
             self.log_warn(str(e))
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
         if ifaceobj.get_attr_value('link-type'):
-            if not self.ipcmd.link_exists(ifaceobj.name):
+            if not self.cache.link_exists(ifaceobj.name):
                 ifaceobjcurr.update_config_with_status('link-type', 'None', 1)
             else:
                 link_type = ifaceobj.get_attr_value_first('link-type')
-                if self.ipcmd.link_get_kind(ifaceobj.name) == link_type:
-                    ifaceobjcurr.update_config_with_status('link-type',
-                                                            link_type, 0)
+                if self.cache.get_link_kind(ifaceobj.name) == link_type:
+                    ifaceobjcurr.update_config_with_status('link-type', link_type, 0)
                 else:
-                    ifaceobjcurr.update_config_with_status('link-type',
-                                                            link_type, 1)
+                    ifaceobjcurr.update_config_with_status('link-type', link_type, 1)
 
         link_down = ifaceobj.get_attr_value_first('link-down')
         if link_down:
-            link_up = self.ipcmd.is_link_up(ifaceobj.name)
+            link_up = self.cache.link_is_up(ifaceobj.name)
             link_should_be_down = utils.get_boolean_from_string(link_down)
 
             if link_should_be_down and link_up:
@@ -117,17 +118,15 @@ class link(moduleBase):
 
             ifaceobjcurr.update_config_with_status('link-down', link_down, status)
 
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check}
+    _run_ops = {
+        "pre-up": _up,
+        "post-down": _down,
+        "query-checkcurr": _query_check
+    }
 
     def get_ops(self):
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
@@ -135,7 +134,6 @@ class link(moduleBase):
         if (operation != 'query-running' and
                 not self._is_my_interface(ifaceobj)):
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 324de02e2da838fd259dd63064c828acd41b22d4..8727dd539a5ef50736adefa75bb78422d0c4b284 100644 (file)
@@ -9,27 +9,27 @@ import os
 from sets import Set
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
 
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.policymanager as policymanager
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
     from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil
     from ifupdown2.ifupdownaddons.systemutils import systemUtils
     from ifupdown2.ifupdown.exceptions import moduleNotSupported
 except ImportError:
+    from lib.addon import Addon
+
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
 
     import ifupdown.ifupdownflags as ifupdownflags
     import ifupdown.policymanager as policymanager
 
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
     from ifupdownaddons.mstpctlutil import mstpctlutil
     from ifupdownaddons.systemutils import systemUtils
@@ -39,176 +39,221 @@ except ImportError:
 class mstpctlFlags:
     PORT_PROCESSED = 0x1
 
-class mstpctl(moduleBase):
+class mstpctl(Addon, moduleBase):
     """  ifupdown2 addon module to configure mstp attributes """
 
-    _modinfo = {'mhelp' : 'mstp configuration module for bridges',
-                'attrs' : {
-                   'mstpctl-ports' :
-                        {'help' : 'mstp ports',
-                         'compat' : True,
-                         'deprecated': True,
-                         'new-attribute': 'bridge-ports'},
-                   'mstpctl-stp' :
-                        {'help': 'bridge stp yes/no',
-                         'validvals' : ['yes', 'no', 'on', 'off'],
-                         'compat' : True,
-                         'default' : 'no',
-                         'deprecated': True,
-                         'new-attribute': 'bridge-stp'},
-                   'mstpctl-treeprio' :
-                        {'help': 'tree priority',
-                         'default' : '32768',
-                         'validvals' : ['0', '4096', '8192', '12288', '16384',
-                                        '20480', '24576', '28672', '32768',
-                                        '36864', '40960', '45056', '49152',
-                                        '53248', '57344', '61440'],
-                         'required' : False,
-                         'example' : ['mstpctl-treeprio 32768']},
-                   'mstpctl-ageing' :
-                        {'help': 'ageing time',
-                         'validrange' : ['0', '4096'],
-                         'default' : '300',
-                         'required' : False,
-                         'jsonAttr': 'ageingTime',
-                         'example' : ['mstpctl-ageing 300']},
-                    'mstpctl-maxage' :
-                        { 'help' : 'max message age',
-                          'validrange' : ['0', '255'],
-                          'default' : '20',
-                          'jsonAttr': 'bridgeMaxAge',
-                          'required' : False,
-                          'example' : ['mstpctl-maxage 20']},
-                    'mstpctl-fdelay' :
-                        { 'help' : 'set forwarding delay',
-                          'validrange' : ['0', '255'],
-                          'default' : '15',
-                          'jsonAttr': 'bridgeFwdDelay',
-                          'required' : False,
-                          'example' : ['mstpctl-fdelay 15']},
-                    'mstpctl-maxhops' :
-                        { 'help' : 'bridge max hops',
-                          'validrange' : ['0', '255'],
-                          'default' : '20',
-                          'jsonAttr': 'maxHops',
-                          'required' : False,
-                          'example' : ['mstpctl-maxhops 15']},
-                    'mstpctl-txholdcount' :
-                        { 'help' : 'bridge transmit holdcount',
-                          'validrange' : ['0', '255'],
-                          'default' : '6',
-                          'jsonAttr': 'txHoldCounter',
-                          'required' : False,
-                          'example' : ['mstpctl-txholdcount 6']},
-                    'mstpctl-forcevers' :
-                        { 'help' : 'bridge force stp version',
-                          'validvals' : ['rstp', ],
-                          'default' : 'rstp',
-                          'required' : False,
-                          'jsonAttr': 'forceProtocolVersion',
-                          'example' : ['mstpctl-forcevers rstp']},
-                    'mstpctl-portpathcost' :
-                        { 'help' : 'bridge port path cost',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '65535'],
-                          'default' : '0',
-                          'jsonAttr' : 'adminExtPortCost',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portpathcost swp1=0 swp2=1',
-                                       'under the port (recommended): mstpctl-portpathcost 0']},
-                    'mstpctl-portp2p' :
-                        { 'help' : 'bridge port p2p detection mode',
-                          'default' : 'auto',
-                          'jsonAttr' : 'adminPointToPoint',
-                          'validvals' : ['<interface-yes-no-auto-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portp2p swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portp2p yes']},
-                    'mstpctl-portrestrrole' :
-                        { 'help' :
-                          'enable/disable port ability to take root role of the port',
-                          'default' : 'no',
-                          'jsonAttr' : 'restrictedRole',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portrestrrole swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portrestrrole yes']},
-                    'mstpctl-portrestrtcn' :
-                        { 'help' :
-                          'enable/disable port ability to propagate received topology change notification of the port',
-                          'default' : 'no',
-                          'jsonAttr' : 'restrictedTcn',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portrestrtcn yes']},
-                    'mstpctl-bpduguard' :
-                        { 'help' :
-                          'enable/disable bpduguard',
-                          'default' : 'no',
-                          'jsonAttr' : 'bpduGuardPort',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-bpduguard yes']},
-                    'mstpctl-treeportprio' :
-                        { 'help': 'Sets the <port>\'s priority MSTI instance. '
-                                  'The priority value must be a number between 0 and 240 and a multiple of 16.',
-                          'default' : '128',
-                          'validvals': ['<interface-range-list-multiple-of-16>'],
-                          'validrange' : ['0', '240'],
-                          'jsonAttr': 'treeportprio',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128',
-                                       'under the port (recommended): mstpctl-treeportprio 128']},
-                    'mstpctl-hello' :
-                        { 'help' : 'set hello time',
-                          'validrange' : ['0', '255'],
-                          'default' : '2',
-                          'required' : False,
-                          'jsonAttr': 'helloTime',
-                          'example' : ['mstpctl-hello 2']},
-                    'mstpctl-portnetwork' :
-                        { 'help' : 'enable/disable bridge assurance capability for a port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'default' : 'no',
-                          'jsonAttr' : 'networkPort',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portnetwork swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portnetwork yes']},
-                    'mstpctl-portadminedge' :
-                        { 'help' : 'enable/disable initial edge state of the port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'default' : 'no',
-                          'jsonAttr' : 'adminEdgePort',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portadminedge swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portadminedge yes']},
-                    'mstpctl-portautoedge' :
-                        { 'help' : 'enable/disable auto transition to/from edge state of the port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'default' : 'yes',
-                          'jsonAttr' : 'autoEdgePort',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portautoedge swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portautoedge yes']},
-                    'mstpctl-treeportcost' :
-                        { 'help' : 'port tree cost',
-                          'validrange' : ['0', '255'],
-                          'required' : False,
-                          'jsonAttr': 'extPortCost',
-                          },
-                    'mstpctl-portbpdufilter' :
-                        { 'help' : 'enable/disable bpdu filter on a port. ' +
-                                'syntax varies when defined under a bridge ' +
-                                'vs under a port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'jsonAttr' : 'bpduFilterPort',
-                          'default' : 'no',
-                          'required' : False,
-                          'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
-                                       'under a port: mstpctl-portbpdufilter yes']},
-                        }}
+    _modinfo = {
+        "mhelp": "mstp configuration module for bridges",
+        "attrs": {
+            "mstpctl-ports": {
+                "help": "mstp ports",
+                "compat": True,
+                "deprecated": True,
+                "new-attribute": "bridge-ports"
+            },
+            "mstpctl-stp": {
+                "help": "bridge stp yes/no",
+                "validvals": ["yes", "no", "on", "off"],
+                "compat": True,
+                "default": "no",
+                "deprecated": True,
+                "new-attribute": "bridge-stp"
+            },
+            "mstpctl-treeprio": {
+                "help": "tree priority",
+                "default": "32768",
+                "validvals": [
+                    "0", "4096", "8192", "12288", "16384",
+                    "20480", "24576", "28672", "32768",
+                    "36864", "40960", "45056", "49152",
+                    "53248", "57344", "61440"
+                ],
+                "required": False,
+                "example": ["mstpctl-treeprio 32768"]
+            },
+            "mstpctl-ageing": {
+                "help": "ageing time",
+                "validrange": ["0", "4096"],
+                "default": "300",
+                "required": False,
+                "jsonAttr": "ageingTime",
+                "example": ["mstpctl-ageing 300"]
+            },
+            "mstpctl-maxage": {
+                "help": "max message age",
+                "validrange": ["0", "255"],
+                "default": "20",
+                "jsonAttr": "bridgeMaxAge",
+                "required": False,
+                "example": ["mstpctl-maxage 20"]
+            },
+            "mstpctl-fdelay": {
+                "help": "set forwarding delay",
+                "validrange": ["0", "255"],
+                "default": "15",
+                "jsonAttr": "bridgeFwdDelay",
+                "required": False,
+                "example": ["mstpctl-fdelay 15"]
+            },
+            "mstpctl-maxhops": {
+                "help": "bridge max hops",
+                "validrange": ["0", "255"],
+                "default": "20",
+                "jsonAttr": "maxHops",
+                "required": False,
+                "example": ["mstpctl-maxhops 15"]
+            },
+            "mstpctl-txholdcount": {
+                "help": "bridge transmit holdcount",
+                "validrange": ["0", "255"],
+                "default": "6",
+                "jsonAttr": "txHoldCounter",
+                "required": False,
+                "example": ["mstpctl-txholdcount 6"]
+            },
+            "mstpctl-forcevers": {
+                "help": "bridge force stp version",
+                "validvals": ["rstp", ],
+                "default": "rstp",
+                "required": False,
+                "jsonAttr": "forceProtocolVersion",
+                "example": ["mstpctl-forcevers rstp"]
+            },
+            "mstpctl-portpathcost": {
+                "help": "bridge port path cost",
+                "validvals": ["<interface-range-list>"],
+                "validrange": ["0", "65535"],
+                "default": "0",
+                "jsonAttr": "adminExtPortCost",
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portpathcost swp1=0 swp2=1",
+                    "under the port (recommended): mstpctl-portpathcost 0"
+                ]
+            },
+            "mstpctl-portp2p": {
+                "help": "bridge port p2p detection mode",
+                "default": "auto",
+                "jsonAttr": "adminPointToPoint",
+                "validvals": ["<interface-yes-no-auto-list>"],
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portp2p swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-portp2p yes"
+                ]
+            },
+            "mstpctl-portrestrrole": {
+                "help":
+                    "enable/disable port ability to take root role of the port",
+                "default": "no",
+                "jsonAttr": "restrictedRole",
+                "validvals": ["<interface-yes-no-list>"],
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portrestrrole swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-portrestrrole yes"
+                ]
+            },
+            "mstpctl-portrestrtcn": {
+                "help":
+                    "enable/disable port ability to propagate received "
+                    "topology change notification of the port",
+                "default": "no",
+                "jsonAttr": "restrictedTcn",
+                "validvals": ["<interface-yes-no-list>"],
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-portrestrtcn yes"
+                ]
+            },
+            "mstpctl-bpduguard": {
+                "help":
+                    "enable/disable bpduguard",
+                "default": "no",
+                "jsonAttr": "bpduGuardPort",
+                "validvals": ["<interface-yes-no-list>"],
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-bpduguard swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-bpduguard yes"
+                ]
+            },
+            "mstpctl-treeportprio": {
+                "help": "Sets the <port>'s priority MSTI instance. "
+                        "The priority value must be a number between 0 and 240 "
+                        "and a multiple of 16.",
+                "default": "128",
+                "validvals": ["<interface-range-list-multiple-of-16>"],
+                "validrange": ["0", "240"],
+                "jsonAttr": "treeportprio",
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-treeportprio swp1=128 swp2=128",
+                    "under the port (recommended): mstpctl-treeportprio 128"
+                ]
+            },
+            "mstpctl-hello": {
+                "help": "set hello time",
+                "validrange": ["0", "255"],
+                "default": "2",
+                "required": False,
+                "jsonAttr": "helloTime",
+                "example": ["mstpctl-hello 2"]
+            },
+            "mstpctl-portnetwork": {
+                "help": "enable/disable bridge assurance capability for a port",
+                "validvals": ["<interface-yes-no-list>"],
+                "default": "no",
+                "jsonAttr": "networkPort",
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portnetwork swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-portnetwork yes"
+                ]
+            },
+            "mstpctl-portadminedge": {
+                "help": "enable/disable initial edge state of the port",
+                "validvals": ["<interface-yes-no-list>"],
+                "default": "no",
+                "jsonAttr": "adminEdgePort",
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portadminedge swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-portadminedge yes"
+                ]
+            },
+            "mstpctl-portautoedge": {
+                "help": "enable/disable auto transition to/from edge state of the port",
+                "validvals": ["<interface-yes-no-list>"],
+                "default": "yes",
+                "jsonAttr": "autoEdgePort",
+                "required": False,
+                "example": [
+                    "under the bridge: mstpctl-portautoedge swp1=yes swp2=no",
+                    "under the port (recommended): mstpctl-portautoedge yes"
+                ]
+            },
+            "mstpctl-treeportcost": {
+                "help": "port tree cost",
+                # "validrange": ["0", "255"],
+                "required": False,
+                "jsonAttr": "extPortCost",
+            },
+            "mstpctl-portbpdufilter": {
+                "help": "enable/disable bpdu filter on a port. syntax varies "
+                        "when defined under a bridge vs under a port",
+                "validvals": ["<interface-yes-no-list>"],
+                "jsonAttr": "bpduFilterPort",
+                "default": "no",
+                "required": False,
+                "example": [
+                    "under a bridge: mstpctl-portbpdufilter swp1=no swp2=no",
+                    "under a port: mstpctl-portbpdufilter yes"
+                ]
+            },
+        }
+    }
 
     # Maps mstp bridge attribute names to corresponding mstpctl commands
     # XXX: This can be encoded in the modules dict above
@@ -236,12 +281,11 @@ class mstpctl(moduleBase):
                  'mstpctl-portbpdufilter' : 'portbpdufilter'}
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
         if not os.path.exists('/sbin/mstpctl'):
             raise moduleNotSupported('module init failed: no /sbin/mstpctl found')
-        self.ipcmd = None
         self.name = self.__class__.__name__
-        self.brctlcmd = None
         self.mstpctlcmd = None
         self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
                              else False)
@@ -290,15 +334,9 @@ class mstpctl(moduleBase):
         return True
 
     def _is_bridge(self, ifaceobj):
-        if (ifaceobj.get_attr_value_first('mstpctl-ports') or
-                ifaceobj.get_attr_value_first('bridge-ports')):
-            return True
-        return False
-
-    def _is_bridge_port(self, ifaceobj):
-        if self.brctlcmd.is_bridge_port(ifaceobj.name):
-            return True
-        return False
+        return ifaceobj.link_kind & ifaceLinkKind.BRIDGE \
+               or ifaceobj.get_attr_value_first('mstpctl-ports') \
+               or ifaceobj.get_attr_value_first('bridge-ports')
 
     def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
         if not self._is_bridge(ifaceobj):
@@ -309,10 +347,10 @@ class mstpctl(moduleBase):
 
     def get_dependent_ifacenames_running(self, ifaceobj):
         self._init_command_handlers()
-        if (self.brctlcmd.bridge_exists(ifaceobj.name) and
+        if (self.cache.bridge_exists(ifaceobj.name) and
                 not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
             return None
-        return self.brctlcmd.get_bridge_ports(ifaceobj.name)
+        return self.cache.get_slaves(ifaceobj.name)
 
     def _get_bridge_port_attr_value(self, bridgename, portname, attr):
         json_attr = self.get_mod_subattr(attr, 'jsonAttr')
@@ -349,11 +387,11 @@ class mstpctl(moduleBase):
         runningbridgeports = []
         # Delete active ports not in the new port list
         if not ifupdownflags.flags.PERFMODE:
-            runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+            runningbridgeports = self.cache.get_slaves(ifaceobj.name)
             if runningbridgeports:
-                [self.ipcmd.link_set(bport, 'nomaster')
-                    for bport in runningbridgeports
-                        if not bridgeports or bport not in bridgeports]
+                for bport in runningbridgeports:
+                    if not bridgeports or bport not in bridgeports:
+                        self.netlink.link_set_nomaster(bport)
             else:
                 runningbridgeports = []
         if not bridgeports:
@@ -362,13 +400,13 @@ class mstpctl(moduleBase):
         for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
             try:
                 if (not ifupdownflags.flags.DRYRUN and
-                    not self.ipcmd.link_exists(bridgeport)):
+                    not self.cache.link_exists(bridgeport)):
                     self.log_warn('%s: bridge port %s does not exist'
                             %(ifaceobj.name, bridgeport))
                     err += 1
                     continue
-                self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
-                self.ipcmd.addr_flush(bridgeport)
+                self.netlink.link_set_master(bridgeport, ifaceobj.name)
+                self.netlink.addr_flush(bridgeport)
             except Exception, e:
                 self.log_error(str(e), ifaceobj)
 
@@ -410,7 +448,10 @@ class mstpctl(moduleBase):
                     self.logger.warn('%s' %str(e))
                     pass
 
-            if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
+            if self.cache.bridge_is_vlan_aware(ifaceobj.name):
+                return
+            bridgeports = self._get_bridge_port_list(ifaceobj)
+            if not bridgeports:
                 return
             # set bridge port attributes
             for attrname, dstattrname in self._port_attrs_map.items():
@@ -422,9 +463,8 @@ class mstpctl(moduleBase):
                     try:
                         jsonAttr =  self.get_mod_subattr(attrname, 'jsonAttr')
                         if default_val and jsonAttr:
-                            bridgeports = self._get_bridge_port_list(ifaceobj)
                             for port in bridgeports:
-                                if not self.brctlcmd.is_bridge_port(port):
+                                if not self.cache.link_is_bridge_port(port):
                                     continue
 
                                 bport_ifaceobjs = ifaceobj_getfunc(port)
@@ -502,7 +542,7 @@ class mstpctl(moduleBase):
                 return self.get_mod_subattr(attr,'default')
             return default_val
 
-    def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
+    def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None,
                                     bridgeifaceobj=None,
                                     stp_running_on=True,
                                     mstpd_running=True):
@@ -517,7 +557,6 @@ class mstpctl(moduleBase):
                              %(ifaceobj.name) +
                              ' (stp on bridge %s is not on yet)' %bridgename)
             return applied
-        bvlan_aware = self.ipcmd.bridge_is_vlan_aware(bridgename)
         if (not mstpd_running or
             not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
             not bvlan_aware):
@@ -602,15 +641,18 @@ class mstpctl(moduleBase):
         self.logger.info('%s: applying mstp configuration '
                           %ifaceobj.name + 'specific to ports')
         # Query running bridge ports. and only apply attributes on them
-        bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+        bridgeports = self.cache.get_slaves(ifaceobj.name)
         if not bridgeports:
            self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
            return
+
+        bvlan_aware = self.cache.bridge_is_vlan_aware(ifaceobj.name)
+
         for bport in bridgeports:
             self.logger.info('%s: processing mstp config for port %s'
                              %(ifaceobj.name, bport))
-            if not self.ipcmd.link_exists(bport):
-               continue
+            if not self.cache.link_exists(bport):
+                continue
             if not os.path.exists('/sys/class/net/%s/brport' %bport):
                 continue
             bportifaceobjlist = ifaceobj_getfunc(bport)
@@ -622,7 +664,7 @@ class mstpctl(moduleBase):
                     mstpctlFlags.PORT_PROCESSED):
                     continue
                 try:
-                    self._apply_bridge_port_settings(bportifaceobj,
+                    self._apply_bridge_port_settings(bportifaceobj, bvlan_aware,
                                             ifaceobj.name, ifaceobj)
                 except Exception, e:
                     pass
@@ -638,12 +680,17 @@ class mstpctl(moduleBase):
         return False
 
     def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        # Check if bridge port
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
-        if bridgename:
+
+        # bridge port specific:
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+            bridgename = self.cache.get_master(ifaceobj.name)
+
+            if not bridgename:
+                return
+            bvlan_aware = self.cache.bridge_is_vlan_aware(bridgename)
             mstpd_running = self.mstpd_running
-            stp_running_on = self._is_running_userspace_stp_state_on(bridgename)
-            applied = self._apply_bridge_port_settings(ifaceobj, bridgename,
+            stp_running_on = bool(self.cache.get_bridge_stp(bridgename))
+            applied = self._apply_bridge_port_settings(ifaceobj, bvlan_aware, bridgename,
                                                        None, stp_running_on,
                                                        mstpd_running)
             if applied:
@@ -651,7 +698,8 @@ class mstpctl(moduleBase):
                         ifaceobj.module_flags.setdefault(self.name,0) | \
                         mstpctlFlags.PORT_PROCESSED
             return
-        if not self._is_bridge(ifaceobj):
+
+        elif not self._is_bridge(ifaceobj):
             return
         # we are now here because the ifaceobj is a bridge
         stp = None
@@ -661,21 +709,17 @@ class mstpctl(moduleBase):
             if ifaceobj.get_attr_value_first('mstpctl-ports'):
                 # If bridge ports specified with mstpctl attr, create the
                 # bridge and also add its ports
-                self.ipcmd.batch_start()
-                if not ifupdownflags.flags.PERFMODE:
-                    if not self.ipcmd.link_exists(ifaceobj.name):
-                        self.ipcmd.link_create(ifaceobj.name, 'bridge')
-                else:
-                    self.ipcmd.link_create(ifaceobj.name, 'bridge')
+                if not self.cache.link_exists(ifaceobj.name):
+                    self.netlink.link_add_bridge(ifaceobj.name)
+
                 try:
                     self._add_ports(ifaceobj)
                 except Exception, e:
                     porterr = True
                     porterrstr = str(e)
                     pass
-                finally:
-                    self.ipcmd.batch_commit()
-                running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+
+                running_ports = self.cache.get_slaves(ifaceobj.name)
                 if running_ports:
                     # disable ipv6 for ports that were added to bridge
                     self._ports_enable_disable_ipv6(running_ports, '1')
@@ -683,14 +727,16 @@ class mstpctl(moduleBase):
             stp = ifaceobj.get_attr_value_first('mstpctl-stp')
             if stp:
                self.set_iface_attr(ifaceobj, 'mstpctl-stp',
-                                   self.brctlcmd.bridge_set_stp)
+                                   self.iproute2.bridge_set_stp)
+               stp = utils.get_boolean_from_string(stp)
             else:
-               stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
-            if (self.mstpd_running and
-                    (stp == 'yes' or stp == 'on')):
+               stp = self.cache.get_bridge_stp(ifaceobj.name)
+            if self.mstpd_running and stp:
+                self.mstpctlcmd.batch_start()
                 self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc)
                 self._apply_bridge_port_settings_all(ifaceobj,
                             ifaceobj_getfunc=ifaceobj_getfunc)
+                self.mstpctlcmd.batch_commit()
         except Exception, e:
             self.log_error(str(e), ifaceobj)
         if porterr:
@@ -703,10 +749,10 @@ class mstpctl(moduleBase):
             if ifaceobj.get_attr_value_first('mstpctl-ports'):
                 # If bridge ports specified with mstpctl attr, delete the
                 # bridge
-                ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+                ports = self.cache.get_slaves(ifaceobj.name)
                 if ports:
                     self._ports_enable_disable_ipv6(ports, '0')
-                self.brctlcmd.delete_bridge(ifaceobj.name)
+                self.netlink.link_del(ifaceobj.name)
         except Exception, e:
             self.log_error(str(e), ifaceobj)
 
@@ -729,7 +775,7 @@ class mstpctl(moduleBase):
                 and attrname != 'mstpctl-maxhops'):
                 bridgeattrdict[attrname] = [v]
 
-        ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name)
+        ports = self.cache.get_slaves(ifaceobjrunning.name)
         # Do this only for vlan-UNAWARE-bridge
         if ports and not bridge_vlan_aware:
             portconfig = {'mstpctl-portautoedge' : '',
@@ -778,16 +824,12 @@ class mstpctl(moduleBase):
                policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
         return utils.get_boolean_from_string(stp)
 
-    def _get_running_stp(self, ifaceobj):
-        stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
-        return utils.get_boolean_from_string(stp)
-
     def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
                             ifaceobj_getfunc=None):
         # list of attributes that are not supported currently
         blacklistedattrs = ['mstpctl-portpathcost',
                 'mstpctl-treeportprio', 'mstpctl-treeportcost']
-        if not self.brctlcmd.bridge_exists(ifaceobj.name):
+        if not self.cache.bridge_exists(ifaceobj.name):
             self.logger.debug('bridge %s does not exist' %ifaceobj.name)
             return
         ifaceattrs = self.dict_key_subset(ifaceobj.config,
@@ -803,8 +845,8 @@ class mstpctl(moduleBase):
         if not runningattrs:
             runningattrs = {}
         config_stp = self._get_config_stp(ifaceobj)
-        running_stp = self._get_running_stp(ifaceobj)
-        running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+        running_stp = self.cache.get_bridge_stp(ifaceobj.name)
+        running_port_list = self.cache.get_slaves(ifaceobj.name)
         for k in ifaceattrs:
             # for all mstpctl options
             if k in blacklistedattrs:
@@ -875,7 +917,7 @@ class mstpctl(moduleBase):
                 # contain more than one valid values
                 stp_on_vals = ['on', 'yes']
                 stp_off_vals = ['off']
-                rv = self.brctlcmd.bridge_get_stp(ifaceobj.name)
+                rv = self.sysfs.bridge_get_stp(ifaceobj.name)
                 if ((v in stp_on_vals and rv in stp_on_vals) or
                     (v in stp_off_vals and rv in stp_off_vals)):
                     ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
@@ -945,7 +987,7 @@ class mstpctl(moduleBase):
                     self.set_default_mstp_vxlan_bridge_config and
                     (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
                         config_stp = self._get_config_stp(bifaceobj)
-                        running_stp = self._get_running_stp(bifaceobj)
+                        running_stp = self.cache.get_bridge_stp(bifaceobj.name)
                         if (not config_stp or not running_stp):
                             continue
                         for attr in (
@@ -981,17 +1023,17 @@ class mstpctl(moduleBase):
 
 
     def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             #self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
             ifaceobjcurr.status = ifaceStatus.NOTFOUND
             return
         # Check if this is a bridge port
-        if not self._is_bridge_port(ifaceobj):
+        if not self.cache.link_is_bridge_port(ifaceobj.name):
             # mark all the bridge attributes as error
             ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
                             self._port_attrs_map.keys(), 0)
             return
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+        bridgename = self.cache.get_master(ifaceobj.name)
         # list of attributes that are not supported currently
         blacklistedattrs = ['mstpctl-portpathcost',
                 'mstpctl-treeportprio', 'mstpctl-treeportcost']
@@ -1038,13 +1080,12 @@ class mstpctl(moduleBase):
             ifaceobjrunning.update_config(attr, v)
 
     def _query_running_bridge_port(self, ifaceobjrunning):
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(
-                                ifaceobjrunning.name)
+        bridgename = self.cache.get_master(ifaceobjrunning.name)
         if not bridgename:
             self.logger.warn('%s: unable to determine bridgename'
                              %ifaceobjrunning.name)
             return
-        if self.brctlcmd.bridge_get_stp(bridgename) == 'no':
+        if self.sysfs.bridge_get_stp(bridgename) == 'no':
            # This bridge does not run stp, return
            return
         # if userspace stp not set, return
@@ -1103,7 +1144,7 @@ class mstpctl(moduleBase):
         #    portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
 
     def _query_running_bridge(self, ifaceobjrunning):
-        if self.brctlcmd.bridge_get_stp(ifaceobjrunning.name) == 'no':
+        if self.sysfs.bridge_get_stp(ifaceobjrunning.name) == 'no':
            # This bridge does not run stp, return
            return
         # if userspace stp not set, return
@@ -1120,9 +1161,9 @@ class mstpctl(moduleBase):
                                            bridge_vlan_aware))
 
     def _query_running(self, ifaceobjrunning, **extra_args):
-        if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+        if self.cache.bridge_exists(ifaceobjrunning.name):
             self._query_running_bridge(ifaceobjrunning)
-        elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+        elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
             self._query_running_bridge_port(ifaceobjrunning)
 
     def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
@@ -1235,21 +1276,19 @@ class mstpctl(moduleBase):
                 if config:
                     ifaceobj.replace_config(attr, config)
 
-
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running,
-               'query' : _query}
+    _run_ops = {
+        "pre-up": _up,
+        "post-down": _down,
+        "query-checkcurr": _query_check,
+        "query-running": _query_running,
+        "query": _query
+    }
 
     def get_ops(self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
     def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = self.brctlcmd = LinkUtils()
         if not self.mstpctlcmd:
             self.mstpctlcmd = mstpctlutil()
 
index 014600bdea22b9fdb47d9c7c6a4c00193f3c089e..628433c877668f617e91e45de701230c12d6cf39 100644 (file)
@@ -4,28 +4,28 @@ import os
 import hashlib
 
 try:
+    from ifupdown2.lib.addon import Addon
     import ifupdown2.ifupdown.statemanager as statemanager
 
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     from ifupdown2.ifupdown.exceptions import moduleNotSupported
 except ImportError:
+    from lib.addon import Addon
     import ifupdown.statemanager as statemanager
 
     from ifupdown.iface import *
     from ifupdown.utils import utils
 
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     from ifupdown.exceptions import moduleNotSupported
 
 
-class ppp(moduleBase):
+class ppp(Addon, moduleBase):
     """
     ifupdown2 addon module to configure ppp
     """
@@ -48,10 +48,10 @@ class ppp(moduleBase):
     }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
         if not os.path.exists('/usr/bin/pon'):
             raise moduleNotSupported('module init failed: no /usr/bin/pon found')
-        self.ipcmd = None
 
     @staticmethod
     def _is_my_interface(ifaceobj):
@@ -81,7 +81,7 @@ class ppp(moduleBase):
             # Always save the current config files hash
             ifaceobj.update_config('provider_file', config)
 
-            if not self.ipcmd.link_exists(ifaceobj.name):
+            if not self.cache.link_exists(ifaceobj.name):
                 try:
                     # This fails if not running
                     utils.exec_user_command('/bin/ps ax | /bin/grep pppd | /bin/grep -v grep | /bin/grep ' + provider)
@@ -97,7 +97,7 @@ class ppp(moduleBase):
                 utils.exec_commandl(['/usr/bin/poff', old_provider], stdout=None, stderr=None)
                 utils.exec_commandl(['/usr/bin/pon', provider], stdout=None, stderr=None)
 
-        except Exception, e:
+        except Exception as e:
             self.log_warn(str(e))
 
     def _down(self, ifaceobj):
@@ -109,7 +109,7 @@ class ppp(moduleBase):
             # This fails if not running
             utils.exec_user_command('/bin/ps ax | /bin/grep pppd | /bin/grep -v grep | /bin/grep ' + provider)
             utils.exec_commandl(['/usr/bin/poff', provider], stdout=None, stderr=None)
-        except Exception, e:
+        except Exception as e:
             self.log_warn(str(e))
 
     def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
@@ -124,12 +124,12 @@ class ppp(moduleBase):
         return None
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
         ifaceobjcurr.status = ifaceStatus.SUCCESS
 
     def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+        if not self.cache.link_exists(ifaceobjrunning.name):
             return
 
     # Operations supported by this addon (yet).
@@ -143,10 +143,6 @@ class ppp(moduleBase):
     def get_ops(self):
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         op_handler = self._run_ops.get(operation)
 
@@ -156,7 +152,6 @@ class ppp(moduleBase):
         if operation != 'query-running' and not self._is_my_interface(ifaceobj):
             return
 
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 611a4715802d807d0283f35f621b11bbe7c07f65..63f8d985dc566b7ef8babc39ee40691ca5645161 100644 (file)
@@ -4,18 +4,20 @@
 #  --  Mon 10 Oct 2016 10:53:13 PM CEST
 #
 try:
+    from ifupdown2.lib.addon import Addon
+    from ifupdown2.nlmanager.nlmanager import Link
+
     from ifupdown2.ifupdown.iface import *
-    from ifupdown2.ifupdown.netlink import netlink
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 except ImportError:
+    from lib.addon import Addon
+    from nlmanager.nlmanager import Link
+
     from ifupdown.iface import *
-    from ifupdown.netlink import netlink
 
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.ifupdownflags as ifupdownflags
@@ -24,38 +26,38 @@ except ImportError:
 #
 # TODO: Add checks for ipip tunnels.
 #
-class tunnel(moduleBase):
+class tunnel(Addon, moduleBase):
     """
     ifupdown2 addon module to configure tunnels
     """
     _modinfo = {
         'mhelp': 'create/configure GRE/IPIP/SIT and GRETAP tunnel interfaces',
         'attrs': {
-            'mode': {
+            'tunnel-mode': {
                 'help': 'type of tunnel as in \'ip link\' command.',
                 'validvals': ['gre', 'gretap', 'ipip', 'sit', 'vti', 'ip6gre', 'ipip6', 'ip6ip6', 'vti6'],
                 'required': True,
                 'example': ['mode gre']
             },
-            'local': {
+            'tunnel-local': {
                 'help': 'IP of local tunnel endpoint',
                 'validvals': ['<ipv4>', '<ipv6>'],
                 'required': True,
                 'example': ['local 192.2.0.42']
             },
-            'endpoint': {
+            'tunnel-endpoint': {
                 'help': 'IP of remote tunnel endpoint',
                 'validvals': ['<ipv4>', '<ipv6>'],
                 'required': True,
                 'example': ['endpoint 192.2.0.23']
             },
-            'ttl': {
+            'tunnel-ttl': {
                 'help': 'TTL for tunnel packets',
                 'validvals': ['<number>'],
                 'required': False,
                 'example': ['ttl 64']
             },
-            'tunnel-physdev': {
+            'tunnel-dev': {
                 'help': 'Physical underlay device to use for tunnel packets',
                 'validvals': ['<interface>'],
                 'required': False,
@@ -66,28 +68,72 @@ class tunnel(moduleBase):
 
     def __init__(self, *args, **kargs):
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
+        Addon.__init__(self)
 
     @staticmethod
     def _is_my_interface(ifaceobj):
-        return ifaceobj.addr_method == "tunnel" and ifaceobj.get_attr_value_first('mode')
+        return ifaceobj.get_attr_value_first("tunnel-mode")
 
-    def _has_config_changed(self, attrs_present, attrs_configured):
+    @staticmethod
+    def _has_config_changed(attrs_present, attrs_configured):
         for key, value in attrs_configured.iteritems():
             if attrs_present.get(key) != value:
                 return True
         return False
 
+    def __get_info_data_gre_tunnel(self, info_data):
+        tunnel_link_ifindex = info_data.get(Link.IFLA_GRE_LINK)
+
+        return {
+            "tunnel-endpoint": str(info_data.get(Link.IFLA_GRE_REMOTE)),
+            "tunnel-local": str(info_data.get(Link.IFLA_GRE_LOCAL)),
+            "tunnel-ttl": str(info_data.get(Link.IFLA_GRE_TTL)),
+            "tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
+        }
+
+    def __get_info_data_iptun_tunnel(self, info_data):
+        tunnel_link_ifindex = info_data.get(Link.IFLA_IPTUN_LINK)
+
+        return {
+            "tunnel-endpoint": str(info_data.get(Link.IFLA_IPTUN_REMOTE)),
+            "tunnel-local": str(info_data.get(Link.IFLA_IPTUN_LOCAL)),
+            "tunnel-ttl": str(info_data.get(Link.IFLA_IPTUN_TTL)),
+            "tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
+        }
+
+    def __get_info_data_vti_tunnel(self, info_data):
+        tunnel_link_ifindex = info_data.get(Link.IFLA_VTI_LINK)
+
+        return {
+            "tunnel-endpoint": str(info_data.get(Link.IFLA_VTI_REMOTE)),
+            "tunnel-local": str(info_data.get(Link.IFLA_VTI_LOCAL)),
+            "tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
+        }
+
+    def get_linkinfo_attrs(self, ifname, link_kind):
+        return {
+            "gre": self.__get_info_data_gre_tunnel,
+            "gretap": self.__get_info_data_gre_tunnel,
+            "ip6gre": self.__get_info_data_gre_tunnel,
+            "ip6gretap": self.__get_info_data_gre_tunnel,
+            "ip6erspan": self.__get_info_data_gre_tunnel,
+            "ipip": self.__get_info_data_iptun_tunnel,
+            "sit": self.__get_info_data_iptun_tunnel,
+            "ip6tnl": self.__get_info_data_iptun_tunnel,
+            "vti": self.__get_info_data_vti_tunnel,
+            "vti6": self.__get_info_data_vti_tunnel,
+        }.get(link_kind, lambda x: {})(self.cache.get_link_info_data(ifname))
+
     def _up(self, ifaceobj):
         attr_map = {
             # attr_name -> ip route param name
-            'local': 'local',
-            'endpoint': 'remote',
-            'ttl': 'ttl',
-            'tunnel-physdev': 'dev',
+            'tunnel-local': 'local',
+            'tunnel-endpoint': 'remote',
+            'tunnel-ttl': 'ttl',
+            'tunnel-dev': 'dev',
         }
 
-        mode = ifaceobj.get_attr_value_first('mode')
+        mode = ifaceobj.get_attr_value_first('tunnel-mode')
         attrs = {}
         attrs_mapped = {}
 
@@ -95,33 +141,33 @@ class tunnel(moduleBase):
         # to attribute names expected by iproute
         for attr, iproute_attr in attr_map.items():
             attr_val = ifaceobj.get_attr_value_first(attr)
-            if attr_val != None:
+            if attr_val is not None:
                 attrs_mapped[iproute_attr] = attr_val
                 attrs[attr] = attr_val
 
         # Create the tunnel if it doesn't exist yet...
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            self.ipcmd.tunnel_create(ifaceobj.name, mode, attrs_mapped)
+        if not self.cache.link_exists(ifaceobj.name):
+            self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped)
             return
 
         # If it's present, check if there were changes
-        current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
-        current_mode = self.ipcmd.link_cache_get([ifaceobj.name, 'kind'])
+        current_mode = self.cache.get_link_kind(ifaceobj.name)
+        current_attrs = self.get_linkinfo_attrs(ifaceobj.name, current_mode)
 
         try:
             if current_attrs and current_mode != mode or self._has_config_changed(current_attrs, attrs):
                 # Mode and some other changes are not possible without recreating the interface,
                 # so just recreate it IFF there have been changes.
-                self.ipcmd.link_delete(ifaceobj.name)
-                self.ipcmd.tunnel_create(ifaceobj.name, mode, attrs_mapped)
+                self.netlink.link_del(ifaceobj.name)
+                self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped)
         except Exception, e:
             self.log_warn(str(e))
 
     def _down(self, ifaceobj):
-        if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
+        if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
             return
         try:
-            self.ipcmd.link_delete(ifaceobj.name)
+            self.netlink.link_del(ifaceobj.name)
         except Exception, e:
             self.log_warn(str(e))
 
@@ -129,7 +175,7 @@ class tunnel(moduleBase):
         if not self._is_my_interface(ifaceobj):
             return None
 
-        device = ifaceobj.get_attr_value_first('tunnel-physdev')
+        device = ifaceobj.get_attr_value_first('tunnel-dev')
         if device:
             return [device]
 
@@ -147,19 +193,21 @@ class tunnel(moduleBase):
     def _query_check(self, ifaceobj, ifaceobjcurr):
         ifname = ifaceobj.name
 
-        if not self.ipcmd.link_exists(ifname):
+        if not self.cache.link_exists(ifname):
             return
 
-        tunattrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
+        link_kind = self.cache.get_link_kind(ifname)
+        tunattrs = self.get_linkinfo_attrs(ifaceobj.name, link_kind)
+
         if not tunattrs:
             ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1)
             return
 
-        tunattrs["mode"] = self.ipcmd.link_get_kind(ifname)
+        tunattrs["tunnel-mode"] = link_kind
 
-        user_config_mode = ifaceobj.get_attr_value_first("mode")
+        user_config_mode = ifaceobj.get_attr_value_first("tunnel-mode")
         if user_config_mode in ('ipip6', 'ip6ip6'):
-            ifaceobj.replace_config("mode", "ip6tnl")
+            ifaceobj.replace_config("tunnel-mode", "ip6tnl")
 
         for attr in self.get_mod_attrs():
             if not ifaceobj.get_attr_value_first(attr):
@@ -182,10 +230,6 @@ class tunnel(moduleBase):
     def get_ops(self):
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
@@ -194,7 +238,6 @@ class tunnel(moduleBase):
         if operation != 'query-running' and not self._is_my_interface(ifaceobj):
             return
 
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index caa1941471cfcad3f81fd23d2f1460b57161494c..86928fd28d9b5f253d0915a4622ef5c50d55604a 100644 (file)
@@ -5,52 +5,51 @@
 #
 
 try:
-    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
-
+    from ifupdown2.lib.addon import Addon
     from ifupdown2.ifupdown.iface import *
-    from ifupdown2.ifupdown.netlink import netlink
-
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.nlmanager.nlmanager import Link
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 except ImportError:
-    import ifupdown.ifupdownflags as ifupdownflags
-
+    from lib.addon import Addon
     from ifupdown.iface import *
-    from ifupdown.netlink import netlink
-
-    from ifupdownaddons.LinkUtils import LinkUtils
+    from nlmanager.nlmanager import Link
     from ifupdownaddons.modulebase import moduleBase
+    import ifupdown.ifupdownflags as ifupdownflags
 
 
-
-class vlan(moduleBase):
+class vlan(Addon, moduleBase):
     """  ifupdown2 addon module to configure vlans """
 
-    _modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
-                        'This module understands vlan interfaces with dot ' +
-                        'notations. eg swp1.100. Vlan interfaces with any ' +
-                        'other names need to have raw device and vlan id ' +
-                        'attributes',
-                'attrs' : {
-                        'vlan-raw-device' :
-                            {'help' : 'vlan raw device',
-                             'validvals': ['<interface>']},
-                        'vlan-id' :
-                            {'help' : 'vlan id',
-                             'validrange' : ['0', '4096']},
-                        'vlan-protocol' :
-                            {'help' : 'vlan protocol',
-                             'default' : '802.1q',
-                             'validvals': ['802.1q', '802.1ad'],
-                             'example' : ['vlan-protocol 802.1q']},
-               }}
-
+    _modinfo = {
+        "mhelp": "vlan module configures vlan interfaces. "
+                 "This module understands vlan interfaces with dot "
+                 "notations. eg swp1.100. Vlan interfaces with any "
+                 "other names need to have raw device and vlan id attributes",
+        "attrs": {
+            "vlan-raw-device": {
+                "help": "vlan raw device",
+                "validvals": ["<interface>"]
+            },
+            "vlan-id": {
+                "help": "vlan id",
+                "validrange": ["0", "4096"]
+            },
+            "vlan-protocol": {
+                "help": "vlan protocol",
+                "default": "802.1q",
+                "validvals": ["802.1q", "802.1ad"],
+                "example": ["vlan-protocol 802.1q"]
+            },
+        }
+    }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
 
-    def _is_vlan_device(self, ifaceobj):
+    @staticmethod
+    def _is_vlan_device(ifaceobj):
         vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
         if vlan_raw_device:
             return True
@@ -58,10 +57,12 @@ class vlan(moduleBase):
             return True
         return False
 
-    def _is_vlan_by_name(self, ifacename):
+    @staticmethod
+    def _is_vlan_by_name(ifacename):
         return '.' in ifacename
 
-    def _get_vlan_raw_device_from_ifacename(self, ifacename):
+    @staticmethod
+    def _get_vlan_raw_device_from_ifacename(ifacename):
         """ Returns vlan raw device from ifname
         Example:
             Returns eth0 for ifname eth0.100
@@ -88,22 +89,21 @@ class vlan(moduleBase):
         ifaceobj.link_kind |= ifaceLinkKind.VLAN
         return [self._get_vlan_raw_device(ifaceobj)]
 
-    def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
-                            add=True):
+    def _bridge_vid_add_del(self, bridgename, vlanid, add=True):
         """ If the lower device is a vlan aware bridge, add/del the vlanid
         to the bridge """
-        if self.ipcmd.bridge_is_vlan_aware(bridgename):
-           if add:
-               netlink.link_add_bridge_vlan(bridgename, vlanid)
-           else:
-               netlink.link_del_bridge_vlan(bridgename, vlanid)
+        if self.cache.bridge_is_vlan_aware(bridgename):
+            if add:
+                self.netlink.link_add_bridge_vlan(bridgename, vlanid)
+            else:
+                self.netlink.link_del_bridge_vlan(bridgename, vlanid)
 
-    def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
+    def _bridge_vid_check(self, ifaceobjcurr, bridgename, vlanid):
         """ If the lower device is a vlan aware bridge, check if the vlanid
         is configured on the bridge """
-        if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+        if not self.cache.bridge_is_vlan_aware(bridgename):
             return
-        vids = self.ipcmd.bridge_vlan_get_vids(bridgename)
+        _, vids = self.cache.get_pvid_and_vids(bridgename)
         if not vids or vlanid not in vids:
             ifaceobjcurr.status = ifaceStatus.ERROR
             ifaceobjcurr.status_str = 'bridge vid error'
@@ -116,8 +116,15 @@ class vlan(moduleBase):
         if not vlanrawdevice:
             raise Exception('could not determine vlan raw device')
 
-        vlan_protocol           = ifaceobj.get_attr_value_first('vlan-protocol')
-        cached_vlan_protocol    = self.ipcmd.get_vlan_protocol(ifaceobj.name)
+        ifname = ifaceobj.name
+
+        if ifupdownflags.flags.PERFMODE:
+            cached_vlan_ifla_info_data = {}
+        else:
+            cached_vlan_ifla_info_data = self.cache.get_link_info_data(ifname)
+
+        vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
+        cached_vlan_protocol = cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_PROTOCOL)
 
         if not vlan_protocol:
             vlan_protocol = self.get_attr_default_value('vlan-protocol')
@@ -130,25 +137,28 @@ class vlan(moduleBase):
 
         if not ifupdownflags.flags.PERFMODE:
 
-            vlan_exists = self.ipcmd.link_exists(ifaceobj.name)
+            vlan_exists = self.cache.link_exists(ifaceobj.name)
 
             if vlan_exists:
                 user_vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
-                cached_vlan_raw_device = self.ipcmd.cache_get('link', [ifaceobj.name, 'link'])
+                cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
 
                 if cached_vlan_raw_device and user_vlan_raw_device and cached_vlan_raw_device != user_vlan_raw_device:
                     raise Exception('%s: cannot change vlan-raw-device from %s to %s: operation not supported. '
                                     'Please delete the device with \'ifdown %s\' and recreate it to apply the change.'
                                     % (ifaceobj.name, cached_vlan_raw_device, user_vlan_raw_device, ifaceobj.name))
 
-            if not self.ipcmd.link_exists(vlanrawdevice):
-                raise Exception('rawdevice %s not present' %vlanrawdevice)
+            if not self.cache.link_exists(vlanrawdevice):
+                if ifupdownflags.flags.DRYRUN:
+                    return
+                else:
+                    raise Exception('rawdevice %s not present' % vlanrawdevice)
             if vlan_exists:
-                self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
+                self._bridge_vid_add_del(vlanrawdevice, vlanid)
                 return
 
-        netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
-        self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
+        self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
+        self._bridge_vid_add_del(vlanrawdevice, vlanid)
 
     def _down(self, ifaceobj):
         vlanid = self._get_vlan_id(ifaceobj)
@@ -157,72 +167,103 @@ class vlan(moduleBase):
         vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
         if not vlanrawdevice:
             raise Exception('could not determine vlan raw device')
-        if (not ifupdownflags.flags.PERFMODE and
-            not self.ipcmd.link_exists(ifaceobj.name)):
-           return
+        if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
+            return
         try:
-            netlink.link_del(ifaceobj.name)
-            self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
+            self.netlink.link_del(ifaceobj.name)
+            self._bridge_vid_add_del(vlanrawdevice, vlanid, add=False)
         except Exception, e:
             self.log_warn(str(e))
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-           return
-        if not '.' in ifaceobj.name:
+        if not self.cache.link_exists(ifaceobj.name):
+            return
+        if '.' not in ifaceobj.name:
             # if vlan name is not in the dot format, check its running state
-            (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
-            if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
-                ifaceobjcurr.update_config_with_status('vlan-raw-device',
-                        vlanrawdev, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vlan-raw-device',
-                        vlanrawdev, 0)
+
+            ifname = ifaceobj.name
+            cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
+
+            #
+            # vlan-raw-device
+            #
+            ifaceobjcurr.update_config_with_status(
+                'vlan-raw-device',
+                cached_vlan_raw_device,
+                cached_vlan_raw_device != ifaceobj.get_attr_value_first('vlan-raw-device')
+            )
+
+            cached_vlan_info_data = self.cache.get_link_info_data(ifname)
+
+            #
+            # vlan-id
+            #
             vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
             if not vlanid_config:
                 vlanid_config = str(self._get_vlan_id(ifaceobj))
-            if vlanid != vlanid_config:
-                ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
+
+            cached_vlan_id = cached_vlan_info_data.get(Link.IFLA_VLAN_ID)
+            cached_vlan_id_str = str(cached_vlan_id)
+            ifaceobjcurr.update_config_with_status('vlan-id', cached_vlan_id_str, vlanid_config != cached_vlan_id_str)
+
+            #
+            # vlan-protocol
+            #
             protocol_config = ifaceobj.get_attr_value_first('vlan-protocol')
             if protocol_config:
-                if protocol_config.upper() != protocol.upper():
-                    ifaceobjcurr.update_config_with_status('vlan-protocol',
-                                                           protocol, 1)
+
+                cached_vlan_protocol = cached_vlan_info_data.get(Link.IFLA_VLAN_PROTOCOL)
+
+                if protocol_config.upper() != cached_vlan_protocol.upper():
+                    ifaceobjcurr.update_config_with_status(
+                        'vlan-protocol',
+                        cached_vlan_protocol,
+                        1
+                    )
                 else:
-                    ifaceobjcurr.update_config_with_status('vlan-protocol',
-                                                           protocol, 0)
-            self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, int(vlanid))
+                    ifaceobjcurr.update_config_with_status(
+                        'vlan-protocol',
+                        protocol_config,
+                        0
+                    )
+
+            self._bridge_vid_check(ifaceobjcurr, cached_vlan_raw_device, cached_vlan_id)
 
     def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+        ifname = ifaceobjrunning.name
+
+        if not self.cache.link_exists(ifname):
             return
-        (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
-        if not vlanid:
+
+        if not self.cache.get_link_kind(ifname) == 'vlan':
             return
+
         # If vlan name is not in the dot format, get the
         # vlan dev and vlan id
-        if not '.' in ifaceobjrunning.name:
-            ifaceobjrunning.update_config_dict({k: [v] for k, v in
-                                                {'vlan-raw-device' : vlanrawdev,
-                                                 'vlan-id' : vlanid,
-                                                 'vlan-protocol' : protocol}.items()
-                                                if v})
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
+        if '.' in ifname:
+            return
+
+        cached_vlan_info_data = self.cache.get_link_info_data(ifname)
+
+        for attr_name, nl_attr in (
+                ('vlan-id', Link.IFLA_VLAN_ID),
+                ('vlan-protocol', Link.IFLA_VLAN_PROTOCOL)
+        ):
+            ifaceobjrunning.update_config(attr_name, str(cached_vlan_info_data.get(nl_attr)))
+
+        ifaceobjrunning.update_config('vlan-raw-device', self.cache.get_lower_device_ifname(ifname))
+
+    _run_ops = {
+        "pre-up": _up,
+        "post-down": _down,
+        "query-checkcurr": _query_check,
+        "query-running": _query_running
+    }
 
     def get_ops(self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         """ run vlan configuration on the interface object passed as argument
 
@@ -247,7 +288,6 @@ class vlan(moduleBase):
         if (operation != 'query-running' and
                 not self._is_vlan_device(ifaceobj)):
             return
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 5d7f562e2e33534a851d9997e4f575506cb1304c..c08088b5eb569cce876c414f19a325fc4d069312 100644 (file)
@@ -13,69 +13,86 @@ import signal
 from sets import Set
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     import ifupdown2.ifupdown.policymanager as policymanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
 
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.nlmanager.nlmanager import Link
 
     from ifupdown2.ifupdownaddons.dhclient import dhclient
     from ifupdown2.ifupdownaddons.utilsbase import *
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 except ImportError:
+    from lib.addon import Addon
+
     import ifupdown.policymanager as policymanager
     import ifupdown.ifupdownflags as ifupdownflags
     from ifupdown.statemanager import statemanager_api as statemanager
 
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
+
+    from nlmanager.nlmanager import Link
 
     from ifupdownaddons.dhclient import dhclient
     from ifupdownaddons.utilsbase import *
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
 
 class vrfPrivFlags:
     PROCESSED = 0x1
 
-class vrf(moduleBase):
+
+class vrf(Addon, moduleBase):
     """  ifupdown2 addon module to configure vrfs """
-    _modinfo = { 'mhelp' : 'vrf configuration module',
-                    'attrs' : {
-                    'vrf-table':
-                         {'help' : 'vrf device routing table id. key to ' +
-                                   'creating a vrf device. ' +
-                                   'Table id is either \'auto\' or '+
-                                   '\'valid routing table id\'',
-                          'validvals': ['auto', '<number>'],
-                          'example': ['vrf-table auto', 'vrf-table 1001']},
-                    'vrf':
-                         {'help' : 'vrf the interface is part of.',
-                          'validvals': ['<text>'],
-                          'example': ['vrf blue']}}}
-
-    iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
-    iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
-                           '# It contains the vrf name to table mapping.\n' + \
-                           '# Reserved table range %s %s\n'
+
+    _modinfo = {
+        "mhelp": "vrf configuration module",
+        "attrs": {
+            "vrf-table": {
+                "help": "vrf device routing table id. key to creating a vrf device. "
+                        "Table id is either 'auto' or 'valid routing table id'",
+                "validvals": ["auto", "<number>"],
+                "example": ["vrf-table auto", "vrf-table 1001"]
+            },
+            "vrf": {
+                "help": "vrf the interface is part of",
+                "validvals": ["<text>"],
+                "example": ["vrf blue"]
+            }
+        }
+    }
+
+    iproute2_vrf_filename = "/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf"
+    iproute2_vrf_filehdr = "# This file is autogenerated by ifupdown2.\n" \
+                           "# It contains the vrf name to table mapping.\n" \
+                           "# Reserved table range %s %s\n"
     VRF_TABLE_START = 1001
     VRF_TABLE_END = 5000
 
-    system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
-                                 '253' : 'default', '0' : 'unspec'}
+    system_reserved_rt_tables = {
+        "255": "local",
+        "254": "main",
+        "253": "default",
+        "0": "unspec"
+    }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self.bondcmd = None
         self.dhclientcmd = None
         self.name = self.__class__.__name__
-        self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
+        self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(
+            module_name=self.__class__.__name__,
+            attr="vrf-mgmt-devname"
+        )
+
+        self.at_exit = False
 
         self.user_reserved_vrf_table = []
 
@@ -130,7 +147,6 @@ class vrf(moduleBase):
             self.l3mdev_checked = True
         self._iproute2_vrf_map_initialized = False
         self.iproute2_vrf_map = {}
-        self.iproute2_vrf_map_fd = None
         self.iproute2_vrf_map_sync_to_disk = False
 
         self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
@@ -196,7 +212,7 @@ class vrf(moduleBase):
         iproute2_vrf_map_force_rewrite = False
         # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
         if os.path.exists(self.iproute2_vrf_filename):
-            with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
+            with open(self.iproute2_vrf_filename, "r+" if writetodisk else "r") as vrf_map_fd:
                 lines = vrf_map_fd.readlines()
                 for l in lines:
                     l = l.strip()
@@ -215,19 +231,12 @@ class vrf(moduleBase):
                         self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e)))
                         pass
 
-        vrfs = self.ipcmd.link_get_vrfs()
-        running_vrf_map = {}
-        if vrfs:
-            for v, lattrs in vrfs.iteritems():
-                table = lattrs.get('table', None)
-                if table:
-                   running_vrf_map[int(table)] = v
+        running_vrf_map = self.cache.get_vrf_table_map()
 
         if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
             self.iproute2_vrf_map = running_vrf_map
             iproute2_vrf_map_force_rewrite = True
 
-        self.iproute2_vrf_map_fd = None
         if writetodisk:
             if iproute2_vrf_map_force_rewrite:
                 # reopen the file and rewrite the map
@@ -236,7 +245,8 @@ class vrf(moduleBase):
                 self._iproute2_vrf_map_open(False, True)
 
         self.iproute2_vrf_map_sync_to_disk = False
-        atexit.register(self._iproute2_vrf_map_sync_to_disk)
+        self.at_exit = True
+        #atexit.register(self._iproute2_vrf_map_sync_to_disk)
 
         self.logger.info("vrf: dumping iproute2_vrf_map")
         self.logger.info(self.iproute2_vrf_map)
@@ -287,22 +297,15 @@ class vrf(moduleBase):
         if ifupdownflags.flags.DRYRUN:
             return
         fmode = 'a+' if append else 'w'
-        try:
-            self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
-                                         '%s' %fmode)
-            fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
-        except Exception, e:
-            self._iproute2_map_warn(str(e))
-            return
-
         if not append:
             # write file header
-            self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
-                                           %(self.vrf_table_id_start,
-                                             self.vrf_table_id_end))
-            for t, v in self.iproute2_vrf_map.iteritems():
-                self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
-            self.iproute2_vrf_map_fd.flush()
+            with open(self.iproute2_vrf_filename, fmode) as vrf_map_fd:
+                vrf_map_fd.write(self.iproute2_vrf_filehdr
+                                               %(self.vrf_table_id_start,
+                                                 self.vrf_table_id_end))
+                for t, v in self.iproute2_vrf_map.iteritems():
+                    vrf_map_fd.write('%s %s\n' %(t, v))
+                vrf_map_fd.flush()
 
     def _is_vrf(self, ifaceobj):
         if ifaceobj.get_attr_value_first('vrf-table'):
@@ -364,10 +367,10 @@ class vrf(moduleBase):
         old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
         if not old_vrf_name:
             self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
-            if self.iproute2_vrf_map_fd:
-                self.iproute2_vrf_map_fd.write('%s %s\n'
-                                               %(table_id, vrfifaceobj.name))
-                self.iproute2_vrf_map_fd.flush()
+            with open(self.iproute2_vrf_filename, "a+") as vrf_map_fd:
+                vrf_map_fd.write('%s %s\n'
+                                 (table_id, vrfifaceobj.name))
+                vrf_map_fd.flush()
                 self.vrf_count += 1
             return
         if old_vrf_name != vrfifaceobj.name:
@@ -441,7 +444,7 @@ class vrf(moduleBase):
                 break
         self._handle_existing_connections(ifaceobj, vrfname)
         self.enable_ipv6_if_prev_brport(ifacename)
-        self.ipcmd.link_set(ifacename, 'master', vrfname)
+        self.netlink.link_set_master(ifacename, vrfname)
         return
 
     def enable_ipv6_if_prev_brport(self, ifname):
@@ -449,8 +452,10 @@ class vrf(moduleBase):
         If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
         """
         try:
-            if os.path.exists("/sys/class/net/%s/brport" % ifname):
-                self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
+            for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
+                if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+                    self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
+                    return
         except Exception, e:
             self.logger.info(str(e))
 
@@ -458,7 +463,7 @@ class vrf(moduleBase):
         try:
             dhclient_cmd_prefix = None
             if (vrfname and self.vrf_exec_cmd_prefix and
-                self.ipcmd.link_exists(vrfname)):
+                self.cache.link_exists(vrfname)):
                 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
                                                 vrfname)
             self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
@@ -469,9 +474,10 @@ class vrf(moduleBase):
     def _handle_existing_connections(self, ifaceobj, vrfname):
         if not ifaceobj or ifupdownflags.flags.PERFMODE:
             return
-        if (self.vrf_mgmt_devname and
-            self.vrf_mgmt_devname == vrfname):
-            self._kill_ssh_connections(ifaceobj.name)
+        # XXX: re-evaluate kill_ssh_sessions
+        if (self.vrf_mgmt_devname and self.vrf_mgmt_devname == vrfname):
+            self._kill_ssh_connections(ifaceobj.name, ifaceobj)
+        self._close_sockets(ifaceobj.name)
         if self._is_dhcp_slave(ifaceobj):
             self._down_dhcp_slave(ifaceobj, vrfname)
 
@@ -479,12 +485,12 @@ class vrf(moduleBase):
                       ifaceobj_getfunc=None, vrf_exists=False):
         try:
             master_exists = True
-            if vrf_exists or self.ipcmd.link_exists(vrfname):
-                uppers = self.ipcmd.link_get_uppers(ifacename)
+            if vrf_exists or self.cache.link_exists(vrfname):
+                uppers = self.sysfs.link_get_uppers(ifacename)
                 if not uppers or vrfname not in uppers:
                     self._handle_existing_connections(ifaceobj, vrfname)
                     self.enable_ipv6_if_prev_brport(ifacename)
-                    self.ipcmd.link_set(ifacename, 'master', vrfname)
+                    self.netlink.link_set_master(ifacename, vrfname)
             elif ifaceobj:
                 vrf_master_objs = ifaceobj_getfunc(vrfname)
                 if not vrf_master_objs:
@@ -492,7 +498,7 @@ class vrf(moduleBase):
                     # but user has not provided a vrf interface.
                     # people expect you to warn them but go ahead with the
                     # rest of the config on that interface
-                    netlink.link_set_updown(ifacename, "up")
+                    self.netlink.link_up(ifacename)
                     self.log_error('vrf master ifaceobj %s not found'
                                    %vrfname)
                     return
@@ -511,7 +517,7 @@ class vrf(moduleBase):
                 master_exists = False
             if master_exists:
                 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
-                    netlink.link_set_updown(ifacename, "up")
+                    self.netlink.link_up(ifacename)
             else:
                 self.log_error('vrf %s not around, skipping vrf config'
                                %(vrfname), ifaceobj)
@@ -641,14 +647,14 @@ class vrf(moduleBase):
         #  - check if it is also a macvlan device of the
         #    format <vrf_slave>-v<int> created by the
         #    address virtual module
-        vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
+        vrfslave_lowers = self.sysfs.link_get_lowers(vrfslave)
         if vrfslave_lowers:
             if vrfslave_lowers[0] in config_vrfslaves:
                 return True
         return False
 
     def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
-        running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
+        running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
         config_slaves = ifaceobj.lowerifaces
         if not config_slaves and not running_slaves:
             return
@@ -660,7 +666,7 @@ class vrf(moduleBase):
         if add_slaves:
             for s in add_slaves:
                 try:
-                    if not self.ipcmd.link_exists(s):
+                    if not self.cache.link_exists(s):
                         continue
                     sobj = None
                     if ifaceobj_getfunc:
@@ -691,7 +697,7 @@ class vrf(moduleBase):
                     for slave_ifaceobj in ifaceobj_getfunc(s) or []:
                         if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
                             raise Exception("link-down yes: keeping VRF slave down")
-                    netlink.link_set_updown(s, "up")
+                    self.netlink.link_up(s)
                 except Exception, e:
                     self.logger.debug("%s: %s" % (s, str(e)))
                     pass
@@ -707,7 +713,7 @@ class vrf(moduleBase):
         return False
 
     def _create_vrf_dev(self, ifaceobj, vrf_table):
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             self._check_vrf_system_reserved_names(ifaceobj)
 
             if self.vrf_count == self.vrf_max_count:
@@ -739,8 +745,7 @@ class vrf(moduleBase):
                                      self.vrf_table_id_start,
                                      self.vrf_table_id_end), ifaceobj)
             try:
-                self.ipcmd.link_create(ifaceobj.name, 'vrf',
-                                       {'table' : '%s' %vrf_table})
+                self.netlink.link_add_vrf(ifaceobj.name, vrf_table)
             except Exception, e:
                 self.log_error('create failed (%s)\n' % str(e), ifaceobj)
             if vrf_table != 'auto':
@@ -752,13 +757,12 @@ class vrf(moduleBase):
                     self.log_error('unable to get vrf table id', ifaceobj)
 
             # if the device exists, check if table id is same
-            vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
-            if vrfdev_attrs:
-                running_table = vrfdev_attrs.get('table', None)
-                if vrf_table != running_table:
-                    self.log_error('cannot change vrf table id,running table id'
-                                   ' %s is different from config id %s'
-                                   % (running_table, vrf_table), ifaceobj)
+            running_table = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE)
+
+            if running_table is not None and vrf_table != str(running_table):
+                self.log_error('cannot change vrf table id,running table id'
+                               ' %s is different from config id %s'
+                               % (running_table, vrf_table), ifaceobj)
         return vrf_table
 
     def _up_vrf_helper(self, ifaceobj, vrf_table):
@@ -794,16 +798,22 @@ class vrf(moduleBase):
             self._set_vrf_dev_processed_flag(ifaceobj)
 
             if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
-                netlink.link_set_updown(ifaceobj.name, "up")
+                self.netlink.link_up(ifaceobj.name)
         except Exception, e:
             self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
 
-    def _kill_ssh_connections(self, ifacename):
+    def _kill_ssh_connections(self, ifacename, ifaceobj):
         try:
-            runningaddrsdict = self.ipcmd.get_running_addrs(None, ifacename)
-            if not runningaddrsdict:
+            running_addrs_list = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
+                ifaceobj_list=[ifaceobj],
+                ifname=ifacename,
+            )]
+
+            if not running_addrs_list:
                 return
-            iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
+
+            iplist = [i.split('/', 1)[0] for i in running_addrs_list]
+
             if not iplist:
                 return
             proc=[]
@@ -884,13 +894,17 @@ class vrf(moduleBase):
             else:
                 vrf = ifaceobj.get_attr_value_first('vrf')
                 if vrf:
+                    if not self.cache.link_exists(ifaceobj.name):
+                        self.logger.warning("%s: device not found - please check your configuration" % ifaceobj.name)
+                        return
+
                     self._iproute2_vrf_map_initialize()
                     # This is a vrf slave
                     self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
                                        ifaceobj_getfunc)
                 elif not ifupdownflags.flags.PERFMODE:
                     # check if we were a slave before
-                    master = self.ipcmd.link_get_master(ifaceobj.name)
+                    master = self.cache.get_master(ifaceobj.name)
                     if master:
                         self._iproute2_vrf_map_initialize()
                         if self._is_vrf_dev(master):
@@ -910,27 +924,36 @@ class vrf(moduleBase):
                                 vrf_table,
                                 mode))
 
-    def _close_sockets(self, ifaceobj, ifindex):
+    def _close_sockets(self, ifacename):
         if not self.vrf_close_socks_on_down:
             return
 
+        try:
+            ifindex = self.cache.get_ifindex(ifacename)
+        except Exception as e:
+            self.logger.debug("%s: vrf: close sockets error: %s" % str(e))
+            ifindex = 0
+
+        if not ifindex:
+            return
+
         try:
             utils.exec_command('%s -aK \"dev == %s\"'
                                %(utils.ss_cmd, ifindex))
         except Exception, e:
             self.logger.info('%s: closing socks using ss'
-                             ' failed (%s)\n' %(ifaceobj.name, str(e)))
+                             ' failed (%s)\n' %(ifacename, str(e)))
             pass
 
     def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
 
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
 
         if vrf_table == 'auto':
             vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
 
-        running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
+        running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
         if running_slaves:
             for s in running_slaves:
                 if ifaceobj_getfunc:
@@ -943,10 +966,10 @@ class vrf(moduleBase):
                         self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
                         pass
                 try:
-                    self.ipcmd.addr_flush(s)
-                    netlink.link_set_updown(s, "down")
+                    self.netlink.addr_flush(s)
+                    self.netlink.link_down(s)
                 except Exception, e:
-                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+                    self.logger.info('%s: %s' %(s, str(e)))
                     pass
 
         try:
@@ -961,16 +984,13 @@ class vrf(moduleBase):
             self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
             pass
 
-        ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
+        self._close_sockets(ifaceobj.name)
 
-        if ifindex:
-            try:
-                self.ipcmd.link_delete(ifaceobj.name)
-            except Exception, e:
-                self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-                pass
-
-        self._close_sockets(ifaceobj, ifindex)
+        try:
+            self.netlink.link_del(ifaceobj.name)
+        except Exception, e:
+            self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+            pass
 
         try:
             self._iproute2_vrf_table_entry_del(vrf_table)
@@ -982,13 +1002,13 @@ class vrf(moduleBase):
     def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
         try:
             self._handle_existing_connections(ifaceobj, vrfname)
-            self.ipcmd.link_set(ifacename, 'nomaster')
+            self.netlink.link_set_nomaster(ifacename)
             # Down this slave only if it is a slave ifupdown2 manages.
             # we dont want to down slaves that maybe up'ed by
             # somebody else. One such example is a macvlan device
             # which ifupdown2 addressvirtual addon module auto creates
             if ifaceobj:
-                netlink.link_set_updown(ifacename, "down")
+                self.netlink.link_down(ifacename)
         except Exception, e:
             self.logger.warn('%s: %s' %(ifacename, str(e)))
 
@@ -1008,7 +1028,7 @@ class vrf(moduleBase):
 
     def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
         try:
-            master = self.ipcmd.link_get_master(ifaceobj.name)
+            master = self.cache.get_master(ifaceobj.name)
             if not master or master != vrf:
                 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
             else:
@@ -1018,27 +1038,18 @@ class vrf(moduleBase):
 
     def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
         try:
-            if not self.ipcmd.link_exists(ifaceobj.name):
+            if not self.cache.link_exists(ifaceobj.name):
                 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
                 return
             if vrf_table == 'auto':
-                config_table = self._get_iproute2_vrf_table(ifaceobj.name)
+                config_table = str(self._get_iproute2_vrf_table(ifaceobj.name) or 0)
             else:
                 config_table = vrf_table
-            vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
-            if not vrfdev_attrs:
-                ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
-                return
-            running_table = vrfdev_attrs.get('table')
-            if not running_table:
-                ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
-                return
-            if config_table != running_table:
-                ifaceobjcurr.update_config_with_status('vrf-table',
-                                                       running_table, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vrf-table',
-                                                       running_table, 0)
+
+            running_vrf_table = str(self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE))
+
+            ifaceobjcurr.update_config_with_status('vrf-table', running_vrf_table, config_table != running_vrf_table)
+
             if not ifupdownflags.flags.WITHDEFAULTS:
                 return
             if self.vrf_helper:
@@ -1077,18 +1088,17 @@ class vrf(moduleBase):
 
     def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
         try:
-            kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
+            kind = self.cache.get_link_kind(ifaceobjrunning.name)
             if kind == 'vrf':
-                vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
-                if vrfdev_attrs:
-                    running_table = vrfdev_attrs.get('table')
-                    if running_table:
-                        ifaceobjrunning.update_config('vrf-table',
-                                                      running_table)
-                        return
-            slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
+                running_table = self.cache.get_link_info_data_attribute(ifaceobjrunning.name, Link.IFLA_VRF_TABLE)
+
+                if running_table is not None:
+                    ifaceobjrunning.update_config('vrf-table', str(running_table))
+                    return
+
+            slave_kind = self.cache.get_link_slave_kind(ifaceobjrunning.name)
             if slave_kind == 'vrf_slave':
-                vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
+                vrf = self.cache.get_master(ifaceobjrunning.name)
                 if vrf:
                     ifaceobjrunning.update_config('vrf', vrf)
         except Exception, e:
@@ -1101,19 +1111,19 @@ class vrf(moduleBase):
             ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
                                    ifaceobj.name))
 
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-running' : _query_running,
-               'query-checkcurr' : _query_check,
-               'query' : _query}
+    _run_ops = {
+        "pre-up": _up,
+        "post-down": _down,
+        "query-running": _query_running,
+        "query-checkcurr": _query_check,
+        "query": _query
+    }
 
     def get_ops(self):
         """ returns list of ops supported by this module """
         return self._run_ops.keys()
 
     def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = self.bondcmd = LinkUtils()
         if not self.dhclientcmd:
             self.dhclientcmd = dhclient()
 
@@ -1143,3 +1153,6 @@ class vrf(moduleBase):
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
             op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
+        if self.at_exit:
+            self._iproute2_vrf_map_sync_to_disk()
+            self.at_exit = False
index 5f409c8cd0f899a911b65eaf136b9fb48b557ab1..d3ade8e2a5abfe5e989e8350529f331a030432df 100644 (file)
 
 
 from sets import Set
-from ipaddr import IPNetwork, IPv4Address, IPv4Network, AddressValueError
+from ipaddr import IPNetwork, IPAddress, IPv4Address, IPv4Network, AddressValueError
 
 try:
     import ifupdown2.ifupdown.policymanager as policymanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 
+    from ifupdown2.lib.addon import Addon
+    from ifupdown2.lib.nlcache import NetlinkCacheIfnameNotFoundError
+
     from ifupdown2.nlmanager.nlmanager import Link
 
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
     from ifupdown2.ifupdownaddons.cache import *
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 except ImportError:
     import ifupdown.policymanager as policymanager
     import ifupdown.ifupdownflags as ifupdownflags
 
+    from lib.addon import Addon
+    from lib.nlcache import NetlinkCacheIfnameNotFoundError
+
     from nlmanager.nlmanager import Link
 
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
 
     from ifupdownaddons.cache import *
-    from ifupdownaddons.LinkUtils import LinkUtils
     from ifupdownaddons.modulebase import moduleBase
 
 
-class vxlan(moduleBase):
-    _modinfo = {'mhelp' : 'vxlan module configures vxlan interfaces.',
-                'attrs' : {
-                        'vxlan-id' :
-                            {'help' : 'vxlan id',
-                             'validrange' : ['1', '16777214'],
-                             'required' : True,
-                             'example': ['vxlan-id 100']},
-                        'vxlan-local-tunnelip' :
-                            {'help' : 'vxlan local tunnel ip',
-                             'validvals' : ['<ipv4>'],
-                             'example': ['vxlan-local-tunnelip 172.16.20.103']},
-                        'vxlan-svcnodeip' :
-                            {'help' : 'vxlan id',
-                             'validvals' : ['<ipv4>'],
-                             'example': ['vxlan-svcnodeip 172.16.22.125']},
-                        'vxlan-remoteip' :
-                            {'help' : 'vxlan remote ip',
-                             'validvals' : ['<ipv4>'],
-                             'example': ['vxlan-remoteip 172.16.22.127'],
-                             'multiline': True},
-                        'vxlan-learning' :
-                            {'help' : 'vxlan learning yes/no',
-                             'validvals' : ['yes', 'no', 'on', 'off'],
-                             'example': ['vxlan-learning no'],
-                             'default': 'yes'},
-                        'vxlan-ageing' :
-                            {'help' : 'vxlan aging timer',
-                             'validrange' : ['0', '4096'],
-                             'example': ['vxlan-ageing 300'],
-                             'default': '300'},
-                        'vxlan-purge-remotes' :
-                            {'help' : 'vxlan purge existing remote entries',
-                             'validvals' : ['yes', 'no'],
-                             'example': ['vxlan-purge-remotes yes'],},
-                    'vxlan-port': {
-                        'help': 'vxlan UDP port (transmitted to vxlan driver)',
-                        'example': ['vxlan-port 4789'],
-                        'validrange': ['1', '65536'],
-                        'default': '4789',
-                    },
-                    'vxlan-physdev':
-                        {'help': 'vxlan physical device',
-                         'example': ['vxlan-physdev eth1']},
-                    "vxlan-ttl": {
-                        "help": "specifies the TTL value to use in outgoing packets (range 1..255)",
-                        "validvals": ['<number>', 'auto'],
-                        "example": ['vxlan-ttl 42'],
-                    }
-                }}
-    _clagd_vxlan_anycast_ip = ""
-    _vxlan_local_tunnelip = None
+class vxlan(Addon, moduleBase):
+    _modinfo = {
+        "mhelp": "vxlan module configures vxlan interfaces.",
+        "attrs": {
+            "vxlan-id": {
+                "help": "vxlan id",
+                "validrange": ["1", "16777214"],
+                "required": True,
+                "example": ["vxlan-id 100"]
+            },
+            "vxlan-local-tunnelip": {
+                "help": "vxlan local tunnel ip",
+                "validvals": ["<ipv4>"],
+                "example": ["vxlan-local-tunnelip 172.16.20.103"]
+            },
+            "vxlan-svcnodeip": {
+                "help": "vxlan id",
+                "validvals": ["<ipv4>"],
+                "example": ["vxlan-svcnodeip 172.16.22.125"]
+            },
+            "vxlan-remoteip": {
+                "help": "vxlan remote ip",
+                "validvals": ["<ipv4>"],
+                "example": ["vxlan-remoteip 172.16.22.127"],
+                "multiline": True
+            },
+            "vxlan-learning": {
+                "help": "vxlan learning yes/no",
+                "validvals": ["yes", "no", "on", "off"],
+                "example": ["vxlan-learning no"],
+                "default": "yes"
+            },
+            "vxlan-ageing": {
+                "help": "vxlan aging timer",
+                "validrange": ["0", "4096"],
+                "example": ["vxlan-ageing 300"],
+                "default": "300"
+            },
+            "vxlan-purge-remotes": {
+                "help": "vxlan purge existing remote entries",
+                "validvals": ["yes", "no"],
+                "example": ["vxlan-purge-remotes yes"],
+            },
+            "vxlan-port": {
+                "help": "vxlan UDP port (transmitted to vxlan driver)",
+                "example": ["vxlan-port 4789"],
+                "validrange": ["1", "65536"],
+                "default": "4789",
+            },
+            "vxlan-physdev": {
+                "help": "vxlan physical device",
+                "example": ["vxlan-physdev eth1"]
+            },
+            "vxlan-ttl": {
+                "help": "specifies the TTL value to use in outgoing packets "
+                        "(range 0..255), 0=auto",
+                "default": "0",
+                "validvals": ["0", "255"],
+                "example": ['vxlan-ttl 42'],
+            },
+            "vxlan-mcastgrp": {
+                "help": "vxlan multicast group",
+                "validvals": ["<ip>"],
+                "example": ["vxlan-mcastgrp 172.16.22.127"],
+            }
+        }
+    }
+
+    VXLAN_PHYSDEV_MCASTGRP_DEFAULT = "ipmr-lo"
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        purge_remotes = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vxlan-purge-remotes')
-        if purge_remotes:
-            self._purge_remotes = utils.get_boolean_from_string(purge_remotes)
-        else:
-            self._purge_remotes = False
+
+        self._vxlan_purge_remotes = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr="vxlan-purge-remotes"
+            )
+        )
+        self._vxlan_local_tunnelip = None
+        self._clagd_vxlan_anycast_ip = ""
+
+        # If mcastgrp is specified we need to rely on a user-configred device (via physdev)
+        # or via a policy variable "vxlan-physdev_mcastgrp". If the device doesn't exist we
+        # create it as a dummy device. We need to keep track of the user configuration to
+        # know when to delete this dummy device (when user remove mcastgrp from it's config)
+        self.vxlan_mcastgrp_ref = False
+        self.vxlan_physdev_mcast = policymanager.policymanager_api.get_module_globals(
+            module_name=self.__class__.__name__,
+            attr="vxlan-physdev-mcastgrp"
+        ) or self.VXLAN_PHYSDEV_MCASTGRP_DEFAULT
+
+    def reset(self):
+        # in daemon mode we need to reset mcastgrp_ref for every new command
+        # this variable has to be set in get_dependent_ifacenames
+        self.vxlan_mcastgrp_ref = False
 
     def syntax_check(self, ifaceobj, ifaceobj_getfunc):
         if self._is_vxlan_device(ifaceobj):
-            if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not vxlan._vxlan_local_tunnelip:
+            if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not self._vxlan_local_tunnelip:
                 self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name)
                 return False
             return self.syntax_check_localip_anycastip_equal(
                 ifaceobj.name,
-                ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or vxlan._vxlan_local_tunnelip,
-                vxlan._clagd_vxlan_anycast_ip
+                ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or self._vxlan_local_tunnelip,
+                self._clagd_vxlan_anycast_ip
             )
         return True
 
     def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip):
         try:
-            if IPNetwork(local_ip) == IPNetwork(anycast_ip):
+            if local_ip and anycast_ip and IPNetwork(local_ip) == IPNetwork(anycast_ip):
                 self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)'
                                     % (ifname, local_ip))
                 return False
@@ -123,13 +161,19 @@ class vxlan(moduleBase):
         if self._is_vxlan_device(ifaceobj):
             ifaceobj.link_kind |= ifaceLinkKind.VXLAN
             self._set_global_local_ip(ifaceobj)
+
+            # if we detect a vxlan we check if mcastgrp is set (if so we set vxlan_mcastgrp_ref)
+            # to know when to delete this device.
+            if not self.vxlan_mcastgrp_ref and ifaceobj.get_attr_value("vxlan-mcastgrp"):
+                self.vxlan_mcastgrp_ref = True
+
         elif ifaceobj.name == 'lo':
             clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip')
             if clagd_vxlan_list:
                 if len(clagd_vxlan_list) != 1:
                     self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
                                   % (ifaceobj.name,))
-                vxlan._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
+                self._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
 
             self._set_global_local_ip(ifaceobj)
 
@@ -144,52 +188,23 @@ class vxlan(moduleBase):
 
     def _set_global_local_ip(self, ifaceobj):
         vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
-        if vxlan_local_tunnel_ip and not vxlan._vxlan_local_tunnelip:
-            vxlan._vxlan_local_tunnelip = vxlan_local_tunnel_ip
+        if vxlan_local_tunnel_ip and not self._vxlan_local_tunnelip:
+            self._vxlan_local_tunnelip = vxlan_local_tunnel_ip
 
-    def _is_vxlan_device(self, ifaceobj):
-        if ifaceobj.get_attr_value_first('vxlan-id'):
-            return True
-        return False
+    @staticmethod
+    def _is_vxlan_device(ifaceobj):
+        return ifaceobj.link_kind & ifaceLinkKind.VXLAN or ifaceobj.get_attr_value_first('vxlan-id')
 
-    def _get_purge_remotes(self, ifaceobj):
+    def __get_vlxan_purge_remotes(self, ifaceobj):
         if not ifaceobj:
-            return self._purge_remotes
+            return self._vxlan_purge_remotes
         purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes')
         if purge_remotes:
             purge_remotes = utils.get_boolean_from_string(purge_remotes)
         else:
-            purge_remotes = self._purge_remotes
+            purge_remotes = self._vxlan_purge_remotes
         return purge_remotes
 
-    def should_create_set_vxlan(self, link_exists, ifname, vxlan_id, local, learning, ageing, group, ttl):
-        """
-            should we issue a netlink: ip link add dev %ifname type vxlan ...?
-            checking each attribute against the cache
-        """
-        if not link_exists:
-            return True
-
-        try:
-            if ageing:
-                ageing = int(ageing)
-        except:
-            pass
-
-        if ttl is not None and not self.ipcmd.cache_check((ifname, 'linkinfo', Link.IFLA_VXLAN_TTL), ttl):
-                return True
-
-        for attr_list, value in (
-            ((ifname, 'linkinfo', Link.IFLA_VXLAN_ID), vxlan_id),
-            ((ifname, 'linkinfo', Link.IFLA_VXLAN_AGEING), ageing),
-            ((ifname, 'linkinfo', Link.IFLA_VXLAN_LOCAL), local),
-            ((ifname, 'linkinfo', Link.IFLA_VXLAN_LEARNING), learning),
-            ((ifname, 'linkinfo', Link.IFLA_VXLAN_GROUP), group),
-        ):
-            if value and not self.ipcmd.cache_check(attr_list, value):
-                return True
-        return False
-
     def get_vxlan_ttl_from_string(self, ttl_config):
         ttl = 0
         if ttl_config:
@@ -199,252 +214,549 @@ class vxlan(moduleBase):
                 ttl = int(ttl_config)
         return ttl
 
-    def _vxlan_create(self, ifaceobj):
-        vxlanid = ifaceobj.get_attr_value_first('vxlan-id')
-        if vxlanid:
-            ifname = ifaceobj.name
-            anycastip = self._clagd_vxlan_anycast_ip
-            group = ifaceobj.get_attr_value_first('vxlan-svcnodeip')
+    def __config_vxlan_id(self, ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        """
+        Get vxlan-id user config and check it's value before inserting it in our netlink dictionary
+        :param ifname:
+        :param ifaceobj:
+        :param vxlan_id_str:
+        :param user_request_vxlan_info_data:
+        :param cached_vxlan_ifla_info_data:
+        :return:
+        """
+        try:
+            vxlan_id = int(vxlan_id_str)
+            cached_vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID)
+
+            if cached_vxlan_id and cached_vxlan_id != vxlan_id:
+                self.log_error(
+                    "%s: Cannot change running vxlan id (%s): Operation not supported"
+                    % (ifname, cached_vxlan_id),
+                    ifaceobj
+                )
+            user_request_vxlan_info_data[Link.IFLA_VXLAN_ID] = vxlan_id
+        except ValueError:
+            self.log_error("%s: invalid vxlan-id '%s'" % (ifname, vxlan_id_str), ifaceobj)
+
+    def __get_vxlan_ageing_int(self, ifname, ifaceobj, link_exists):
+        """
+        Get vxlan-ageing user config or via policy, return integer value, None or raise on error
+        :param ifname:
+        :param ifaceobj:
+        :param link_exists:
+        :return:
+        """
+        vxlan_ageing_str = ifaceobj.get_attr_value_first("vxlan-ageing")
+        try:
+            if vxlan_ageing_str:
+                return int(vxlan_ageing_str)
+
+            vxlan_ageing_str = policymanager.policymanager_api.get_attr_default(
+                module_name=self.__class__.__name__,
+                attr="vxlan-ageing"
+            )
+
+            if not vxlan_ageing_str and link_exists:
+                # if link doesn't exist we let the kernel define ageing
+                vxlan_ageing_str = self.get_attr_default_value("vxlan-ageing")
+
+                if vxlan_ageing_str:
+                    return int(vxlan_ageing_str)
+        except:
+            self.log_error("%s: invalid vxlan-ageing '%s'" % (ifname, vxlan_ageing_str), ifaceobj)
 
-            local = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
-            if not local and vxlan._vxlan_local_tunnelip:
-                local = vxlan._vxlan_local_tunnelip
+    def __config_vxlan_ageing(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        """
+        Check user config vxlan-ageing and insert it in our netlink dictionary if needed
+        """
+        vxlan_ageing = self.__get_vxlan_ageing_int(ifname, ifaceobj, link_exists)
+
+        if not vxlan_ageing or (link_exists and vxlan_ageing == cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_AGEING)):
+            return
+
+        self.logger.info("%s: set vxlan-ageing %s" % (ifname, vxlan_ageing))
+        user_request_vxlan_info_data[Link.IFLA_VXLAN_AGEING] = vxlan_ageing
+
+    def __config_vxlan_port(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        """
+        Check vxlan-port user config, validate the integer value and insert it in the netlink dictionary if needed
+        :param ifname:
+        :param ifaceobj:
+        :param link_exists:
+        :param user_request_vxlan_info_data:
+        :param cached_vxlan_ifla_info_data:
+        :return:
+        """
+        vxlan_port_str = ifaceobj.get_attr_value_first("vxlan-port")
+        try:
+            if not vxlan_port_str:
+                vxlan_port_str = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr="vxlan-port"
+                )
 
-            ttl_config = ifaceobj.get_attr_value_first('vxlan-ttl')
             try:
-                if ttl_config:
-                    ttl = self.get_vxlan_ttl_from_string(ttl_config)
-                else:
-                    ttl = self.get_vxlan_ttl_from_string(
-                        policymanager.policymanager_api.get_attr_default(
-                            module_name=self.__class__.__name__,
-                            attr='vxlan-ttl'
-                        )
+                vxlan_port = int(vxlan_port_str)
+            except TypeError:
+                # TypeError means vxlan_port was None
+                # ie: not provided by the user or the policy
+                vxlan_port = self.netlink.VXLAN_UDP_PORT
+            except ValueError as e:
+                self.logger.warning(
+                    "%s: vxlan-port: using default %s: invalid configured value %s"
+                    % (ifname, self.netlink.VXLAN_UDP_PORT, str(e))
+                )
+                vxlan_port = self.netlink.VXLAN_UDP_PORT
+
+            cached_vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT)
+
+            if link_exists:
+                if vxlan_port != cached_vxlan_port:
+                    self.logger.warning(
+                        "%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s"
+                        % (ifname, cached_vxlan_port, ifname, ifname)
                     )
-            except:
-                self.log_error('%s: invalid vxlan-ttl \'%s\'' % (ifname, ttl_config), ifaceobj)
                 return
 
-            self.syntax_check_localip_anycastip_equal(ifname, local, anycastip)
-            # if both local-ip and anycast-ip are identical the function prints a warning
+            self.logger.info("%s: set vxlan-port %s" % (ifname, vxlan_port))
+            user_request_vxlan_info_data[Link.IFLA_VXLAN_PORT] = vxlan_port
+        except:
+            self.log_error("%s: invalid vxlan-port '%s'" % (ifname, vxlan_port_str), ifaceobj)
+
+    def __config_vxlan_ttl(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        """
+        Get vxlan-ttl from user config or policy, validate integer value and insert in netlink dict
+        :param ifname:
+        :param ifaceobj:
+        :param user_request_vxlan_info_data:
+        :param cached_vxlan_ifla_info_data:
+        :return:
+        """
+        vxlan_ttl_str = ifaceobj.get_attr_value_first("vxlan-ttl")
+        try:
+            if vxlan_ttl_str:
+                vxlan_ttl = self.get_vxlan_ttl_from_string(vxlan_ttl_str)
+            else:
+                vxlan_ttl = self.get_vxlan_ttl_from_string(
+                    policymanager.policymanager_api.get_attr_default(
+                        module_name=self.__class__.__name__,
+                        attr="vxlan-ttl"
+                    )
+                )
+
+            cached_ifla_vxlan_ttl = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TTL)
+            if vxlan_ttl != cached_ifla_vxlan_ttl:
+
+                if cached_ifla_vxlan_ttl is not None:
+                    self.logger.info("%s: set vxlan-ttl %s (cache %s)" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl, cached_ifla_vxlan_ttl))
+                else:
+                    self.logger.info("%s: set vxlan-ttl %s" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl))
+
+                user_request_vxlan_info_data[Link.IFLA_VXLAN_TTL] = vxlan_ttl
+        except:
+            self.log_error("%s: invalid vxlan-ttl '%s'" % (ifname, vxlan_ttl_str), ifaceobj)
+
+    def __config_vxlan_local_tunnelip(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        """
+        Get vxlan-local-tunnelip user config or policy, validate ip address format and insert in netlink dict
+        :param ifname:
+        :param ifaceobj:
+        :param link_exists:
+        :param user_request_vxlan_info_data:
+        :param cached_vxlan_ifla_info_data:
+        :return:
+        """
+        local = ifaceobj.get_attr_value_first("vxlan-local-tunnelip")
+
+        if not local and self._vxlan_local_tunnelip:
+            local = self._vxlan_local_tunnelip
+
+        if link_exists:
+            # on ifreload do not overwrite anycast_ip to individual ip
+            # if clagd has modified
+            running_localtunnelip = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
+
+            if self._clagd_vxlan_anycast_ip and running_localtunnelip:
+                anycastip = IPAddress(self._clagd_vxlan_anycast_ip)
+                if anycastip == running_localtunnelip:
+                    local = running_localtunnelip
+
+        if not local:
+            local = policymanager.policymanager_api.get_attr_default(
+                module_name=self.__class__.__name__,
+                attr="vxlan-local-tunnelip"
+            )
+
+        if local:
+            try:
+                local = IPv4Address(local)
+            except AddressValueError:
+                try:
+                    local_ip = IPv4Network(local).ip
+                    self.logger.warning("%s: vxlan-local-tunnelip %s: netmask ignored" % (ifname, local))
+                    local = local_ip
+                except:
+                    raise Exception("%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format" % (ifname, local))
+
+        cached_ifla_vxlan_local = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
+
+        if local:
+            if local != cached_ifla_vxlan_local:
+                self.logger.info("%s: set vxlan-local-tunnelip %s" % (ifname, local))
+                user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = local
+
+                # if both local-ip and anycast-ip are identical the function prints a warning
+                self.syntax_check_localip_anycastip_equal(ifname, local, self._clagd_vxlan_anycast_ip)
+        elif cached_ifla_vxlan_local:
+            self.logger.info("%s: removing vxlan-local-tunnelip (cache %s)" % (ifname, cached_ifla_vxlan_local))
+            user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = None
+
+        return local
+
+    def __get_vxlan_mcast_grp(self, ifaceobj):
+        """
+        Get vxlan-mcastgrp user config or policy
+        :param ifaceobj:
+        :return:
+        """
+        vxlan_mcast_grp = ifaceobj.get_attr_value_first("vxlan-mcastgrp")
+
+        if not vxlan_mcast_grp:
+            vxlan_mcast_grp = policymanager.policymanager_api.get_attr_default(
+                module_name=self.__class__.__name__,
+                attr="vxlan-mcastgrp"
+            )
+
+        return vxlan_mcast_grp
+
+    def __get_vxlan_svcnodeip(self, ifaceobj):
+        """
+        Get vxlan-svcnodeip user config or policy
+        :param ifaceobj:
+        :return:
+        """
+        vxlan_svcnodeip = ifaceobj.get_attr_value_first('vxlan-svcnodeip')
 
-            ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
-            vxlan_port = ifaceobj.get_attr_value_first('vxlan-port')
-            physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
-            purge_remotes = self._get_purge_remotes(ifaceobj)
+        if not vxlan_svcnodeip:
+            vxlan_svcnodeip = policymanager.policymanager_api.get_attr_default(
+                module_name=self.__class__.__name__,
+                attr="vxlan-svcnodeip"
+            )
 
-            link_exists = self.ipcmd.link_exists(ifname)
+        return vxlan_svcnodeip
 
-            if (not link_exists or
-                not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
-                vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning')
-                if not vxlan_learning:
-                    vxlan_learning = self.get_attr_default_value('vxlan-learning')
-                learning = utils.get_boolean_from_string(vxlan_learning)
+    def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        """
+        vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive
+        this function validates ip format for both attribute and tries to understand
+        what the user really want (remote or group option).
+
+        :param ifname:
+        :param ifaceobj:
+        :param mcast_grp:
+        :param group:
+        :param physdev:
+        :param user_request_vxlan_info_data:
+        :param cached_vxlan_ifla_info_data:
+        :return:
+        """
+        if mcast_grp and group:
+            self.log_error("%s: both group (vxlan-mcastgrp %s) and "
+                           "remote (vxlan-svcnodeip %s) cannot be specified"
+                           % (ifname, mcast_grp, group), ifaceobj)
+
+        attribute_name = "vxlan-svcnodeip"
+        multicast_group_change = False
+
+        if group:
+            try:
+                group = IPv4Address(group)
+            except AddressValueError:
+                try:
+                    group_ip = IPv4Network(group).ip
+                    self.logger.warning("%s: vxlan-svcnodeip %s: netmask ignored" % (ifname, group))
+                    group = group_ip
+                except:
+                    raise Exception("%s: invalid vxlan-svcnodeip %s: must be in ipv4 format" % (ifname, group))
+
+            if group.is_multicast:
+                self.logger.warning("%s: vxlan-svcnodeip %s: invalid group address, "
+                                    "for multicast IP please use attribute \"vxlan-mcastgrp\"" % (ifname, group))
+                # if svcnodeip is used instead of mcastgrp we warn the user
+                # if mcast_grp is not provided by the user we can instead
+                # use the svcnodeip value
+                if not physdev:
+                    self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname))
+
+        elif mcast_grp:
+            try:
+                mcast_grp = IPv4Address(mcast_grp)
+            except AddressValueError:
+                try:
+                    group_ip = IPv4Network(mcast_grp).ip
+                    self.logger.warning("%s: vxlan-mcastgrp %s: netmask ignored" % (ifname, mcast_grp))
+                    mcast_grp = group_ip
+                except:
+                    raise Exception("%s: invalid vxlan-mcastgrp %s: must be in ipv4 format" % (ifname, mcast_grp))
+
+            if not mcast_grp.is_multicast:
+                self.logger.warning("%s: vxlan-mcastgrp %s: invalid group address, "
+                                    "for non-multicast IP please use attribute \"vxlan-svcnodeip\""
+                                    % (ifname, mcast_grp))
+                # if mcastgrp is specified with a non-multicast address
+                # we warn the user. If the svcnodeip wasn't specified by
+                # the user we can use the mcastgrp value as svcnodeip
+                if not group:
+                    group = mcast_grp
+                    mcast_grp = None
             else:
-                learning = utils.get_boolean_from_string(
-                                self.ipcmd.get_vxlandev_learning(ifname))
+                attribute_name = "vxlan-mcastgrp"
+
+            if mcast_grp:
+                group = mcast_grp
+
+                if not physdev:
+                    self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname))
+
+        cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
+
+        if group != cached_ifla_vxlan_group:
+
+            if not group:
+                group = IPAddress("0.0.0.0")
+                attribute_name = "vxlan-svcnodeip/vxlan-mcastgrp"
+
+            self.logger.info("%s: set %s %s" % (ifname, attribute_name, group))
+            user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP] = group
+
+            # if the mcastgrp address is changed we need to signal this to the upper function
+            # in this case vxlan needs to be down before applying changes then up'd
+            multicast_group_change = True
 
             if link_exists:
-                vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifname)
-                # on ifreload do not overwrite anycast_ip to individual ip
-                # if clagd has modified
-                if vxlanattrs:
-                    running_localtunnelip = vxlanattrs.get('local')
-                    if (anycastip and running_localtunnelip and
-                                anycastip == running_localtunnelip):
-                        local = running_localtunnelip
-                    if vxlanattrs.get('vxlanid') != vxlanid:
-                        self.log_error('%s: Cannot change running vxlan id: '
-                                       'Operation not supported' % ifname, ifaceobj)
+                if cached_ifla_vxlan_group:
+                    self.logger.info(
+                        "%s: vxlan-mcastgrp configuration changed (cache %s): flapping vxlan device required"
+                        % (ifname, cached_ifla_vxlan_group)
+                    )
                 else:
-                    device_link_kind = self.ipcmd.link_get_kind(ifname)
-                    if not device_link_kind:
-                        self.logger.error("%s: device already exists and is not a vxlan" % ifname)
-                        ifaceobj.set_status(ifaceStatus.ERROR)
-                        return
-                    elif device_link_kind != "vxlan":
-                        self.logger.error("%s: device already exists and is not a vxlan (type %s)" % (ifname, device_link_kind))
-                        ifaceobj.set_status(ifaceStatus.ERROR)
-                        return
+                    self.logger.info(
+                        "%s: vxlan-mcastgrp configuration changed: flapping vxlan device required" % ifname
+                    )
 
-            try:
-                vxlanid = int(vxlanid)
-            except:
-                self.log_error('%s: invalid vxlan-id \'%s\'' % (ifname, vxlanid), ifaceobj)
+        return group, multicast_group_change
 
-            if not group:
-                group = policymanager.policymanager_api.get_attr_default(
-                    module_name=self.__class__.__name__,
-                    attr='vxlan-svcnodeip'
-                )
+    def __config_vxlan_learning(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        if not link_exists or not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+            vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning')
+            if not vxlan_learning:
+                vxlan_learning = self.get_attr_default_value('vxlan-learning')
+            vxlan_learning = utils.get_boolean_from_string(vxlan_learning)
+        else:
+            vxlan_learning = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING)
 
-            if group:
-                try:
-                    group = IPv4Address(group)
-                except AddressValueError:
-                    try:
-                        group_ip = IPv4Network(group).ip
-                        self.logger.warning('%s: vxlan-svcnodeip %s: netmask ignored' % (ifname, group))
-                        group = group_ip
-                    except:
-                        raise Exception('%s: invalid vxlan-svcnodeip %s: must be in ipv4 format' % (ifname, group))
-
-            if not local:
-                local = policymanager.policymanager_api.get_attr_default(
-                    module_name=self.__class__.__name__,
-                    attr='vxlan-local-tunnelip'
-                )
+        if vxlan_learning != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING):
+            self.logger.info("%s: set vxlan-learning %s" % (ifaceobj.name, "on" if vxlan_learning else "off"))
+            user_request_vxlan_info_data[Link.IFLA_VXLAN_LEARNING] = vxlan_learning
+
+    def __get_vxlan_physdev(self, ifaceobj, mcastgrp):
+        """
+        vxlan-physdev wrapper, special handling is required for mcastgrp is provided
+        the vxlan needs to use a dummy or real device for tunnel endpoint communication
+        This wrapper will get the physdev from user config or policy. IF the device
+        doesnt exists we create a dummy device.
+
+        :param ifaceobj:
+        :param mcastgrp:
+        :return physdev:
+        """
+        physdev = ifaceobj.get_attr_value_first("vxlan-physdev")
 
-            if local:
+        # if the user provided a physdev we need to honor his config
+        # or if mcastgrp wasn't specified we don't need to go further
+        if physdev or not mcastgrp:
+            return physdev
+
+        physdev = self.vxlan_physdev_mcast
+
+        if not self.cache.link_exists(physdev):
+            self.logger.info("%s: needs a dummy device (%s) to use for "
+                             "multicast termination (vxlan-mcastgrp %s)"
+                             % (ifaceobj.name, physdev, mcastgrp))
+            self.netlink.link_add_with_attributes(ifname=physdev, kind="dummy", ifla={Link.IFLA_MTU: 16000, Link.IFLA_LINKMODE: 1})
+            self.netlink.link_up(physdev)
+
+        return physdev
+
+    def __config_vxlan_physdev(self, link_exists, ifaceobj, vxlan_physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+        if vxlan_physdev:
+            try:
+                vxlan_physdev_ifindex = self.cache.get_ifindex(vxlan_physdev)
+            except NetlinkCacheIfnameNotFoundError:
                 try:
-                    local = IPv4Address(local)
-                except AddressValueError:
-                    try:
-                        local_ip = IPv4Network(local).ip
-                        self.logger.warning('%s: vxlan-local-tunnelip %s: netmask ignored' % (ifname, local))
-                        local = local_ip
-                    except:
-                        raise Exception('%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format' % (ifname, local))
-
-            if not ageing:
-                ageing = policymanager.policymanager_api.get_attr_default(
-                    module_name=self.__class__.__name__,
-                    attr='vxlan-ageing'
-                )
+                    vxlan_physdev_ifindex = int(self.sysfs.read_file_oneline("/sys/class/net/%s/ifindex" % vxlan_physdev))
+                except:
+                    self.logger.error("%s: physdev %s doesn't exists" % (ifaceobj.name, vxlan_physdev))
+                    return
 
-                if not ageing and link_exists:
-                    # if link doesn't exist we let the kernel define ageing
-                    ageing = self.get_attr_default_value('vxlan-ageing')
+            if vxlan_physdev_ifindex != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LINK):
+                self.logger.info("%s: set vxlan-physdev %s" % (ifaceobj.name, vxlan_physdev))
+                user_request_vxlan_info_data[Link.IFLA_VXLAN_LINK] = vxlan_physdev_ifindex
 
-            if not vxlan_port:
-                vxlan_port = policymanager.policymanager_api.get_attr_default(
-                    module_name=self.__class__.__name__,
-                    attr='vxlan-port'
+                # if the vxlan exists we need to return True, meaning that the vxlan
+                # needs to be flapped because we detected a vxlan-physdev change
+                if link_exists:
+                    self.logger.info("%s: vxlan-physdev configuration changed: flapping vxlan device required" % ifaceobj.name)
+                    return True
+
+        return False
+
+    def _up(self, ifaceobj):
+        vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id")
+
+        if not vxlan_id_str:
+            return
+
+        ifname = ifaceobj.name
+        link_exists = self.cache.link_exists(ifname)
+
+        if link_exists:
+            # if link already exists make sure this is a vxlan
+            device_link_kind = self.cache.get_link_kind(ifname)
+
+            if device_link_kind != "vxlan":
+                self.logger.error(
+                    "%s: device already exists and is not a vxlan (type %s)"
+                    % (ifname, device_link_kind)
                 )
+                ifaceobj.set_status(ifaceStatus.ERROR)
+                return
 
-            try:
-                vxlan_port = int(vxlan_port)
-            except TypeError:
-                # TypeError means vxlan_port was None
-                # ie: not provided by the user or the policy
-                vxlan_port = netlink.VXLAN_UDP_PORT
-            except ValueError as e:
-                self.logger.warning('%s: vxlan-port: using default %s: invalid configured value %s' % (ifname, netlink.VXLAN_UDP_PORT, str(e)))
-                vxlan_port = netlink.VXLAN_UDP_PORT
+            # get vxlan running attributes
+            cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
+        else:
+            cached_vxlan_ifla_info_data = {}
 
-            if link_exists and vxlanattrs and not ifupdownflags.flags.DRYRUN:
-                cache_port = vxlanattrs.get(Link.IFLA_VXLAN_PORT)
-                if vxlan_port != cache_port:
-                    self.logger.warning('%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s'
-                                        % (ifname, cache_port, ifname, ifname))
-                    vxlan_port = cache_port
+        user_request_vxlan_info_data = {}
 
-            if self.should_create_set_vxlan(link_exists, ifname, vxlanid, local, learning, ageing, group, ttl):
+        self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+        self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+        self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+        self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+        self.__config_vxlan_ttl(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+        local = self.__config_vxlan_local_tunnelip(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+
+        vxlan_mcast_grp = self.__get_vxlan_mcast_grp(ifaceobj)
+        vxlan_svcnodeip = self.__get_vxlan_svcnodeip(ifaceobj)
+        vxlan_physdev = self.__get_vxlan_physdev(ifaceobj, vxlan_mcast_grp)
+
+        vxlan_physdev_changed = self.__config_vxlan_physdev(
+            link_exists,
+            ifaceobj,
+            vxlan_physdev,
+            user_request_vxlan_info_data,
+            cached_vxlan_ifla_info_data
+        )
+
+        group, multicast_group_changed = self.__config_vxlan_group(
+            ifname,
+            ifaceobj,
+            link_exists,
+            vxlan_mcast_grp,
+            vxlan_svcnodeip,
+            vxlan_physdev,
+            user_request_vxlan_info_data,
+            cached_vxlan_ifla_info_data
+        )
+
+        flap_vxlan_device = link_exists and (multicast_group_changed or vxlan_physdev_changed)
+
+        if user_request_vxlan_info_data:
+
+            if link_exists and not len(user_request_vxlan_info_data) > 1:
+                # if the vxlan already exists it's already cached
+                # user_request_vxlan_info_data always contains at least one
+                # element: vxlan-id
+                self.logger.info('%s: vxlan already exists - no change detected' % ifname)
+            else:
                 try:
-                    netlink.link_add_vxlan(ifname, vxlanid,
-                                           local=local,
-                                           learning=learning,
-                                           ageing=ageing,
-                                           group=group,
-                                           dstport=vxlan_port,
-                                           physdev=physdev,
-                                           ttl=ttl)
-                except Exception as e_netlink:
-                    self.logger.debug('%s: vxlan netlink: %s' % (ifname, str(e_netlink)))
-                    try:
-                        self.ipcmd.link_create_vxlan(ifname, vxlanid,
-                                                     localtunnelip=local,
-                                                     svcnodeip=group,
-                                                     remoteips=ifaceobj.get_attr_value('vxlan-remoteip'),
-                                                     learning='on' if learning else 'off',
-                                                     ageing=ageing,
-                                                     ttl=ttl)
-                    except Exception as e_iproute2:
-                        self.logger.warning('%s: vxlan add/set failed: %s' % (ifname, str(e_iproute2)))
-                        return
+                    if flap_vxlan_device:
+                        self.netlink.link_down_force(ifname)
+
+                    self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data)
+
+                    if flap_vxlan_device:
+                        self.netlink.link_up_force(ifname)
+                except Exception as e:
+                    if link_exists:
+                        self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj)
+                    else:
+                        self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj)
+                    return
+
+        vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
+
+        remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
+        if remoteips:
+            try:
+                for remoteip in remoteips:
+                    IPv4Address(remoteip)
+            except Exception as e:
+                self.log_error('%s: vxlan-remoteip: %s' % (ifaceobj.name, str(e)))
+
+        if vxlan_purge_remotes or remoteips:
+            # figure out the diff for remotes and do the bridge fdb updates
+            # only if provisioned by user and not by an vxlan external
+            # controller.
+            peers = self.iproute2.get_vxlan_peers(ifaceobj.name, group)
+            if local and remoteips and local in remoteips:
+                remoteips.remove(local)
+            cur_peers = set(peers)
+            if remoteips:
+                new_peers = set(remoteips)
+                del_list = cur_peers.difference(new_peers)
+                add_list = new_peers.difference(cur_peers)
+            else:
+                del_list = cur_peers
+                add_list = []
 
+            for addr in del_list:
                 try:
-                    # manually adding an entry to the caching after creating/updating the vxlan
-                    if not ifname in linkCache.links:
-                        linkCache.links[ifname] = {'linkinfo': {}}
-                    linkCache.links[ifname]['linkinfo'].update({
-                        'learning': learning,
-                        Link.IFLA_VXLAN_LEARNING: learning,
-                        'vxlanid': str(vxlanid),
-                        Link.IFLA_VXLAN_ID: vxlanid
-                    })
-                    if ageing:
-                        linkCache.links[ifname]['linkinfo'].update({
-                            'ageing': ageing,
-                            Link.IFLA_VXLAN_AGEING: int(ageing)
-                        })
+                    self.iproute2.bridge_fdb_del(
+                        ifaceobj.name,
+                        "00:00:00:00:00:00",
+                        None, True, addr
+                    )
                 except:
                     pass
-            else:
-                self.logger.info('%s: vxlan already exists' % ifname)
-                # if the vxlan already exists it's already cached
 
-            remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
-            if remoteips:
+            for addr in add_list:
                 try:
-                    for remoteip in remoteips:
-                        IPv4Address(remoteip)
-                except Exception as e:
-                    self.log_error('%s: vxlan-remoteip: %s' %(ifaceobj.name, str(e)))
-
-            if purge_remotes or remoteips:
-                # figure out the diff for remotes and do the bridge fdb updates
-                # only if provisioned by user and not by an vxlan external
-                # controller.
-                peers = self.ipcmd.get_vxlan_peers(ifaceobj.name, group)
-                if local and remoteips and local in remoteips:
-                    remoteips.remove(local)
-                cur_peers = set(peers)
-                if remoteips:
-                    new_peers = set(remoteips)
-                    del_list = cur_peers.difference(new_peers)
-                    add_list = new_peers.difference(cur_peers)
-                else:
-                    del_list = cur_peers
-                    add_list = []
-
-                for addr in del_list:
-                    try:
-                        self.ipcmd.bridge_fdb_del(ifaceobj.name,
-                                                  '00:00:00:00:00:00',
-                                                  None, True, addr)
-                    except:
-                        pass
-
-                for addr in add_list:
-                    try:
-                        self.ipcmd.bridge_fdb_append(ifaceobj.name,
-                                                     '00:00:00:00:00:00',
-                                                     None, True, addr)
-                    except:
-                        pass
-
-    def _up(self, ifaceobj):
-        self._vxlan_create(ifaceobj)
+                    self.iproute2.bridge_fdb_append(
+                        ifaceobj.name,
+                        "00:00:00:00:00:00",
+                        None, True, addr
+                    )
+                except:
+                    pass
 
     def _down(self, ifaceobj):
         try:
-            self.ipcmd.link_delete(ifaceobj.name)
+            self.netlink.link_del(ifaceobj.name)
         except Exception, e:
             self.log_warn(str(e))
 
-    def _query_check_n_update(self, ifaceobj, ifaceobjcurr, attrname, attrval,
-                              running_attrval):
+    @staticmethod
+    def _query_check_n_update(ifaceobj, ifaceobjcurr, attrname, attrval, running_attrval):
         if not ifaceobj.get_attr_value_first(attrname):
             return
         if running_attrval and attrval == running_attrval:
-           ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
+            ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
         else:
-           ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
+            ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
 
-    def _query_check_n_update_addresses(self, ifaceobjcurr, attrname,
-                                        addresses, running_addresses):
+    @staticmethod
+    def _query_check_n_update_addresses(ifaceobjcurr, attrname, addresses, running_addresses):
         if addresses:
             for a in addresses:
                 if a in running_addresses:
@@ -453,153 +765,198 @@ class vxlan(moduleBase):
                     ifaceobjcurr.update_config_with_status(attrname, a, 1)
             running_addresses = Set(running_addresses).difference(
                                                     Set(addresses))
-        [ifaceobjcurr.update_config_with_status(attrname, a, 1)
-                    for a in running_addresses]
+        [ifaceobjcurr.update_config_with_status(attrname, a, 1) for a in running_addresses]
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-           return
-        # Update vxlan object
-        vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobj.name)
-        if not vxlanattrs:
-            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
-                    self.get_mod_attrs(), -1)
+        ifname = ifaceobj.name
+
+        if not self.cache.link_exists(ifname):
             return
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-id',
-                       ifaceobj.get_attr_value_first('vxlan-id'),
-                       vxlanattrs.get('vxlanid'))
 
-        self._query_check_n_update(
-            ifaceobj,
-            ifaceobjcurr,
-            'vxlan-port',
-            ifaceobj.get_attr_value_first('vxlan-port'),
-            str(vxlanattrs.get(Link.IFLA_VXLAN_PORT))
-        )
+        cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
+
+        if not cached_vxlan_ifla_info_data:
+            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1)
+            return
+
+        for vxlan_attr_str, vxlan_attr_nl, callable_type in (
+                ('vxlan-id', Link.IFLA_VXLAN_ID, int),
+                ('vxlan-ttl', Link.IFLA_VXLAN_TTL, int),
+                ('vxlan-port', Link.IFLA_VXLAN_PORT, int),
+                ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, int),
+                ('vxlan-mcastgrp', Link.IFLA_VXLAN_GROUP, IPv4Address),
+                ('vxlan-svcnodeip', Link.IFLA_VXLAN_GROUP, IPv4Address),
+                ('vxlan-physdev', Link.IFLA_VXLAN_LINK, lambda x: self.cache.get_ifindex(x)),
+                ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda boolean_str: utils.get_boolean_from_string(boolean_str)),
+        ):
+            vxlan_attr_value = ifaceobj.get_attr_value_first(vxlan_attr_str)
 
-        running_attrval = vxlanattrs.get('local')
+            if not vxlan_attr_value:
+                continue
+
+            cached_vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl)
+
+            try:
+                vxlan_attr_value_nl = callable_type(vxlan_attr_value)
+            except Exception as e:
+                self.logger.warning('%s: %s: %s' % (ifname, vxlan_attr_str, str(e)))
+                ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
+                continue
+
+            if vxlan_attr_value_nl == cached_vxlan_attr_value:
+                ifaceobjcurr.update_config_with_status(vxlan_attr_str, vxlan_attr_value, 0)
+            else:
+                ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
+
+        #
+        # vxlan-local-tunnelip
+        #
+        running_attrval = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
         attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
         if not attrval:
-            attrval = vxlan._vxlan_local_tunnelip
+            attrval = self._vxlan_local_tunnelip
+            # TODO: vxlan._vxlan_local_tunnelip should be a IPNetwork obj
             ifaceobj.update_config('vxlan-local-tunnelip', attrval)
 
-        if running_attrval == self._clagd_vxlan_anycast_ip:
+        if str(running_attrval) == self._clagd_vxlan_anycast_ip:
             # if local ip is anycast_ip, then let query_check to go through
             attrval = self._clagd_vxlan_anycast_ip
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-local-tunnelip',
-                                   attrval, running_attrval)
 
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-svcnodeip',
-                       ifaceobj.get_attr_value_first('vxlan-svcnodeip'),
-                       vxlanattrs.get('svcnode'))
+        self._query_check_n_update(
+            ifaceobj,
+            ifaceobjcurr,
+            'vxlan-local-tunnelip',
+            str(attrval),
+            str(running_attrval)
+        )
 
-        purge_remotes = self._get_purge_remotes(ifaceobj)
+        #
+        # vxlan-remoteip
+        #
+        purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
         if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'):
             # If purge remotes or if vxlan-remoteip's are set
             # in the config file, we are owners of the installed
             # remote-ip's, lets check and report any remote ips we don't
             # understand
-            self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-remoteip',
-                           ifaceobj.get_attr_value('vxlan-remoteip'),
-                                                 self.ipcmd.get_vxlan_peers(ifaceobj.name, vxlanattrs.get('svcnode')))
-
-        learning = ifaceobj.get_attr_value_first('vxlan-learning')
-        if learning:
-            running_learning = vxlanattrs.get('learning')
-            if learning == 'yes' and running_learning == 'on':
-                running_learning = 'yes'
-            elif learning == 'no' and running_learning == 'off':
-                running_learning = 'no'
-            if learning == running_learning:
-                ifaceobjcurr.update_config_with_status('vxlan-learning',
-                                                        running_learning, 0)
-            else:
-                ifaceobjcurr.update_config_with_status('vxlan-learning',
-                                                        running_learning, 1)
-        ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
-        if not ageing:
-            ageing = self.get_mod_subattr('vxlan-ageing', 'default')
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-ageing',
-                       ageing, vxlanattrs.get('ageing'))
+            cached_svcnode = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
 
-        physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
+            self._query_check_n_update_addresses(
+                ifaceobjcurr,
+                'vxlan-remoteip',
+                ifaceobj.get_attr_value('vxlan-remoteip'),
+                self.iproute2.get_vxlan_peers(ifaceobj.name, str(cached_svcnode))
+            )
 
-        if physdev:
-            ifla_vxlan_link = vxlanattrs.get(Link.IFLA_VXLAN_LINK)
-
-            if ifla_vxlan_link:
-                self._query_check_n_update(
-                    ifaceobj,
-                    ifaceobjcurr,
-                    'vxlan-physdev',
-                    physdev,
-                    netlink.get_iface_name(ifla_vxlan_link)
-                )
-            else:
-                ifaceobjcurr.update_config_with_status('vxlan-physdev', physdev, 1)
+    def _query_running(self, ifaceobjrunning):
+        ifname = ifaceobjrunning.name
 
+        if not self.cache.link_exists(ifname):
+            return
 
-    def _query_running(self, ifaceobjrunning):
-        vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobjrunning.name)
-        if not vxlanattrs:
+        if not self.cache.get_link_kind(ifname) == 'vxlan':
             return
-        attrval = vxlanattrs.get('vxlanid')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-id', vxlanattrs.get('vxlanid'))
-        else:
-            # if there is no vxlan id, this is not a vxlan port
+
+        cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
+
+        if not cached_vxlan_ifla_info_data:
             return
 
-        ifaceobjrunning.update_config('vxlan-port', vxlanattrs.get(Link.IFLA_VXLAN_PORT))
+        #
+        # vxlan-id
+        #
+        vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID)
+
+        if not vxlan_id:
+            # no vxlan id, meaning this not a vxlan
+            return
+
+        ifaceobjrunning.update_config('vxlan-id', str(vxlan_id))
+
+        #
+        # vxlan-port
+        #
+        vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT)
+
+        if vxlan_port:
+            ifaceobjrunning.update_config('vxlan-port', vxlan_port)
+
+        #
+        # vxlan-svcnode
+        #
+        vxlan_svcnode_value = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
+
+        if vxlan_svcnode_value:
+            vxlan_svcnode_value = str(vxlan_svcnode_value)
+            ifaceobjrunning.update_config('vxlan-svcnode', vxlan_svcnode_value)
 
-        attrval = vxlanattrs.get('local')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-local-tunnelip', attrval)
-        attrval = vxlanattrs.get('svcnode')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-svcnode', attrval)
-        purge_remotes = self._get_purge_remotes(None)
+        #
+        # vxlan-remoteip
+        #
+        purge_remotes = self.__get_vlxan_purge_remotes(None)
         if purge_remotes:
             # if purge_remotes is on, it means we own the
             # remote ips. Query them and add it to the running config
-            attrval = self.ipcmd.get_vxlan_peers(ifaceobjrunning.name, vxlanattrs.get('svcnode'))
+            attrval = self.iproute2.get_vxlan_peers(ifname, vxlan_svcnode_value)
             if attrval:
-                [ifaceobjrunning.update_config('vxlan-remoteip', a)
-                            for a in attrval]
-        attrval = vxlanattrs.get('learning')
-        if attrval and attrval == 'on':
-            ifaceobjrunning.update_config('vxlan-learning', 'on')
-        attrval = vxlanattrs.get('ageing')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-ageing', vxlanattrs.get('ageing'))
-
-        ifla_vxlan_link = vxlanattrs.get(Link.IFLA_VXLAN_LINK)
-        if ifla_vxlan_link:
-            ifaceobjrunning.update_config(
-                'vxlan-physdev',
-                netlink.get_iface_name(ifla_vxlan_link)
-            )
+                [ifaceobjrunning.update_config('vxlan-remoteip', a) for a in attrval]
+
+        #
+        # vxlan-link
+        # vxlan-ageing
+        # vxlan-learning
+        # vxlan-local-tunnelip
+        #
+        for vxlan_attr_name, vxlan_attr_nl, callable_netlink_value_to_string in (
+                ('vxlan-physdev', Link.IFLA_VXLAN_LINK, self._get_ifname_for_ifindex),
+                ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, str),
+                ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda value: 'on' if value else 'off'),
+                ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL, str),
+        ):
+            vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl)
+
+            if vxlan_attr_value is not None:
+                vxlan_attr_value_str = callable_netlink_value_to_string(vxlan_attr_nl)
+
+                if vxlan_attr_value:
+                    ifaceobjrunning.update_config(vxlan_attr_name, vxlan_attr_value_str)
+
+    def _get_ifname_for_ifindex(self, ifindex):
+        """
+        we need this middle-man function to query the cache
+        cache.get_ifname can raise KeyError, we need to catch
+        it and return None
+        """
+        try:
+            return self.cache.get_ifname(ifindex)
+        except KeyError:
+            return None
 
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
+    _run_ops = {
+        "pre-up": _up,
+        "post-down": _down,
+        "query-running": _query_running,
+        "query-checkcurr": _query_check
+    }
 
     def get_ops(self):
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         op_handler = self._run_ops.get(operation)
         if not op_handler:
             return
-        if (operation != 'query-running' and
-                not self._is_vxlan_device(ifaceobj)):
-            return
-        self._init_command_handlers()
+
+        if operation != 'query-running':
+            if not self._is_vxlan_device(ifaceobj):
+                return
+
+            if not self.vxlan_mcastgrp_ref \
+                    and self.vxlan_physdev_mcast \
+                    and self.cache.link_exists(self.vxlan_physdev_mcast):
+                self.netlink.link_del(self.vxlan_physdev_mcast)
+                self.reset()
+
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index d2e4e7f624e714d95c931b720bbdbee4a8317559..9b0917dcd4d3d0dbfeccab21a8056c74a6b899ca 100644 (file)
@@ -11,11 +11,13 @@ import socket
 from ipaddr import IPNetwork, IPv6Network
 
 try:
+    from ifupdown2.lib.addon import Addon
+
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
 
-    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.nlmanager.nlpacket import Link
+
     from ifupdown2.ifupdownaddons.modulebase import moduleBase
 
     import ifupdown2.ifupdown.statemanager as statemanager
@@ -23,11 +25,13 @@ try:
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
 except ImportError:
+    from lib.addon import Addon
+
     from ifupdown.iface import *
     from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
 
-    from ifupdownaddons.LinkUtils import LinkUtils
+    from nlmanager.nlpacket import Link
+
     from ifupdownaddons.modulebase import moduleBase
 
     import ifupdown.statemanager as statemanager
@@ -36,26 +40,26 @@ except ImportError:
     import ifupdown.ifupdownconfig as ifupdownconfig
 
 
-class xfrm(moduleBase):
+class xfrm(Addon, moduleBase):
     """  ifupdown2 addon module to create a xfrm interface """
-    _modinfo = {'mhelp' : 'xfrm module creates a xfrm interface for',
-                'attrs' : {
-                    'xfrm-id' :
-                        { 'help' : 'xfrm id',
-                          'validrange' : ['1', '65535'],
-                          'example': ['xfrm-id 1'] 
-                        },
-                    'xfrm-physdev':
-                        {'help': 'xfrm physical device',
-                         'example': ['xfrm-physdev lo']
-                        },
-                    },
-                }
-
+    _modinfo = {
+        'mhelp': 'xfrm module creates a xfrm interface for',
+        'attrs': {
+            'xfrm-id': {
+                'help': 'xfrm id',
+                'validrange': ['1', '65535'],
+                'example': ['xfrm-id 1']
+            },
+            'xfrm-physdev': {
+                'help': 'xfrm physical device',
+                'example': ['xfrm-physdev lo']
+            },
+        },
+    }
 
     def __init__(self, *args, **kargs):
+        Addon.__init__(self)
         moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
 
     def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
 
@@ -96,25 +100,29 @@ class xfrm(moduleBase):
         xfrm_ifacename = self._get_xfrm_name(ifaceobj)
         physdev = self._get_parent_ifacename(ifaceobj)
         xfrmid = self._get_xfrmid(ifaceobj)
-        if not self.ipcmd.link_exists(xfrm_ifacename):
-            try:
-                netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
-            except:
-                self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
-            link_created = True
+        if not self.cache.link_exists(xfrm_ifacename):
+            self.iproute2.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
         else:
-            current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
-            xfrmid_cur = current_attrs.get('xfrm-id', None)
-            physdev_cur = current_attrs.get('xfrm-physdev', None)
+            xfrmid_cur = str(
+                self.cache.get_link_info_data_attribute(
+                    xfrm_ifacename,
+                    Link.IFLA_XFRM_IF_ID,
+                    0
+                )
+            )
+            physdev_cur = self.cache.get_ifname(
+                self.cache.get_link_info_data_attribute(
+                    xfrm_ifacename,
+                    Link.IFLA_XFRM_LINK,
+                    0
+                )
+            )
+
             # Check XFRM Values
             if xfrmid != xfrmid_cur or physdev != physdev_cur:
                 # Delete and recreate
-                self.ipcmd.link_delete(xfrm_ifacename)
-                try:
-                    netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
-                except:
-                    self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
-                link_created = True
+                self.netlink.link_del(xfrm_ifacename)
+                self.iproute2.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
 
     def _down(self, ifaceobj, ifaceobj_getfunc=None):
         """
@@ -122,17 +130,17 @@ class xfrm(moduleBase):
         """
         try:
             xfrm_ifacename = self._get_xfrm_name(ifaceobj)
-            self.ipcmd.link_delete(xfrm_ifacename)
-        except Exception, e:
+            self.netlink.link_del(xfrm_ifacename)
+        except Exception as e:
             self.log_warn(str(e))
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
+        if not self.cache.link_exists(ifaceobj.name):
             return
         ifaceobjcurr.status = ifaceStatus.SUCCESS
 
     def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+        if not self.cache.link_exists(ifaceobjrunning.name):
             return
 
     # Operations supported by this addon (yet).
@@ -146,10 +154,6 @@ class xfrm(moduleBase):
     def get_ops(self):
         return self._run_ops.keys()
 
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = LinkUtils()
-
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         op_handler = self._run_ops.get(operation)
 
@@ -159,7 +163,6 @@ class xfrm(moduleBase):
         if operation != 'query-running' and not self._is_my_interface(ifaceobj):
             return
 
-        self._init_command_handlers()
         if operation == 'query-checkcurr':
             op_handler(self, ifaceobj, query_ifaceobj)
         else:
index 5c442679b0d3a28103aeb7b7c98825da845c92c8..4e1da40880b5f313006ce0558261cad0c21dc81a 100644 (file)
@@ -12,10 +12,10 @@ import argcomplete
 
 try:
     from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.exceptions import ArgvParseError
+    from ifupdown2.ifupdown.exceptions import ArgvParseError, ArgvParseHelp
 except:
     from ifupdown.utils import utils
-    from ifupdown.exceptions import ArgvParseError
+    from ifupdown.exceptions import ArgvParseError, ArgvParseHelp
 
 
 class VersionAction(argparse.Action):
@@ -78,7 +78,18 @@ class Parse:
                 self.update_ifquery_argparser(argparser)
         self.update_common_argparser(argparser)
         argcomplete.autocomplete(argparser)
-        self.args = argparser.parse_args(self.argv)
+
+        try:
+            self.args = argparser.parse_args(self.argv)
+        except SystemExit as e:
+            # on "--help" parse_args will raise SystemExit.
+            # We need to catch this behavior and raise a custom
+            # exception to return 0 properly
+            #raise ArgvParseHelp()
+            for help_str in ('-h', '--help'):
+                if help_str in argv:
+                    raise ArgvParseHelp()
+            raise
 
     def validate(self):
         if self.op == 'query' and (self.args.syntaxhelp or self.args.list):
@@ -102,8 +113,9 @@ class Parse:
             for key, value in self.valid_ops.iteritems():
                 if self.executable_name.endswith(key):
                     return value
+            raise ArgvParseError("Unexpected executable. Should be '%s'" % "' or '".join(self.valid_ops.keys()))
         except:
-            raise ArgvParseError("Unexpected executable. Should be 'ifup' or 'ifdown' or 'ifquery'")
+            raise ArgvParseError("Unexpected executable. Should be '%s'" % "' or '".join(self.valid_ops.keys()))
 
     def get_args(self):
         return self.args
@@ -140,7 +152,7 @@ class Parse:
     def update_ifupdown_argparser(self, argparser):
         """ common arg parser for ifup and ifdown """
         argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
-        argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true')
         group = argparser.add_mutually_exclusive_group(required=False)
         group.add_argument('-n', '--no-act', dest='noact', action='store_true',
                            help="print out what would happen, but don't do it")
@@ -220,7 +232,7 @@ class Parse:
                                     'With this option ifreload will only look at the current interfaces file. '
                                     'Useful when your state file is corrupted or you want down to use the latest '
                                     'from the interfaces file')
-        argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true')
         argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
         argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck', action='store_true',
                                help='Only run the interfaces file parser')
diff --git a/ifupdown2/ifupdown/client.py b/ifupdown2/ifupdown/client.py
new file mode 100644 (file)
index 0000000..2776880
--- /dev/null
@@ -0,0 +1,232 @@
+# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown2 client-side
+#
+
+import struct
+import pickle
+
+import SocketServer
+
+import logging
+import logging.handlers
+
+import os
+import re
+import sys
+import json
+import socket
+import signal
+
+try:
+    from ifupdown2.lib.io import SocketIO
+    from ifupdown2.lib.status import Status
+    from ifupdown2.lib.log import LogManager, root_logger
+    from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError
+
+    from ifupdown2.ifupdown.argv import Parse
+except:
+    from lib.status import Status
+    from lib.io import SocketIO
+    from lib.log import LogManager, root_logger
+    from lib.exceptions import ExitWithStatus, ExitWithStatusAndError
+
+    from ifupdown.argv import Parse
+
+
+class LogRecordStreamHandler(SocketServer.StreamRequestHandler):
+    """
+    Handler for a streaming logging request.
+    This basically logs the record using whatever logging policy is configured
+    locally.
+    """
+
+    def handle(self):
+        """
+        Handle multiple requests - each expected to be a 4-byte length,
+        followed by the LogRecord in pickle format.
+        """
+        while True:
+            chunk = self.connection.recv(4)
+            if len(chunk) < 4:
+                break
+            slen = struct.unpack(">L", chunk)[0]
+            chunk = self.connection.recv(slen)
+
+            while len(chunk) < slen:
+                chunk = chunk + self.connection.recv(slen - len(chunk))
+
+            record = logging.makeLogRecord(pickle.loads(chunk))
+            logging.getLogger(record.name).handle(record)
+
+
+class LogRecordSocketReceiver(SocketServer.TCPServer):
+    """
+    Simple TCP socket-based logging receiver. In ifupdown2d context, the running
+    daemon is the "sender" and the client is the "receiver". The TCPServer is
+    setup on the client/receiver side, the daemon will connect to the server to
+    transmit and stream LogRecord to a socket.
+    """
+    allow_reuse_address = True
+
+    def __init__(
+            self,
+            host="localhost",
+            handler=LogRecordStreamHandler,
+            port=LogManager.DEFAULT_TCP_LOGGING_PORT
+    ):
+        SocketServer.TCPServer.__init__(self, (host, port), handler)
+
+
+class Client(SocketIO):
+    def __init__(self, argv):
+        SocketIO.__init__(self)
+
+        # we setup our log receiver which reads LogRecord from a socket
+        # thus handing the logging-handling to the logging module and it's
+        # dedicated classes.
+        self.socket_receiver = LogRecordSocketReceiver()
+
+        self.stdin = None
+        self.argv = argv
+
+        # First we need to set the correct log level for the client.
+        # Unfortunately the only reliable way to do this is to use our main
+        # argument parser. We can't simply have a parse to catch -v and -d, a
+        # simple command like "ifup -av" wouldn't be recognized...
+        # Ideally it would be great to be able to send the Namespace returned by
+        # parse_args, and send it on the socket in pickle format. Unfortunately
+        # this might be a serious security issue. It needs to be studied and
+        # evaluated a bit more, that way we would save time and resources only
+        # parsing argv once.
+        args_parse = Parse(argv)
+        args_parse.validate()
+        # store the args namespace to send it to the daemon, we don't want
+        # the daemon to spend time to parsing argv again...
+        self.args = args_parse.get_args()
+
+        LogManager.get_instance().start_client_logging(self.args)
+
+        root_logger.info("starting ifupdown2 client...")
+
+        self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        try:
+            self.uds.connect("/var/run/ifupdown2d/uds")
+        except socket.error:
+            self.__shutdown()
+            sys.stderr.write("""
+    ERROR: %s could not connect to ifupdown2 daemon
+
+    Try starting ifupdown2 daemon with:
+    sudo systemctl start ifupdown2
+
+    To configure ifupdown2d to start when the box boots:
+    sudo systemctl enable ifupdown2\n\n""" % argv[0])
+            raise ExitWithStatus(status=Status.Client.STATUS_COULD_NOT_CONNECT)
+
+        signal.signal(signal.SIGINT, self.__signal_handler)
+        signal.signal(signal.SIGTERM, self.__signal_handler)
+        signal.signal(signal.SIGQUIT, self.__signal_handler)
+
+        try:
+            self.SO_PEERCRED = socket.SO_PEERCRED
+        except AttributeError:
+            # powerpc is the only non-generic we care about. alpha, mips,
+            # sparc, and parisc also have non-generic values.
+            machine = os.uname()[4]
+            if re.search(r"^(ppc|powerpc)", machine):
+                self.SO_PASSCRED = 20
+                self.SO_PEERCRED = 21
+            else:
+                self.SO_PASSCRED = 16
+                self.SO_PEERCRED = 17
+        try:
+            self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
+        except Exception as e:
+            self.__shutdown()
+            raise Exception("setsockopt: %s" % str(e))
+
+        self.daemon_pid, _, _ = self.get_socket_peer_cred(self.uds)
+
+        if self.daemon_pid < 0:
+            self.__shutdown()
+            raise ExitWithStatusAndError(
+                status=Status.Client.STATUS_NO_PID,
+                message="could not get ifupdown2 daemon PID"
+            )
+
+        root_logger.info("connection to ifupdown2d successful (server pid %s)" % self.daemon_pid)
+
+    def __shutdown(self):
+        try:
+            self.uds.close()
+            self.uds = None
+        except:
+            pass
+        try:
+            self.socket_receiver.server_close()
+            self.socket_receiver = None
+        except:
+            pass
+
+    def __signal_handler(self, sig, frame):
+        """ Forward all signals to daemon """
+        if self.daemon_pid > 0:
+            os.kill(self.daemon_pid, sig)
+
+    def __get_stdin(self):
+        """
+        If stdin data is provided we need to store it to forward it to the
+        daemon
+        """
+        if hasattr(self.args, "interfacesfile") and self.args.interfacesfile == "-":
+            return sys.stdin.read()
+
+    def run(self):
+        try:
+            # First we need to send the user request to the daemon (argv + stdin)
+            self.tx_data(self.uds, json.dumps({
+                "argv": self.argv,
+                "stdin": self.__get_stdin()
+            }))
+
+            # Then "handle_request" will block until the daemon closes
+            # the channel, meaning that the request was processed.
+            self.socket_receiver.handle_request()
+            self.socket_receiver.server_close()
+
+            # Next the daemon should send us a dictionary containing stdout and
+            # stderr buffers as well as the request's exit status. We print those
+            # buffers in the correct channel and exit with the request status.
+            response = self.rx_json_packet(self.uds)
+
+            if response:
+                sys.stdout.write(response.get("stdout", ""))
+                sys.stderr.write(response.get("stderr", ""))
+
+                status = response.get("status", Status.Client.STATUS_EMPTY)
+            else:
+                status = Status.Client.STATUS_EMPTY
+
+            return status
+        finally:
+            self.__shutdown()
index 9537f21d30c506b1b2a7fa728a7a0df36e7d3188..0dd16a60369eddc8a3514a1d757789fd744b97f8 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
 # Authors:
 #           Roopa Prabhu, roopa@cumulusnetworks.com
@@ -9,10 +7,9 @@
 #    exceptions
 #
 
-try:
-    from ifupdown2.ifupdown.log import log
-except:
-    from ifupdown.log import log
+import logging
+
+log = logging.getLogger()
 
 
 class Error(Exception):
@@ -38,6 +35,14 @@ class ArgvParseError(Error):
     pass
 
 
+class ArgvParseHelp(Error):
+    """
+    When ifupdown2 is called with --help argparse raise SystemExit
+    we need to catch this to properly print the help and exit 0 not 1
+    """
+    pass
+
+
 class ifaceNotFoundError(Error):
     pass
 
index bb095358997223f4a84431a0723de0aa9906ad44..729fc7e768c8f199ff4b616b058fd04dc2750144 100644 (file)
@@ -14,9 +14,12 @@ file. But can be extended to include any other network interface format
 """
 
 import json
+import logging
 
 from collections import OrderedDict
 
+log = logging.getLogger()
+
 
 class ifaceStatusUserStrs():
     """ This class declares strings user can see during an ifquery --check
@@ -47,16 +50,16 @@ class ifaceLinkKind():
         bond have an ifaceobj.role attribute of SLAVE and the bridge or
         bond itself has ifaceobj.role of MASTER.
     """
-    UNKNOWN         = 0x0000000
-    BRIDGE          = 0x0000001
-    BOND            = 0x0000010
-    VLAN            = 0x0000100
-    VXLAN           = 0x0001000
-    VRF             = 0x0010000
-    BATMAN_ADV      = 0x0100000
+    UNKNOWN = 0x000000
+    BRIDGE =  0x000001
+    BOND =    0x000010
+    VLAN =    0x000100
+    VXLAN =   0x001000
+    VRF =     0x010000
+    BATMAN_ADV = 0x0100000
     # to indicate logical interface created by an external entity.
     # the 'kind' of which ifupdown2 does not really understand
-    OTHER           = 0x1000000
+    OTHER =     0x100000
 
     @classmethod
     def to_str(cls, kind):
@@ -70,6 +73,8 @@ class ifaceLinkKind():
             return "vxlan"
         elif kind == cls.VRF:
             return "vrf"
+        else:
+            return "OTHER"
 
 class ifaceLinkPrivFlags():
     """ This corresponds to kernel netdev->priv_flags
@@ -86,33 +91,34 @@ class ifaceLinkPrivFlags():
 
     @classmethod
     def get_str(cls, flag):
-        if flag == cls.UNKNOWN:
-            return 'unknown'
-        elif flag == cls.BRIDGE_PORT:
-            return 'bridge port'
-        elif flag == cls.BOND_SLAVE:
-            return 'bond slave'
-        elif flag == cls.VRF_SLAVE:
-            return 'vrf slave'
-        elif flag == cls.BRIDGE_VLAN_AWARE:
-            return 'vlan aware bridge'
-        elif flag == cls.BRIDGE_VXLAN:
-            return 'vxlan bridge'
+        string_list = []
+
+        if flag & cls.BRIDGE_PORT:
+            string_list.append("bridge port")
+
+        if flag & cls.BOND_SLAVE:
+            string_list.append("bond slave")
+
+        if flag & cls.VRF_SLAVE:
+            string_list.append("vrf slave")
+
+        if flag & cls.BRIDGE_VLAN_AWARE:
+            string_list.append("vlan aware bridge")
+
+        if flag & cls.BRIDGE_VXLAN:
+            string_list.append("vxlan bridge")
+
+        if flag & cls.ADDRESS_VIRTUAL_SLAVE:
+            string_list.append("address virtual slave")
+
+        if flag & cls.LOOPBACK:
+            string_list.append("loopback")
+
+        if flag & cls.KEEP_LINK_DOWN:
+            string_list.append("keep ling down")
+
+        return ", ".join(string_list)
 
-    @classmethod
-    def get_all_str(cls, flags):
-        str = ''
-        if flags & cls.BRIDGE_PORT:
-            str += 'bridgeport '
-        if flags & cls.BOND_SLAVE:
-            str += 'bondslave '
-        if flags & cls.VRF_SLAVE:
-            str += 'vrfslave '
-        if flags & cls.BRIDGE_VLAN_AWARE:
-            str += 'vlanawarebridge '
-        if flags & cls.BRIDGE_VXLAN:
-            str += 'vxlanbridge '
-        return str
 
 class ifaceLinkType():
     LINK_UNKNOWN = 0x0
@@ -647,14 +653,13 @@ class iface():
         del odict['env']
         del odict['link_type']
         del odict['link_kind']
-        del odict['link_privflags']
+        #del odict['link_privflags']
         del odict['role']
         del odict['dependency_type']
         del odict['blacklisted']
         return odict
 
     def __setstate__(self, dict):
-        self.__dict__.update(dict)
         self._config_status = {}
         self.state = ifaceState.NEW
         self.status = ifaceStatus.UNKNOWN
@@ -674,6 +679,7 @@ class iface():
         self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
         self.dependency_type = ifaceDependencyType.UNKNOWN
         self.blacklisted = False
+        self.__dict__.update(dict)
 
     def dump_raw(self, logger):
         indent = '  '
@@ -706,6 +712,24 @@ class iface():
         else:
             logger.info(indent + 'upperdevs: None')
 
+        logger.info("%srole: %s" % (indent, {
+            ifaceRole.UNKNOWN: "UNKNOWN",
+            ifaceRole.SLAVE: "SLAVE",
+            ifaceRole.MASTER: "MASTER"}.get(self.role)))
+
+        logger.info("%stype: %s" % (indent, {
+            ifaceType.UNKNOWN: "UNKNOWN",
+            ifaceType.IFACE: "IFACE",
+            ifaceType.BRIDGE_VLAN: "BRIDGE_VLAN"}.get(self.type)))
+
+        logger.info("%slink_kind: %s" % (indent, ifaceLinkKind.to_str(self.link_kind)))
+
+        logger.info("%slink_privflags: %s" % (indent, ifaceLinkPrivFlags.get_str(self.link_privflags)))
+
+        if self.priv_flags:
+            if self.priv_flags.BUILTIN:
+                logger.info("%spriv_flags: BUILTIN" % indent)
+
         logger.info(indent + 'config: ')
         config = self.config
         if config:
@@ -768,7 +792,8 @@ class iface():
                         outbuf += (indent + '{0:55} {1:>10}'.format(
                               '%s %s' %(cname, cv), status_str)) + '\n'
                     else:
-                        outbuf += indent + '%s %s\n' %(cname, cv)
+                        if cv:
+                            outbuf += indent + '%s %s\n' % (cname, cv)
                     idx += 1
         if with_status:
             outbuf = (outbuf.encode('utf8')
diff --git a/ifupdown2/ifupdown/iff.py b/ifupdown2/ifupdown/iff.py
deleted file mode 100644 (file)
index c7c5d65..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Scott Feldman, sfeldma@cumulusnetworks.com
-#
-#
-# from /usr/include/linux/if.h
-#
-
-# Standard interface flags (netdevice->flags).
-
-IFF_UP = 0x1                  # interface is up
-IFF_BROADCAST = 0x2           # broadcast address valid
-IFF_DEBUG = 0x4               # turn on debugging
-IFF_LOOPBACK = 0x8            # is a loopback net
-IFF_POINTOPOINT = 0x10        # interface is has p-p link
-IFF_NOTRAILERS = 0x20         # avoid use of trailers
-IFF_RUNNING = 0x40            # interface RFC2863 OPER_UP
-IFF_NOARP = 0x80              # no ARP protocol
-IFF_PROMISC = 0x100           # receive all packets
-IFF_ALLMULTI = 0x200          # receive all multicast packets
-
-IFF_MASTER = 0x400            # master of a load balancer
-IFF_SLAVE = 0x800             # slave of a load balancer
-
-IFF_MULTICAST = 0x1000        # Supports multicast
-
-IFF_PORTSEL = 0x2000          # can set media type
-IFF_AUTOMEDIA = 0x4000        # auto media select active
-IFF_DYNAMIC = 0x8000          # dialup device with changing addresses
-
-IFF_LOWER_UP = 0x10000        # driver signals L1 up
-IFF_DORMANT = 0x20000         # driver signals dormant
-
-IFF_ECHO = 0x40000            # echo sent packets
diff --git a/ifupdown2/ifupdown/ifupdownbase.py b/ifupdown2/ifupdown/ifupdownbase.py
deleted file mode 100644 (file)
index fd7c730..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifupdownBase --
-#    base object for various ifupdown objects
-#
-
-import re
-import os
-import logging
-import traceback
-
-try:
-    from ifupdown2.ifupdown.netlink import netlink
-
-    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
-except ImportError:
-    from ifupdown.netlink import netlink
-
-    import ifupdown.ifupdownflags as ifupdownflags
-
-
-class ifupdownBase(object):
-
-    def __init__(self):
-        modulename = self.__class__.__name__
-        self.logger = logging.getLogger('ifupdown.' + modulename)
-
-    def ignore_error(self, errmsg):
-        if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
-            re.IGNORECASE | re.MULTILINE) is not None):
-            return True
-        return False
-
-    def log_warn(self, str):
-        if self.ignore_error(str) == False:
-            if self.logger.getEffectiveLevel() == logging.DEBUG:
-                traceback.print_stack()
-                traceback.print_exc()
-            self.logger.warn(str)
-        pass
-
-    def log_error(self, str):
-        if self.ignore_error(str) == False:
-            raise Exception(str)
-        else:
-            pass
-
-    def link_exists(self, ifacename):
-        return os.path.exists('/sys/class/net/%s' %ifacename)
-
-    def link_up(self, ifacename):
-        netlink.link_set_updown(ifacename, "up")
-
-    def link_down(self, ifacename):
-        netlink.link_set_updown(ifacename, "down")
index 57b17a2743a906e0a1a69b854b6fed4f877c9986..b6afe58deb5f4355e0b0dd799e94b46ca488c8a9 100644 (file)
@@ -7,6 +7,10 @@
 #    ifupdown main module
 #
 
+import re
+import os
+import logging
+import traceback
 import pprint
 
 from collections import OrderedDict
@@ -14,12 +18,11 @@ from collections import OrderedDict
 from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
 
 try:
-    import ifupdown2.ifupdownaddons.cache
-    import ifupdown2.ifupdownaddons.LinkUtils
+    import ifupdown2.lib.nlcache as nlcache
+
     import ifupdown2.ifupdownaddons.mstpctlutil
 
     import ifupdown2.ifupdown.policymanager
-    import ifupdown2.ifupdown.ifupdownflags
     import ifupdown2.ifupdown.statemanager as statemanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
     import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig
@@ -31,8 +34,8 @@ try:
     from ifupdown2.ifupdown.networkinterfaces import *
     from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
 except ImportError:
-    import ifupdownaddons.cache
-    import ifupdownaddons.LinkUtils
+    import lib.nlcache as nlcache
+
     import ifupdownaddons.mstpctlutil
 
     import ifupdown.ifupdownflags
@@ -80,7 +83,7 @@ class ifacePrivFlags():
         self.BUILTIN = builtin
         self.NOCONFIG = noconfig
 
-class ifupdownMain(ifupdownBase):
+class ifupdownMain:
     """ ifupdown2 main class """
 
     scripts_dir = '/etc/network'
@@ -111,7 +114,7 @@ class ifupdownMain(ifupdownBase):
         if self._keep_link_down(ifaceobj):
             return
         try:
-            self.link_up(ifaceobj.name)
+            self.netlink.link_up(ifaceobj.name)
         except:
             if ifaceobj.addr_method == 'manual':
                 pass
@@ -123,7 +126,7 @@ class ifupdownMain(ifupdownBase):
             # user has asked to explicitly keep the link down,
             # so, force link down
             self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
-            self.link_down(ifaceobj.name)
+            self.netlink.link_down(ifaceobj.name)
             return True
         return False
 
@@ -147,7 +150,11 @@ class ifupdownMain(ifupdownBase):
         if not self.link_exists(ifaceobj.name):
            return
         try:
-            self.link_down(ifaceobj.name)
+            if not ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
+                # set intf down (except loopback)
+                self.netlink.link_down(ifaceobj.name)
+            else:
+                self.logger.info("%s: ifupdown2 cannot bring loopback interface down" % ifaceobj.name)
         except:
             if ifaceobj.addr_method == 'manual':
                 pass
@@ -169,17 +176,48 @@ class ifupdownMain(ifupdownBase):
     sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
 
     def reset_ifupdown2(self):
+        self.modules = OrderedDict({})
+        self.module_attrs = {}
+
         ifaceScheduler.reset()
+        try:
+            ifupdown2.ifupdown.statemanager.reset()
+            ifupdown2.ifupdown.policymanager.reset()
+            ifupdown2.ifupdown.ifupdownflags.reset()
+            ifupdownConfig.reset()
+            ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
+        except:
+            try:
+                ifupdown.statemanager.reset()
+                ifupdown.policymanager.reset()
+                ifupdown.ifupdownflags.reset()
+                ifupdownConfig.reset()
+                ifupdownaddons.mstpctlutil.mstpctlutil.reset()
+            except:
+                pass
+
+    def ignore_error(self, errmsg):
+        if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
+            re.IGNORECASE | re.MULTILINE) is not None):
+            return True
+        return False
 
-        ifupdown2.ifupdown.statemanager.reset()
-        ifupdown2.ifupdown.policymanager.reset()
-        ifupdown2.ifupdown.ifupdownflags.reset()
-        ifupdownConfig.reset()
-        ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
-        ifupdown2.ifupdownaddons.LinkUtils.LinkUtils.reset()
+    def log_warn(self, str):
+        if self.ignore_error(str) == False:
+            if self.logger.getEffectiveLevel() == logging.DEBUG:
+                traceback.print_stack()
+                traceback.print_exc()
+            self.logger.warn(str)
+        pass
+
+    def log_error(self, str):
+        if self.ignore_error(str) == False:
+            raise Exception(str)
+        else:
+            pass
 
-        ifupdown2.ifupdownaddons.cache.linkCache.reset()
-        ifupdown2.ifupdownaddons.cache.MSTPAttrsCache.invalidate()
+    def link_exists(self, ifacename):
+        return os.path.exists('/sys/class/net/%s' %ifacename)
 
     def __init__(self, config={},
                  daemon=False, force=False, dryrun=False, nowait=False,
@@ -202,8 +240,21 @@ class ifupdownMain(ifupdownBase):
         Raises:
             AttributeError, KeyError """
 
+        modulename = self.__class__.__name__
+        self.logger = logging.getLogger('ifupdown.' + modulename)
+
         if daemon:
             self.reset_ifupdown2()
+        else:
+            # init nlcache with appropriate log level
+            nlcache.NetlinkListenerWithCache.init(logging.root.level)
+
+            # start netlink listener and cache link/addr/netconf dumps
+            nlcache.NetlinkListenerWithCache.get_instance().start()
+
+        # save reference to nlcache
+        self.netlink = nlcache.NetlinkListenerWithCache.get_instance()
+        self.netlink.reset_errorq()
 
         # iface dictionary in the below format:
         # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
@@ -444,9 +495,6 @@ class ifupdownMain(ifupdownBase):
         else:
             return ifaceobjlist[idx]
 
-    def get_ifaceobjrunning(self, ifacename):
-        return self.ifaceobjrunningdict.get(ifacename)
-
     def get_iface_refcnt(self, ifacename):
         """ Return iface ref count """
         max = 0
@@ -522,7 +570,7 @@ class ifupdownMain(ifupdownBase):
             (role == ifaceRole.SLAVE) and (upperifaceobj.role & ifaceRole.MASTER)):
                self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
                                   %(ifaceobj.name,
-                                    ifaceLinkPrivFlags.get_all_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
+                                    ifaceLinkPrivFlags.get_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
                 ifaceobj.set_status(ifaceStatus.ERROR)
                 return
         ifaceobj.role = role
@@ -855,13 +903,12 @@ class ifupdownMain(ifupdownBase):
 
     def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
         """
-            <mac> <ip> [<ip> ...]
+            MAC address followed by optional list of ip addresses
+            <mac> [<ip> <ip> ...]
             ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
         """
         try:
             res = value.split()
-            if len(res) < 2:
-                return False
             if not self._keyword_mac(res[0]):
                 return False
             for ip in res[1:]:
@@ -1254,7 +1301,7 @@ class ifupdownMain(ifupdownBase):
                 continue
         return ret
 
-    def read_iface_config(self):
+    def read_iface_config(self, raw=False):
         """ Reads default network interface config /etc/network/interfaces. """
         ret = True
         nifaces = networkInterfaces(self.interfacesfile,
@@ -1262,7 +1309,8 @@ class ifupdownMain(ifupdownBase):
                         self.interfacesfileformat,
                         template_enable=self.config.get('template_enable', 0),
                         template_engine=self.config.get('template_engine'),
-                template_lookuppath=self.config.get('template_lookuppath'))
+                template_lookuppath=self.config.get('template_lookuppath'),
+                                    raw=raw)
         if self._ifaceobj_squash or self._ifaceobj_squash_internal:
             nifaces.subscribe('iface_found', self._save_iface_squash)
         else:
@@ -1331,7 +1379,7 @@ class ifupdownMain(ifupdownBase):
                                 script_override = minstance.get_overrides_ifupdown_scripts()
                                 self.overridden_ifupdown_scripts.extend(script_override)
                             except moduleNotSupported, e:
-                                self.logger.info('module %s not loaded (%s)\n'
+                                self.logger.info('module %s not loaded (%s)'
                                                  %(mname, str(e)))
                                 continue
                             except:
@@ -1598,9 +1646,9 @@ class ifupdownMain(ifupdownBase):
         if not self._delay_admin_state_iface_queue:
            return
         if op == 'up':
-           func = self.link_up
+           func = self.netlink.link_up
         elif op == 'down':
-           func = self.link_down
+           func = self.netlink.link_down
         else:
            return
         for i in self._delay_admin_state_iface_queue:
@@ -1611,6 +1659,38 @@ class ifupdownMain(ifupdownBase):
                 self.logger.warn(str(e))
                 pass
 
+    def _get_iface_exclude_companion(self, ifacename):
+        try:
+            return ifupdown2.ifupdown.policymanager.policymanager_api.get_iface_default(
+                                    module_name='main', ifname=ifacename,
+                                    attr='exclude-companion')
+        except:
+            return ifupdown.policymanager.policymanager_api.get_iface_default(
+                                    module_name='main', ifname=ifacename,
+                                    attr='exclude-companion')
+
+    def _preprocess_excludepats(self, excludepats):
+        new_excludepats = excludepats
+        for e in excludepats:
+            ifaceobjs = self.get_ifaceobjs(e)
+            for iobj in ifaceobjs or []:
+                ec = iobj.get_attr_value_first('exclude-companion')
+                if not ec:
+                    ec = self._get_iface_exclude_companion(e)
+                    if not ec:
+                        continue
+                    else:
+                        ec = ec.encode('ascii','ignore')
+                        for ee in ec.split():
+                            if ee in new_excludepats:
+                                continue
+                            if self.get_ifaceobjs(ee):
+                                # if we know the object add it to the new
+                                # excludepats list
+                                new_excludepats.append(ee)
+        self.logger.info('excludepats after processing companions [%s]' %' '.join(new_excludepats))
+        return new_excludepats
+
     def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
            excludepats=None, printdependency=None, syntaxcheck=False,
            type=None, skipupperifaces=False):
@@ -1642,6 +1722,9 @@ class ifupdownMain(ifupdownBase):
         except Exception:
             raise
 
+        if excludepats:
+            excludepats = self._preprocess_excludepats(excludepats)
+
         filtered_ifacenames = None
         if ifacenames:
             ifacenames = self._preprocess_ifacenames(ifacenames)
@@ -1695,6 +1778,35 @@ class ifupdownMain(ifupdownBase):
         if not iface_read_ret or not ret:
             raise Exception()
 
+        self.check_running_configuration(filtered_ifacenames)
+
+    def check_running_configuration(self, filtered_ifacenames, all=False):
+        """
+        Print warning and better info message when we don't recognize an interface
+        AKA when the interface wasn't created.
+
+        :param filtered_ifacenames:
+        :param all:
+        :return:
+        """
+        if not filtered_ifacenames:
+            filtered_ifacenames = []
+
+        for ifname, ifaceobj_list in self.ifaceobjdict.iteritems():
+
+            if not all and ifname not in filtered_ifacenames:
+                continue
+
+            auto = True
+
+            for ifaceobj in ifaceobj_list:
+                if not ifaceobj.auto:
+                    auto = False
+                    break
+
+            if auto and not os.path.exists("/sys/class/net/%s" % ifname):
+                self.logger.warning("%s: interface not recognized - please check interface configuration" % ifname)
+
     def _get_filtered_ifacenames_with_classes(self, auto, allow_classes, excludepats, ifacenames):
         # if user has specified ifacelist and allow_classes
         # append the allow_classes interfaces to user
@@ -1738,6 +1850,10 @@ class ifupdownMain(ifupdownBase):
                 self.read_iface_config()
             except Exception, e:
                 raise Exception('error reading iface config (%s)' %str(e))
+
+        if excludepats:
+            excludepats = self._preprocess_excludepats(excludepats)
+
         filtered_ifacenames = None
         if ifacenames:
             # If iface list is given by the caller, always check if iface
@@ -1817,7 +1933,7 @@ class ifupdownMain(ifupdownBase):
                                 ifacePrivFlags(False, True)), ifacenames)
         else:
             try:
-                iface_read_ret = self.read_iface_config()
+                iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw")
             except Exception:
                 raise
 
@@ -1859,7 +1975,7 @@ class ifupdownMain(ifupdownBase):
         if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
             return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
         elif ops[0] == 'query-raw':
-            return self.print_ifaceobjs_raw(filtered_ifacenames)
+            return self.print_ifaceobjs_raw(filtered_ifacenames, format)
 
         ret = self._sched_ifaces(filtered_ifacenames, ops,
                                  followdependents=True
@@ -2028,6 +2144,9 @@ class ifupdownMain(ifupdownBase):
             new_ifaceobjdict = self.ifaceobjdict
             new_dependency_graph = self.dependency_graph
 
+        if excludepats:
+            excludepats = self._preprocess_excludepats(excludepats)
+
         if op == 'reload' and ifacenames:
             ifacenames = self.ifaceobjdict.keys()
             old_filtered_ifacenames = [i for i in ifacenames
@@ -2236,6 +2355,8 @@ class ifupdownMain(ifupdownBase):
         else:
             self._reload_default(*args, **kargs)
 
+        self.check_running_configuration([], all=True)
+
     def _any_iface_errors(self, ifacenames):
         for i in ifacenames:
             ifaceobjs = self.get_ifaceobjs(i)
@@ -2271,9 +2392,13 @@ class ifupdownMain(ifupdownBase):
         for i in ifacenames:
             print i
 
-    def print_ifaceobjs_raw(self, ifacenames):
+    def print_ifaceobjs_raw(self, ifacenames, format=None):
         """ prints raw lines for ifaces from config file """
 
+        if format == "json":
+            self.print_ifaceobjs_pretty(ifacenames, format)
+            return
+
         for i in ifacenames:
             for ifaceobj in self.get_ifaceobjs(i):
                 if self.is_ifaceobj_builtin(ifaceobj):
@@ -2310,7 +2435,7 @@ class ifupdownMain(ifupdownBase):
         if not ifaceobjs: return
         if format == 'json':
             print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
-                             indent=4, separators=(',', ': '))
+                             indent=2, separators=(',', ': '))
         else:
             expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
             for i in ifaceobjs:
diff --git a/ifupdown2/ifupdown/log.py b/ifupdown2/ifupdown/log.py
deleted file mode 100644 (file)
index cd09e34..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
-# Author: Julien Fortin, julien@cumulusnetworks.com
-#
-
-import sys
-import json
-import struct
-import select
-import logging
-import logging.handlers
-
-from cStringIO import StringIO
-
-
-class Log:
-    LOGGER_NAME = sys.argv[0].split('/')[-1]
-
-    def __init__(self):
-        """
-            - On start the daemon will log on syslog.
-            - For each client commands we might need to adjust the target
-            (stderr/stdout):
-                if -v --verbose or -d --debug are provided we override
-                sys.stdout and sys.stderr with string buffers to be able to send
-                back the content of these buffer on the UNIX socket back to the
-                client.
-                if -l or --syslog we make sure to use syslog.
-        """
-
-        self.stdout_buffer = None
-        self.stderr_buffer = None
-
-        self.root = logging.getLogger()
-        self.root.name = Log.LOGGER_NAME
-        self.root.setLevel(logging.INFO)
-
-        self.root_info = self.root.info
-        self.root_debug = self.root.debug
-        self.root_error = self.root.error
-        self.root_warning = self.root.warning
-        self.root_critical = self.root.critical
-
-        self.root.info = self.info
-        self.root.debug = self.debug
-        self.root.error = self.error
-        self.root.warning = self.warning
-        self.root.critical = self.critical
-
-        logging.addLevelName(logging.CRITICAL, 'critical')
-        logging.addLevelName(logging.WARNING, 'warning')
-        logging.addLevelName(logging.ERROR, 'error')
-        logging.addLevelName(logging.DEBUG, 'debug')
-        logging.addLevelName(logging.INFO, 'info')
-
-        self.syslog = True
-        self.socket = None
-
-        # syslog
-        facility = logging.handlers.SysLogHandler.LOG_DAEMON
-        address = '/dev/log'
-        format = '%(name)s: %(levelname)s: %(message)s'
-
-        try:
-            self.syslog_handler = logging.handlers.SysLogHandler(address=address, facility=facility)
-            self.syslog_handler.setFormatter(logging.Formatter(format))
-        except Exception as e:
-            sys.stderr.write("warning: syslogs: %s\n" % str(e))
-            self.syslog_handler = None
-
-        # console
-        format = '%(levelname)s: %(message)s'
-        self.console_handler = logging.StreamHandler(sys.stderr)
-        self.console_handler.setFormatter(logging.Formatter(format))
-
-        if self.syslog_handler and self.LOGGER_NAME[-1] == 'd':
-            self.update_current_logger(syslog=True, verbose=True, debug=False)
-        else:
-            self.update_current_logger(syslog=False, verbose=False, debug=False)
-
-    def update_current_logger(self, syslog, verbose, debug):
-        self.syslog = syslog
-        self.root.setLevel(self.get_log_level(verbose=verbose, debug=debug))
-        self.root.handlers = [self.syslog_handler if self.syslog and self.syslog_handler else self.console_handler]
-        self.flush()
-
-    def flush(self):
-        if self.socket:
-            result = dict()
-            stdout = self._flush_buffer('stdout', self.stdout_buffer, result)
-            stderr = self._flush_buffer('stderr', self.stderr_buffer, result)
-            if stdout or stderr:
-                try:
-                    self.tx_data(json.dumps(result))
-                    self.redirect_stdouput()
-                except select.error as e:
-                    # haven't seen the case yet
-                    self.socket = None
-                    self.update_current_logger(syslog=True, verbose=True)
-                    self.critical(str(e))
-                    exit(84)
-        self.console_handler.flush()
-
-        if self.syslog_handler:
-            self.syslog_handler.flush()
-
-    def tx_data(self, data, socket=None):
-        socket_obj = socket if socket else self.socket
-        ready = select.select([], [socket_obj], [])
-        if ready and ready[1] and ready[1][0] == socket_obj:
-            frmt = "=%ds" % len(data)
-            packed_msg = struct.pack(frmt, data)
-            packed_hdr = struct.pack('=I', len(packed_msg))
-            socket_obj.sendall(packed_hdr)
-            socket_obj.sendall(packed_msg)
-
-    def set_socket(self, socket):
-        self.socket = socket
-        self.redirect_stdouput()
-
-    def redirect_stdouput(self):
-        self.stdout_buffer = sys.stdout = StringIO()
-        self.stderr_buffer = self.console_handler.stream = sys.stderr = StringIO()
-
-    def error(self, msg, *args, **kwargs):
-        self.root_error(msg, *args, **kwargs)
-        self.flush()
-
-    def critical(self, msg, *args, **kwargs):
-        self.root_critical(msg, *args, **kwargs)
-        self.flush()
-
-    def warning(self, msg, *args, **kwargs):
-        self.root_warning(msg, *args, **kwargs)
-        self.flush()
-
-    def info(self, msg, *args, **kwargs):
-        self.root_info(msg, *args, **kwargs)
-        self.flush()
-
-    def debug(self, msg, *args, **kwargs):
-        self.root_debug(msg, *args, **kwargs)
-        self.flush()
-
-    def get_current_log_level(self):
-        return self.root.level
-
-    def is_syslog(self): return self.syslog
-
-    @staticmethod
-    def get_log_level(verbose=False, debug=False):
-        log_level = logging.WARNING
-        if debug:
-            log_level = logging.DEBUG
-        elif verbose:
-            log_level = logging.INFO
-        return log_level
-
-    @staticmethod
-    def _flush_buffer(stream, buff, dictionary):
-        if buff:
-            data = buff.getvalue()
-            if data:
-                dictionary[stream] = data
-                return True
-
-
-log = Log()
index e54ace876c79b3f894ebb2425bacc77453bd9a20..7847ba155fc49136f857e0146b46ad163da874d5 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 #
-# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Copyright 2014-2019 Cumulus Networks, Inc. All rights reserved.
 # Authors:
 #           Roopa Prabhu, roopa@cumulusnetworks.com
 #           Julien Fortin, julien@cumulusnetworks.com
 
 import os
 import sys
-import signal
+import logging
 import StringIO
 import ConfigParser
 
 try:
-    from ifupdown2.ifupdown.log import log
     from ifupdown2.ifupdown.argv import Parse
     from ifupdown2.ifupdown.config import IFUPDOWN2_CONF_PATH
     from ifupdown2.ifupdown.ifupdownmain import ifupdownMain
+
+    from ifupdown2.lib.dry_run import DryRunManager
+
 except ImportError:
-    from ifupdown.log import log
     from ifupdown.argv import Parse
     from ifupdown.config import IFUPDOWN2_CONF_PATH
     from ifupdown.ifupdownmain import ifupdownMain
 
+    from lib.dry_run import DryRunManager
 
-_SIGINT = signal.getsignal(signal.SIGINT)
-_SIGTERM = signal.getsignal(signal.SIGTERM)
-_SIGQUIT = signal.getsignal(signal.SIGQUIT)
 
+log = logging.getLogger()
 configmap_g = None
 
 
@@ -57,14 +57,6 @@ class Ifupdown2:
         self.args = args_parse.get_args()
         self.op = args_parse.get_op()
 
-    def update_logger(self, socket=None):
-        syslog = self.args.syslog if hasattr(self.args, 'syslog') else False
-        log.update_current_logger(syslog=syslog,
-                                  verbose=self.args.verbose,
-                                  debug=self.args.debug)
-        if socket:
-            log.set_socket(socket)
-
     def main(self, stdin_buffer=None):
         if self.op != 'query' and self.uid != 0:
             raise Exception('must be root to run this command')
@@ -80,7 +72,7 @@ class Ifupdown2:
                 raise
             # else:
             if log:
-                log.error(str(e))
+                log.error('main exception: ' + str(e))
             else:
                 print str(e)
                 # if args and not args.debug:
@@ -89,6 +81,23 @@ class Ifupdown2:
         return 0
 
     def init(self, stdin_buffer):
+        ##############
+        # dry run mode
+        ##############
+        dry_run_mode_on = DryRunManager.get_instance().is_dry_mode_on()
+
+        if hasattr(self.args, 'noact'):
+            if self.args.noact and not dry_run_mode_on:
+                DryRunManager.get_instance().dry_run_mode_on()
+            elif not self.args.noact and dry_run_mode_on:
+                DryRunManager.get_instance().dry_run_mode_off()
+        elif dry_run_mode_on:
+            # if noact is not in self.args we are probably in
+            # ifquery mode so we need to turn off dry run mode.
+            DryRunManager.get_instance().dry_run_mode_off()
+
+        ###
+
         if hasattr(self.args, 'interfacesfile') and self.args.interfacesfile != None:
             # Check to see if -i option is allowed by config file
             # But for ifquery, we will not check this
@@ -269,9 +278,3 @@ class Ifupdown2:
                                    currentlyup=args.currentlyup)
         except:
             raise
-
-    @staticmethod
-    def set_signal_handlers():
-        signal.signal(signal.SIGQUIT, _SIGQUIT)
-        signal.signal(signal.SIGTERM, _SIGTERM)
-        signal.signal(signal.SIGINT, _SIGINT)
diff --git a/ifupdown2/ifupdown/netlink.py b/ifupdown2/ifupdown/netlink.py
deleted file mode 100644 (file)
index 099208b..0000000
+++ /dev/null
@@ -1,700 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
-# Author: Julien Fortin, julien@cumulusnetworks.com
-#
-
-import sys
-import socket
-import logging
-
-from collections import OrderedDict
-
-try:
-    import ifupdown2.nlmanager.nlpacket
-    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
-
-    from ifupdown2.ifupdownaddons.cache import *
-    from ifupdown2.ifupdownaddons.utilsbase import utilsBase
-
-    from ifupdown2.nlmanager.nlmanager import Link, Address, Route, NetlinkPacket
-except ImportError:
-    import nlmanager.nlpacket
-    import ifupdown.ifupdownflags as ifupdownflags
-
-    from ifupdownaddons.cache import *
-    from ifupdownaddons.utilsbase import utilsBase
-
-    from nlmanager.nlmanager import Link, Address, Route, NetlinkPacket
-
-
-class Netlink(utilsBase):
-    VXLAN_UDP_PORT = 4789
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-        try:
-            sys.path.insert(0, '/usr/share/ifupdown2/')
-            try:
-                from ifupdown2.nlmanager.nlmanager import NetlinkManager
-                # Override the nlmanager's mac_int_to_str function to print the MACs
-                # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
-                ifupdown2.nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str
-            except ImportError:
-                from nlmanager.nlmanager import NetlinkManager
-                # Override the nlmanager's mac_int_to_str function to print the MACs
-                # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
-                nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str
-
-            # this should force the use of the local nlmanager
-            self._nlmanager_api = NetlinkManager(log_level=logging.WARNING)
-
-            self.link_kind_handlers = {
-                'vlan': self._link_dump_info_data_vlan,
-                'vrf': self._link_dump_info_data_vrf,
-                'vxlan': self._link_dump_info_data_vxlan,
-                'bond': self._link_dump_info_data_bond,
-                'bridge': self._link_dump_info_data_bridge,
-                'gre': self._link_dump_info_data_gre_tunnel,
-                'gretap': self._link_dump_info_data_gre_tunnel,
-                "ip6gre": self._link_dump_info_data_gre_tunnel,
-                "ip6gretap": self._link_dump_info_data_gre_tunnel,
-                "ip6erspan": self._link_dump_info_data_gre_tunnel,
-                'ipip': self._link_dump_info_data_iptun_tunnel,
-                'sit': self._link_dump_info_data_iptun_tunnel,
-                'ip6tnl': self._link_dump_info_data_iptun_tunnel,
-                'vti': self._link_dump_info_data_vti_tunnel,
-                'vti6': self._link_dump_info_data_vti_tunnel,
-                'xfrm': self._link_dump_info_data_xfrm
-            }
-
-        except Exception as e:
-            self.logger.error('cannot initialize ifupdown2\'s '
-                              'netlink manager: %s' % str(e))
-            raise
-
-    @staticmethod
-    def IN_MULTICAST(a):
-        """
-            /include/uapi/linux/in.h
-
-            #define IN_CLASSD(a)            ((((long int) (a)) & 0xf0000000) == 0xe0000000)
-            #define IN_MULTICAST(a)         IN_CLASSD(a)
-        """
-        return (int(a) & 0xf0000000) == 0xe0000000
-
-    @staticmethod
-    def mac_int_to_str(mac_int):
-        """
-        Return an integer in MAC string format: xx:xx:xx:xx:xx:xx
-        """
-        return ':'.join(("%012x" % mac_int)[i:i + 2] for i in range(0, 12, 2))
-
-    def get_iface_index(self, ifacename):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.get_iface_index(ifacename)
-        except Exception as e:
-            raise Exception('%s: netlink: %s: cannot get ifindex: %s'
-                            % (ifacename, ifacename, str(e)))
-
-    def get_iface_name(self, ifindex):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.get_iface_name(ifindex)
-        except Exception as e:
-            raise Exception('netlink: cannot get ifname for index %s: %s' % (ifindex, str(e)))
-
-    def get_bridge_vlan(self, ifname):
-        self.logger.info('%s: netlink: /sbin/bridge -d -c -json vlan show' % ifname)
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.vlan_get()
-        except Exception as e:
-            raise Exception('netlink: get bridge vlan: %s' % str(e))
-
-    def bridge_set_vlan_filtering(self, ifname, vlan_filtering):
-        self.logger.info('%s: netlink: ip link set dev %s type bridge vlan_filtering %s'
-                         % (ifname, ifname, vlan_filtering))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            ifla_info_data = {Link.IFLA_BR_VLAN_FILTERING: int(vlan_filtering)}
-            return self._nlmanager_api.link_set_attrs(ifname, 'bridge', ifla_info_data=ifla_info_data)
-        except Exception as e:
-            raise Exception('%s: cannot set %s vlan_filtering %s' % (ifname, ifname, vlan_filtering))
-
-    def link_add_set(self,
-                     ifname=None, ifindex=0,
-                     kind=None, slave_kind=None,
-                     ifla={},
-                     ifla_info_data={},
-                     ifla_info_slave_data={},
-                     link_exists=False):
-        action = 'set' if ifindex or link_exists else 'add'
-
-        if slave_kind:
-            self.logger.info('%s: netlink: ip link set dev %s: %s slave attributes' % (ifname, ifname, slave_kind))
-        else:
-            self.logger.info('%s: netlink: ip link %s %s type %s with attributes' % (ifname, action, ifname, kind))
-        if ifla:
-            self.logger.debug('%s: ifla attributes a %s' % (ifname, ifla))
-        if ifla_info_data:
-            self.logger.debug('%s: ifla_info_data %s' % (ifname, ifla_info_data))
-        if ifla_info_slave_data:
-            self.logger.debug('%s: ifla_info_slave_data %s' % (ifname, ifla_info_slave_data))
-
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            self._nlmanager_api.link_add_set(ifname=ifname,
-                                             ifindex=ifindex,
-                                             kind=kind,
-                                             slave_kind=slave_kind,
-                                             ifla=ifla,
-                                             ifla_info_data=ifla_info_data,
-                                             ifla_info_slave_data=ifla_info_slave_data)
-        except Exception as e:
-            if kind and not slave_kind:
-                kind_str = kind
-            elif kind and slave_kind:
-                kind_str = '%s (%s slave)' % (kind, slave_kind)
-            else:
-                kind_str = '(%s slave)' % slave_kind
-
-            raise Exception('netlink: cannot %s %s %s with options: %s' % (action, kind_str, ifname, str(e)))
-
-    def link_del(self, ifname):
-        self.logger.info('%s: netlink: ip link del %s' % (ifname, ifname))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            self._nlmanager_api.link_del(ifname=ifname)
-        except Exception as e:
-            raise Exception('netlink: cannot delete link %s: %s' % (ifname, str(e)))
-
-    def link_set_master(self, ifacename, master_dev, state=None):
-        self.logger.info('%s: netlink: ip link set dev %s master %s %s'
-                         % (ifacename, ifacename, master_dev,
-                            state if state else ''))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            master = 0 if not master_dev else self.get_iface_index(master_dev)
-            return self._nlmanager_api.link_set_master(ifacename,
-                                                       master_ifindex=master,
-                                                       state=state)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot set %s master %s: %s'
-                            % (ifacename, ifacename, master_dev, str(e)))
-
-    def link_set_nomaster(self, ifacename, state=None):
-        self.logger.info('%s: netlink: ip link set dev %s nomaster %s'
-                         % (ifacename, ifacename, state if state else ''))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_set_master(ifacename,
-                                                       master_ifindex=0,
-                                                       state=state)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot set %s nomaster: %s'
-                            % (ifacename, ifacename, str(e)))
-
-    def link_add_vlan(self, vlanrawdevice, ifacename, vlanid, vlan_protocol):
-        if vlan_protocol:
-            self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s protocol %s'
-                             % (ifacename, vlanrawdevice, ifacename, vlanid, vlan_protocol))
-
-        else:
-            self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s'
-                             % (ifacename, vlanrawdevice, ifacename, vlanid))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(vlanrawdevice)
-        try:
-            return self._nlmanager_api.link_add_vlan(ifindex, ifacename, vlanid, vlan_protocol)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create vlan %s: %s'
-                            % (vlanrawdevice, vlanid, str(e)))
-
-    def link_add_macvlan(self, ifacename, macvlan_ifacename):
-        self.logger.info('%s: netlink: ip link add link %s name %s type macvlan mode private'
-                         % (ifacename, ifacename, macvlan_ifacename))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_add_macvlan(ifindex, macvlan_ifacename)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create macvlan %s: %s'
-                            % (ifacename, macvlan_ifacename, str(e)))
-    
-    def link_add_xfrm(self, ifacename, xfrm_ifacename, xfrm_id):
-        self.logger.info('%s: netlink: ip link add %s type xfrm dev %s if_id %s'
-                         % (xfrm_ifacename, xfrm_ifacename, ifacename, xfrm_id))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_add_xfrm(ifindex, xfrm_ifacename, xfrm_id)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create xfrm %s id %s: %s'
-                            % (ifacename, xfrm_ifacename, xfrm_id, str(e)))
-
-    def link_set_updown(self, ifacename, state):
-        self.logger.info('%s: netlink: ip link set dev %s %s'
-                         % (ifacename, ifacename, state))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_set_updown(ifacename, state)
-        except Exception as e:
-            raise Exception('netlink: cannot set link %s %s: %s'
-                            % (ifacename, state, str(e)))
-
-    def link_set_protodown(self, ifacename, state):
-        self.logger.info('%s: netlink: set link %s protodown %s'
-                         % (ifacename, ifacename, state))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_set_protodown(ifacename, state)
-        except Exception as e:
-            raise Exception('netlink: cannot set link %s protodown %s: %s'
-                            % (ifacename, state, str(e)))
-
-    def link_add_bridge(self, ifname, mtu=None):
-        self.logger.info('%s: netlink: ip link add %s type bridge' % (ifname, ifname))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_add_bridge(ifname, mtu=mtu)
-        except Exception as e:
-            raise Exception('netlink: cannot create bridge %s: %s' % (ifname, str(e)))
-
-    def link_add_bridge_vlan(self, ifacename, vlanid):
-        self.logger.info('%s: netlink: bridge vlan add vid %s dev %s'
-                         % (ifacename, vlanid, ifacename))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_add_bridge_vlan(ifindex, vlanid)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create bridge vlan %s: %s'
-                            % (ifacename, vlanid, str(e)))
-
-    def link_del_bridge_vlan(self, ifacename, vlanid):
-        self.logger.info('%s: netlink: bridge vlan del vid %s dev %s'
-                         % (ifacename, vlanid, ifacename))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_del_bridge_vlan(ifindex, vlanid)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot remove bridge vlan %s: %s'
-                            % (ifacename, vlanid, str(e)))
-
-    def link_add_vxlan(self, ifacename, vxlanid, local=None, dstport=VXLAN_UDP_PORT,
-                       group=None, learning=True, ageing=None, physdev=None, ttl=None):
-        cmd = 'ip link add %s type vxlan id %s dstport %s' % (ifacename,
-                                                              vxlanid,
-                                                              dstport)
-        cmd += ' local %s' % local if local else ''
-        cmd += ' ageing %s' % ageing if ageing else ''
-        cmd += ' remote %s' % group if group else ' noremote'
-        cmd += ' nolearning' if not learning else ''
-        cmd += ' dev %s' % physdev if physdev else ''
-
-        if ttl is not None:
-            cmd += ' ttl %s' % ttl
-
-        self.logger.info('%s: netlink: %s' % (ifacename, cmd))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            if physdev:
-                physdev = self.get_iface_index(physdev)
-            return self._nlmanager_api.link_add_vxlan(ifacename,
-                                                      vxlanid,
-                                                      dstport=dstport,
-                                                      local=local,
-                                                      group=group,
-                                                      learning=learning,
-                                                      ageing=ageing,
-                                                      physdev=physdev,
-                                                      ttl=ttl)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create vxlan %s: %s'
-                            % (ifacename, vxlanid, str(e)))
-
-    @staticmethod
-    def _link_dump_attr(link, ifla_attributes, dump):
-        for obj in ifla_attributes:
-            attr = link.attributes.get(obj['attr'])
-            if attr:
-                dump[obj['name']] = attr.get_pretty_value(obj=obj.get('func'))
-
-    @staticmethod
-    def _link_dump_linkdata_attr(linkdata, ifla_linkdata_attr, dump):
-        for obj in ifla_linkdata_attr:
-            attr = obj['attr']
-            if attr in linkdata:
-                func    = obj.get('func')
-                value   = linkdata.get(attr)
-
-                if func:
-                    value = func(value)
-
-                if value or obj['accept_none']:
-                    dump[obj['name']] = value
-
-    ifla_attributes = [
-        {
-            'attr': Link.IFLA_LINK,
-            'name': 'link',
-            'func': lambda x: netlink.get_iface_name(x) if x > 0 else None
-        },
-        {
-            'attr': Link.IFLA_MASTER,
-            'name': 'master',
-            'func': lambda x: netlink.get_iface_name(x) if x > 0 else None
-        },
-        {
-            'attr': Link.IFLA_IFNAME,
-            'name': 'ifname',
-            'func': str,
-        },
-        {
-            'attr': Link.IFLA_MTU,
-            'name': 'mtu',
-            'func': str
-        },
-        {
-            'attr': Link.IFLA_OPERSTATE,
-            'name': 'state',
-            'func': lambda x: '0%x' % int(x) if x > len(Link.oper_to_string) else Link.oper_to_string[x][8:]
-        },
-        {
-            'attr': Link.IFLA_AF_SPEC,
-            'name': 'af_spec',
-            'func': dict
-        }
-    ]
-
-    ifla_address = {'attr': Link.IFLA_ADDRESS, 'name': 'hwaddress', 'func': str}
-
-    ifla_vxlan_attributes = [
-        {
-            'attr': Link.IFLA_VXLAN_LOCAL,
-            'name': 'local',
-            'func': str,
-            'accept_none': True
-        },
-        {
-            'attr': Link.IFLA_VXLAN_LOCAL6,
-            'name': 'local',
-            'func': str,
-            'accept_none': True
-        },
-        {
-            'attr': Link.IFLA_VXLAN_GROUP,
-            'name': 'svcnode',
-            'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None,
-            'accept_none': False
-        },
-        {
-            'attr': Link.IFLA_VXLAN_GROUP6,
-            'name': 'svcnode',
-            'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None,
-            'accept_none': False
-        },
-        {
-            'attr': Link.IFLA_VXLAN_LEARNING,
-            'name': 'learning',
-            'func': lambda x: 'on' if x else 'off',
-            'accept_none': True
-        }
-    ]
-
-    def _link_dump_info_data_vlan(self, ifname, linkdata):
-        return {
-            'vlanid': str(linkdata.get(Link.IFLA_VLAN_ID, '')),
-            'vlan_protocol': linkdata.get(Link.IFLA_VLAN_PROTOCOL)
-        }
-
-    def _link_dump_info_data_vrf(self, ifname, linkdata):
-        vrf_info = {'table': str(linkdata.get(Link.IFLA_VRF_TABLE, ''))}
-
-        # to remove later when moved to a true netlink cache
-        linkCache.vrfs[ifname] = vrf_info
-        return vrf_info
-
-    def _link_dump_info_data_vxlan(self, ifname, linkdata):
-        for attr, value in (
-                ('learning', 'on'),
-                ('svcnode', None),
-                ('vxlanid', str(linkdata.get(Link.IFLA_VXLAN_ID, ''))),
-                ('ageing', str(linkdata.get(Link.IFLA_VXLAN_AGEING, ''))),
-                (Link.IFLA_VXLAN_PORT, linkdata.get(Link.IFLA_VXLAN_PORT))
-        ):
-            linkdata[attr] = value
-        self._link_dump_linkdata_attr(linkdata, self.ifla_vxlan_attributes, linkdata)
-        return linkdata
-
-    ifla_bond_attributes = (
-        Link.IFLA_BOND_MODE,
-        Link.IFLA_BOND_MIIMON,
-        Link.IFLA_BOND_USE_CARRIER,
-        Link.IFLA_BOND_AD_LACP_RATE,
-        Link.IFLA_BOND_XMIT_HASH_POLICY,
-        Link.IFLA_BOND_MIN_LINKS,
-        Link.IFLA_BOND_NUM_PEER_NOTIF,
-        Link.IFLA_BOND_AD_ACTOR_SYSTEM,
-        Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
-        Link.IFLA_BOND_AD_LACP_BYPASS,
-        Link.IFLA_BOND_UPDELAY,
-        Link.IFLA_BOND_DOWNDELAY,
-    )
-
-    def _link_dump_info_data_bond(self, ifname, linkdata):
-        linkinfo = {}
-        for nl_attr in self.ifla_bond_attributes:
-            try:
-                linkinfo[nl_attr] = linkdata.get(nl_attr)
-            except Exception as e:
-                self.logger.debug('%s: parsing bond IFLA_INFO_DATA (%s): %s'
-                                  % (ifname, nl_attr, str(e)))
-        return linkinfo
-
-    # this dict contains the netlink attribute, cache key,
-    # and a callable to translate the netlink value into
-    # whatever value we need to store in the old cache to
-    # make sure we don't break anything
-    ifla_bridge_attributes = (
-        (Link.IFLA_BR_UNSPEC, Link.IFLA_BR_UNSPEC, None),
-        (Link.IFLA_BR_FORWARD_DELAY, "fd", lambda x: str(x / 100)),
-        (Link.IFLA_BR_HELLO_TIME, "hello", lambda x: str(x / 100)),
-        (Link.IFLA_BR_MAX_AGE, "maxage", lambda x: str(x / 100)),
-        (Link.IFLA_BR_AGEING_TIME, "ageing", lambda x: str(x / 100)),
-        (Link.IFLA_BR_STP_STATE, "stp", lambda x: 'yes' if x else 'no'),
-        (Link.IFLA_BR_PRIORITY, "bridgeprio", str),
-        (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering', str),
-        (Link.IFLA_BR_VLAN_PROTOCOL, "vlan-protocol", str),
-        (Link.IFLA_BR_GROUP_FWD_MASK, Link.IFLA_BR_GROUP_FWD_MASK, None),
-        (Link.IFLA_BR_ROOT_ID, Link.IFLA_BR_ROOT_ID, None),
-        (Link.IFLA_BR_BRIDGE_ID, Link.IFLA_BR_BRIDGE_ID, None),
-        (Link.IFLA_BR_ROOT_PORT, Link.IFLA_BR_ROOT_PORT, None),
-        (Link.IFLA_BR_ROOT_PATH_COST, Link.IFLA_BR_ROOT_PATH_COST, None),
-        (Link.IFLA_BR_TOPOLOGY_CHANGE, Link.IFLA_BR_TOPOLOGY_CHANGE, None),
-        (Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, None),
-        (Link.IFLA_BR_HELLO_TIMER, Link.IFLA_BR_HELLO_TIMER, None),
-        (Link.IFLA_BR_TCN_TIMER, Link.IFLA_BR_TCN_TIMER, None),
-        (Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, None),
-        (Link.IFLA_BR_GC_TIMER, Link.IFLA_BR_GC_TIMER, None),
-        (Link.IFLA_BR_GROUP_ADDR, Link.IFLA_BR_GROUP_ADDR, None),
-        (Link.IFLA_BR_FDB_FLUSH, Link.IFLA_BR_FDB_FLUSH, None),
-        (Link.IFLA_BR_MCAST_ROUTER, "mcrouter", str),
-        (Link.IFLA_BR_MCAST_SNOOPING, "mcsnoop", str),
-        (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, "mcqifaddr", str),
-        (Link.IFLA_BR_MCAST_QUERIER, "mcquerier", str),
-        (Link.IFLA_BR_MCAST_HASH_ELASTICITY, "hashel", str),
-        (Link.IFLA_BR_MCAST_HASH_MAX, "hashmax", str),
-        (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, "mclmc", str),
-        (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, "mcsqc", str),
-        (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, "mclmi", lambda x: str(x / 100)),
-        (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, "mcmi", lambda x: str(x / 100)),
-        (Link.IFLA_BR_MCAST_QUERIER_INTVL, "mcqpi", lambda x: str(x / 100)),
-        (Link.IFLA_BR_MCAST_QUERY_INTVL, "mcqi", lambda x: str(x / 100)),
-        (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, "mcqri", lambda x: str(x / 100)),
-        (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, "mcsqi", lambda x: str(x / 100)),
-        (Link.IFLA_BR_NF_CALL_IPTABLES, Link.IFLA_BR_NF_CALL_IPTABLES, None),
-        (Link.IFLA_BR_NF_CALL_IP6TABLES, Link.IFLA_BR_NF_CALL_IP6TABLES, None),
-        (Link.IFLA_BR_NF_CALL_ARPTABLES, Link.IFLA_BR_NF_CALL_ARPTABLES, None),
-        (Link.IFLA_BR_VLAN_DEFAULT_PVID, Link.IFLA_BR_VLAN_DEFAULT_PVID, None),
-        (Link.IFLA_BR_PAD, Link.IFLA_BR_PAD, None),
-        (Link.IFLA_BR_VLAN_STATS_ENABLED, "vlan-stats", str),
-        (Link.IFLA_BR_MCAST_STATS_ENABLED, "mcstats", str),
-        (Link.IFLA_BR_MCAST_IGMP_VERSION, "igmp-version", str),
-        (Link.IFLA_BR_MCAST_MLD_VERSION, "mld-version", str)
-    )
-
-    def _link_dump_info_data_bridge(self, ifname, linkdata):
-        linkinfo = {}
-        for nl_attr, cache_key, func in self.ifla_bridge_attributes:
-            try:
-                if func:
-                    linkinfo[cache_key] = func(linkdata.get(nl_attr))
-                else:
-                    linkinfo[cache_key] = linkdata.get(nl_attr)
-
-                # we also store the value in pure netlink,
-                # to make the transition easier in the future
-                linkinfo[nl_attr] = linkdata.get(nl_attr)
-            except Exception as e:
-                self.logger.error('%s: parsing birdge IFLA_INFO_DATA %s: %s'
-                                  % (ifname, nl_attr, str(e)))
-        return linkinfo
-
-    def _link_dump_info_slave_data_bridge(self, ifname, info_slave_data):
-        return info_slave_data
-
-    def _link_dump_info_data_gre_tunnel(self, ifname, info_slave_data):
-        tunnel_link_ifindex = info_slave_data.get(Link.IFLA_GRE_LINK)
-
-        return {
-            "endpoint": str(info_slave_data.get(Link.IFLA_GRE_REMOTE)),
-            "local": str(info_slave_data.get(Link.IFLA_GRE_LOCAL)),
-            "ttl": str(info_slave_data.get(Link.IFLA_GRE_TTL)),
-            "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
-        }
-
-    def _link_dump_info_data_iptun_tunnel(self, ifname, info_slave_data):
-        tunnel_link_ifindex = info_slave_data.get(Link.IFLA_IPTUN_LINK)
-
-        return {
-            "endpoint": str(info_slave_data.get(Link.IFLA_IPTUN_REMOTE)),
-            "local": str(info_slave_data.get(Link.IFLA_IPTUN_LOCAL)),
-            "ttl": str(info_slave_data.get(Link.IFLA_IPTUN_TTL)),
-            "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
-        }
-
-    def _link_dump_info_data_vti_tunnel(self, ifname, info_slave_data):
-        tunnel_link_ifindex = info_slave_data.get(Link.IFLA_VTI_LINK)
-
-        return {
-            "endpoint": str(info_slave_data.get(Link.IFLA_VTI_REMOTE)),
-            "local": str(info_slave_data.get(Link.IFLA_VTI_LOCAL)),
-            "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
-        }
-
-    def _link_dump_info_data_xfrm(self, ifname, linkdata):
-        xfrm_physdev_link_ifindex = linkdata.get(Link.IFLA_XFRM_LINK)
-
-        return {
-            'xfrm-id': str(linkdata.get(Link.IFLA_XFRM_IF_ID, '')),
-            'xfrm-physdev': self.get_iface_name(xfrm_physdev_link_ifindex) if xfrm_physdev_link_ifindex else ""
-        }
-
-    def _link_dump_linkinfo(self, link, dump):
-        linkinfo = link.attributes[Link.IFLA_LINKINFO].get_pretty_value(dict)
-
-        if linkinfo:
-            info_kind = linkinfo.get(Link.IFLA_INFO_KIND)
-            info_data = linkinfo.get(Link.IFLA_INFO_DATA)
-
-            info_slave_kind = linkinfo.get(Link.IFLA_INFO_SLAVE_KIND)
-            info_slave_data = linkinfo.get(Link.IFLA_INFO_SLAVE_DATA)
-
-            dump['kind']        = info_kind
-            dump['slave_kind']  = info_slave_kind
-
-            if info_data:
-                link_kind_handler = self.link_kind_handlers.get(info_kind)
-                if callable(link_kind_handler):
-                    dump['linkinfo'] = link_kind_handler(dump['ifname'], info_data)
-
-            if info_slave_data:
-                dump['info_slave_data'] = info_slave_data
-
-    def link_dump(self, ifname=None):
-        if ifname:
-            self.logger.info('netlink: ip link show dev %s' % ifname)
-        else:
-            self.logger.info('netlink: ip link show')
-
-        if ifupdownflags.flags.DRYRUN: return {}
-
-        links = dict()
-
-        try:
-            links_dump = self._nlmanager_api.link_dump(ifname)
-        except Exception as e:
-            raise Exception('netlink: link dump failed: %s' % str(e))
-
-        for link in links_dump:
-            try:
-                dump = dict()
-
-                flags = []
-                for flag, string in Link.flag_to_string.items():
-                    if link.flags & flag:
-                        flags.append(string[4:])
-
-                dump['flags'] = flags
-                dump['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
-                dump['ifindex'] = str(link.ifindex)
-
-                if link.device_type == Link.ARPHRD_ETHER:
-                    self._link_dump_attr(link, [self.ifla_address], dump)
-
-                self._link_dump_attr(link, self.ifla_attributes, dump)
-
-                if Link.IFLA_LINKINFO in link.attributes:
-                    self._link_dump_linkinfo(link, dump)
-
-                links[dump['ifname']] = dump
-            except Exception as e:
-                self.logger.warning('netlink: ip link show: %s' % str(e))
-        return links
-
-    def _addr_dump_extract_ifname(self, addr_packet):
-        addr_ifname_attr = addr_packet.attributes.get(Address.IFA_LABEL)
-
-        if addr_ifname_attr:
-            return addr_ifname_attr.get_pretty_value(str)
-        else:
-            return self.get_iface_name(addr_packet.ifindex)
-
-    @staticmethod
-    def _addr_filter(addr_ifname, addr):
-        return addr_ifname == 'lo' and addr in ['127.0.0.1/8', '::1/128', '0.0.0.0']
-
-    def _addr_dump_entry(self, ifaces, addr_packet, addr_ifname, ifa_attr):
-        attribute = addr_packet.attributes.get(ifa_attr)
-
-        if attribute:
-            address = attribute.get_pretty_value(str)
-
-            if hasattr(addr_packet, 'prefixlen'):
-                address = '%s/%d' % (address, addr_packet.prefixlen)
-
-            if self._addr_filter(addr_ifname, address):
-                return
-
-            addr_family = NetlinkPacket.af_family_to_string.get(addr_packet.family)
-            if not addr_family:
-                return
-
-            ifaces[addr_ifname]['addrs'][address] = {
-                'type': addr_family,
-                'scope': addr_packet.scope
-            }
-
-    ifa_address_attributes = [
-        Address.IFA_ADDRESS,
-        Address.IFA_LOCAL,
-        Address.IFA_BROADCAST,
-        Address.IFA_ANYCAST,
-        Address.IFA_MULTICAST
-    ]
-
-    def addr_dump(self, ifname=None):
-        if ifname:
-            self.logger.info('netlink: ip addr show dev %s' % ifname)
-        else:
-            self.logger.info('netlink: ip addr show')
-
-        ifaces = dict()
-        addr_dump = self._nlmanager_api.addr_dump()
-
-        for addr_packet in addr_dump:
-            addr_ifname = self._addr_dump_extract_ifname(addr_packet)
-
-            if addr_packet.family not in [socket.AF_INET, socket.AF_INET6]:
-                continue
-
-            if ifname and ifname != addr_ifname:
-                continue
-
-            if addr_ifname not in ifaces:
-                ifaces[addr_ifname] = {'addrs': OrderedDict({})}
-
-            for ifa_attr in self.ifa_address_attributes:
-                self._addr_dump_entry(ifaces, addr_packet, addr_ifname, ifa_attr)
-
-        if ifname:
-            return {ifname: ifaces.get(ifname, {})}
-
-        return ifaces
-
-
-netlink = Netlink()
index 6cfe456e2af085cd9113f366fe9704bbce118755..30ebc3aeea3beb9ea028893b5c530c629f19de4b 100644 (file)
@@ -30,11 +30,12 @@ class networkInterfaces():
 
     _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel'],
                  'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel']}
+    # tunnel is part of the address family for backward compatibility but is not required.
 
     def __init__(self, interfacesfile='/etc/network/interfaces',
                  interfacesfileiobuf=None, interfacesfileformat='native',
                  template_enable='0', template_engine=None,
-                 template_lookuppath=None):
+                 template_lookuppath=None, raw=False):
         """This member function initializes the networkinterfaces parser object.
 
         Kwargs:
@@ -54,7 +55,7 @@ class networkInterfaces():
         self.auto_ifaces = []
         self.callbacks = {}
         self.auto_all = False
-
+        self.raw = raw
         self.logger = logging.getLogger('ifupdown.' +
                     self.__class__.__name__)
         self.callbacks = {'iface_found' : None,
@@ -257,12 +258,14 @@ class networkInterfaces():
            self._parse_warn(self._currentfile, lineno,
                     '%s: unexpected characters in interface name' %ifacename)
 
-        ifaceobj.raw_config.append(iface_line)
+        if self.raw:
+            ifaceobj.raw_config.append(iface_line)
         iface_config = collections.OrderedDict()
         for line_idx in range(cur_idx + 1, len(lines)):
             l = lines[line_idx].strip(whitespaces)
             if self.ignore_line(l) == 1:
-                ifaceobj.raw_config.append(l)
+                if self.raw:
+                    ifaceobj.raw_config.append(l)
                 continue
             attrs = re.split(self._ws_split_regex, l, 1)
             if self._is_keyword(attrs[0]):
@@ -273,7 +276,8 @@ class networkInterfaces():
                 self._parse_error(self._currentfile, line_idx,
                         'iface %s: invalid syntax \'%s\'' %(ifacename, l))
                 continue
-            ifaceobj.raw_config.append(l)
+            if self.raw:
+                ifaceobj.raw_config.append(l)
             attrname = attrs[0]
             # preprocess vars (XXX: only preprocesses $IFACE for now)
             attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
index 4dadc0d881e74d078edabcf14d83b01d5fc58e1d..f99d1968add69b9e9e87aeca941bf5d26625b5eb 100644 (file)
@@ -40,6 +40,8 @@ class policymanager():
         self.logger = logging.getLogger('ifupdown.' +
                             self.__class__.__name__)
 
+        self.logger.info("policymanager init")
+
         # we grab the json files from a known location and make sure that
         # the defaults_policy is checked first
         user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
@@ -64,7 +66,16 @@ class policymanager():
                     self.logger.debug("policymanager: merging system module %s policy with file %s" % (module, filename))
                     self.system_policy_array[module].update(system_array[module])
                 else:
-                    self.system_policy_array[module] = system_array[module]
+                    json_dict = system_array[module]
+
+                    if isinstance(json_dict, dict):
+                        self.system_policy_array[module] = system_array[module]
+                    elif module != "README":
+                        self.logger.warning(
+                            "file %s contains an invalid policy schema, key "
+                            "\"%s\" contains %s when a dictionary is expected" %
+                            (filename, module, type(json_dict))
+                        )
 
         # take care of user defined policy defaults
         self.user_policy_array = {}
index 940c4f13866baf48850712a4ebe07bfd90ae6f51..00130ad9c76a1e4cba85d9bf9bfa65a28646f244 100644 (file)
@@ -14,22 +14,20 @@ from sets import Set
 
 try:
     from ifupdown2.ifupdown.graph import *
-    from ifupdown2.ifupdown.ifupdownbase import *
-
     from ifupdown2.ifupdown.iface import *
     from ifupdown2.ifupdown.utils import utils
     from ifupdown2.ifupdown.statemanager import *
 
+    import ifupdown2.ifupdown.policymanager as policymanager
     import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
 except ImportError:
     from ifupdown.graph import *
-    from ifupdown.ifupdownbase import *
-
     from ifupdown.iface import *
     from ifupdown.utils import utils
     from ifupdown.statemanager import *
 
     import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.policymanager as policymanager
 
 
 class ifaceSchedulerFlags():
@@ -50,6 +48,11 @@ class ifaceScheduler():
 
     _SCHED_STATUS = True
 
+    VRF_MGMT_DEVNAME = policymanager.policymanager_api.get_module_globals(
+        module_name="vrf",
+        attr="vrf-mgmt-devname"
+    )
+
     @classmethod
     def reset(cls):
         cls._STATE_CHECK = True
@@ -102,8 +105,10 @@ class ifaceScheduler():
                               ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
             except Exception, e:
                 if not ifupdownobj.ignore_error(str(e)):
-                   err = 1
-                   ifupdownobj.logger.error(str(e))
+                    err = 1
+                    #import traceback
+                    #traceback.print_exc()
+                    ifupdownobj.logger.error(str(e))
                 # Continue with rest of the modules
                 pass
             finally:
@@ -148,7 +153,6 @@ class ifaceScheduler():
         # if interface exists in the system
         ifacename = ifaceobjs[0].name
         ifupdownobj.logger.info('%s: running ops ...' %ifacename)
-
         if ('down' in ops[0] and
                 ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
                 ifaceobjs[0].addr_method != 'ppp' and
@@ -265,6 +269,14 @@ class ifaceScheduler():
             # Run lowerifaces or dependents
             dlist = ifaceobj.lowerifaces
             if dlist:
+
+                if ifaceobj.link_kind == ifaceLinkKind.VRF:
+                    # remove non-auto lowerifaces from 'dlist'
+                    for lower_ifname in list(dlist):
+                        for lower_ifaceobj in ifupdownobj.get_ifaceobjs(lower_ifname) or []:
+                            if lower_ifaceobj and not lower_ifaceobj.auto and ifaceobj.name == cls.VRF_MGMT_DEVNAME:
+                                dlist.remove(lower_ifname)
+
                 ifupdownobj.logger.debug('%s: found dependents %s'
                             %(ifacename, str(dlist)))
                 try:
index 5e8f1c748ab476d4545f3ea4560144890b5524d4..394b502bef4eb4f7f836b95b74023d4488121ea0 100644 (file)
@@ -63,7 +63,7 @@ class stateManager():
 
     """
 
-    __DEFAULT_STATE_DIR = "/run/network/"
+    __DEFAULT_STATE_DIR = "/var/tmp/network/"
 
     state_filename = 'ifstatenew'
     """name of the satefile """
@@ -84,6 +84,7 @@ class stateManager():
         self.ifaceobjdict = OrderedDict()
         self.logger = logging.getLogger('ifupdown.' +
                     self.__class__.__name__)
+        self.logger.info("stateManager init")
 
     def init(self):
         self.state_dir = ifupdownConfig.config.get("state_dir")
@@ -172,6 +173,11 @@ class stateManager():
                 return
             if ifaceobj.status != ifaceStatus.SUCCESS:
                 return
+            # ifupdown2 prevents user from bringing the loopback interface
+            # down - to avoid any issue (like wrong error messages) we
+            # shouldn't remove lo ifaceobj from the statemanager
+            if ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
+                return
             # If it matches any of the object, return
             oidx = 0
             for o in old_ifaceobjs:
index 9c259ceb2c0821bc2b0daffb73153664a01e754e..08091e5a1fa94cd0496becf5af5691e31d21813f 100644 (file)
@@ -15,6 +15,7 @@ import signal
 import logging
 import subprocess
 
+from string import maketrans
 from functools import partial
 from ipaddr import IPNetwork, IPAddress
 
@@ -103,6 +104,7 @@ class utils():
     systemctl_cmd   = '/bin/systemctl'
     dpkg_cmd        = '/usr/bin/dpkg'
 
+    logger.info("utils init command paths")
     for cmd in ['bridge',
                 'ip',
                 'brctl',
@@ -130,6 +132,17 @@ class utils():
             else:
                 logger.debug('warning: path %s not found: %s won\'t be usable' % (path + cmd, cmd))
 
+    mac_translate_tab = maketrans(":.-,", "    ")
+
+    @classmethod
+    def mac_str_to_int(cls, hw_address):
+        mac = 0
+        if hw_address:
+            for i in hw_address.translate(cls.mac_translate_tab).split():
+                mac = mac << 8
+                mac += int(i, 16)
+        return mac
+
     @staticmethod
     def get_onff_from_onezero(value):
         if value in utils._onoff_onezero:
diff --git a/ifupdown2/ifupdownaddons/LinkUtils.py b/ifupdown2/ifupdownaddons/LinkUtils.py
deleted file mode 100644 (file)
index 615241a..0000000
+++ /dev/null
@@ -1,2791 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#         Julien Fortin, julien@cumulusnetworks.com
-#
-
-import os
-import re
-import glob
-import shlex
-import signal
-import socket
-import subprocess
-
-from string import maketrans
-from ipaddr import IPNetwork, IPv6Network
-
-try:
-    import ifupdown2.ifupdown.statemanager as statemanager
-    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
-
-    from ifupdown2.nlmanager.nlmanager import Link, Route
-
-    from ifupdown2.ifupdown.iface import *
-    from ifupdown2.ifupdown.utils import utils
-    from ifupdown2.ifupdown.netlink import netlink
-
-    from ifupdown2.ifupdownaddons.utilsbase import utilsBase
-    from ifupdown2.ifupdownaddons.cache import linkCache, MSTPAttrsCache
-except ImportError:
-    import ifupdown.ifupdownflags as ifupdownflags
-    import ifupdown.statemanager as statemanager
-
-    from nlmanager.nlmanager import Link, Route
-
-    from ifupdown.iface import *
-    from ifupdown.utils import utils
-    from ifupdown.netlink import netlink
-
-    from ifupdownaddons.utilsbase import utilsBase
-    from ifupdownaddons.cache import linkCache, MSTPAttrsCache
-
-
-class LinkUtils(utilsBase):
-    """
-    This class contains helper methods to cache and manipulate interfaces through
-    non-netlink APIs (sysfs, iproute2, brctl...)
-    """
-    _CACHE_FILL_DONE = False
-    VXLAN_UDP_PORT = 4789
-
-    ipbatchbuf = ''
-    ipbatch = False
-    ipbatch_pause = False
-
-    bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)
-    bridge_utils_missing_warning = True
-
-    DEFAULT_IP_METRIC = 1024
-    ADDR_METRIC_SUPPORT = None
-
-    mac_translate_tab = maketrans(":.-,", "    ")
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-
-        self.supported_command = {
-            '%s -c -json vlan show' % utils.bridge_cmd: True,
-            'showmcqv4src': True
-        }
-        self.bridge_vlan_cache = {}
-        self.bridge_vlan_cache_fill_done = False
-
-        if not ifupdownflags.flags.PERFMODE and not LinkUtils._CACHE_FILL_DONE:
-            self._fill_cache()
-
-        if LinkUtils.ADDR_METRIC_SUPPORT is None:
-            try:
-                cmd = [utils.ip_cmd, 'addr', 'help']
-                self.logger.info('executing %s addr help' % utils.ip_cmd)
-
-                process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-                stdout, stderr = process.communicate()
-                LinkUtils.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
-                self.logger.info('address metric support: %s' % ('OK' if LinkUtils.ADDR_METRIC_SUPPORT else 'KO'))
-            except Exception:
-                LinkUtils.ADDR_METRIC_SUPPORT = False
-                self.logger.info('address metric support: KO')
-
-    @classmethod
-    def mac_str_to_int(cls, mac):
-        mac_int = 0
-        if mac:
-            for n in mac.translate(cls.mac_translate_tab).split():
-                mac_int += int(n, 16)
-        return mac_int
-
-    @classmethod
-    def addr_metric_support(cls):
-        return cls.ADDR_METRIC_SUPPORT
-
-    @classmethod
-    def get_default_ip_metric(cls):
-        return cls.DEFAULT_IP_METRIC
-
-    @classmethod
-    def reset(cls):
-        LinkUtils._CACHE_FILL_DONE = False
-        LinkUtils.ipbatchbuf = ''
-        LinkUtils.ipbatch = False
-        LinkUtils.ipbatch_pause = False
-
-    def _fill_cache(self):
-        if not LinkUtils._CACHE_FILL_DONE:
-            self._link_fill()
-            self._addr_fill()
-            LinkUtils._CACHE_FILL_DONE = True
-            return True
-        return False
-
-    @staticmethod
-    def _get_vland_id(citems, i, warn):
-        try:
-            sub = citems[i:]
-            index = sub.index('id')
-            int(sub[index + 1])
-            return sub[index + 1]
-        except:
-            if warn:
-                raise Exception('invalid use of \'vlan\' keyword')
-            return None
-
-    def _link_fill(self, ifacename=None, refresh=False):
-        """ fills cache with link information
-
-        if ifacename argument given, fill cache for ifacename, else
-        fill cache for all interfaces in the system
-        """
-
-        if LinkUtils._CACHE_FILL_DONE and not refresh:
-            return
-        try:
-            # if ifacename already present, return
-            if (ifacename and not refresh and
-                    linkCache.get_attr([ifacename, 'ifflag'])):
-                return
-        except:
-            pass
-
-        if True:
-            try:
-                [linkCache.update_attrdict([ifname], linkattrs)
-                 for ifname, linkattrs in netlink.link_dump(ifacename).items()]
-            except Exception as e:
-                self.logger.info('%s' % str(e))
-                # this netlink call replaces the call to _link_fill_iproute2_cmd()
-                # We shouldn't have netlink calls in the iproute2 module, this will
-                # be removed in the future. We plan to release, a flexible backend
-                # (netlink+iproute2) by default we will use netlink backend but with
-                # a CLI arg we can switch to iproute2 backend.
-                # Until we decide to create this "backend" switch capability,
-                # we have to put the netlink call inside the iproute2 module.
-        else:
-            self._link_fill_iproute2_cmd(ifacename, refresh)
-
-        self._fill_bond_info(ifacename)
-        self._fill_bridge_info(ifacename)
-
-    def _fill_bridge_info(self, ifacename):
-
-        if True:  # netlink
-            brports = {}
-
-            if ifacename:
-                cache_dict = {ifacename: linkCache.links.get(ifacename, {})}
-            else:
-                cache_dict = linkCache.links
-
-            for ifname, obj in cache_dict.items():
-                slave_kind = obj.get('slave_kind')
-                if not slave_kind and slave_kind != 'bridge':
-                    continue
-
-                info_slave_data = obj.get('info_slave_data')
-                if not info_slave_data:
-                    continue
-
-                ifla_master = obj.get('master')
-                if not ifla_master:
-                    raise Exception('No master associated with bridge port %s' % ifname)
-
-                for nl_attr in [
-                    Link.IFLA_BRPORT_STATE,
-                    Link.IFLA_BRPORT_COST,
-                    Link.IFLA_BRPORT_PRIORITY,
-                ]:
-                    if nl_attr not in info_slave_data and LinkUtils.bridge_utils_is_installed:
-                        self._fill_bridge_info_brctl()
-                        return
-
-                brport_attrs = {
-                    'pathcost': str(info_slave_data.get(Link.IFLA_BRPORT_COST, 0)),
-                    'fdelay': format(float(info_slave_data.get(Link.IFLA_BRPORT_FORWARD_DELAY_TIMER, 0) / 100), '.2f'),
-                    'portmcrouter': str(info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER, 0)),
-                    'portmcfl': str(info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE, 0)),
-                    'portprio': str(info_slave_data.get(Link.IFLA_BRPORT_PRIORITY, 0)),
-                    'unicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_UNICAST_FLOOD, 0)),
-                    'multicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_MCAST_FLOOD, 0)),
-                    'learning': str(info_slave_data.get(Link.IFLA_BRPORT_LEARNING, 0)),
-                    'arp-nd-suppress': str(info_slave_data.get(Link.IFLA_BRPORT_ARP_SUPPRESS, 0))
-                }
-
-                if ifla_master in brports:
-                    brports[ifla_master][ifname] = brport_attrs
-                else:
-                    brports[ifla_master] = {ifname: brport_attrs}
-
-                linkCache.update_attrdict([ifla_master, 'linkinfo', 'ports'], brports[ifla_master])
-        else:
-            if LinkUtils.bridge_utils_is_installed:
-                self._fill_bridge_info_brctl()
-
-    def _fill_bridge_info_brctl(self):
-        brctlout = utils.exec_command('%s show' % utils.brctl_cmd)
-        if not brctlout:
-            return
-
-        for bline in brctlout.splitlines()[1:]:
-            bitems = bline.split()
-            if len(bitems) < 2:
-                continue
-            try:
-                linkCache.update_attrdict([bitems[0], 'linkinfo'],
-                                          {'stp': bitems[2]})
-            except KeyError:
-                linkCache.update_attrdict([bitems[0]],
-                                          {'linkinfo': {'stp': bitems[2]}})
-            self._bridge_attrs_fill(bitems[0])
-
-    def _bridge_attrs_fill(self, bridgename):
-        battrs = {}
-        bports = {}
-
-        try:
-            # Get all bridge attributes
-            # battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
-
-            try:
-                battrs['maxage'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/max_age' % bridgename)
-            except:
-                pass
-
-
-            try:
-                battrs['hello'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/hello_time' % bridgename)
-            except:
-                pass
-
-            try:
-                battrs['fd'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/forward_delay' % bridgename)
-            except:
-                pass
-
-            try:
-                battrs['ageing'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/ageing_time' % bridgename)
-            except:
-                pass
-
-            try:
-                battrs['mcrouter'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/multicast_router' % bridgename)
-            except:
-                pass
-
-            try:
-                battrs['bridgeprio'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/priority' % bridgename)
-            except:
-                pass
-
-            try:
-                battrs['vlan-protocol'] = VlanProtocols.ID_TO_ETHERTYPES[
-                    self.read_file_oneline(
-                        '/sys/class/net/%s/bridge/vlan_protocol' % bridgename)]
-            except:
-                pass
-
-            try:
-                battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename))
-            except:
-                pass
-
-                # XXX: comment this out until mc attributes become available
-                # with brctl again
-
-                # battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
-                # battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip()
-        except Exception, e:
-            self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
-            pass
-
-        linkCache.update_attrdict([bridgename, 'linkinfo'], battrs)
-
-        names = [os.path.basename(x) for x in glob.glob("/sys/class/net/%s/brif/*" % bridgename)]
-        for pname in names:
-            bportattrs = {}
-            try:
-
-                bportattrs['pathcost'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/path_cost' % pname)
-                bportattrs['fdelay'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/forward_delay_timer' % pname)
-                bportattrs['portmcrouter'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/multicast_router' % pname)
-                bportattrs['portmcfl'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/multicast_fast_leave' % pname)
-                bportattrs['portprio'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/priority' % pname)
-                bportattrs['unicast-flood'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/unicast_flood' % pname)
-                bportattrs['multicast-flood'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/multicast_flood' % pname)
-                bportattrs['learning'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/learning' % pname)
-                bportattrs['arp-nd-suppress'] = self.read_file_oneline(
-                    '/sys/class/net/%s/brport/neigh_suppress' % pname)
-
-                #bportattrs['mcrouters'] = self.read_file_oneline(
-                #    '/sys/class/net/%s/brport/multicast_router' % pname)
-                #bportattrs['mc fast leave'] = self.read_file_oneline(
-                #    '/sys/class/net/%s/brport/multicast_fast_leave' % pname)                                                          
-
-            except Exception, e:
-                self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
-            bports[pname] = bportattrs
-            linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports)
-
-    _bridge_sysfs_mcattrs = {
-        'mclmc': 'multicast_last_member_count',
-        'mcrouter': 'multicast_router',
-        'mcsnoop': 'multicast_snooping',
-        'mcsqc': 'multicast_startup_query_count',
-        'mcqifaddr': 'multicast_query_use_ifaddr',
-        'mcquerier': 'multicast_querier',
-        'hashel': 'hash_elasticity',
-        'hashmax': 'hash_max',
-        'mclmi': 'multicast_last_member_interval',
-        'mcmi': 'multicast_membership_interval',
-        'mcqpi': 'multicast_querier_interval',
-        'mcqi': 'multicast_query_interval',
-        'mcqri': 'multicast_query_response_interval',
-        'mcsqi': 'multicast_startup_query_interval',
-        'igmp-version': 'multicast_igmp_version',
-        'mld-version': 'multicast_mld_version',
-        'vlan-stats': 'vlan_stats_enabled',
-        'mcstats': 'multicast_stats_enabled',
-    }
-
-    def _bridge_get_mcattrs_from_sysfs(self, bridgename):
-        mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi']
-        mcattrs = {}
-
-        for m, s in self._bridge_sysfs_mcattrs.items():
-            n = self.read_file_oneline('/sys/class/net/%s/bridge/%s' % (bridgename, s))
-            if m in mcattrsdivby100:
-                try:
-                    v = int(n) / 100
-                    mcattrs[m] = str(v)
-                except Exception, e:
-                    self.logger.warn('error getting mc attr %s (%s)' % (m, str(e)))
-                    pass
-            else:
-                mcattrs[m] = n
-        return mcattrs
-
-    def _fill_bond_info(self, ifacename):
-        bonding_masters = self.read_file_oneline('/sys/class/net/bonding_masters')
-        if not bonding_masters:
-            return
-
-        bond_masters_list = bonding_masters.split()
-
-        if ifacename:
-            if ifacename in bond_masters_list:
-                bond_masters_list = [ifacename]
-            else:
-                # we want to refresh this interface only if it's a bond master
-                return
-
-        for bondname in bond_masters_list:
-            try:
-                if bondname not in linkCache.links:
-                    linkCache.set_attr([bondname], {'linkinfo': {}})
-                linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
-                                   self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
-                                                          % bondname).split())
-                try:
-                    # if some attribute are missing we try to get the bond attributes via sysfs
-                    bond_linkinfo = linkCache.links[bondname]['linkinfo']
-                    for attr in [Link.IFLA_BOND_MODE, Link.IFLA_BOND_XMIT_HASH_POLICY, Link.IFLA_BOND_MIN_LINKS]:
-                        if attr not in bond_linkinfo:
-                            self._fill_bond_info_sysfs(bondname)
-                            # after we fill in the cache we can continue to the next bond
-                            break
-                except:
-                    self._fill_bond_info_sysfs(bondname)
-
-            except Exception as e:
-                self.logger.debug('LinkUtils: bond cache error: %s' % str(e))
-
-    def _fill_bond_info_sysfs(self, bondname):
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS],
-                               self.read_file_oneline(
-                                   '/sys/class/net/%s/bonding/min_links'
-                                   % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MODE],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/mode'
-                                                      % bondname).split()[0])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY],
-                               self.read_file_oneline(
-                                   '/sys/class/net/%s/bonding/xmit_hash_policy'
-                                   % bondname).split()[0])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
-                                                      % bondname).split()[1])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_sys_prio'
-                                                      % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_system'
-                                                      % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/lacp_bypass'
-                                                      % bondname).split()[1])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/updelay'
-                                                      % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/downdelay'
-                                                      % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/use_carrier' % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/miimon' % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/num_unsol_na' % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF],
-                               self.read_file_oneline('/sys/class/net/%s/bonding/num_grat_arp' % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-
-
-    def _link_fill_iproute2_cmd(self, ifacename=None, refresh=False):
-        warn = True
-        linkout = {}
-        if LinkUtils._CACHE_FILL_DONE and not refresh:
-            return
-        try:
-            # if ifacename already present, return
-            if (ifacename and not refresh and
-                    linkCache.get_attr([ifacename, 'ifflag'])):
-                return
-        except:
-            pass
-        cmdout = self.link_show(ifacename=ifacename)
-        if not cmdout:
-            return
-        for c in cmdout.splitlines():
-            citems = c.split()
-            ifnamenlink = citems[1].split('@')
-            if len(ifnamenlink) > 1:
-                ifname = ifnamenlink[0]
-                iflink = ifnamenlink[1].strip(':')
-            else:
-                ifname = ifnamenlink[0].strip(':')
-                iflink = None
-            linkattrs = dict()
-            linkattrs['link'] = iflink
-            linkattrs['ifindex'] = citems[0].strip(':')
-            flags = citems[2].strip('<>').split(',')
-            linkattrs['flags'] = flags
-            linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
-            for i in range(0, len(citems)):
-                try:
-                    if citems[i] == 'mtu':
-                        linkattrs['mtu'] = citems[i + 1]
-                    elif citems[i] == 'state':
-                        linkattrs['state'] = citems[i + 1]
-                    elif citems[i] == 'link/ether':
-                        linkattrs['hwaddress'] = citems[i + 1]
-                    elif citems[i] in ['link/gre', 'link/ipip', 'link/sit', 'link/gre6', 'link/tunnel6', 'gretap']:
-                        linkattrs['kind'] = 'tunnel'
-                        tunattrs = {'mode': citems[i].split('/')[-1],
-                                    'endpoint' : None,
-                                    'local' : None,
-                                    'ttl' : None,
-                                    'physdev' : None}
-                        for j in range(i, len(citems)):
-                            if citems[j] == 'local':
-                                tunattrs['local'] = citems[j + 1]
-                            elif citems[j] == 'remote':
-                                tunattrs['endpoint'] = citems[j + 1]
-                            elif citems[j] == 'ttl':
-                                tunattrs['ttl'] = citems[j + 1]
-                            elif citems[j] == 'dev':
-                                tunattrs['physdev'] = citems[j + 1]
-                            elif citems[j] in ['vti', 'vti6', 'ip6gre', 'ipip6', 'ip6ip6']:
-                                tunattrs['mode'] = citems[j]
-                        linkattrs['linkinfo'] = tunattrs
-                        break
-                    elif citems[i] == 'link/ppp':
-                        linkattrs['kind'] = 'ppp'
-                    elif citems[i] == 'vlan':
-                        vlanid = self._get_vland_id(citems, i, warn)
-                        if vlanid:
-                            linkattrs['linkinfo'] = {'vlanid': vlanid}
-                            linkattrs['kind'] = 'vlan'
-                    elif citems[i] == 'dummy':
-                        linkattrs['kind'] = 'dummy'
-                    elif citems[i] == 'vxlan' and citems[i + 1] == 'id':
-                        linkattrs['kind'] = 'vxlan'
-                        vattrs = {'vxlanid': citems[i + 2],
-                                  'svcnode': None,
-                                  'remote': [],
-                                  'ageing': citems[i + 2],
-                                  'learning': 'on'}
-                        for j in range(i + 2, len(citems)):
-                            if citems[j] == 'local':
-                                vattrs['local'] = citems[j + 1]
-                            elif citems[j] == 'remote':
-                                vattrs['svcnode'] = citems[j + 1]
-                            elif citems[j] == 'ageing':
-                                vattrs['ageing'] = citems[j + 1]
-                            elif citems[j] == 'nolearning':
-                                vattrs['learning'] = 'off'
-                            elif citems[j] == 'dev':
-                                vattrs['physdev'] = citems[j + 1]
-                        linkattrs['linkinfo'] = vattrs
-                        break
-                    elif citems[i] == 'vrf' and citems[i + 1] == 'table':
-                        vattrs = {'table': citems[i + 2]}
-                        linkattrs['linkinfo'] = vattrs
-                        linkattrs['kind'] = 'vrf'
-                        linkCache.vrfs[ifname] = vattrs
-                        break
-                    elif citems[i] == 'veth':
-                        linkattrs['kind'] = 'veth'
-                    elif citems[i] == 'vrf_slave':
-                        linkattrs['slave_kind'] = 'vrf_slave'
-                        break
-                    elif citems[i] == 'macvlan' and citems[i + 1] == 'mode':
-                        linkattrs['kind'] = 'macvlan'
-                    elif citems[i] == 'xfrm':
-                        linkattrs['kind'] = 'xfrm'
-                except Exception as e:
-                    if warn:
-                        self.logger.debug('%s: parsing error: id, mtu, state, '
-                                          'link/ether, vlan, dummy, vxlan, local, '
-                                          'remote, ageing, nolearning, vrf, table, '
-                                          'vrf_slave are reserved keywords: %s' %
-                                          (ifname, str(e)))
-                        warn = False
-            # linkattrs['alias'] = self.read_file_oneline(
-            #            '/sys/class/net/%s/ifalias' %ifname)
-            linkout[ifname] = linkattrs
-        [linkCache.update_attrdict([ifname], linkattrs)
-         for ifname, linkattrs in linkout.items()]
-
-    @staticmethod
-    def _addr_filter(ifname, addr, scope=None):
-        default_addrs = ['127.0.0.1/8', '::1/128', '0.0.0.0']
-        if ifname == 'lo' and addr in default_addrs:
-            return True
-        if scope and scope == 'link':
-            return True
-        return False
-
-    def _addr_fill(self, ifacename=None, refresh=False):
-        """ fills cache with address information
-
-        if ifacename argument given, fill cache for ifacename, else
-        fill cache for all interfaces in the system
-        """
-        if LinkUtils._CACHE_FILL_DONE and not refresh:
-            return
-        try:
-            # Check if ifacename is already full, in which case, return
-            if ifacename and not refresh:
-                linkCache.get_attr([ifacename, 'addrs'])
-                return
-        except:
-            pass
-
-        if True:
-            try:
-                [linkCache.update_attrdict([ifname], linkattrs)
-                 for ifname, linkattrs in netlink.addr_dump(ifname=ifacename).items()]
-            except Exception as e:
-                self.logger.info(str(e))
-
-                # this netlink call replaces the call to _addr_fill_iproute2_cmd()
-                # We shouldn't have netlink calls in the iproute2 module, this will
-                # be removed in the future. We plan to release, a flexible backend
-                # (netlink+iproute2) by default we will use netlink backend but with
-                # a CLI arg we can switch to iproute2 backend.
-                # Until we decide to create this "backend" switch capability,
-                # we have to put the netlink call inside the iproute2 module.
-
-        else:
-            self._addr_fill_iproute2_cmd(ifacename, refresh)
-
-    def _addr_fill_iproute2_cmd(self, ifacename=None, refresh=False):
-        """ fills cache with address information
-
-        if ifacename argument given, fill cache for ifacename, else
-        fill cache for all interfaces in the system
-        """
-        linkout = {}
-        if LinkUtils._CACHE_FILL_DONE and not refresh:
-            return
-        try:
-            # Check if ifacename is already full, in which case, return
-            if ifacename and not refresh:
-                linkCache.get_attr([ifacename, 'addrs'])
-                return
-        except:
-            pass
-        cmdout = self.addr_show(ifacename=ifacename)
-        if not cmdout:
-            return
-        for c in cmdout.splitlines():
-            citems = c.split()
-            ifnamenlink = citems[1].split('@')
-            if len(ifnamenlink) > 1:
-                ifname = ifnamenlink[0]
-            else:
-                ifname = ifnamenlink[0].strip(':')
-            if not linkout.get(ifname):
-                linkattrs = dict()
-                linkattrs['addrs'] = OrderedDict({})
-                try:
-                    linkout[ifname].update(linkattrs)
-                except KeyError:
-                    linkout[ifname] = linkattrs
-            if citems[2] == 'inet':
-                if self._addr_filter(ifname, citems[3], scope=citems[5]):
-                    continue
-                addrattrs = dict()
-                addrattrs['scope'] = citems[5]
-                addrattrs['type'] = 'inet'
-                linkout[ifname]['addrs'][citems[3]] = addrattrs
-            elif citems[2] == 'inet6':
-                if self._addr_filter(ifname, citems[3], scope=citems[5]):
-                    continue
-                if citems[5] == 'link':
-                    continue  # skip 'link' addresses
-                addrattrs = dict()
-                addrattrs['scope'] = citems[5]
-                addrattrs['type'] = 'inet6'
-                linkout[ifname]['addrs'][citems[3]] = addrattrs
-        [linkCache.update_attrdict([ifname], linkattrs)
-         for ifname, linkattrs in linkout.items()]
-
-    def del_cache_entry(self, ifname):
-        try:
-            del linkCache.links[ifname]
-        except:
-            pass
-
-    def cache_get(self, t, attrlist, refresh=False):
-        return self._cache_get(t, attrlist, refresh)
-
-    def _cache_get(self, t, attrlist, refresh=False):
-        try:
-            if ifupdownflags.flags.DRYRUN:
-                return False
-            if ifupdownflags.flags.CACHE:
-                if self._fill_cache():
-                    # if we filled the cache, return new data
-                    return linkCache.get_attr(attrlist)
-                if not refresh:
-                    return linkCache.get_attr(attrlist)
-            if t == 'link':
-                self._link_fill(attrlist[0], refresh)
-            elif t == 'addr':
-                self._addr_fill(attrlist[0], refresh)
-            else:
-                self._link_fill(attrlist[0], refresh)
-                self._addr_fill(attrlist[0], refresh)
-            return linkCache.get_attr(attrlist)
-        except Exception, e:
-            self.logger.debug('_cache_get(%s) : [%s]' % (str(attrlist), str(e)))
-        return None
-
-    def cache_check(self, attrlist, value, refresh=False):
-        return self._cache_check('link', attrlist, value, refresh=refresh)
-
-    def _cache_check(self, t, attrlist, value, refresh=False):
-        try:
-            return self._cache_get(t, attrlist, refresh) == value
-        except Exception, e:
-            self.logger.debug('_cache_check(%s) : [%s]'
-                              % (str(attrlist), str(e)))
-        return False
-
-    def cache_update(self, attrlist, value):
-        return self._cache_update(attrlist, value)
-
-    @staticmethod
-    def _cache_update(attrlist, value):
-        if ifupdownflags.flags.DRYRUN:
-            return
-        try:
-            if attrlist[-1] == 'slaves':
-                linkCache.append_to_attrlist(attrlist, value)
-                return
-            linkCache.set_attr(attrlist, value)
-        except:
-            pass
-
-    @staticmethod
-    def _cache_delete(attrlist, value=None):
-        if ifupdownflags.flags.DRYRUN:
-            return
-        try:
-            if value:
-                linkCache.remove_from_attrlist(attrlist, value)
-            else:
-                linkCache.del_attr(attrlist)
-        except:
-            pass
-
-    @staticmethod
-    def _cache_invalidate():
-        linkCache.invalidate()
-        LinkUtils._CACHE_FILL_DONE = False
-
-    @staticmethod
-    def batch_start():
-        LinkUtils.ipbatcbuf = ''
-        LinkUtils.ipbatch = True
-        LinkUtils.ipbatch_pause = False
-
-    @staticmethod
-    def add_to_batch(cmd):
-        LinkUtils.ipbatchbuf += cmd + '\n'
-
-    @staticmethod
-    def batch_pause():
-        LinkUtils.ipbatch_pause = True
-
-    @staticmethod
-    def batch_resume():
-        LinkUtils.ipbatch_pause = False
-
-    def batch_commit(self):
-        if not LinkUtils.ipbatchbuf:
-            LinkUtils.ipbatchbuf = ''
-            LinkUtils.ipbatch = False
-            LinkUtils.ipbatch_pause = False
-            return
-        try:
-            utils.exec_command('%s -force -batch -' % utils.ip_cmd,
-                               stdin=self.ipbatchbuf)
-        except:
-            raise
-        finally:
-            LinkUtils.ipbatchbuf = ''
-            LinkUtils.ipbatch = False
-            LinkUtils.ipbatch_pause = False
-
-    def bridge_batch_commit(self):
-        if not LinkUtils.ipbatchbuf:
-            LinkUtils.ipbatchbuf = ''
-            LinkUtils.ipbatch = False
-            LinkUtils.ipbatch_pause = False
-            return
-        try:
-            utils.exec_command('%s -force -batch -'
-                               % utils.bridge_cmd, stdin=self.ipbatchbuf)
-        except:
-            raise
-        finally:
-            LinkUtils.ipbatchbuf = ''
-            LinkUtils.ipbatch = False
-            LinkUtils.ipbatch_pause = False
-
-    def addr_show(self, ifacename=None):
-        if ifacename:
-            if not self.link_exists(ifacename):
-                return
-            return utils.exec_commandl([utils.ip_cmd,
-                                        '-o', 'addr', 'show', 'dev', ifacename])
-        else:
-            return utils.exec_commandl([utils.ip_cmd,
-                                        '-o', 'addr', 'show'])
-
-    @staticmethod
-    def link_show(ifacename=None):
-        if ifacename:
-            return utils.exec_commandl([utils.ip_cmd,
-                                        '-o', '-d', 'link', 'show', 'dev', ifacename])
-        else:
-            return utils.exec_commandl([utils.ip_cmd,
-                                        '-o', '-d', 'link', 'show'])
-
-    def addr_add(self, ifacename, address, broadcast=None,
-                 peer=None, scope=None, preferred_lifetime=None, metric=None):
-        if not address:
-            return
-        cmd = 'addr add %s' % address
-        if broadcast:
-            cmd += ' broadcast %s' % broadcast
-        if peer:
-            cmd += ' peer %s' % peer
-        if scope:
-            cmd += ' scope %s' % scope
-        if preferred_lifetime:
-            cmd += ' preferred_lft %s' % preferred_lifetime
-        cmd += ' dev %s' % ifacename
-
-        if metric:
-            cmd += ' metric %s' % metric
-
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        self._cache_update([ifacename, 'addrs', address], {})
-
-    def addr_del(self, ifacename, address, broadcast=None,
-                 peer=None, scope=None):
-        """ Delete ipv4 address """
-        if not address:
-            return
-        if not self._cache_get('addr', [ifacename, 'addrs', address]):
-            return
-        cmd = 'addr del %s' % address
-        if broadcast:
-            cmd += ' broadcast %s' % broadcast
-        if peer:
-            cmd += ' peer %s' % peer
-        if scope:
-            cmd += ' scope %s' % scope
-        cmd += ' dev %s' % ifacename
-        utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        self._cache_delete([ifacename, 'addrs', address])
-
-    def addr_flush(self, ifacename):
-        cmd = 'addr flush dev %s' % ifacename
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        self._cache_delete([ifacename, 'addrs'])
-
-    def del_addr_all(self, ifacename, skip_addrs=[]):
-        if not skip_addrs:
-            skip_addrs = []
-        runningaddrsdict = self.get_running_addrs(ifname=ifacename)
-        try:
-            # XXX: ignore errors. Fix this to delete secondary addresses
-            # first
-            [self.addr_del(ifacename, a) for a in
-             set(runningaddrsdict.keys()).difference(skip_addrs)]
-        except:
-            # ignore errors
-            pass
-
-    def addr_get(self, ifacename, details=True, refresh=False):
-        addrs = self._cache_get('addr', [ifacename, 'addrs'], refresh=refresh)
-        if not addrs:
-            return None
-        if details:
-            return addrs
-        return addrs.keys()
-
-    def get_running_addrs(self, ifaceobj=None, ifname=None, details=True, addr_virtual_ifaceobj=None):
-        """
-            We now support addr with link scope. Since the kernel may add it's
-            own link address to some interfaces we need to filter them out and
-            make sure we only deal with the addresses set by ifupdown2.
-
-            To do so we look at the previous configuration made by ifupdown2
-            (with the help of the statemanager) together with the addresses
-            specified by the user in /etc/network/interfaces, these addresses
-            are then compared to the running state of the intf (ip addr show)
-            made via a netlink addr dump.
-            For each configured addresses of scope link, we check if it was
-            previously configured by ifupdown2 to create a final set of the
-            addresses watched by ifupdown2
-        """
-        if not ifaceobj and not ifname:
-            return None
-
-        config_addrs = set()
-
-        if ifaceobj:
-            interface_name = ifaceobj.name
-        else:
-            interface_name = ifname
-
-        if addr_virtual_ifaceobj:
-            for attr_name in ["address-virtual", "vrrp"]:
-                for virtual in addr_virtual_ifaceobj.get_attr_value(attr_name) or []:
-                    for ip in virtual.split():
-                        try:
-                            IPNetwork(ip)
-                            config_addrs.add(ip)
-                        except:
-                            pass
-
-                saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(addr_virtual_ifaceobj.name)
-                for saved_ifaceobj in saved_ifaceobjs or []:
-                    for virtual in saved_ifaceobj.get_attr_value(attr_name) or []:
-                        for ip in virtual.split():
-                            try:
-                                IPNetwork(ip)
-                                config_addrs.add(ip)
-                            except:
-                                pass
-        else:
-            if ifaceobj:
-                for addr in ifaceobj.get_attr_value('address') or []:
-                    config_addrs.add(addr)
-
-            saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(interface_name)
-            for saved_ifaceobj in saved_ifaceobjs or []:
-                for addr in saved_ifaceobj.get_attr_value('address') or []:
-                    config_addrs.add(addr)
-
-        running_addrs = OrderedDict()
-        cached_addrs = self.addr_get(interface_name)
-        if cached_addrs:
-            for addr, addr_details in cached_addrs.items():
-                try:
-                    scope = int(addr_details['scope'])
-                except Exception:
-                    try:
-                        d = {}
-                        addr_obj = IPNetwork(addr)
-                        if isinstance(addr_obj, IPv6Network):
-                            d['family'] = 'inet6'
-                        else:
-                            d['family'] = 'inet'
-                        running_addrs[addr] = d
-                    except:
-                        running_addrs[addr] = {}
-                    continue
-                if (scope & Route.RT_SCOPE_LINK and addr in config_addrs) or not scope & Route.RT_SCOPE_LINK:
-                    running_addrs[addr] = addr_details
-        else:
-            return None
-
-        if details:
-            return running_addrs
-        return running_addrs.keys()
-
-    @staticmethod
-    def compare_user_config_vs_running_state(running_addrs, user_addrs):
-        ip4 = []
-        ip6 = []
-
-        for ip in user_addrs or []:
-            obj = IPNetwork(ip)
-
-            if type(obj) == IPv6Network:
-                ip6.append(str(obj))
-            else:
-                ip4.append(str(obj))
-
-        running_ipobj = []
-        for ip in running_addrs or []:
-            running_ipobj.append(str(IPNetwork(ip)))
-
-        return running_ipobj == (ip4 + ip6)
-
-    def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None):
-        # purges address
-        if purge_existing:
-            # if perfmode is not set and also if iface has no sibling
-            # objects, purge addresses that are not present in the new
-            # config
-            runningaddrs = self.get_running_addrs(
-                ifname=ifacename,
-                details=False,
-                addr_virtual_ifaceobj=ifaceobj
-            )
-            addrs = utils.get_normalized_ip_addr(ifacename, addrs)
-
-            if self.compare_user_config_vs_running_state(runningaddrs, addrs):
-                return
-            try:
-                # if primary address is not same, there is no need to keep any.
-                # reset all addresses
-                if (addrs and runningaddrs and
-                        (addrs[0] != runningaddrs[0])):
-                    self.del_addr_all(ifacename)
-                else:
-                    self.del_addr_all(ifacename, addrs)
-            except Exception, e:
-                self.logger.warning('%s: %s' % (ifacename, str(e)))
-        for a in addrs:
-            try:
-                self.addr_add(ifacename, a, metric=metric)
-            except Exception, e:
-                self.logger.error(str(e))
-
-    def _link_set_ifflag(self, ifacename, value):
-        # Dont look at the cache, the cache may have stale value
-        # because link status can be changed by external
-        # entity (One such entity is ifupdown main program)
-        cmd = 'link set dev %s %s' % (ifacename, value.lower())
-        if LinkUtils.ipbatch:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-
-    def link_up(self, ifacename):
-        self._link_set_ifflag(ifacename, 'UP')
-
-    def link_down(self, ifacename):
-        self._link_set_ifflag(ifacename, 'DOWN')
-
-    def link_set(self, ifacename, key, value=None,
-                 force=False, t=None, state=None):
-        if not force:
-            if (key not in ['master', 'nomaster'] and
-                    self._cache_check('link', [ifacename, key], value)):
-                return
-        cmd = 'link set dev %s' % ifacename
-        if t:
-            cmd += ' type %s' % t
-        cmd += ' %s' % key
-        if value:
-            cmd += ' %s' % value
-        if state:
-            cmd += ' %s' % state
-        if LinkUtils.ipbatch:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        if key not in ['master', 'nomaster']:
-            self._cache_update([ifacename, key], value)
-
-    def link_set_hwaddress(self, ifacename, hwaddress, force=False, keep_down=False):
-        if not force:
-            link_hwaddress = self.link_get_hwaddress(ifacename)
-
-            if self.mac_str_to_int(link_hwaddress) == self.mac_str_to_int(hwaddress):
-                return False
-
-        self.link_down(ifacename)
-        cmd = 'link set dev %s address %s' % (ifacename, hwaddress)
-        if LinkUtils.ipbatch:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-
-        if not keep_down:
-            self.link_up(ifacename)
-        self._cache_update([ifacename, 'hwaddress'], hwaddress)
-        return True
-
-    def link_set_mtu(self, ifacename, mtu):
-        if ifupdownflags.flags.DRYRUN:
-            return True
-        if not mtu or not ifacename: return
-        self.write_file('/sys/class/net/%s/mtu' % ifacename, mtu)
-        self._cache_update([ifacename, 'mtu'], mtu)
-
-    def link_set_alias(self, ifacename, alias):
-        self.write_file('/sys/class/net/%s/ifalias' % ifacename,
-                        '\n' if not alias else alias)
-
-    def link_get_alias(self, ifacename):
-        return self.read_file_oneline('/sys/class/net/%s/ifalias'
-                                      % ifacename)
-
-    def link_isloopback(self, ifacename):
-        flags = self._cache_get('link', [ifacename, 'flags'])
-        if not flags:
-            return
-        if 'LOOPBACK' in flags:
-            return True
-        return False
-
-    def link_get_status(self, ifacename):
-        return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
-
-    @staticmethod
-    def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True):
-        if not gateway:
-            return
-        if not vrf:
-            cmd = '%s route add default via %s proto kernel' % (utils.ip_cmd,
-                                                   gateway)
-        else:
-            cmd = ('%s route add table %s default via %s proto kernel' %
-                   (utils.ip_cmd, vrf, gateway))
-        # Add metric
-        if metric:
-            cmd += ' metric %s' % metric
-        cmd += ' dev %s' % ifacename
-
-        if onlink:
-            cmd += " onlink"
-
-        utils.exec_command(cmd)
-
-    @staticmethod
-    def route_del_gateway(ifacename, gateway, vrf=None, metric=None):
-        # delete default gw
-        if not gateway:
-            return
-        if not vrf:
-            cmd = ('%s route del default via %s proto kernel' %
-                   (utils.ip_cmd, gateway))
-        else:
-            cmd = ('%s route del table %s default via %s proto kernel' %
-                   (utils.ip_cmd, vrf, gateway))
-        if metric:
-            cmd += ' metric %s' % metric
-        cmd += ' dev %s' % ifacename
-        utils.exec_command(cmd)
-
-    @staticmethod
-    def _get_vrf_id(ifacename):
-        try:
-            return linkCache.vrfs[ifacename]['table']
-        except KeyError:
-            dump = netlink.link_dump(ifacename)
-
-            [linkCache.update_attrdict([ifname], linkattrs)
-             for ifname, linkattrs in dump.items()]
-
-            if dump and dump.get(ifacename, {}).get('kind') == 'vrf':
-                vrf_table = dump.get(ifacename, {}).get('linkinfo', {}).get('table')
-                linkCache.vrfs[ifacename] = {'table': vrf_table}
-                return vrf_table
-
-            return None
-
-    def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
-        vrf_table = None
-
-        if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
-            try:
-                for upper_iface in ifaceobj.upperifaces:
-                    vrf_table = self._get_vrf_id(upper_iface)
-                    if vrf_table:
-                        break
-            except:
-                pass
-
-        ip_route_del = []
-        for ip in ips:
-            ip_network_obj = IPNetwork(ip)
-
-            if type(ip_network_obj) == IPv6Network:
-                route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
-
-                if vrf_table:
-                    if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-                        LinkUtils.add_to_batch('route del %s table %s dev %s' % (route_prefix, vrf_table, macvlan_ifacename))
-                    else:
-                        utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'table', vrf_table, 'dev', macvlan_ifacename])
-                else:
-                    if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-                        LinkUtils.add_to_batch('route del %s dev %s' % (route_prefix, macvlan_ifacename))
-                    else:
-                        utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'dev', macvlan_ifacename])
-                ip_route_del.append((route_prefix, vrf_table))
-
-        for ip, vrf_table in ip_route_del:
-            if vrf_table:
-                if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-                    LinkUtils.add_to_batch('route add %s table %s dev %s proto kernel metric 9999' % (ip, vrf_table, macvlan_ifacename))
-                else:
-                    utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'table', vrf_table, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
-            else:
-                if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-                    LinkUtils.add_to_batch('route add %s dev %s proto kernel metric 9999' % (ip, macvlan_ifacename))
-                else:
-                    utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
-
-    def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid):
-        if self.link_exists(vlan_device_name):
-            return
-        utils.exec_command('%s link add link %s name %s type vlan id %d' %
-                           (utils.ip_cmd,
-                            vlan_raw_device, vlan_device_name, vlanid))
-        self._cache_update([vlan_device_name], {})
-
-    def link_create_vlan_from_name(self, vlan_device_name):
-        v = vlan_device_name.split('.')
-        if len(v) != 2:
-            self.logger.warn('invalid vlan device name %s' % vlan_device_name)
-            return
-        self.link_create_vlan(vlan_device_name, v[0], v[1])
-
-    def link_create_macvlan(self, name, linkdev, mode='private'):
-        if self.link_exists(name):
-            return
-        cmd = ('link add link %s' % linkdev +
-               ' name %s' % name +
-               ' type macvlan mode %s' % mode)
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        self._cache_update([name], {})
-
-    def get_vxlan_peers(self, dev, svcnodeip):
-        cmd = '%s fdb show brport %s' % (utils.bridge_cmd,
-                                         dev)
-        cur_peers = []
-        try:
-            ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
-            utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
-            output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout)
-            ps.wait()
-            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
-            try:
-                ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+')
-                for l in output.split('\n'):
-                    m = ppat.search(l)
-                    if m and m.group(1) != svcnodeip:
-                        cur_peers.append(m.group(1))
-            except:
-                self.logger.warn('error parsing ip link output')
-        except subprocess.CalledProcessError as e:
-            if e.returncode != 1:
-                self.logger.error(str(e))
-        finally:
-            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
-
-        return cur_peers
-
-    def tunnel_create(self, tunnelname, mode, attrs={}):
-        """ generic link_create function """
-        if self.link_exists(tunnelname):
-            return
-
-        cmd = ''
-        if '6' in mode:
-            cmd = ' -6'
-
-        if mode in ['gretap']:
-            cmd += ' link add %s type %s' % (tunnelname, mode)
-        else:
-            cmd += ' tunnel add %s mode %s' % (tunnelname, mode)
-
-        if attrs:
-            for k, v in attrs.iteritems():
-                cmd += ' %s' % k
-                if v:
-                    cmd += ' %s' % v
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_update([tunnelname], {})
-
-    def tunnel_change(self, tunnelname, attrs={}):
-        """ tunnel change function """
-        if not self.link_exists(tunnelname):
-            return
-        cmd = 'tunnel change'
-        cmd += ' %s' %(tunnelname)
-        if attrs:
-            for k, v in attrs.iteritems():
-                cmd += ' %s' %k
-                if v:
-                    cmd += ' %s' %v
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-
-    def link_create_vxlan(self, name, vxlanid,
-                          localtunnelip=None,
-                          svcnodeip=None,
-                          remoteips=None,
-                          learning='on',
-                          ageing=None,
-                          anycastip=None,
-                          ttl=None):
-        if svcnodeip and remoteips:
-            raise Exception("svcnodeip and remoteip is mutually exclusive")
-        args = ''
-        if svcnodeip:
-            args += ' remote %s' % svcnodeip
-        if ageing:
-            args += ' ageing %s' % ageing
-        if learning == 'off':
-            args += ' nolearning'
-        if ttl is not None:
-            args += ' ttl %s' % ttl
-
-        if self.link_exists(name):
-            cmd = 'link set dev %s type vxlan dstport %d' % (name, LinkUtils.VXLAN_UDP_PORT)
-            vxlanattrs = self.get_vxlandev_attrs(name)
-            # on ifreload do not overwrite anycast_ip to individual ip if clagd
-            # has modified
-            if vxlanattrs:
-                running_localtunnelip = vxlanattrs.get('local')
-                if anycastip and running_localtunnelip and anycastip == running_localtunnelip:
-                    localtunnelip = running_localtunnelip
-                running_svcnode = vxlanattrs.get('svcnode')
-                if running_svcnode and not svcnodeip:
-                    args += ' noremote'
-        else:
-            cmd = 'link add dev %s type vxlan id %s dstport %d' % (name, vxlanid, LinkUtils.VXLAN_UDP_PORT)
-
-        if localtunnelip:
-            args += ' local %s' % localtunnelip
-        cmd += args
-
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-
-        # XXX: update linkinfo correctly
-        #self._cache_update([name], {})
-
-    @staticmethod
-    def link_exists(ifacename):
-        if ifupdownflags.flags.DRYRUN:
-            return True
-        return os.path.exists('/sys/class/net/%s' % ifacename)
-
-    @staticmethod
-    def link_exists_nodryrun(ifname):
-        return os.path.exists('/sys/class/net/%s' % ifname)
-
-    def link_get_ifindex(self, ifacename):
-        if ifupdownflags.flags.DRYRUN:
-            return True
-        return self.read_file_oneline('/sys/class/net/%s/ifindex' % ifacename)
-
-    def is_vlan_device_by_name(self, ifacename):
-        if re.search(r'\.', ifacename):
-            return True
-        return False
-
-    @staticmethod
-    def link_add_macvlan(ifname, macvlan_ifacename, mode):
-        utils.exec_commandl(['ip', 'link', 'add',  'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', mode])
-
-    @staticmethod
-    def link_add_xfrm(ifname, xfrm_name, xfrm_id):
-        utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id])
-
-    @staticmethod
-    def route_add(route):
-        utils.exec_command('%s route add %s' % (utils.ip_cmd,
-                                                route))
-
-    @staticmethod
-    def route6_add(route):
-        utils.exec_command('%s -6 route add %s' % (utils.ip_cmd,
-                                                   route))
-
-    def get_vlandev_attrs(self, ifacename):
-        return (self._cache_get('link', [ifacename, 'link']),
-                self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']),
-                self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol']))
-
-    def get_vlan_protocol(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol'])
-
-    def get_vxlandev_attrs(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo'])
-
-    def get_vxlandev_learning(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo', Link.IFLA_VXLAN_LEARNING])
-
-    def set_vxlandev_learning(self, ifacename, learn):
-        if learn == 'on':
-            utils.exec_command('%s link set dev %s type vxlan learning' %
-                               (utils.ip_cmd, ifacename))
-            self._cache_update([ifacename, 'linkinfo', 'learning'], 'on')
-        else:
-            utils.exec_command('%s link set dev %s type vxlan nolearning' %
-                               (utils.ip_cmd, ifacename))
-            self._cache_update([ifacename, 'linkinfo', 'learning'], 'off')
-
-    def link_get_linkinfo_attrs(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo'])
-
-    def link_get_mtu(self, ifacename, refresh=False):
-        return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh)
-
-    def link_get_mtu_sysfs(self, ifacename):
-        return self.read_file_oneline('/sys/class/net/%s/mtu'
-                                      % ifacename)
-
-    def link_get_kind(self, ifacename):
-        return self._cache_get('link', [ifacename, 'kind'])
-
-    def link_get_slave_kind(self, ifacename):
-        return self._cache_get('link', [ifacename, 'slave_kind'])
-
-    def link_get_hwaddress(self, ifacename):
-        address = self._cache_get('link', [ifacename, 'hwaddress'])
-        # newly created logical interface addresses dont end up in the cache
-        # read hwaddress from sysfs file for these interfaces
-        if not address:
-            address = self.read_file_oneline('/sys/class/net/%s/address'
-                                             % ifacename)
-        return address
-
-    def link_create(self, ifacename, t, attrs={}):
-        """ generic link_create function """
-        if self.link_exists(ifacename):
-            return
-        cmd = 'link add'
-        cmd += ' name %s type %s' % (ifacename, t)
-        if attrs:
-            for k, v in attrs.iteritems():
-                cmd += ' %s' % k
-                if v:
-                    cmd += ' %s' % v
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        self._cache_update([ifacename], {})
-
-    def link_delete(self, ifacename):
-        if not self.link_exists(ifacename):
-            return
-        cmd = 'link del %s' % ifacename
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-        self._cache_invalidate()
-
-    def link_get_master(self, ifacename):
-        sysfs_master_path = '/sys/class/net/%s/master' % ifacename
-        if os.path.exists(sysfs_master_path):
-            link_path = os.readlink(sysfs_master_path)
-            if link_path:
-                return os.path.basename(link_path)
-            else:
-                return None
-        else:
-            return self._cache_get('link', [ifacename, 'master'])
-
-    def get_brport_peer_link(self, bridgename):
-        try:
-            return self._cache_get('link', [bridgename, 'info_slave_data', Link.IFLA_BRPORT_PEER_LINK])
-        except:
-            return None
-
-    @staticmethod
-    def bridge_port_vids_add(bridgeportname, vids):
-        [utils.exec_command('%s vlan add vid %s dev %s' %
-                            (utils.bridge_cmd,
-                             v, bridgeportname)) for v in vids]
-
-    @staticmethod
-    def bridge_port_vids_del(bridgeportname, vids):
-        if not vids:
-            return
-        [utils.exec_command('%s vlan del vid %s dev %s' %
-                            (utils.bridge_cmd,
-                             v, bridgeportname)) for v in vids]
-
-    @staticmethod
-    def bridge_port_vids_flush(bridgeportname, vid):
-        utils.exec_command('%s vlan del vid %s dev %s' %
-                           (utils.bridge_cmd,
-                            vid, bridgeportname))
-
-    @staticmethod
-    def bridge_port_vids_get(bridgeportname):
-        bridgeout = utils.exec_command('%s vlan show dev %s' %
-                                       (utils.bridge_cmd,
-                                        bridgeportname))
-        if not bridgeout:
-            return []
-        brvlanlines = bridgeout.readlines()[2:]
-        vids = [l.strip() for l in brvlanlines]
-        return [v for v in vids if v]
-
-    @staticmethod
-    def bridge_port_vids_get_all():
-        brvlaninfo = {}
-        bridgeout = utils.exec_command('%s -c vlan show'
-                                       % utils.bridge_cmd)
-        if not bridgeout:
-            return brvlaninfo
-        brvlanlines = bridgeout.splitlines()
-        brportname = None
-        for l in brvlanlines[1:]:
-            if l and not l.startswith(' ') and not l.startswith('\t'):
-                attrs = l.split()
-                brportname = attrs[0].strip()
-                brvlaninfo[brportname] = {'pvid': None, 'vlan': []}
-                l = ' '.join(attrs[1:])
-            if not brportname or not l:
-                continue
-            l = l.strip()
-            if 'PVID' in l:
-                brvlaninfo[brportname]['pvid'] = l.split()[0]
-            elif 'Egress Untagged' not in l:
-                brvlaninfo[brportname]['vlan'].append(l)
-        return brvlaninfo
-
-    def bridge_port_vids_get_all_json(self):
-        if not self.supported_command['%s -c -json vlan show'
-                % utils.bridge_cmd]:
-            return {}
-        brvlaninfo = {}
-        try:
-            bridgeout = utils.exec_command('%s -c -json vlan show'
-                                           % utils.bridge_cmd)
-        except:
-            self.supported_command['%s -c -json vlan show'
-                                   % utils.bridge_cmd] = False
-            self.logger.info('%s -c -json vlan show: skipping unsupported command'
-                             % utils.bridge_cmd)
-            try:
-                return self.get_bridge_vlan_nojson()
-            except Exception as e:
-                self.logger.info('bridge: get_bridge_vlan_nojson: %s' % str(e))
-                return {}
-
-        if not bridgeout: return brvlaninfo
-        try:
-            vlan_json = json.loads(bridgeout, encoding="utf-8")
-        except Exception, e:
-            self.logger.info('json loads failed with (%s)' % str(e))
-            return {}
-
-        try:
-            if isinstance(vlan_json, list):
-                # newer iproute2 version changed the bridge vlan show output
-                # ifupdown2 relies on the previous format, we have the convert
-                # data into old format
-                bridge_port_vids = dict()
-
-                for intf in vlan_json:
-                    bridge_port_vids[intf["ifname"]] = intf["vlans"]
-
-                return bridge_port_vids
-            else:
-                # older iproute2 version have different ways to dump vlans
-                # ifupdown2 prefers the following syntax:
-                # {
-                #    "vx-1002": [{
-                #        "vlan": 1002,
-                #        "flags": ["PVID", "Egress Untagged"]
-                #    }
-                #    ],
-                #    "vx-1004": [{
-                #        "vlan": 1004,
-                #        "flags": ["PVID", "Egress Untagged"]
-                #    }]
-                # }
-                return vlan_json
-        except Exception as e:
-            self.logger.debug("bridge vlan show: Unknown json output: %s" % str(e))
-            return vlan_json
-
-    @staticmethod
-    def get_bridge_vlan_nojson():
-        vlan_json = {}
-        bridgeout = utils.exec_commandl([utils.bridge_cmd, '-c', 'vlan', 'show'])
-        if bridgeout:
-            output = [line.split('\n') for line in bridgeout.split('\n\n')]
-            output[0] = output[0][1:]
-            for line in output:
-                current_swp = None
-                if not line:
-                    continue
-                for entry in line:
-                    if not entry:
-                        continue
-                    prefix, vlan = entry.split('\t')
-                    if prefix:
-                        current_swp = prefix
-                        vlan_json[prefix] = []
-                    v = {}
-                    vlan = vlan[1:]
-                    try:
-                        v['vlan'] = int(vlan)
-                    except:
-                        try:
-                            if '-' in vlan:
-                                start, end = vlan.split('-')
-                                if ' ' in end:
-                                    end = end[0:end.index(' ')]
-                                v['vlan'] = int(start)
-                                v['vlanEnd'] = int(end)
-                            else:
-                                v['vlan'] = int(vlan[0:vlan.index(' ')])
-                            flags = []
-                            if 'PVID' in vlan:
-                                flags.append('PVID')
-                            if 'Egress Untagged' in vlan:
-                                flags.append('Egress Untagged')
-                            v['flags'] = flags
-                        except:
-                            continue
-                    vlan_json[current_swp].append(v)
-        return vlan_json
-
-    def bridge_vlan_cache_get(self, ifacename, refresh=False):
-        if not self.bridge_vlan_cache_fill_done or refresh:
-            self.bridge_vlan_cache = self.bridge_port_vids_get_all_json()
-            self.bridge_vlan_cache_fill_done = True
-        return self.bridge_vlan_cache.get(ifacename, {})
-
-    def bridge_vlan_get_pvid(self, ifacename, refresh=False):
-        pvid = 0
-
-        for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
-            v = vinfo.get('vlan')
-            pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-            if pvid:
-                return pvid
-        return pvid
-
-    def bridge_vlan_get_vids(self, ifacename, refresh=False):
-        vids = []
-
-        for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
-            v = vinfo.get('vlan')
-            ispvid = True if 'PVID' in vinfo.get('flags', []) else False
-            if ispvid:
-                pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-                if pvid == 1:
-                    continue
-            vEnd = vinfo.get('vlanEnd')
-            if vEnd:
-                vids.extend(range(v, vEnd + 1))
-            else:
-                vids.append(v)
-        return vids
-
-    def bridge_vlan_get_vids_n_pvid(self, ifacename, refresh=False):
-        vids = []
-        pvid = 0
-
-        for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
-            v = vinfo.get('vlan')
-            ispvid = True if 'PVID' in vinfo.get('flags', []) else False
-            if ispvid:
-                pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-            vEnd = vinfo.get('vlanEnd')
-            if vEnd:
-                vids.extend(range(v, vEnd + 1))
-            else:
-                vids.append(v)
-        return vids, pvid
-
-    def bridge_port_pvid_add(self, bridgeportname, pvid):
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch('vlan add vid %s untagged pvid dev %s' %
-                              (pvid, bridgeportname))
-        else:
-            utils.exec_command('%s vlan add vid %s untagged pvid dev %s' %
-                               (utils.bridge_cmd,
-                                pvid, bridgeportname))
-
-    def bridge_port_pvid_del(self, bridgeportname, pvid):
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            self.add_to_batch('vlan del vid %s untagged pvid dev %s' %
-                              (pvid, bridgeportname))
-        else:
-            utils.exec_command('%s vlan del vid %s untagged pvid dev %s' %
-                               (utils.bridge_cmd,
-                                pvid, bridgeportname))
-
-    def bridge_port_pvids_get(self, bridgeportname):
-        return self.read_file_oneline('/sys/class/net/%s/brport/pvid'
-                                      % bridgeportname)
-
-    def bridge_vids_add(self, bridgeportname, vids, bridge=True):
-        target = 'self' if bridge else ''
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            [self.add_to_batch('vlan add vid %s dev %s %s' %
-                               (v, bridgeportname, target)) for v in vids]
-        else:
-            [utils.exec_command('%s vlan add vid %s dev %s %s' %
-                                (utils.bridge_cmd,
-                                 v, bridgeportname, target)) for v in vids]
-
-    def bridge_vids_del(self, bridgeportname, vids, bridge=True):
-        target = 'self' if bridge else ''
-        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
-            [self.add_to_batch('vlan del vid %s dev %s %s' %
-                               (v, bridgeportname, target)) for v in vids]
-        else:
-            [utils.exec_command('%s vlan del vid %s dev %s %s' %
-                                (utils.bridge_cmd,
-                                 v, bridgeportname, target)) for v in vids]
-
-    @staticmethod
-    def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
-        target = 'self' if bridge else ''
-        vlan_str = ''
-        if vlan:
-            vlan_str = 'vlan %s ' % vlan
-
-        dst_str = ''
-        if remote:
-            dst_str = 'dst %s ' % remote
-
-        utils.exec_command('%s fdb replace %s dev %s %s %s %s' %
-                           (utils.bridge_cmd,
-                            address, dev, vlan_str, target, dst_str))
-
-    @staticmethod
-    def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
-        target = 'self' if bridge else ''
-        vlan_str = ''
-        if vlan:
-            vlan_str = 'vlan %s ' % vlan
-
-        dst_str = ''
-        if remote:
-            dst_str = 'dst %s ' % remote
-
-        utils.exec_command('%s fdb append %s dev %s %s %s %s' %
-                           (utils.bridge_cmd,
-                            address, dev, vlan_str, target, dst_str))
-
-    @staticmethod
-    def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
-        target = 'self' if bridge else ''
-        vlan_str = ''
-        if vlan:
-            vlan_str = 'vlan %s ' % vlan
-
-        dst_str = ''
-        if remote:
-            dst_str = 'dst %s ' % remote
-        utils.exec_command('%s fdb del %s dev %s %s %s %s' %
-                           (utils.bridge_cmd,
-                            address, dev, vlan_str, target, dst_str))
-
-    def bridge_is_vlan_aware(self, bridgename):
-        filename = '/sys/class/net/%s/bridge/vlan_filtering' % bridgename
-        if os.path.exists(filename) and self.read_file_oneline(filename) == '1':
-            return True
-        return False
-
-    @staticmethod
-    def bridge_port_get_bridge_name(bridgeport):
-        filename = '/sys/class/net/%s/brport/bridge' % bridgeport
-        try:
-            return os.path.basename(os.readlink(filename))
-        except:
-            return None
-
-    @staticmethod
-    def bridge_port_exists(bridge, bridgeportname):
-        try:
-            return os.path.exists('/sys/class/net/%s/brif/%s'
-                                  % (bridge, bridgeportname))
-        except Exception:
-            return False
-
-    def bridge_fdb_show_dev(self, dev):
-        try:
-            fdbs = {}
-            output = utils.exec_command('%s fdb show dev %s'
-                                        % (utils.bridge_cmd, dev))
-            if output:
-                for fdb_entry in output.splitlines():
-                    try:
-                        entries = fdb_entry.split()
-                        fdbs.setdefault(entries[2], []).append(entries[0])
-                    except:
-                        self.logger.debug('%s: invalid fdb line \'%s\''
-                                          % (dev, fdb_entry))
-            return fdbs
-        except Exception:
-            return None
-
-    @staticmethod
-    def is_bridge(bridge):
-        return os.path.exists('/sys/class/net/%s/bridge' % bridge)
-
-    def is_link_up(self, ifacename):
-        ret = False
-        try:
-            flags = self.read_file_oneline('/sys/class/net/%s/flags' % ifacename)
-            iflags = int(flags, 16)
-            if iflags & 0x0001:
-                ret = True
-        except:
-            ret = False
-        return ret
-
-    def ip_route_get_dev(self, prefix, vrf_master=None):
-        try:
-            if vrf_master:
-                cmd = '%s route get %s vrf %s' % (utils.ip_cmd, prefix, vrf_master)
-            else:
-                cmd = '%s route get %s' % (utils.ip_cmd, prefix)
-
-            output = utils.exec_command(cmd)
-            if output:
-                rline = output.splitlines()[0]
-                if rline:
-                    rattrs = rline.split()
-                    return rattrs[rattrs.index('dev') + 1]
-        except Exception, e:
-            self.logger.debug('ip_route_get_dev: failed .. %s' % str(e))
-        return None
-
-    @staticmethod
-    def link_get_lowers(ifacename):
-        try:
-            lowers = glob.glob("/sys/class/net/%s/lower_*" % ifacename)
-            if not lowers:
-                return []
-            return [os.path.basename(l)[6:] for l in lowers]
-        except:
-            return []
-
-    @staticmethod
-    def link_get_uppers(ifacename):
-        try:
-            uppers = glob.glob("/sys/class/net/%s/upper_*" % ifacename)
-            if not uppers:
-                return None
-            return [os.path.basename(u)[6:] for u in uppers]
-        except Exception:
-            return None
-
-    def link_get_vrfs(self):
-        if not LinkUtils._CACHE_FILL_DONE:
-            self._fill_cache()
-        return linkCache.vrfs
-
-    @staticmethod
-    def cache_get_info_slave(attrlist):
-        try:
-            return linkCache.get_attr(attrlist)
-        except:
-            return None
-
-    def get_brport_learning(self, ifacename):
-        learn = self.read_file_oneline('/sys/class/net/%s/brport/learning'
-                                       % ifacename)
-        if learn and learn == '1':
-            return 'on'
-        else:
-            return 'off'
-
-    def get_brport_learning_bool(self, ifacename):
-        return utils.get_boolean_from_string(self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename))
-
-    def set_brport_learning(self, ifacename, learn):
-        if learn == 'off':
-            return self.write_file('/sys/class/net/%s/brport/learning'
-                                   % ifacename, '0')
-        else:
-            return self.write_file('/sys/class/net/%s/brport/learning'
-                                   % ifacename, '1')
-
-    #################################################################################
-    ################################### BOND UTILS ##################################
-    #################################################################################
-
-    def _link_cache_get(self, attrlist, refresh=False):
-        return self._cache_get('link', attrlist, refresh)
-
-    def cache_delete(self, attrlist, value=None):
-        return self._cache_delete(attrlist, value)
-
-    def link_cache_get(self, attrlist, refresh=False):
-        return self._link_cache_get(attrlist, refresh)
-
-    def link_cache_check(self, attrlist, value, refresh=False):
-        return self._link_cache_check(attrlist, value, refresh)
-
-    def _link_cache_check(self, attrlist, value, refresh=False):
-        try:
-            return self._link_cache_get(attrlist, refresh) == value
-        except Exception, e:
-            self.logger.debug('_cache_check(%s) : [%s]'
-                              % (str(attrlist), str(e)))
-            pass
-        return False
-
-    bondcmd_attrmap = {
-        Link.IFLA_BOND_MODE: 'mode',
-        Link.IFLA_BOND_MIIMON: 'miimon',
-        Link.IFLA_BOND_USE_CARRIER: 'use_carrier',
-        Link.IFLA_BOND_AD_LACP_RATE: 'lacp_rate',
-        Link.IFLA_BOND_XMIT_HASH_POLICY: 'xmit_hash_policy',
-        Link.IFLA_BOND_MIN_LINKS: 'min_links',
-        Link.IFLA_BOND_NUM_PEER_NOTIF: 'num_grat_arp',
-        Link.IFLA_BOND_AD_ACTOR_SYSTEM: 'ad_actor_system',
-        Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: 'ad_actor_sys_prio',
-        Link.IFLA_BOND_AD_LACP_BYPASS: 'lacp_bypass',
-        Link.IFLA_BOND_UPDELAY: 'updelay',
-        Link.IFLA_BOND_DOWNDELAY: 'downdelay',
-    }
-
-    def bond_set_attrs_nl(self, bondname, ifla_info_data):
-        bond_attr_name = 'None'  # for log purpose (in case an exception raised)
-        for nl_attr, value in ifla_info_data.items():
-            try:
-                bond_attr_name = self.bondcmd_attrmap[nl_attr]
-                file_path = '/sys/class/net/%s/bonding/%s' % (bondname, bond_attr_name)
-                if os.path.exists(file_path):
-                    self.write_file(file_path, str(value))
-            except Exception as e:
-                exception_str = '%s: %s %s: %s' % (bondname, bond_attr_name, value, str(e))
-                if ifupdownflags.flags.FORCE:
-                    self.logger.warning(exception_str)
-                else:
-                    self.logger.debug(exception_str)
-
-    def bond_set_attrs(self, bondname, attrdict, prehook):
-        for attrname, attrval in attrdict.items():
-            if (self._link_cache_check([bondname, 'linkinfo',
-                                        attrname], attrval)):
-                continue
-            if (attrname == 'mode'
-                or attrname == 'xmit_hash_policy'
-                or attrname == 'lacp_rate' or attrname == 'min_links'):
-                if prehook:
-                    prehook(bondname)
-            try:
-                if ((attrname not in ['lacp_rate',
-                                      'lacp_bypass']) or
-                        self._link_cache_check([bondname, 'linkinfo', 'mode'], '802.3ad',
-                                               True)):
-                    self.write_file('/sys/class/net/%s/bonding/%s'
-                                    % (bondname, attrname), attrval)
-            except Exception, e:
-                if ifupdownflags.flags.FORCE:
-                    self.logger.warn(str(e))
-                    pass
-                else:
-                    raise
-
-    def bond_set_use_carrier(self, bondname, use_carrier):
-        if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
-            return
-        if (self._link_cache_check([bondname, 'linkinfo', 'use_carrier'],
-                                   use_carrier)):
-            return
-        self.write_file('/sys/class/net/%s' % bondname +
-                        '/bonding/use_carrier', use_carrier)
-        self._cache_update([bondname, 'linkinfo',
-                            'use_carrier'], use_carrier)
-
-    def bond_get_use_carrier(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'use_carrier'])
-
-    def bond_get_use_carrier_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])
-
-    def bond_set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
-        valid_values = ['layer2', 'layer3+4', 'layer2+3']
-        if not hash_policy:
-            return
-        if hash_policy not in valid_values:
-            raise Exception('invalid hash policy value %s' % hash_policy)
-        if (self._link_cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
-                                   hash_policy)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s' % bondname +
-                        '/bonding/xmit_hash_policy', hash_policy)
-        self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
-                           hash_policy)
-
-    def bond_get_xmit_hash_policy(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
-
-    def bond_get_xmit_hash_policy_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])
-
-    def bond_set_miimon(self, bondname, miimon):
-        if (self._link_cache_check([bondname, 'linkinfo', 'miimon'],
-                                   miimon)):
-            return
-        self.write_file('/sys/class/net/%s' % bondname +
-                        '/bonding/miimon', miimon)
-        self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
-
-    def bond_get_miimon(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'miimon'])
-
-    def bond_get_miimon_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON])
-
-    def bond_set_mode(self, bondname, mode, prehook=None):
-        valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
-                       'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
-        if not mode:
-            return
-        if mode not in valid_modes:
-            raise Exception('invalid mode %s' % mode)
-        if (self._link_cache_check([bondname, 'linkinfo', 'mode'],
-                                   mode)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s' % bondname + '/bonding/mode', mode)
-        self._cache_update([bondname, 'linkinfo', 'mode'], mode)
-
-    def bond_get_mode(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'mode'])
-
-    def bond_get_mode_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])
-
-    def bond_set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
-        if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
-            return
-        if (self._link_cache_check([bondname, 'linkinfo', 'lacp_rate'],
-                                   lacp_rate)):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' % bondname +
-                            '/bonding/lacp_rate', lacp_rate)
-        except:
-            raise
-        finally:
-            if posthook:
-                prehook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                                'lacp_rate'], lacp_rate)
-
-    def bond_get_lacp_rate(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'lacp_rate'])
-
-    def bond_get_lacp_rate_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])
-
-    def bond_set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None):
-        if self._link_cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' % bondname +
-                            '/bonding/lacp_bypass', allow)
-        except:
-            raise
-        finally:
-            if posthook:
-                posthook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                                'lacp_bypass'], allow)
-
-    def bond_get_lacp_bypass_allow(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'lacp_bypass'])
-
-    def bond_get_lacp_bypass_allow_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])
-
-    def bond_set_min_links(self, bondname, min_links, prehook=None):
-        if (self._link_cache_check([bondname, 'linkinfo', 'min_links'],
-                                   min_links)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s/bonding/min_links' % bondname,
-                        min_links)
-        self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
-
-    def bond_get_min_links(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'min_links'])
-
-    def get_min_links_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
-
-    def bond_get_ad_actor_system(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_system'])
-
-    def bond_get_ad_actor_system_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM])
-
-    def bond_get_ad_actor_sys_prio(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio'])
-
-    def bond_get_ad_actor_sys_prio_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO])
-
-    def bond_get_num_unsol_na(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'num_unsol_na'])
-
-    def bond_get_num_unsol_na_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
-
-    def bond_get_num_grat_arp(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'num_grat_arp'])
-
-    def bond_get_num_grat_arp_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
-
-    def bond_get_updelay(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'updelay'])
-
-    def bond_get_updelay_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY])
-
-    def bond_get_downdelay(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', 'downdelay'])
-
-    def bond_get_downdelay_nl(self, bondname):
-        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
-
-    def bond_enslave_slave(self, bondname, slave, prehook=None, posthook=None):
-        slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
-        if slaves and slave in slaves:
-            return
-        if prehook:
-            prehook(slave)
-        self.write_file('/sys/class/net/%s' % bondname +
-                        '/bonding/slaves', '+' + slave)
-        if posthook:
-            posthook(slave)
-        self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
-
-    def bond_remove_slave(self, bondname, slave):
-        slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
-        if not slaves or slave not in slaves:
-            return
-        sysfs_bond_path = ('/sys/class/net/%s' % bondname +
-                           '/bonding/slaves')
-        if not os.path.exists(sysfs_bond_path):
-            return
-        self.write_file(sysfs_bond_path, '-' + slave)
-        self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
-
-    def bond_remove_slaves_all(self, bondname):
-        if not self._link_cache_get([bondname, 'linkinfo', 'slaves']):
-            return
-        slaves = None
-        sysfs_bond_path = ('/sys/class/net/%s' % bondname +
-                           '/bonding/slaves')
-        try:
-            with open(sysfs_bond_path, 'r') as f:
-                slaves = f.readline().strip().split()
-        except IOError, e:
-            raise Exception('error reading slaves of bond %s (%s)' % (bondname, str(e)))
-        for slave in slaves:
-            self.link_down(slave)
-            try:
-                self.bond_remove_slave(bondname, slave)
-            except Exception, e:
-                if not ifupdownflags.flags.FORCE:
-                    raise Exception('error removing slave %s from bond %s (%s)' % (slave, bondname, str(e)))
-                else:
-                    pass
-        self._cache_delete([bondname, 'linkinfo', 'slaves'])
-
-    @staticmethod
-    def bond_load_bonding_module():
-        return utils.exec_command('%s -q bonding' % utils.modprobe_cmd)
-
-    def create_bond(self, bondname):
-        if self.bond_exists(bondname):
-            return
-        # load_bonding_module() has already been run
-        self.write_file('/sys/class/net/bonding_masters', '+' + bondname)
-        self._cache_update([bondname], {})
-
-    def delete_bond(self, bondname):
-        if not os.path.exists('/sys/class/net/%s' % bondname):
-            return
-        self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
-        self._cache_delete([bondname])
-
-    def bond_get_slaves(self, bondname):
-        slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
-        if slaves:
-            return list(slaves)
-        slavefile = '/sys/class/net/%s/bonding/slaves' % bondname
-        if os.path.exists(slavefile):
-            buf = self.read_file_oneline(slavefile)
-            if buf:
-                slaves = buf.split()
-        if not slaves:
-            return []
-        self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
-        return list(slaves)
-
-    def bond_slave_exists(self, bond, slave):
-        slaves = self.bond_get_slaves(bond)
-        if not slaves:
-            return False
-        return slave in slaves
-
-    @staticmethod
-    def bond_exists(bondname):
-        return os.path.exists('/sys/class/net/%s/bonding' % bondname)
-
-    #################################################################################
-    ################################## BRIDGE UTILS #################################
-    #################################################################################
-
-    def create_bridge(self, bridgename):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        if self.bridge_exists(bridgename):
-            return
-        utils.exec_command('%s addbr %s' % (utils.brctl_cmd, bridgename))
-        self._cache_update([bridgename], {})
-
-    def delete_bridge(self, bridgename):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        if not self.bridge_exists(bridgename):
-            return
-        utils.exec_command('%s delbr %s' % (utils.brctl_cmd, bridgename))
-        self._cache_invalidate()
-
-    def add_bridge_port(self, bridgename, bridgeportname):
-        """ Add port to bridge """
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
-        if ports and ports.get(bridgeportname):
-            return
-        utils.exec_command('%s addif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
-        self._cache_update([bridgename, 'linkinfo', 'ports', bridgeportname], {})
-
-    def delete_bridge_port(self, bridgename, bridgeportname):
-        """ Delete port from bridge """
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
-        if not ports or not ports.get(bridgeportname):
-            return
-        utils.exec_command('%s delif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
-        self._cache_delete([bridgename, 'linkinfo', 'ports', 'bridgeportname'])
-
-    def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
-        portattrs = self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname])
-        if portattrs == None:
-            portattrs = {}
-        for k, v in attrdict.iteritems():
-            if ifupdownflags.flags.CACHE:
-                curval = portattrs.get(k)
-                if curval and curval == v:
-                    continue
-            if k == 'unicast-flood':
-                self.write_file('/sys/class/net/%s/brport/unicast_flood' % bridgeportname, v)
-            elif k == 'multicast-flood':
-                self.write_file('/sys/class/net/%s/brport/multicast_flood' % bridgeportname, v)
-            elif k == 'learning':
-                self.write_file('/sys/class/net/%s/brport/learning' % bridgeportname, v)
-            elif k == 'arp-nd-suppress':
-                self.write_file('/sys/class/net/%s/brport/neigh_suppress' % bridgeportname, v)
-            else:
-                if not LinkUtils.bridge_utils_is_installed:
-                    continue
-                utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, k, bridgename, bridgeportname, v))
-
-    def set_bridgeport_attr(self, bridgename, bridgeportname,
-                            attrname, attrval):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        if self._link_cache_check([bridgename, 'linkinfo', 'ports', bridgeportname, attrname], attrval):
-            return
-        utils.exec_command('%s set%s %s %s %s' %
-                           (utils.brctl_cmd,
-                            attrname,
-                            bridgename,
-                            bridgeportname,
-                            attrval))
-
-    def set_bridge_attrs(self, bridgename, attrdict):
-        for k, v in attrdict.iteritems():
-            if not v:
-                continue
-            if self._link_cache_check([bridgename, 'linkinfo', k], v):
-                continue
-            try:
-                if k == 'igmp-version':
-                    self.write_file('/sys/class/net/%s/bridge/'
-                                    'multicast_igmp_version' % bridgename, v)
-                elif k == 'mld-version':
-                    self.write_file('/sys/class/net/%s/bridge/'
-                                    'multicast_mld_version' % bridgename, v)
-                elif k == 'vlan-protocol':
-                    self.write_file('/sys/class/net/%s/bridge/'
-                                    'vlan_protocol' % bridgename,
-                                    VlanProtocols.ETHERTYPES_TO_ID.get(v.upper(),
-                                                                       None))
-                elif k == 'vlan-stats':
-                    self.write_file('/sys/class/net/%s/bridge/'
-                                    'vlan_stats_enabled' % bridgename, v)
-                elif k == 'mcstats':
-                    self.write_file('/sys/class/net/%s/bridge/'
-                                    'multicast_stats_enabled' % bridgename, v)
-                else:
-                    if not LinkUtils.bridge_utils_is_installed:
-                        continue
-                    cmd = ('%s set%s %s %s' %
-                           (utils.brctl_cmd, k, bridgename, v))
-                    utils.exec_command(cmd)
-            except Exception, e:
-                self.logger.warn('%s: %s' % (bridgename, str(e)))
-                pass
-
-    def set_bridge_attr(self, bridgename, attrname, attrval):
-        if self._link_cache_check([bridgename, 'linkinfo', attrname], attrval):
-            return
-        if attrname == 'igmp-version':
-            self.write_file('/sys/class/net/%s/bridge/multicast_igmp_version'
-                            % bridgename, attrval)
-        elif attrname == 'mld-version':
-            self.write_file('/sys/class/net/%s/bridge/multicast_mld_version'
-                            % bridgename, attrval)
-        elif attrname == 'vlan-protocol':
-            self.write_file('/sys/class/net/%s/bridge/vlan_protocol'
-                            % bridgename, VlanProtocols.ETHERTYPES_TO_ID[attrval.upper()])
-        elif attrname == 'vlan-stats':
-            self.write_file('/sys/class/net/%s/bridge/vlan_stats_enabled'
-                            % bridgename, attrval)
-        elif attrname == 'mcstats':
-            self.write_file('/sys/class/net/%s/bridge/multicast_stats_enabled'
-                            % bridgename, attrval)
-        else:
-            if not LinkUtils.bridge_utils_is_installed:
-                return
-            cmd = '%s set%s %s %s' % (utils.brctl_cmd,
-                                      attrname, bridgename, attrval)
-            utils.exec_command(cmd)
-
-    def get_bridge_attrs(self, bridgename):
-        attrs = self._link_cache_get([bridgename, 'linkinfo'])
-        no_ints_attrs = {}
-        for key, value in attrs.items():
-            if type(key) == str:
-                no_ints_attrs[key] = value
-        return no_ints_attrs
-
-    def get_bridgeport_attrs(self, bridgename, bridgeportname):
-        return self._link_cache_get([bridgename, 'linkinfo', 'ports',
-                                     bridgeportname])
-
-    def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
-        return self._link_cache_get([bridgename, 'linkinfo', 'ports',
-                                     bridgeportname, attrname])
-
-    @staticmethod
-    def bridge_set_stp(bridge, stp_state):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s stp %s %s' % (utils.brctl_cmd, bridge, stp_state))
-
-    def bridge_get_stp(self, bridge):
-        sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' % bridge
-        if not os.path.exists(sysfs_stpstate):
-            return 'error'
-        stpstate = self.read_file_oneline(sysfs_stpstate)
-        if not stpstate:
-            return 'error'
-        try:
-            if int(stpstate) > 0:
-                return 'yes'
-            elif int(stpstate) == 0:
-                return 'no'
-        except:
-            return 'unknown'
-
-    @staticmethod
-    def _conv_value_to_user(s):
-        try:
-            ret = int(s) / 100
-            return '%d' % ret
-        except:
-            return None
-
-    def read_value_from_sysfs(self, filename, preprocess_func):
-        value = self.read_file_oneline(filename)
-        if not value:
-            return None
-        return preprocess_func(value)
-
-    @staticmethod
-    def bridge_set_ageing(bridge, ageing):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setageing %s %s' % (utils.brctl_cmd, bridge, ageing))
-
-    def bridge_get_ageing(self, bridge):
-        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
-                                          % bridge, self._conv_value_to_user)
-
-    @staticmethod
-    def set_bridgeprio(bridge, prio):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setbridgeprio %s %s' % (utils.brctl_cmd, bridge, prio))
-
-    def get_bridgeprio(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/priority' % bridge)
-
-    @staticmethod
-    def bridge_set_fd(bridge, fd):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setfd %s %s' % (utils.brctl_cmd, bridge, fd))
-
-    def bridge_get_fd(self, bridge):
-        return self.read_value_from_sysfs(
-            '/sys/class/net/%s/bridge/forward_delay'
-            % bridge, self._conv_value_to_user)
-
-    def bridge_set_gcint(self, bridge, gcint):
-        raise Exception('set_gcint not implemented')
-
-    @staticmethod
-    def bridge_set_hello(bridge, hello):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s sethello %s %s' % (utils.brctl_cmd, bridge, hello))
-
-    def bridge_get_hello(self, bridge):
-        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
-                                          % bridge, self._conv_value_to_user)
-
-    @staticmethod
-    def bridge_set_maxage(bridge, maxage):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmaxage %s %s' % (utils.brctl_cmd, bridge, maxage))
-
-    def bridge_get_maxage(self, bridge):
-        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
-                                          % bridge, self._conv_value_to_user)
-
-    @staticmethod
-    def bridge_set_pathcost(bridge, port, pathcost):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setpathcost %s %s %s' % (utils.brctl_cmd, bridge, port, pathcost))
-
-    def bridge_get_pathcost(self, bridge, port):
-        return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
-                                      % port)
-
-    @staticmethod
-    def bridge_set_portprio(bridge, port, prio):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setportprio %s %s %s' % (utils.brctl_cmd, bridge, port, prio))
-
-    def bridge_get_portprio(self, bridge, port):
-        return self.read_file_oneline('/sys/class/net/%s/brport/priority'
-                                      % port)
-
-    @staticmethod
-    def bridge_set_hashmax(bridge, hashmax):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s sethashmax %s %s' % (utils.brctl_cmd, bridge, hashmax))
-
-    def bridge_get_hashmax(self, bridge):
-        return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
-                                      % bridge)
-
-    @staticmethod
-    def bridge_set_hashel(bridge, hashel):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s sethashel %s %s' % (utils.brctl_cmd, bridge, hashel))
-
-    def bridge_get_hashel(self, bridge):
-        return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
-                                      % bridge)
-
-    @staticmethod
-    def bridge_set_mclmc(bridge, mclmc):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmclmc %s %s' % (utils.brctl_cmd, bridge, mclmc))
-
-    def bridge_get_mclmc(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_last_member_count'
-            % bridge)
-
-    @staticmethod
-    def bridge_set_mcrouter(bridge, mcrouter):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmcrouter %s %s' % (utils.brctl_cmd, bridge, mcrouter))
-
-    def bridge_get_mcrouter(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_router' % bridge)
-
-    @staticmethod
-    def bridge_set_mcsnoop(bridge, mcsnoop):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmcsnoop %s %s' % (utils.brctl_cmd, bridge, mcsnoop))
-
-    def bridge_get_mcsnoop(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_snooping' % bridge)
-
-    @staticmethod
-    def bridge_set_mcsqc(bridge, mcsqc):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmcsqc %s %s' % (utils.brctl_cmd, bridge, mcsqc))
-
-    def bridge_get_mcsqc(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_startup_query_count'
-            % bridge)
-
-    @staticmethod
-    def bridge_set_mcqifaddr(bridge, mcqifaddr):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmcqifaddr %s %s' % (utils.brctl_cmd, bridge, mcqifaddr))
-
-    def bridge_get_mcqifaddr(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
-            % bridge)
-
-    @staticmethod
-    def bridge_set_mcquerier(bridge, mcquerier):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmcquerier %s %s' % (utils.brctl_cmd, bridge, mcquerier))
-
-    def bridge_get_mcquerier(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_querier' % bridge)
-
-    def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
-        try:
-            vlan = int(vlan)
-        except:
-            self.logger.info('%s: set mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
-            return
-        if vlan == 0 or vlan > 4095:
-            self.logger.warn('mcqv4src vlan \'%d\' invalid range' % vlan)
-            return
-
-        ip = mcquerier.split('.')
-        if len(ip) != 4:
-            self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
-            return
-        for k in ip:
-            if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
-                self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
-                return
-
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-
-        utils.exec_command('%s setmcqv4src %s %d %s' %
-                           (utils.brctl_cmd, bridge, vlan, mcquerier))
-
-    def bridge_del_mcqv4src(self, bridge, vlan):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        try:
-            vlan = int(vlan)
-        except:
-            self.logger.info('%s: del mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
-            return
-        utils.exec_command('%s delmcqv4src %s %d' % (utils.brctl_cmd, bridge, vlan))
-
-    def bridge_get_mcqv4src(self, bridge, vlan=None):
-        if not LinkUtils.bridge_utils_is_installed:
-            return {}
-        if not self.supported_command['showmcqv4src']:
-            return {}
-        mcqv4src = {}
-        try:
-            mcqout = utils.exec_command('%s showmcqv4src %s' %
-                                        (utils.brctl_cmd, bridge))
-        except Exception as e:
-            s = str(e).lower()
-            if 'never heard' in s:
-                msg = ('%s showmcqv4src: skipping unsupported command'
-                       % utils.brctl_cmd)
-                self.logger.info(msg)
-                self.supported_command['showmcqv4src'] = False
-                return {}
-            raise
-        if not mcqout:
-            return {}
-        mcqlines = mcqout.splitlines()
-        for l in mcqlines[1:]:
-            l = l.strip()
-            k, d, v = l.split('\t')
-            if not k or not v:
-                continue
-            mcqv4src[k] = v
-        if vlan:
-            return mcqv4src.get(vlan)
-        return mcqv4src
-
-    def bridge_get_mcqv4src_sysfs(self, bridge, vlan=None):
-        if not LinkUtils.bridge_utils_is_installed:
-            return {}
-        if not self.supported_command['showmcqv4src']:
-            return {}
-        if ifupdownflags.flags.PERFMODE:
-            return {}
-        mcqv4src = {}
-        try:
-            filename = '/sys/class/net/%s/bridge/multicast_v4_queriers' % bridge
-            if os.path.exists(filename):
-                for line in self.read_file(filename) or []:
-                    vlan_id, ip = line.split('=')
-                    mcqv4src[vlan_id] = ip.strip()
-        except Exception as e:
-            s = str(e).lower()
-            msg = ('%s showmcqv4src: skipping unsupported command'
-                   % utils.brctl_cmd)
-            self.logger.info(msg)
-            self.supported_command['showmcqv4src'] = False
-            return {}
-        if vlan:
-            return mcqv4src.get(vlan)
-        return mcqv4src
-
-    @staticmethod
-    def bridge_set_mclmi(bridge, mclmi):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmclmi %s %s' % (utils.brctl_cmd, bridge, mclmi))
-
-    def bridge_get_mclmi(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_last_member_interval'
-            % bridge)
-
-    @staticmethod
-    def bridge_set_mcmi(bridge, mcmi):
-        if not LinkUtils.bridge_utils_is_installed:
-            return
-        utils.exec_command('%s setmcmi %s %s' % (utils.brctl_cmd, bridge, mcmi))
-
-    def bridge_get_mcmi(self, bridge):
-        return self.read_file_oneline(
-            '/sys/class/net/%s/bridge/multicast_membership_interval'
-            % bridge)
-
-    @staticmethod
-    def bridge_exists(bridge):
-        return os.path.exists('/sys/class/net/%s/bridge' % bridge)
-
-    @staticmethod
-    def is_bridge_port(ifacename):
-        return os.path.exists('/sys/class/net/%s/brport' % ifacename)
-
-    @staticmethod
-    def bridge_port_exists(bridge, bridgeportname):
-        try:
-            return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname))
-        except:
-            return False
-
-    @staticmethod
-    def get_bridge_ports(bridgename):
-        try:
-            return os.listdir('/sys/class/net/%s/brif/' % bridgename)
-        except:
-            return []
-
-    def reset_addr_cache(self, ifname):
-        try:
-            linkCache.links[ifname]['addrs'] = {}
-            self.logger.debug('%s: reset address cache' % ifname)
-        except:
-            pass
-
-    def get_ipv6_addrgen_mode(self, ifname):
-        try:
-            return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE]
-        except:
-            # default to 0 (eui64)
-            return 0
-
-    def ipv6_addrgen(self, ifname, addrgen, link_created):
-        try:
-            # IFLA_INET6_ADDR_GEN_MODE values:
-            # 0 = eui64
-            # 1 = none
-            if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen:
-                self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on'))
-                return
-
-            disabled_ipv6 = self.read_file_oneline('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % ifname)
-            if not disabled_ipv6 or int(disabled_ipv6) == 1:
-                self.logger.info('%s: cannot set addrgen: ipv6 is disabled on this device' % ifname)
-                return
-
-            if int(self._link_cache_get([ifname, 'mtu'])) < 1280:
-                self.logger.info('%s: ipv6 addrgen is disabled on device with MTU '
-                                 'lower than 1280: cannot set addrgen %s' % (ifname, 'off' if addrgen else 'on'))
-                return
-        except (KeyError, TypeError):
-            self.logger.debug('%s: ipv6 addrgen probably not supported or disabled on this device' % ifname)
-            return
-        except Exception:
-            pass
-
-        if not link_created:
-            # When setting addrgenmode it is necessary to flap the macvlan
-            # device. After flapping the device we also need to re-add all
-            # the user configuration. The best way to add the user config
-            # is to flush our internal address cache
-            self.reset_addr_cache(ifname)
-
-        cmd = 'link set dev %s addrgenmode %s' % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen))
-
-        is_link_up = self.is_link_up(ifname)
-
-        if is_link_up:
-            self.link_down(ifname)
-
-        #if LinkUtils.ipbatch:
-        #    self.add_to_batch(cmd)
-        #else:
-        # because this command might fail on older kernel its better to not batch it
-        utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
-
-        if is_link_up:
-            self.link_up(ifname)
index b74f3276bfa02269fd406feee1f7f4e9a7bad667..483b59f9719411cb6a83fec626450bae4dab53c0 100644 (file)
@@ -4,8 +4,6 @@
 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
 #
 
-import pprint
-
 
 class MSTPAttrsCache():
     bridges = {}
@@ -24,91 +22,3 @@ class MSTPAttrsCache():
     @classmethod
     def invalidate(cls):
         MSTPAttrsCache.bridges = {}
-
-
-class linkCache():
-    """ This class contains methods and instance variables to cache
-    link info """
-
-    """ { <ifacename> : { 'ifindex': <index>,
-                          'mtu': <mtu>,
-                          'state' : <state>',
-                          'flags' : <flags>,
-                          'kind' : <kind: bridge, bond, vlan>,
-                          'linkinfo' : {<attr1> : <attrval1>,
-                                        <attr2> : <attrval2>,
-                                        <ports> : {
-                                                  } """
-    links = {}
-    vrfs = {}
-
-    @classmethod
-    def get_attr(cls, mapList):
-        return reduce(lambda d, k: d[k], mapList, linkCache.links)
-
-    @classmethod
-    def set_attr(cls, mapList, value):
-        cls.get_attr(mapList[:-1])[mapList[-1]] = value
-
-    @classmethod
-    def del_attr(cls, mapList):
-        try:
-            del cls.get_attr(mapList[:-1])[mapList[-1]]
-        except:
-            pass
-
-    @classmethod
-    def update_attrdict(cls, mapList, valuedict):
-        try:
-            cls.get_attr(mapList[:-1])[mapList[-1]].update(valuedict)
-        except:
-            cls.get_attr(mapList[:-1])[mapList[-1]] = valuedict
-            pass
-
-    @classmethod
-    def append_to_attrlist(cls, mapList, value):
-        cls.get_attr(mapList[:-1])[mapList[-1]].append(value)
-
-    @classmethod
-    def remove_from_attrlist(cls, mapList, value):
-        try:
-            cls.get_attr(mapList[:-1])[mapList[-1]].remove(value)
-        except:
-            pass
-
-    @classmethod
-    def check_attr(cls, attrlist, value=None):
-        try:
-            cachedvalue = cls.get_attr(attrlist)
-            if value:
-                if cachedvalue == value:
-                    return True
-                else:
-                    return False
-            elif cachedvalue:
-                return True
-            else:
-                return False
-        except:
-            return False
-
-    @classmethod
-    def invalidate(cls):
-        cls.links = {}
-
-    @classmethod
-    def reset(cls):
-        cls.invalidate()
-        cls.vrfs = {}
-
-    @classmethod
-    def dump(cls):
-        print 'Dumping link cache'
-        pp = pprint.PrettyPrinter(indent=4)
-        pp.pprint(cls.links)
-
-    @classmethod
-    def dump_link(cls, linkname):
-        print 'Dumping link %s' % linkname
-        pp = pprint.PrettyPrinter(indent=4)
-        pp.pprint(cls.links.get(linkname))
index 4d907f59507eb45c8fee714a3d3b84d4e0e75879..7e67bf78e717aa9b6612b667d49c190a74e9e4ef 100644 (file)
@@ -5,6 +5,7 @@
 #
 
 import os
+import errno
 
 try:
     from ifupdown2.ifupdown.utils import utils
@@ -20,12 +21,19 @@ class dhclient(utilsBase):
 
     def _pid_exists(self, pidfilename):
         if os.path.exists(pidfilename):
-            pid = self.read_file_oneline(pidfilename)
-            if not os.path.exists('/proc/%s' %pid):
+            try:
+                return os.readlink(
+                    "/proc/%s/exe" % self.read_file_oneline(pidfilename)
+                ).endswith("dhclient")
+            except OSError as e:
+                try:
+                    if e.errno == errno.EACCES:
+                        return os.path.exists("/proc/%s" % self.read_file_oneline(pidfilename))
+                except:
+                    return False
+            except:
                 return False
-        else:
-            return False
-        return True
+        return False
 
     def is_running(self, ifacename):
         return self._pid_exists('/run/dhclient.%s.pid' %ifacename)
index ca54743a82cb748a1e2c0b0f8f2b65fe146efce3..6ac92753f9e608e53e0dc79c9e684482d54bb66d 100644 (file)
@@ -60,6 +60,53 @@ class mstpctlutil(utilsBase):
     def __init__(self, *args, **kargs):
         utilsBase.__init__(self, *args, **kargs)
 
+        self.__batch = []
+        self.__batch_mode = False
+
+    def __add_to_batch(self, cmd):
+        self.__batch.append(cmd)
+
+    def __execute_or_batch(self, cmd):
+        if self.__batch_mode:
+            self.__add_to_batch(cmd)
+        else:
+            utils.exec_command("%s %s" % (utils.mstpctl_cmd, cmd))
+
+    def __execute_or_batch_dry_run(self, cmd):
+        """
+        The batch function has it's own dryrun handler so we only handle
+        dryrun for non-batch mode. Which will be removed once the "utils"
+        module has it's own dryrun handlers
+        """
+        if self.__batch_mode:
+            self.__add_to_batch(cmd)
+        else:
+            self.logger.info("DRY-RUN: executing: %s %s" % (utils.mstpctl_cmd, cmd))
+
+    def batch_start(self):
+        if not self.__batch_mode:
+            self.__batch_mode = True
+            self.__batch = []
+
+    def batch_commit(self):
+        if not self.__batch_mode or not self.__batch:
+            return
+        try:
+            utils.exec_command(
+                "%s batch -" % utils.mstpctl_cmd,
+                stdin="\n".join(self.__batch)
+            )
+        except:
+            raise
+        finally:
+            self.__batch_mode = False
+            del self.__batch
+            self.__batch = None
+
+    ###############################################################################
+    ###############################################################################
+    ###############################################################################
+
     @classmethod
     def reset(cls):
         cls._cache_fill_done = False
@@ -93,6 +140,7 @@ class mstpctlutil(utilsBase):
         except Exception as e:
             self.logger.info(str(e))
             return mstpctl_bridgeport_attrs_dict
+        portname = bridgename  # assigning portname to avoid an exception, in the exception handler
         try:
             mstpctl_bridge_cache = json.loads(output.strip('\n'))
             for portname in mstpctl_bridge_cache.keys():
@@ -104,7 +152,7 @@ class mstpctlutil(utilsBase):
                         mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal)
             MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict)
         except Exception as e:
-            self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % str(e))
+            self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % (portname, str(e)))
 
         mstpctl_bridge_attrs_dict = {}
         try:
@@ -127,7 +175,7 @@ class mstpctlutil(utilsBase):
             del mstpctl_bridge_attrs_dict['bridgeId']
             MSTPAttrsCache.bridges[bridgename].update(mstpctl_bridge_attrs_dict)
         except Exception as e:
-            self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % str(e))
+            self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % (bridgename, str(e)))
         return MSTPAttrsCache.get(bridgename)
 
     def get_bridge_ports_attrs(self, bridgename):
@@ -161,11 +209,9 @@ class mstpctlutil(utilsBase):
         if cache_value and cache_value == value:
             return
         if attrname == 'treeportcost' or attrname == 'treeportprio':
-            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
-                                 bridgename, portname, '0', value])
+            self.__execute_or_batch("set%s %s %s 0 %s" % (attrname, bridgename, portname, value))
         else:
-            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
-                                 bridgename, portname, value])
+            self.__execute_or_batch("set%s %s %s %s" % (attrname, bridgename, portname, value))
         if json_attr:
             self.update_bridge_port_cache(bridgename, portname, json_attr, value)
 
@@ -195,13 +241,12 @@ class mstpctlutil(utilsBase):
                                         self._bridge_jsonAttr_map[attrname])
             if attrvalue_curr and attrvalue_curr == attrvalue:
                 return
+
         if attrname == 'treeprio':
-            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
-                                 '%s' % bridgename, '0', '%s' % attrvalue], stderr=None)
+            self.__execute_or_batch("set%s %s 0 %s" % (attrname, bridgename, attrvalue))
             self.update_bridge_cache(bridgename, attrname, str(attrvalue))
         else:
-            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
-                                 '%s' % bridgename, '%s' % attrvalue], stderr=None)
+            self.__execute_or_batch("set%s %s %s" % (attrname, bridgename, attrvalue))
             self.update_bridge_cache(bridgename,
                                      self._bridge_jsonAttr_map[attrname],
                                      str(attrvalue))
@@ -223,9 +268,8 @@ class mstpctlutil(utilsBase):
             attrvalue_curr = self.get_bridge_treeprio(bridgename)
             if attrvalue_curr and attrvalue_curr == attrvalue:
                 return
-        utils.exec_commandl([utils.mstpctl_cmd,
-                             'settreeprio', bridgename, '0',
-                             str(attrvalue)])
+        self.__execute_or_batch("settreeprio %s 0 %s" % (bridgename, str(attrvalue)))
+
         self.update_bridge_cache(bridgename, 'treeprio', str(attrvalue))
 
     def showbridge(self, bridgename=None):
diff --git a/ifupdown2/lib/__init__.py b/ifupdown2/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py
new file mode 100644 (file)
index 0000000..12c5020
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# addon -- Addon base class
+#
+
+import logging
+
+try:
+    from ifupdown2.lib.io import IO
+    from ifupdown2.lib.sysfs import Sysfs
+    from ifupdown2.lib.iproute2 import IPRoute2
+    from ifupdown2.lib.base_objects import Netlink, Cache, Requirements
+except ImportError:
+    from lib.io import IO
+    from lib.sysfs import Sysfs
+    from lib.iproute2 import IPRoute2
+    from lib.base_objects import Netlink, Cache, Requirements
+
+
+class Addon(Netlink, Cache):
+    """
+    Base class for ifupdown2 addon modules
+    Provides common infrastructure methods for all addon modules
+    """
+
+    def __init__(self):
+        Netlink.__init__(self)
+        Cache.__init__(self)
+
+        self.logger = logging.getLogger("ifupdown2.addons.%s" % self.__class__.__name__)
+
+        self.io = IO()
+        self.sysfs = Sysfs
+        self.iproute2 = IPRoute2()
+        self.requirements = Requirements()
diff --git a/ifupdown2/lib/base_objects.py b/ifupdown2/lib/base_objects.py
new file mode 100644 (file)
index 0000000..786edc5
--- /dev/null
@@ -0,0 +1,69 @@
+# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# base_objects -- Base classes used by higher level classes
+#
+
+import os
+import logging
+
+try:
+    from ifupdown2.lib.dry_run import DryRun
+    from ifupdown2.ifupdown.utils import utils
+except ImportError:
+    from lib.dry_run import DryRun
+    from ifupdown.utils import utils
+
+
+class BaseObject(DryRun):
+    """
+    BaseObject should be the parent of any ifupdown2 object that wishes to
+    implement any "dry run" specific code and have a default logger.
+    More classes can inherit BaseObject and add features like Addon, FileIO or Sysfs...
+    """
+
+    def __init__(self):
+        DryRun.__init__(self)
+        self.logger = logging.getLogger("ifupdown2.%s" % self.__class__.__name__)
+
+
+def _import_NetlinkListenerWithCache():
+    try:
+        from ifupdown2.lib.nlcache import NetlinkListenerWithCache
+    except ImportError:
+        from lib.nlcache import NetlinkListenerWithCache
+    return NetlinkListenerWithCache
+
+
+class Cache(BaseObject):
+    def __init__(self):
+        BaseObject.__init__(self)
+        self.cache = _import_NetlinkListenerWithCache().get_instance().cache
+
+
+class Netlink(BaseObject):
+    def __init__(self):
+        BaseObject.__init__(self)
+        self.netlink = _import_NetlinkListenerWithCache().get_instance()
+
+
+class Requirements(BaseObject):
+    bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)
diff --git a/ifupdown2/lib/dry_run.py b/ifupdown2/lib/dry_run.py
new file mode 100644 (file)
index 0000000..b0c6f9a
--- /dev/null
@@ -0,0 +1,250 @@
+#!/usr/bin/python
+# Copyright (C) 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown2 -- dry_run module
+#
+#
+# __WeakMethodBound and __WeakMethodFree classes as well as the WeakMethod
+# function were inspired by the implementation of ActiveState recipes 81253
+# weakmethod.
+# This code solves an important issue here. You can't have weakrefs on bound
+# methods. Here is quote from the recipie:
+#
+# "Normal weakref.refs to bound methods don't quite work the way one expects,
+# because bound methods are first-class objects; weakrefs to bound methods are
+# dead-on-arrival unless some other strong reference to the same bound method
+# exists."
+#
+
+import logging
+import inspect
+import weakref
+
+
+class __WeakMethodBound:
+    """ ActiveState recipes 81253-weakmethod """
+
+    def __init__(self, f):
+        self.f = f.im_func
+        self.c = weakref.ref(f.im_self)
+
+    def __call__(self, *arg, **kwargs):
+        if not self.c():
+            raise TypeError("Method called on dead object")
+        apply(self.f, (self.c(),) + arg, kwargs)
+
+
+class __WeakMethodFree:
+    """ ActiveState recipes 81253-weakmethod """
+
+    def __init__(self, f):
+        self.f = weakref.ref(f)
+
+    def __call__(self, *arg, **kwargs):
+        if not self.f():
+            raise TypeError("Function no longer exist")
+        apply(self.f(), arg, kwargs)
+
+
+def WeakMethod(f):
+    """ ActiveState recipes 81253-weakmethod """
+    try:
+        f.im_func
+    except AttributeError:
+        return __WeakMethodFree(f)
+    return __WeakMethodBound(f)
+
+
+def _weakref_call_back_delete(reference):
+    try:
+        DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(reference)
+    except:
+        pass
+
+
+class DryRun(object):
+    """
+    Detect dry_run functions and save the associated handler
+    """
+    __DRY_RUN_PREFIX = "DRY-RUN"
+
+    def __init__(self):
+        self.logger = logging.getLogger("ifupdown2.%s" % self.__class__.__name__)
+
+        for attr_name in dir(self):
+            try:
+                # We need to iterate through the object attribute
+                # to find dryrun methods
+                if attr_name.lower().endswith("_dry_run"):
+                    attr_value = getattr(self, attr_name)
+
+                    # When we find a dryrun attribute we need to make sure
+                    # it is a callable function or method.
+                    if not self.__is_method_or_function(attr_value):
+                        continue
+
+                    base_attr_name = attr_name[:-8]
+                    base_attr_value = getattr(self, base_attr_name)
+                    # We try infere the base method/function name
+                    # then make sure its a function or method
+                    if not self.__is_method_or_function(base_attr_value):
+                        continue
+
+                    # now we are pretty sure we have want we want:
+                    # - the base function
+                    # - the associated dry_run code
+                    # we will now register this couple in the DryRunManager
+                    DryRunManager.get_instance().register_dry_run_handler(
+                        weakref.ref(self, _weakref_call_back_delete),
+                        handler_name=base_attr_name,
+                        handler_code_weakref=WeakMethod(base_attr_value),
+                        dry_run_code_weakref=WeakMethod(attr_value)
+                    )
+            except:
+                pass
+
+    def log_info_ifname_dry_run(self, ifname, string):
+        self.logger.info("DRY-RUN: %s: %s" % (ifname, string))
+
+    def log_info_dry_run(self, string):
+        self.logger.info("DRY-RUN: %s" % string)
+
+    @staticmethod
+    def __is_method_or_function(obj):
+        return callable(obj) and (inspect.ismethod(obj) or inspect.isfunction(obj))
+
+
+class _DryRunEntry(object):
+    def __init__(self, target_module_weakref, handler_name, handler_code_weakref, dry_run_code_weakref):
+        self.target_module_weakref = target_module_weakref
+        self.handler_name = handler_name
+        self.handler_code_weakref = handler_code_weakref
+        self.dry_run_code_weakref = dry_run_code_weakref
+        self.__status = False
+
+    def set(self):
+        target_module_ref = self.target_module_weakref()
+
+        if target_module_ref:
+            if self.dry_run_code_weakref:
+                target_module_ref.__dict__[self.handler_name] = self.dry_run_code_weakref
+                self.__status = True
+        else:
+            # if the reference is dead we need to unregister it
+            DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(self.target_module_weakref)
+
+    def unset(self):
+        target_module_ref = self.target_module_weakref()
+
+        if target_module_ref:
+            if self.handler_code_weakref:
+                target_module_ref.__dict__[self.handler_name] = self.handler_code_weakref
+                self.__status = False
+        else:
+            # if the reference is dead we need to unregister it
+            DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(self.target_module_weakref)
+
+    def get_status(self):
+        return self.__status
+
+
+class DryRunManager(object):
+    __instance = None
+
+    @staticmethod
+    def get_instance():
+        if not DryRunManager.__instance:
+            DryRunManager.__instance = DryRunManager()
+        return DryRunManager.__instance
+
+    def __init__(self):
+        if DryRunManager.__instance:
+            raise RuntimeError("DryRunManager: invalid access. Please use DryRunManager.getInstance()")
+        else:
+            DryRunManager.__instance = self
+
+        self.__entries = dict()
+        self.__is_on = False
+
+    def register_dry_run_handler(self, module_weakref, handler_name, handler_code_weakref, dry_run_code_weakref):
+        """
+        Register the dry run handler only using weakrefs - we don't want to mess up with garbage collection
+        :param module_weakref:
+        :param handler_name:
+        :param handler_code_weakref:
+        :param dry_run_code_weakref:
+        :return:
+        """
+        dry_run_entry = _DryRunEntry(
+            target_module_weakref=module_weakref,
+            handler_name=handler_name,
+            handler_code_weakref=handler_code_weakref,
+            dry_run_code_weakref=dry_run_code_weakref
+        )
+        if self.__is_on:
+            dry_run_entry.set()
+
+        if module_weakref in self.__entries:
+            self.__entries[module_weakref].append(dry_run_entry)
+        else:
+            self.__entries[module_weakref] = [dry_run_entry]
+
+    def unregister_dry_run_handler_weakref_callback(self, reference):
+        """
+        If we detect a dead reference, we should remove this reference from our
+        internal data structure
+        """
+        try:
+            del self.__entries[reference]
+        except:
+            pass
+
+    def dry_run_mode_on(self):
+        """
+        Enable the dry run mode
+        WARNING: not thread-safe
+        """
+        for entries in self.__entries.itervalues():
+            for entry in entries:
+                entry.set()
+        self.__is_on = True
+
+    def dry_run_mode_off(self):
+        """
+        Disable the dry run mode
+        WARNING: not thread-safe
+        """
+        for entries in self.__entries.itervalues():
+            for entry in entries:
+                entry.unset()
+        self.__is_on = False
+
+    def dump_entries_stdout(self):
+        print "== DryRunManager dump =="
+        print "  MODULE:    HANDLER  STATUS"
+        for entries in self.__entries.itervalues():
+            for entry in entries:
+                print "  %s: %s() %s" % (repr(entry.target_module_weakref), entry.handler_name, "ON" if entry.get_status() else "OFF")
+        print "========================"
+
+    def is_dry_mode_on(self):
+        return self.__is_on
diff --git a/ifupdown2/lib/exceptions.py b/ifupdown2/lib/exceptions.py
new file mode 100644 (file)
index 0000000..25e928b
--- /dev/null
@@ -0,0 +1,43 @@
+# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown2 custom exceptions
+#
+
+
+class Ifupdown2Exception(Exception):
+    pass
+
+
+class ExitWithStatus(Ifupdown2Exception):
+
+    def __init__(self, status):
+        Ifupdown2Exception.__init__(self)
+        self.status = status
+
+    def get_status(self):
+        return self.status
+
+
+class ExitWithStatusAndError(ExitWithStatus):
+    def __init__(self, status, message):
+        ExitWithStatus.__init__(self, status)
+        self.message = message
diff --git a/ifupdown2/lib/io.py b/ifupdown2/lib/io.py
new file mode 100644 (file)
index 0000000..057b325
--- /dev/null
@@ -0,0 +1,130 @@
+# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# io -- all io (file) handlers
+#
+
+import json
+import struct
+import socket
+import select
+
+try:
+    from ifupdown2.lib.base_objects import BaseObject
+except ImportError:
+    from lib.base_objects import BaseObject
+
+
+class IO(BaseObject):
+    def __init__(self):
+        BaseObject.__init__(self)
+
+    def write_to_file(self, path, string):
+        try:
+            self.logger.info("writing \"%s\" to file %s" % (string, path))
+            with open(path, "w") as f:
+                f.write(string)
+            return True
+        except IOError, e:
+            self.logger.warn("error while writing to file %s: %s" % (path, str(e)))
+            return False
+
+    def write_to_file_dry_run(self, path, string):
+        self.log_info_dry_run("writing \"%s\" to file %s" % (string, path))
+        return True
+
+    def read_file_oneline(self, path):
+        try:
+            self.logger.info("reading '%s'" % path)
+            with open(path, "r") as f:
+                return f.readline().strip("\n")
+        except:
+            return None
+
+    def read_file_oneline_dry_run(self, path):
+        self.log_info_dry_run("reading \"%s\"" % path)
+        return None
+
+    def read_file(self, path):
+        """ read file and return lines from the file """
+        try:
+            self.logger.info("reading '%s'" % path)
+            with open(path, "r") as f:
+                return f.readlines()
+        except:
+            return None
+
+
+class SocketIO(object):
+    """
+    Helper class to provide common TX/RX methods for socket
+    communication to both client and daemon.
+    """
+
+    @staticmethod
+    def tx_data(_socket, data):
+        """
+        We don't send raw data over the socket, we pack it with the length
+        (first 4 bytes) then with the data. That way the the transfer is more
+        reliable
+        """
+        ready = select.select([], [_socket], [])
+        if ready and ready[1] and ready[1][0] == _socket:
+            frmt = "=%ds" % len(data)
+            packed_msg = struct.pack(frmt, data)
+            packed_hdr = struct.pack("=I", len(packed_msg))
+            _socket.sendall(packed_hdr + packed_msg)
+
+    @staticmethod
+    def rx_json_packet(_socket):
+        """
+        Reading data from socket. Unpacking the packets sent by "tx_data"
+        first 4 bytes are the length of the following data. The data should
+        be in json format
+        """
+        ready = select.select([_socket], [], [])
+
+        if ready and ready[0] and ready[0][0] == _socket:
+
+            header_data = _socket.recv(4)
+
+            if not header_data:
+                raise Exception("rx_json_packet: socket closed")
+            if len(header_data) < 4:
+                raise Exception("rx_json_packet: invalid data received")
+
+            data_len = struct.unpack("=I", header_data)[0]
+            data = _socket.recv(data_len)
+
+            while len(data) < data_len:
+                data = data + _socket.recv(data_len - len(data))
+
+            return json.loads(data)
+
+        return None
+
+    def get_socket_peer_cred(self, _socket):
+        """
+        Returns tuple of (pid, uid, gid) of connected AF_UNIX stream socket
+        :param _socket:
+        :return:
+        """
+        return struct.unpack("3i", _socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize("3i")))
diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py
new file mode 100644 (file)
index 0000000..704d120
--- /dev/null
@@ -0,0 +1,743 @@
+# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# iproute2 -- contains all iproute2 related operation
+#
+
+import re
+import shlex
+import signal
+import subprocess
+
+from ipaddr import IPNetwork
+
+try:
+    from ifupdown2.lib.sysfs import Sysfs
+    from ifupdown2.lib.base_objects import Cache, Requirements
+
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.iface import ifaceLinkPrivFlags
+    from ifupdown2.nlmanager.nlpacket import Link
+except ImportError:
+    from lib.sysfs import Sysfs
+    from lib.base_objects import Cache, Requirements
+
+    from ifupdown.utils import utils
+    from ifupdown.iface import ifaceLinkPrivFlags
+    from nlmanager.nlpacket import Link
+
+# WORK AROUND - Tunnel creation should be done via netlink and not iproute2 ####
+import struct                                                                  #
+import socket                                                                  #
+                                                                               #
+try:                                                                           #
+    import ifupdown2.nlmanager.nlpacket as nlpacket                            #
+except:                                                                        #
+    import nlmanager.nlpacket as nlpacket                                      #
+################################################################################
+
+
+class IPRoute2(Cache, Requirements):
+
+    VXLAN_UDP_PORT = 4789
+    VXLAN_PEER_REGEX_PATTERN = re.compile("\s+dst\s+(\d+.\d+.\d+.\d+)\s+")
+
+    def __init__(self):
+        Cache.__init__(self)
+        Requirements.__init__(self)
+
+        self.sysfs = Sysfs
+
+        self.__batch = {}
+        self.__batch_mode = False
+
+        # if bridge utils is not installed overrrides specific functions to
+        # avoid constantly checking bridge_utils_is_installed
+        if not Requirements.bridge_utils_is_installed:
+            self.bridge_set_stp = lambda _, __: None
+            self.bridge_del_mcqv4src = lambda _, __: None
+            self.bridge_set_mcqv4src = lambda _, __, ___: None
+
+    ############################################################################
+    # WORK-AROUND
+    ############################################################################
+
+    def __update_cache_after_link_creation(self, ifname, kind):
+        """
+        WORK AROUND - when creating tunnel via iproute2 we still need to fill
+        our internal cache to keep track of this interface until we receive the
+        NEWLINK notification. This code is a copy-paste from:
+            nlcache.tx_nlpacket_get_response_with_error_and_cache_on_ack
+
+        :param ifname:
+        :param kind:
+        :return:
+        """
+        packet = nlpacket.Link(nlpacket.RTM_NEWLINK, False, use_color=False)
+        packet.flags = nlpacket.NLM_F_CREATE | nlpacket.NLM_F_REQUEST | nlpacket.NLM_F_ACK
+        packet.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        packet.add_attribute(nlpacket.Link.IFLA_IFNAME, ifname)
+        packet.add_attribute(nlpacket.Link.IFLA_LINKINFO, {
+            nlpacket.Link.IFLA_INFO_KIND: kind,
+            nlpacket.Link.IFLA_INFO_DATA: {}
+        })
+        packet.build_message(0, 0)
+        # When creating a new link via netlink, we don't always wait for the kernel
+        # NEWLINK notification to be cached to continue. If our request is ACKed by
+        # the OS we assume that the link was successfully created. Since we aren't
+        # waiting for the kernel notification to continue we need to manually fill
+        # our cache with the packet we just TX'ed. Once the NEWLINK notification
+        # is received it will simply override the previous entry.
+        # We need to keep track of those manually cached packets. We set a private
+        # flag on the objects via the attribute priv_flags
+        packet.priv_flags |= nlpacket.NLM_F_REQUEST
+        try:
+            # we need to decode the service header so all the attribute are properly
+            # filled in the packet object that we are about to store in cache.
+            # i.e.: packet.flags shouldn't contain NLM_F_* values but IFF_* (in case of Link object)
+            # otherwise call to cache.link_is_up() will probably return True
+            packet.decode_service_header()
+        except:
+            # we can ignore all errors
+            pass
+
+        # Then we can use our normal "add_link" API call to cache the packet
+        # and fill up our additional internal data structures.
+        self.cache.add_link(packet)
+
+    ############################################################################
+    # BATCH
+    ############################################################################
+
+    def __add_to_batch(self, prefix, cmd):
+        if prefix in self.__batch:
+            self.__batch[prefix].append(cmd)
+        else:
+            self.__batch[prefix] = [cmd]
+
+    def __execute_or_batch(self, prefix, cmd):
+        if self.__batch_mode:
+            self.__add_to_batch(prefix, cmd)
+        else:
+            utils.exec_command("%s %s" % (prefix, cmd))
+
+    def __execute_or_batch_dry_run(self, prefix, cmd):
+        """
+        The batch function has it's own dryrun handler so we only handle
+        dryrun for non-batch mode. Which will be removed once the "utils"
+        module has it's own dryrun handlers
+        """
+        if self.__batch_mode:
+            self.__add_to_batch(prefix, cmd)
+        else:
+            self.log_info_dry_run("executing: %s %s" % (prefix, cmd))
+
+    def batch_start(self):
+        if not self.__batch_mode:
+            self.__batch_mode = True
+            self.__batch = {}
+
+    def batch_commit(self):
+        try:
+            if not self.__batch_mode or not self.__batch:
+                return
+            for prefix, commands in self.__batch.iteritems():
+                utils.exec_command(
+                    "%s -force -batch -" % prefix,
+                    stdin="\n".join(commands)
+                )
+        except:
+            raise
+        finally:
+            self.__batch_mode = False
+            del self.__batch
+            self.__batch = None
+
+    ############################################################################
+    # LINK
+    ############################################################################
+
+    def link_up(self, ifname):
+        if not self.cache.link_is_up(ifname):
+            self.link_up_force(ifname)
+
+    def link_down(self, ifname):
+        if self.cache.link_is_up(ifname):
+            self.link_down_force(ifname)
+
+    def link_up_dry_run(self, ifname):
+        self.link_up_force(ifname)
+
+    def link_down_dry_run(self, ifname):
+        self.link_down_force(ifname)
+
+    def link_up_force(self, ifname):
+        self.__execute_or_batch(utils.ip_cmd, "link set dev %s up" % ifname)
+
+    def link_down_force(self, ifname):
+        self.__execute_or_batch(utils.ip_cmd, "link set dev %s down" % ifname)
+
+    ###
+
+    def link_set_master(self, ifname, master):
+        if master != self.cache.get_master(ifname):
+            self.__execute_or_batch(
+                utils.ip_cmd,
+                "link set dev %s master %s" % (ifname, master)
+            )
+
+    def link_set_master_dry_run(self, ifname, master):
+        self.__execute_or_batch(
+            utils.ip_cmd,
+            "link set dev %s master %s" % (ifname, master)
+        )
+
+    ###
+
+    def link_set_address(self, ifname, address):
+        if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
+            self.link_down(ifname)
+            self.__execute_or_batch(
+                utils.ip_cmd,
+                "link set dev %s address %s" % (ifname, address)
+            )
+            self.link_up(ifname)
+
+    def link_set_address_dry_run(self, ifname, address):
+        self.link_down(ifname)
+        self.__execute_or_batch(
+            utils.ip_cmd,
+            "link set dev %s address %s" % (ifname, address)
+        )
+        self.link_up(ifname)
+
+    def link_set_address_and_keep_down(self, ifname, address, keep_down=False):
+        if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
+            self.link_down(ifname)
+            self.__execute_or_batch(
+                utils.ip_cmd,
+                "link set dev %s address %s" % (ifname, address)
+            )
+            if not keep_down:
+                self.link_up(ifname)
+
+    def link_set_address_and_keep_down_dry_run(self, ifname, address, keep_down=False):
+        self.link_down(ifname)
+        self.__execute_or_batch(
+            utils.ip_cmd,
+            "link set dev %s address %s" % (ifname, address)
+        )
+        if not keep_down:
+            self.link_up(ifname)
+
+    ###
+
+    def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode):
+        utils.exec_command(
+            "%s link add link %s name %s type macvlan mode %s"
+            % (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
+        )
+
+    def link_add_macvlan_dry_run(self, ifname, macvlan_ifname, macvlan_mode):
+        # this dryrun method can be removed once dryrun handlers
+        # are added to the utils module
+        self.log_info_ifname_dry_run(ifname, "executing %s link add link %s name %s type macvlan mode %s"
+            % (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
+        )
+
+    ###
+
+    def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None,
+                          remoteips=None, learning='on', ageing=None, ttl=None, physdev=None):
+        if svcnodeip and remoteips:
+            raise Exception("svcnodeip and remoteip are mutually exclusive")
+
+        if self.cache.link_exists(name):
+            cmd = [
+                "link set dev %s type vxlan dstport %d"
+                % (name, self.VXLAN_UDP_PORT)
+            ]
+        else:
+            cmd = [
+                "link add dev %s type vxlan id %s dstport %d"
+                % (name, vxlanid, self.VXLAN_UDP_PORT)
+            ]
+
+        if svcnodeip:
+            if svcnodeip.is_multicast:
+                cmd.append("group %s" % svcnodeip)
+            else:
+                cmd.append("remote %s" % svcnodeip)
+
+        if ageing:
+            cmd.append("ageing %s" % ageing)
+
+        if learning == 'off':
+            cmd.append("nolearning")
+
+        if ttl is not None:
+            cmd.append("ttl %s" % ttl)
+
+        if physdev:
+            cmd.append("dev %s" % physdev)
+
+        if localtunnelip:
+            cmd.append("local %s" % localtunnelip)
+
+        self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
+
+    def get_vxlan_peers(self, dev, svcnodeip):
+        cmd = "%s fdb show brport %s" % (utils.bridge_cmd, dev)
+        cur_peers = []
+        try:
+            ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
+            utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
+            output = subprocess.check_output(("grep", "00:00:00:00:00:00"), stdin=ps.stdout)
+            ps.wait()
+            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
+            try:
+                for l in output.split('\n'):
+                    m = self.VXLAN_PEER_REGEX_PATTERN.search(l)
+                    if m and m.group(1) != svcnodeip:
+                        cur_peers.append(m.group(1))
+            except:
+                self.logger.warn('error parsing ip link output')
+        except subprocess.CalledProcessError as e:
+            if e.returncode != 1:
+                self.logger.error(str(e))
+        finally:
+            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
+
+        return cur_peers
+
+    ###
+
+    @staticmethod
+    def link_add_xfrm(ifname, xfrm_name, xfrm_id):
+        utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id])
+
+    ############################################################################
+    # TUNNEL
+    ############################################################################
+
+    def tunnel_create(self, tunnelname, mode, attrs=None):
+        if self.cache.link_exists(tunnelname):
+            return
+
+        cmd = []
+        if "6" in mode:
+            cmd.append("-6")
+
+        if mode in ["gretap"]:
+            cmd.append("link add %s type %s" % (tunnelname, mode))
+        else:
+            cmd.append("tunnel add %s mode %s" % (tunnelname, mode))
+
+        if attrs:
+            for k, v in attrs.iteritems():
+                cmd.append(k)
+                if v:
+                    cmd.append(v)
+
+        utils.exec_command("%s %s" % (utils.ip_cmd, " ".join(cmd)))
+        self.__update_cache_after_link_creation(tunnelname, mode)
+
+    def tunnel_change(self, tunnelname, attrs=None):
+        """ tunnel change function """
+        if not self.cache.link_exists(tunnelname):
+            return
+        cmd = ["tunnel change %s" % tunnelname]
+        if attrs:
+            for k, v in attrs.iteritems():
+                cmd.append(k)
+                if v:
+                    cmd.append(v)
+        self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
+
+    ############################################################################
+    # ADDRESS
+    ############################################################################
+
+    def addr_flush(self, ifname):
+        if self.cache.link_has_ip(ifname):
+            self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
+
+    def link_set_ipv6_addrgen_dry_run(self, ifname, addrgen, link_created):
+        addrgen_str = "none" if addrgen else "eui64"
+        self.link_down(ifname)
+        self.__execute_or_batch(utils.ip_cmd, "link set dev %s addrgenmode %s" % (ifname, addrgen_str))
+        self.link_up(ifname)
+
+    def link_set_ipv6_addrgen(self, ifname, addrgen, link_created):
+        """
+        IFLA_INET6_ADDR_GEN_MODE values:
+        0 = eui64
+        1 = none
+
+        :param ifname:
+        :param addrgen:
+        :param link_created:
+        :return:
+        """
+        cached_ipv6_addr_gen_mode = self.cache.get_link_ipv6_addrgen_mode(ifname)
+
+        if cached_ipv6_addr_gen_mode == addrgen:
+            return True
+
+        disabled_ipv6 = self.sysfs.get_ipv6_conf_disable_ipv6(ifname)
+
+        if disabled_ipv6:
+            self.logger.info("%s: cannot set addrgen: ipv6 is disabled on this device" % ifname)
+            return False
+
+        if link_created:
+            link_mtu = self.sysfs.link_get_mtu(ifname)
+        else:
+            link_mtu = self.cache.get_link_mtu(ifname)
+
+        if link_mtu < 1280:
+            self.logger.info("%s: ipv6 addrgen is disabled on device with MTU "
+                             "lower than 1280 (current mtu %s): cannot set addrgen %s"
+                             % (ifname, link_mtu, "off" if addrgen else "on"))
+            return False
+
+        if not link_created:
+            # When setting addrgenmode it is necessary to flap the macvlan
+            # device. After flapping the device we also need to re-add all
+            # the user configuration. The best way to add the user config
+            # is to flush our internal address cache
+            self.cache.address_flush_link(ifname)
+
+        is_link_up = self.cache.link_is_up(ifname)
+
+        if is_link_up:
+            self.link_down_force(ifname)
+
+        self.__execute_or_batch(
+            utils.ip_cmd,
+            "link set dev %s addrgenmode %s" % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen))
+        )
+
+        if is_link_up:
+            self.link_up_force(ifname)
+
+        return True
+
+    @staticmethod
+    def __compare_user_config_vs_running_state(running_addrs, user_addrs):
+        ip4 = []
+        ip6 = []
+
+        for ip in user_addrs or []:
+            obj = IPNetwork(ip)
+
+            if obj.version == 6:
+                ip6.append(str(obj))
+            else:
+                ip4.append(str(obj))
+
+        running_ipobj = []
+        for ip in running_addrs or []:
+            running_ipobj.append(str(ip))
+
+        return running_ipobj == (ip4 + ip6)
+
+    def add_addresses(self, ifacobj, ifname, address_list, purge_existing=False, metric=None, with_address_virtual=False):
+        if purge_existing:
+            running_address_list = self.cache.get_ifupdown2_addresses_list(
+                [ifacobj],
+                ifname,
+                with_address_virtual=with_address_virtual
+            )
+            address_list = utils.get_normalized_ip_addr(ifname, address_list)
+
+            if self.__compare_user_config_vs_running_state(running_address_list, address_list):
+                return
+
+            try:
+                self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
+            except Exception, e:
+                self.logger.warning("%s: flushing all ip address failed: %s" % (ifname, str(e)))
+        for addr in address_list:
+            try:
+                if metric:
+                    self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s metric %s" % (addr, ifname, metric))
+                else:
+                    self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s" % (addr, ifname))
+            except Exception as e:
+                self.logger.error("%s: add_address: %s" % (ifname, str(e)))
+
+    ############################################################################
+    # BRIDGE
+    ############################################################################
+
+    @staticmethod
+    def bridge_set_stp(bridge, stp_state):
+        utils.exec_command("%s stp %s %s" % (utils.brctl_cmd, bridge, stp_state))
+
+    @staticmethod
+    def bridge_fdb_show_dev(dev):
+        try:
+            fdbs = {}
+            output = utils.exec_command("%s fdb show dev %s" % (utils.bridge_cmd, dev))
+            if output:
+                for fdb_entry in output.splitlines():
+                    try:
+                        entries = fdb_entry.split()
+                        fdbs.setdefault(entries[2], []).append(entries[0])
+                    except:
+                        pass
+            return fdbs
+        except Exception:
+            return None
+
+    @staticmethod
+    def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
+        target = "self" if bridge else ""
+        vlan_str = "vlan %s " % vlan if vlan else ""
+        dst_str = "dst %s " % remote if remote else ""
+
+        utils.exec_command(
+            "%s fdb replace %s dev %s %s %s %s"
+            % (
+                utils.bridge_cmd,
+                address,
+                dev,
+                vlan_str,
+                target,
+                dst_str
+            )
+        )
+
+    @staticmethod
+    def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
+        target = "self" if bridge else ""
+        vlan_str = "vlan %s " % vlan if vlan else ""
+        dst_str = "dst %s " % remote if remote else ""
+
+        utils.exec_command(
+            "%s fdb append %s dev %s %s %s %s"
+            % (
+                utils.bridge_cmd,
+                address,
+                dev,
+                vlan_str,
+                target,
+                dst_str
+            )
+        )
+
+    @staticmethod
+    def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
+        target = "self" if bridge else ""
+        vlan_str = "vlan %s " % vlan if vlan else ""
+        dst_str = "dst %s " % remote if remote else ""
+
+        utils.exec_command(
+            "%s fdb del %s dev %s %s %s %s"
+            % (
+                utils.bridge_cmd,
+                address,
+                dev,
+                vlan_str,
+                target,
+                dst_str
+            )
+        )
+
+    @staticmethod
+    def bridge_vlan_del_vid_list(ifname, vids):
+        if not vids:
+            return
+        for v in vids:
+            utils.exec_command(
+                "%s vlan del vid %s dev %s" % (utils.bridge_cmd, v, ifname)
+            )
+
+    def bridge_vlan_del_vid_list_self(self, ifname, vids, is_bridge=True):
+        target = "self" if is_bridge else ""
+        for v in vids:
+            self.__execute_or_batch(
+                utils.bridge_cmd,
+                "vlan del vid %s dev %s %s" % (v, ifname, target)
+            )
+
+    @staticmethod
+    def bridge_vlan_add_vid_list(ifname, vids):
+        for v in vids:
+            utils.exec_command(
+                "%s vlan add vid %s dev %s" % (utils.bridge_cmd, v, ifname)
+            )
+
+    def bridge_vlan_add_vid_list_self(self, ifname, vids, is_bridge=True):
+        target = "self" if is_bridge else ""
+        for v in vids:
+            self.__execute_or_batch(
+                utils.bridge_cmd,
+                "vlan add vid %s dev %s %s" % (v, ifname, target)
+            )
+
+    def bridge_vlan_del_pvid(self, ifname, pvid):
+        self.__execute_or_batch(
+            utils.bridge_cmd,
+            "vlan del vid %s untagged pvid dev %s" % (pvid, ifname)
+        )
+
+    def bridge_vlan_add_pvid(self, ifname, pvid):
+        self.__execute_or_batch(
+            utils.bridge_cmd,
+            "vlan add vid %s untagged pvid dev %s" % (pvid, ifname)
+        )
+
+    def bridge_del_mcqv4src(self, bridge, vlan):
+        try:
+            vlan = int(vlan)
+        except Exception as e:
+            self.logger.info("%s: del mcqv4src vlan: invalid parameter %s: %s"
+                             % (bridge, vlan, str(e)))
+            return
+        utils.exec_command("%s delmcqv4src %s %d" % (utils.brctl_cmd, bridge, vlan))
+
+    def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
+        try:
+            vlan = int(vlan)
+        except Exception as e:
+            self.logger.info("%s: set mcqv4src vlan: invalid parameter %s: %s" % (bridge, vlan, str(e)))
+            return
+        if vlan == 0 or vlan > 4095:
+            self.logger.warn("mcqv4src vlan '%d' invalid range" % vlan)
+            return
+
+        ip = mcquerier.split(".")
+        if len(ip) != 4:
+            self.logger.warn("mcqv4src '%s' invalid IPv4 address" % mcquerier)
+            return
+        for k in ip:
+            if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
+                self.logger.warn("mcqv4src '%s' invalid IPv4 address" % mcquerier)
+                return
+
+        utils.exec_command("%s setmcqv4src %s %d %s" % (utils.brctl_cmd, bridge, vlan, mcquerier))
+
+    ############################################################################
+    # ROUTE
+    ############################################################################
+
+    @staticmethod
+    def route_add_gateway(ifname, gateway, vrf=None, metric=None, onlink=True):
+        if not gateway:
+            return
+
+        if not vrf:
+            cmd = "%s route add default via %s proto kernel" % (utils.ip_cmd, gateway)
+        else:
+            cmd = "%s route add table %s default via %s proto kernel" % (utils.ip_cmd, vrf, gateway)
+
+        if metric:
+            cmd += " metric %s" % metric
+
+        cmd += " dev %s" % ifname
+
+        if onlink:
+            cmd += " onlink"
+
+        utils.exec_command(cmd)
+
+    @staticmethod
+    def route_del_gateway(ifname, gateway, vrf=None, metric=None):
+        """
+        delete default gw
+        we don't need a DRYRUN handler here as utils.exec_command should have one
+        """
+        if not gateway:
+            return
+
+        if not vrf:
+            cmd = "%s route del default via %s proto kernel" % (utils.ip_cmd, gateway)
+        else:
+            cmd = "%s route del table %s default via %s proto kernel" % (utils.ip_cmd, vrf, gateway)
+
+        if metric:
+            cmd += " metric %s" % metric
+
+        cmd += " dev %s" % ifname
+        utils.exec_command(cmd)
+
+    def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
+        vrf_table = None
+
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
+            try:
+                for upper_iface in ifaceobj.upperifaces:
+                    vrf_table = self.cache.get_vrf_table(upper_iface)
+                    if vrf_table:
+                        break
+            except:
+                pass
+
+        ip_route_del = []
+        for ip in ips:
+            ip_network_obj = IPNetwork(ip)
+
+            if ip_network_obj.version == 6:
+                route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
+
+                if vrf_table:
+                    self.__execute_or_batch(
+                        utils.ip_cmd,
+                        "route del %s table %s dev %s" % (route_prefix, vrf_table, macvlan_ifacename)
+                    )
+                else:
+                    self.__execute_or_batch(
+                        utils.ip_cmd,
+                        "route del %s dev %s" % (route_prefix, macvlan_ifacename)
+                    )
+
+                ip_route_del.append((route_prefix, vrf_table))
+
+        for ip, vrf_table in ip_route_del:
+            if vrf_table:
+                self.__execute_or_batch(
+                    utils.ip_cmd,
+                    "route add %s table %s dev %s proto kernel metric 9999" % (ip, vrf_table, macvlan_ifacename)
+                )
+            else:
+                self.__execute_or_batch(
+                    utils.ip_cmd,
+                    "route add %s dev %s proto kernel metric 9999" % (ip, macvlan_ifacename)
+                )
+
+    def ip_route_get_dev(self, prefix, vrf_master=None):
+        try:
+            if vrf_master:
+                cmd = "%s route get %s vrf %s" % (utils.ip_cmd, prefix, vrf_master)
+            else:
+                cmd = "%s route get %s" % (utils.ip_cmd, prefix)
+
+            output = utils.exec_command(cmd)
+            if output:
+                rline = output.splitlines()[0]
+                if rline:
+                    rattrs = rline.split()
+                    return rattrs[rattrs.index("dev") + 1]
+        except Exception, e:
+            self.logger.debug("ip_route_get_dev: failed .. %s" % str(e))
+        return None
diff --git a/ifupdown2/lib/log.py b/ifupdown2/lib/log.py
new file mode 100644 (file)
index 0000000..5f19404
--- /dev/null
@@ -0,0 +1,224 @@
+# Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+
+import os
+import sys
+import traceback
+
+import logging
+import logging.handlers
+
+root_logger = logging.getLogger()
+
+
+class LogManager:
+    LOGGER_NAME = "ifupdown2"
+    LOGGER_NAME_DAEMON = "ifupdown2d"
+
+    DEFAULT_TCP_LOGGING_PORT = 42422
+    DEFAULT_LOGGING_LEVEL_DAEMON = logging.INFO
+    DEFAULT_LOGGING_LEVEL_NORMAL = logging.WARNING
+
+    __instance = None
+
+    @staticmethod
+    def get_instance():
+        if not LogManager.__instance:
+            try:
+                LogManager.__instance = LogManager()
+            except Exception as e:
+                sys.stderr.write("warning: ifupdown2.Log: %s\n" % str(e))
+                traceback.print_exc()
+        return LogManager.__instance
+
+    def __init__(self):
+        """
+        Setup root logger and console handler (stderr). To enable daemon, client
+        or standalone logging please call the proper function, see:
+            "start_(daemon|client|standlone)_logging"
+        """
+        if LogManager.__instance:
+            raise RuntimeError("Log: invalid access. Please use Log.getInstance()")
+        else:
+            LogManager.__instance = self
+
+        self.__fmt = "%(levelname)s: %(message)s"
+
+        self.__debug_fmt = "%(asctime)s: %(threadName)s: %(name)s: " \
+                           "%(filename)s:%(lineno)d:%(funcName)s(): " \
+                           "%(levelname)s: %(message)s"
+
+        self.__root_logger = logging.getLogger()
+        self.__root_logger.name = self.LOGGER_NAME
+
+        self.__socket_handler = None
+        self.__syslog_handler = None
+        self.__console_handler = None
+
+        self.daemon = None
+
+        # by default we attach a console handler that logs on stderr
+        # the daemon can manually remove this handler on startup
+        self.__console_handler = logging.StreamHandler(sys.stderr)
+        self.__console_handler.setFormatter(logging.Formatter(self.__fmt))
+        self.__root_logger.addHandler(self.__console_handler)
+
+        if os.path.exists("/dev/log"):
+            try:
+                self.__syslog_handler = logging.handlers.SysLogHandler(
+                    address="/dev/log",
+                    facility=logging.handlers.SysLogHandler.LOG_DAEMON
+                )
+                self.__syslog_handler.setFormatter(logging.Formatter(self.__fmt))
+            except Exception as e:
+                sys.stderr.write("warning: syslog: %s\n" % str(e))
+                self.__syslog_handler = None
+
+        logging.addLevelName(logging.CRITICAL, "critical")
+        logging.addLevelName(logging.WARNING, "warning")
+        logging.addLevelName(logging.ERROR, "error")
+        logging.addLevelName(logging.DEBUG, "debug")
+        logging.addLevelName(logging.INFO, "info")
+
+    def set_level(self, default, error=False, warning=False, info=False, debug=False):
+        """
+        Set root handler logging level
+        :param default:
+        :param error:
+        :param warning:
+        :param info:
+        :param debug:
+        """
+        if debug:
+            log_level = logging.DEBUG
+        elif info:
+            log_level = logging.INFO
+        elif warning:
+            log_level = logging.WARNING
+        elif error:
+            log_level = logging.ERROR
+        else:
+            log_level = default
+
+        for handler in self.__root_logger.handlers:
+            handler.setLevel(log_level)
+        self.__root_logger.setLevel(log_level)
+
+    def enable_console(self):
+        """ Add console handler to root logger """
+        self.__root_logger.addHandler(self.__console_handler)
+
+    def disable_console(self):
+        """ Remove console handler from root logger """
+        self.__root_logger.removeHandler(self.__console_handler)
+
+    def enable_syslog(self):
+        """ Add syslog handler to root logger """
+        if self.__syslog_handler:
+            self.__root_logger.addHandler(self.__syslog_handler)
+
+    def disable_syslog(self):
+        """ Remove syslog handler from root logger """
+        if self.__syslog_handler:
+            self.__root_logger.removeHandler(self.__syslog_handler)
+
+    def close_log_stream(self):
+        """ Close socket to disconnect client.
+        We first have to perform this little hack: it seems like the socket is
+        not opened until data (LogRecord) are transmitted. In our most basic use
+        case (client sends "ifup -a") the daemon doesn't send back any LogRecord
+        but we can't predict that in the client. The client is already in a
+        blocking-select waiting for data on it's socket handler
+        (StreamRequestHandler). For this special case we need to manually call
+        "createSocket" to open the channel to the client so that we can properly
+        close it. That way the client can exit cleanly.
+        """
+        self.__root_logger.removeHandler(self.__socket_handler)
+        self.__socket_handler.acquire()
+        self.__socket_handler.retryTime = None
+        try:
+            if not self.__socket_handler.sock:
+                self.__socket_handler.createSocket()
+        finally:
+            self.__socket_handler.close()
+            self.__socket_handler.release()
+
+    def start_stream(self):
+        self.__root_logger.addHandler(self.__socket_handler)
+
+    def set_daemon_logging_level(self, args):
+        self.set_level(self.DEFAULT_LOGGING_LEVEL_DAEMON, info=args.verbose, debug=args.debug)
+
+    def set_request_logging_level(self, args):
+        if not hasattr(args, "syslog") or not args.syslog:
+            self.disable_syslog()
+        else:
+            self.__root_logger.removeHandler(self.__socket_handler)
+        self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
+
+    def start_client_logging(self, args):
+        """ Setup root logger name and client log level
+        syslog is handled by the daemon directly
+        """
+        self.__root_logger.name = self.LOGGER_NAME
+
+        if hasattr(args, "syslog") and args.syslog:
+            self.enable_syslog()
+            self.disable_console()
+
+        self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
+
+    def start_standalone_logging(self, args):
+        self.__root_logger.name = self.LOGGER_NAME
+
+        if hasattr(args, "syslog") and args.syslog:
+            self.enable_syslog()
+            self.disable_console()
+
+            self.__root_logger.removeHandler(self.__console_handler)
+
+        self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
+
+    def start_daemon_logging(self, args):
+        """
+        Daemon mode initialize a socket handler to transmit logging to the
+        client, we can also do syslog logging and/or console logging (probably
+        just for debugging purpose)
+        :param args:
+        :return:
+        """
+        self.__root_logger.name = self.LOGGER_NAME_DAEMON
+        self.daemon = True
+
+        self.enable_syslog()
+
+        # Create SocketHandler for daemon-client communication
+        self.__socket_handler = logging.handlers.SocketHandler(
+            "localhost",
+            port=self.DEFAULT_TCP_LOGGING_PORT
+        )
+        self.__root_logger.addHandler(self.__socket_handler)
+
+        if not args.console:
+            self.disable_console()
+
+        self.set_daemon_logging_level(args)
diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py
new file mode 100644 (file)
index 0000000..6b188bd
--- /dev/null
@@ -0,0 +1,3216 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# Netlink cache --
+#
+
+import os
+import socket
+import struct
+import signal
+import inspect
+import logging
+import threading
+import traceback
+
+from ipaddr import IPNetwork
+from logging import DEBUG, WARNING
+from collections import OrderedDict
+
+try:
+    from ifupdown2.lib.sysfs import Sysfs
+    from ifupdown2.lib.base_objects import BaseObject
+
+    from ifupdown2.nlmanager.nlpacket import \
+        Address, \
+        Netconf, \
+        Link, \
+        Route, \
+        AF_MPLS, \
+        NetlinkPacket, \
+        NLM_F_REQUEST, \
+        RTM_NEWLINK, \
+        RTM_SETLINK, \
+        RTM_DELLINK, \
+        RTM_NEWADDR, \
+        RTM_DELADDR, \
+        RTMGRP_ALL, \
+        NLMSG_DONE, \
+        NLM_F_REQUEST, \
+        NLM_F_CREATE, \
+        NLM_F_ACK, \
+        RT_SCOPES, \
+        INFINITY_LIFE_TIME
+
+    import ifupdown2.nlmanager.nlpacket as nlpacket
+    import ifupdown2.nlmanager.nllistener as nllistener
+    import ifupdown2.nlmanager.nlmanager as nlmanager
+    import ifupdown2.ifupdown.statemanager as statemanager
+except:
+    from lib.sysfs import Sysfs
+    from lib.base_objects import BaseObject
+
+    from nlmanager.nlpacket import \
+        Address, \
+        Netconf, \
+        Link, \
+        Route, \
+        AF_MPLS, \
+        NetlinkPacket, \
+        NLM_F_REQUEST, \
+        RTM_NEWLINK, \
+        RTM_SETLINK, \
+        RTM_DELLINK, \
+        RTM_NEWADDR, \
+        RTM_DELADDR, \
+        RTMGRP_ALL, \
+        NLMSG_DONE, \
+        NLM_F_REQUEST, \
+        NLM_F_CREATE, \
+        NLM_F_ACK, \
+        RT_SCOPES, \
+        INFINITY_LIFE_TIME
+
+    import nlmanager.nlpacket as nlpacket
+    import nlmanager.nllistener as nllistener
+    import nlmanager.nlmanager as nlmanager
+    import ifupdown.statemanager as statemanager
+
+
+log = logging.getLogger()
+
+
+class NetlinkListenerWithCacheErrorNotInitialized(Exception):
+    """
+    If NetlinkListenerWithCache fails on __init__() or / start()
+    we need to raise this custom exception.
+    """
+    pass
+
+
+class NetlinkError(Exception):
+    def __init__(self, exception, prefix=None, ifname=None):
+        netlink_exception_message = ['netlink']
+
+        if ifname:
+            netlink_exception_message.append(ifname)
+
+        if prefix:
+            netlink_exception_message.append(prefix)
+
+        netlink_exception_message.append(str(exception))
+        super(NetlinkError, self).__init__(": ".join(netlink_exception_message))
+
+
+class NetlinkCacheError(Exception):
+    pass
+
+
+class NetlinkCacheIfnameNotFoundError(NetlinkCacheError):
+    pass
+
+
+class NetlinkCacheIfindexNotFoundError(NetlinkCacheError):
+    pass
+
+
+class _NetlinkCache:
+    """ Netlink Cache Class """
+
+    # we need to store these attributes in a static list to be able to iterate
+    # through it when comparing Address objects in add_address()
+    # we ignore IFA_CACHEINFO and IFA_FLAGS
+    _ifa_attributes = (
+        Address.IFA_ADDRESS,
+        Address.IFA_LOCAL,
+        Address.IFA_LABEL,
+        Address.IFA_BROADCAST,
+        Address.IFA_ANYCAST,
+        # Address.IFA_CACHEINFO,
+        Address.IFA_MULTICAST,
+        # Address.IFA_FLAGS
+    )
+
+    def __init__(self):
+        # sysfs API
+        self.__sysfs = Sysfs
+        self.__sysfs.cache = self
+
+        self._link_cache = {}
+        self._addr_cache = {}
+        self._bridge_vlan_cache = {}
+
+        # helper dictionaries
+        # ifindex: ifname
+        # ifname: ifindex
+        self._ifname_by_ifindex  = {}
+        self._ifindex_by_ifname  = {}
+
+        self._ifname_by_ifindex_sysfs = {}
+        self._ifindex_by_ifname_sysfs = {}
+
+        # master/slave(s) dictionary
+        # master_ifname: [slave_ifname, slave_ifname]
+        self._masters_and_slaves = {}
+
+        # slave/master dictionary
+        # slave_ifname: master_ifname
+        self._slaves_master = {}
+
+        # netconf cache data-structure schema:
+        # {
+        #     family: {
+        #         ifindex: obj
+        #     }
+        # }
+        self._netconf_cache = {
+            socket.AF_INET: {},
+            socket.AF_INET6: {},
+            AF_MPLS: {}
+        }
+        # custom lock mechanism for netconf cache
+        self._netconf_cache_lock = threading.Lock()
+
+        # RLock is needed because we don't want to have separate handling in
+        # get_ifname, get_ifindex and all the API function
+        self._cache_lock = threading.RLock()
+
+        # After sending a RTM_DELLINK request (ip link del DEV) we don't
+        # automatically receive an RTM_DELLINK notification but instead we
+        # have 3 to 5 RTM_NEWLINK notifications (first the device goes
+        # admin-down then, goes through other steps that send notifications...
+        # Because of this behavior the cache is out of sync and may cause
+        # issues. To work-around this behavior we can ignore RTM_NEWLINK for a
+        # given ifname until we receive the RTM_DELLINK. That way our cache is
+        # not stale. When deleting a link, ifupdown2 uses:
+        #   - NetlinkListenerWithCache:link_del(ifname)
+        # Before sending the RTM_DELLINK netlink packet we:
+        #   - register the ifname in the _ignore_rtm_newlinkq
+        #   - force purge the cache because we are not notified right away
+        #   - for every RTM_NEWLINK notification we check _ignore_rtm_newlinkq
+        # to see if we need to ignore that packet
+        #   - for every RTM_DELLINK notification we check if we have a
+        # corresponding entry in _ignore_rtm_newlinkq and remove it
+        self._ignore_rtm_newlinkq = list()
+        self._ignore_rtm_newlinkq_lock = threading.Lock()
+
+        # After sending a no master request (IFLA_MASTER=0) the kernels send
+        # 2 or 3 notifications (with IFLA_MASTER) before sending the final
+        # notification where IFLA_MASTER is removed. For performance purposes
+        # we don't wait for those notifications, we simply update the cache
+        # to reflect the change (if we got an ACK on the nomaster request).
+        # Those extra notification re-add the former slave to it's master
+        # (in our internal data-structures at least). ifupdown2 relies on
+        # the cache to get accurate information, this puts the cache in an
+        # unreliable state. We can detected this bad state and avoid it. Afer
+        # a nomaster request we "register" the device as "nomaster", meaning
+        # that we will manually remove the IFLA_MASTER attribute from any
+        # subsequent packet, until the final packet arrives - then unregister
+        # the device from the nomasterq.
+        # We need an extra data-structure and lock mechanism for this:
+        self._rtm_newlink_nomasterq = list()
+        self._rtm_newlink_nomasterq_lock = threading.Lock()
+
+        # In the scenario of NetlinkListenerWithCache, the listener thread
+        # decode netlink packets and perform caching operation based on their
+        # respective msgtype add_link for RTM_NEWLINK, remove_link for DELLINK
+        # In some cases the main thread is creating a new device with:
+        #   NetlinkListenerWithCache.link_add()
+        # the request is sent and the cache won't have any knowledge of this
+        # new link until we receive a NEWLINK notification on the listener
+        # socket meanwhile the main thread keeps going. The main thread may
+        # query the cache for the newly created device but the cache might not
+        # know about it yet thus creating unexpected situation in the main
+        # thread operations. We need to provide a mechanism to block the main
+        # thread until the desired notification is processed. The main thread
+        # can call:
+        #   register_wait_event(ifname, netlink_msgtype)
+        # to register an event for device name 'ifname' and netlink msgtype.
+        # The main thread should then call wait_event to sleep until the
+        # notification is received the NetlinkListenerWithCache provides the
+        # following API:
+        #   tx_nlpacket_get_response_with_error_and_wait_for_cache(ifname, nl_packet)
+        # to handle both packet transmission, error handling and cache event
+        self._wait_event = None
+        self._wait_event_alarm = threading.Event()
+
+    def __handle_type_error(self, func_name, data, exception, return_value):
+        """
+        TypeError shouldn't happen but if it does, we are prepared to log and recover
+        """
+        log.debug('nlcache: %s: %s: TypeError: %s' % (func_name, data, str(exception)))
+        return return_value
+
+    def __unslave_nolock(self, slave, master=None):
+        """
+        WARNING: LOCK SHOULD BE ACQUIRED BEFORE CALLING THIS FUNCTION
+
+        When unslaving a device we need to manually clear and update our internal
+        data structures to avoid keeping stale information before receiving a proper
+        netlink notification.
+
+        Dictionaries:
+        - master_and_slaves
+        - slaves_master
+        - bridge_vlan_cache
+
+        :param master:
+        :param slave:
+        :return:
+        """
+        try:
+            del self._link_cache[slave].attributes[Link.IFLA_MASTER]
+        except:
+            pass
+
+        try:
+            if not master:
+                master = self._slaves_master[slave]
+
+            self._masters_and_slaves[master].remove(slave)
+        except (KeyError, ValueError):
+            for master, slaves_set in self._masters_and_slaves.iteritems():
+                if slave in slaves_set:
+                    slaves_set.remove(slave)
+                    break
+
+        try:
+            del self._slaves_master[slave]
+        except KeyError:
+            pass
+
+        try:
+            del self._bridge_vlan_cache[slave]
+        except KeyError:
+            pass
+
+    def append_to_ignore_rtm_newlinkq(self, ifname):
+        """
+        Register device 'ifname' to the ignore_rtm_newlinkq list pending
+        RTM_DELLINK (see comments above _ignore_rtm_newlinkq declaration)
+        """
+        with self._ignore_rtm_newlinkq_lock:
+            self._ignore_rtm_newlinkq.append(ifname)
+
+    def remove_from_ignore_rtm_newlinkq(self, ifname):
+        """ Unregister ifname from ignore_newlinkq list """
+        try:
+            with self._ignore_rtm_newlinkq_lock:
+                self._ignore_rtm_newlinkq.remove(ifname)
+        except ValueError:
+            pass
+
+    def append_to_rtm_newlink_nomasterq(self, ifname):
+        """ Register device 'ifname' to the _ignore_rtm_newlink_nomasterq """
+        with self._rtm_newlink_nomasterq_lock:
+            self._rtm_newlink_nomasterq.append(ifname)
+
+    def remove_from_rtm_newlink_nomasterq(self, ifname):
+        """ Unregister ifname from _ignore_rtm_newlink_nomasterq list """
+        try:
+            with self._rtm_newlink_nomasterq_lock:
+                self._rtm_newlink_nomasterq.remove(ifname)
+        except ValueError:
+            pass
+
+    def register_wait_event(self, ifname, msgtype):
+        """
+        Register a cache "wait event" for device named 'ifname' and packet
+        type msgtype
+
+        We only one wait_event to be registered. Currently we don't support
+        multi-threaded application so we need to had a strict check. In the
+        future we could have a wait_event queue for multiple thread could
+        register wait event.
+        :param ifname: target device
+        :param msgtype: netlink message type (RTM_NEWLINK, RTM_DELLINK etc.)
+        :return: boolean: did we successfully register a wait_event?
+        """
+        with self._cache_lock:
+            if self._wait_event:
+                return False
+            self._wait_event = (ifname, msgtype)
+        return True
+
+    def wait_event(self):
+        """
+        Sleep until cache event happened in netlinkq thread or timeout expired
+        :return: None
+
+        We set an arbitrary timeout at 1sec in case the kernel doesn't send
+        out a notification for the event we want to wait for.
+        """
+        if not self._wait_event_alarm.wait(1):
+            log.debug('nlcache: wait event alarm timeout expired for device "%s" and netlink packet type: %s'
+                      % (self._wait_event[0], NetlinkPacket.type_to_string.get(self._wait_event[1], str(self._wait_event[1]))))
+        with self._cache_lock:
+            self._wait_event = None
+            self._wait_event_alarm.clear()
+
+    def unregister_wait_event(self):
+        """
+        Clear current wait event (cache can only handle one at once)
+        :return:
+        """
+        with self._cache_lock:
+            self._wait_event = None
+            self._wait_event_alarm.clear()
+
+    def override_link_flag(self, ifname, flags):
+        # TODO: dont override all the flags just turn on/off IFF_UP
+        try:
+            with self._cache_lock:
+                self._link_cache[ifname].flags = flags
+        except:
+            pass
+
+    def override_link_mtu(self, ifname, mtu):
+        """
+        Manually override link mtu and ignore any failures
+        :param ifname:
+        :param mtu:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                self._link_cache[ifname].attributes[Link.IFLA_MTU].value = mtu
+        except:
+            pass
+
+    def override_cache_unslave_link(self, slave, master):
+        """
+        Manually update the cache unslaving SLAVE from MASTER
+
+        When calling link_set_nomaster, we don't want to wait for the RTM_GETLINK
+        notification - if the operation return with NL_SUCCESS we can manually
+        update our cache and move on.
+
+        :param master:
+        :param slave:
+        :return:
+        """
+        with self._cache_lock:
+            self.__unslave_nolock(slave, master)
+
+    def DEBUG_IFNAME(self, ifname, with_addresses=False):
+        """
+        A very useful function to use while debugging, it dumps the netlink
+        packet with debug and color output.
+        """
+        import logging
+        root = logging.getLogger()
+
+        level = root.level
+
+        try:
+            root.setLevel(DEBUG)
+            for handler in root.handlers:
+                handler.setLevel(DEBUG)
+
+            nllistener.log.setLevel(DEBUG)
+            nlpacket.log.setLevel(DEBUG)
+            nlmanager.log.setLevel(DEBUG)
+            with self._cache_lock:
+                obj = self._link_cache[ifname]
+                save_debug = obj.debug
+                obj.debug = True
+                obj.dump()
+                obj.debug = save_debug
+
+                #if with_addresses:
+                #    addrs = self._addr_cache.get(ifname, [])
+                #    log.error('ADDRESSES=%s' % addrs)
+                #    for addr in addrs:
+                #        save_debug = addr.debug
+                #        addr.debug = True
+                #        addr.dump()
+                #        addr.debug = save_debug
+                #        log.error('-----------')
+                #        log.error('-----------')
+                #        log.error('-----------')
+        except:
+            traceback.print_exc()
+        # TODO: save log_level at entry and re-apply it after the dump
+        nllistener.log.setLevel(WARNING)
+        nlpacket.log.setLevel(WARNING)
+        nlmanager.log.setLevel(WARNING)
+
+        root.setLevel(level)
+        for handler in root.handlers:
+            handler.setLevel(level)
+
+    def DEBUG_MSG(self, msg):
+        import logging
+        root = logging.getLogger()
+        level = root.level
+
+        try:
+            root.setLevel(DEBUG)
+            for handler in root.handlers:
+                handler.setLevel(DEBUG)
+
+            nllistener.log.setLevel(DEBUG)
+            nlpacket.log.setLevel(DEBUG)
+            nlmanager.log.setLevel(DEBUG)
+
+            save_debug = msg.debug
+            msg.debug = True
+            msg.dump()
+            msg.debug = save_debug
+        except:
+            traceback.print_exc()
+        # TODO: save log_level at entry and re-apply it after the dump
+        nllistener.log.setLevel(WARNING)
+        nlpacket.log.setLevel(WARNING)
+        nlmanager.log.setLevel(WARNING)
+
+        root.setLevel(level)
+        for handler in root.handlers:
+            handler.setLevel(level)
+
+    def _populate_sysfs_ifname_ifindex_dicts(self):
+        ifname_by_ifindex_dict = {}
+        ifindex_by_ifname_dict = {}
+        try:
+            for dir_name in os.listdir('/sys/class/net/'):
+                try:
+                    with open('/sys/class/net/%s/ifindex' % dir_name) as f:
+                        ifindex = int(f.readline())
+                        ifname_by_ifindex_dict[ifindex] = dir_name
+                        ifindex_by_ifname_dict[dir_name] = ifindex
+                except (IOError, ValueError):
+                    pass
+        except OSError:
+            pass
+        with self._cache_lock:
+            self._ifname_by_ifindex_sysfs = ifname_by_ifindex_dict
+            self._ifindex_by_ifname_sysfs = ifindex_by_ifname_dict
+
+    def get_ifindex(self, ifname):
+        """
+        Return device index or raise NetlinkCacheIfnameNotFoundError
+        :param ifname:
+        :return: int
+        :raise: NetlinkCacheIfnameNotFoundError(NetlinkCacheError)
+        """
+        try:
+            with self._cache_lock:
+                return self._ifindex_by_ifname[ifname]
+        except KeyError:
+            # We assume that if the user requested a valid device ifindex but
+            # for some reason we don't find any trace of it in our cache, we
+            # then use sysfs to make sure that this device exists and fill our
+            # internal help dictionaries.
+            with self._cache_lock:
+                ifindex = self._ifindex_by_ifname_sysfs.get(ifname)
+
+            if ifindex:
+                return ifindex
+            self._populate_sysfs_ifname_ifindex_dicts()
+            try:
+                return self._ifindex_by_ifname_sysfs[ifname]
+            except KeyError:
+                # if we still haven't found any trace of the requested device
+                # we raise a custom exception
+                raise NetlinkCacheIfnameNotFoundError('ifname %s not present in cache' % ifname)
+
+    def get_ifname(self, ifindex):
+        """
+        Return device name or raise NetlinkCacheIfindexNotFoundError
+        :param ifindex:
+        :return: str
+        :raise: NetlinkCacheIfindexNotFoundError (NetlinkCacheError)
+        """
+        try:
+            with self._cache_lock:
+                return self._ifname_by_ifindex[ifindex]
+        except KeyError:
+            # We assume that if the user requested a valid device ifname but
+            # for some reason we don't find any trace of it in our cache, we
+            # then use sysfs to make sure that this device exists and fill our
+            # internal help dictionaries.
+            with self._cache_lock:
+                ifname = self._ifname_by_ifindex_sysfs.get(ifindex)
+
+            if ifname:
+                return ifname
+            self._populate_sysfs_ifname_ifindex_dicts()
+            try:
+                return self._ifname_by_ifindex_sysfs[ifindex]
+            except KeyError:
+                # if we still haven't found any trace of the requested device
+                # we raise a custom exception
+                raise NetlinkCacheIfindexNotFoundError('ifindex %s not present in cache' % ifindex)
+
+    def link_exists(self, ifname):
+        """
+        Check if we have a cache entry for device 'ifname'
+        :param ifname: device name
+        :return: boolean
+        """
+        with self._cache_lock:
+            return ifname in self._link_cache
+
+    def link_is_up(self, ifname):
+        """
+        Check if device 'ifname' has IFF_UP flag
+        :param ifname:
+        :return: boolean
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].flags & Link.IFF_UP
+        except (KeyError, TypeError):
+            # ifname is not present in the cache
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
+
+    def link_is_loopback(self, ifname):
+        """
+        Check if device has IFF_LOOPBACK flag
+        :param ifname:
+        :return: boolean
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].flags & Link.IFF_LOOPBACK
+                # IFF_LOOPBACK should be enough, otherwise we can also check for
+                # link.device_type & Link.ARPHRD_LOOPBACK
+        except (KeyError, AttributeError):
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
+
+    def link_exists_and_up(self, ifname):
+        """
+        Check if device exists and has IFF_UP flag set
+        :param ifname:
+        :return: tuple (boolean, boolean) -> (link_exists, link_is_up)
+        """
+        try:
+            with self._cache_lock:
+                return True, self._link_cache[ifname].flags & Link.IFF_UP
+        except KeyError:
+            # ifname is not present in the cache
+            return False, False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=(False, False))
+
+    def link_is_bridge(self, ifname):
+        return self.get_link_kind(ifname) == 'bridge'
+
+    def get_link_kind(self, ifname):
+        """
+        Return link IFLA_INFO_KIND
+        :param ifname:
+        :return: string
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_KIND]
+        except (KeyError, AttributeError):
+            return None
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None)
+
+    def get_link_mtu(self, ifname):
+        """
+        Return link IFLA_MTU
+        :param ifname:
+        :return: int
+        """
+        return self.get_link_attribute(ifname, Link.IFLA_MTU, default=0)
+
+    def get_link_mtu_str(self, ifname):
+        """
+        Return link IFLA_MTU as string
+        :param ifname:
+        :return: str
+        """
+        return str(self.get_link_mtu(ifname))
+
+    def get_link_address(self, ifname):
+        """
+        Return link IFLA_ADDRESS
+        :param ifname:
+        :return: str
+        """
+        packet = None
+        default_value = ""
+        try:
+            with self._cache_lock:
+                packet = self._link_cache[ifname]
+                return packet.attributes[Link.IFLA_ADDRESS].value.lower()
+        except (KeyError, AttributeError):
+            # KeyError will be raised if:
+            #   - ifname is missing from the cache (but link_exists should be called prior this call)
+            #   - IFLA_ADDRESS is missing
+            # AttributeError can also be raised if attributes[IFLA_ADDRESS] returns None
+            # If the packet is tagged as a REQUEST packet (priv_flags) we should query sysfs
+            # otherwise default_value is returned.
+            if packet and packet.priv_flags & NLM_F_REQUEST:
+                return self.__sysfs.get_link_address(ifname)
+            else:
+                return default_value
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), default_value)
+
+    def get_link_address_raw(self, ifname):
+        """
+        Return link IFLA_ADDRESS as integer
+        :param ifname:
+        :return: int
+        """
+        return self.get_link_attribute_raw(ifname, Link.IFLA_ADDRESS, default=0)
+
+    def get_link_alias(self, ifname):
+        """
+        Return link IFLA_IFALIAS
+        :param ifname:
+        :return: str
+        """
+        return self.get_link_attribute(ifname, Link.IFLA_IFALIAS)
+
+    def get_link_attribute(self, ifname, attr, default=None):
+        """
+        Return link attribute 'attr'.value
+        :param ifname:
+        :param attr:
+        :param default:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[attr].value
+        except (KeyError, AttributeError):
+            return default
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default)
+
+    def get_link_attribute_raw(self, ifname, attr, default=None):
+        """
+        Return link attribute 'attr'.raw
+        :param ifname:
+        :param attr:
+        :param default:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[attr].raw
+        except (KeyError, AttributeError):
+            return default
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default)
+
+    def get_link_slave_kind(self, ifname):
+        """
+        Return device slave kind
+        :param ifname:
+        :return: str
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND]
+        except (KeyError, AttributeError):
+            return None
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None)
+
+    def get_link_info_data_attribute(self, ifname, info_data_attribute, default=None):
+        """
+        Return device linkinfo:info_data attribute or default value
+        :param ifname:
+        :param info_data_attribute:
+        :param default:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][info_data_attribute]
+        except (KeyError, AttributeError):
+            return default
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default)
+
+    def get_link_info_data(self, ifname):
+        """
+        Return device linkinfo:info_data attribute or default value
+        :param ifname:
+        :param info_data_attribute:
+        :param default:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA]
+        except (KeyError, AttributeError):
+            return {}
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value={})
+
+    def get_link_info_slave_data_attribute(self, ifname, info_slave_data_attribute, default=None):
+        """
+        Return device linkinfo:info_slave_data attribute or default value
+        :param ifname:
+        :param info_data_attribute:
+        :param default:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA][info_slave_data_attribute]
+        except (KeyError, AttributeError):
+            return default
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default)
+
+    ################
+    # MASTER & SLAVE
+    ################
+    def get_master(self, ifname):
+        """
+        Return device master's ifname
+        :param ifname:
+        :return: str
+        """
+        try:
+            with self._cache_lock:
+                return self._slaves_master[ifname]
+        except (KeyError, AttributeError):
+            return None
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None)
+
+    def get_slaves(self, master):
+        """
+        Return all devices ifname enslaved to master device
+        :param master:
+        :return: list of string
+        """
+        try:
+            with self._cache_lock:
+                return list(self._masters_and_slaves[master])
+        except KeyError:
+            return []
+
+    def is_link_enslaved_to(self, slave, master):
+        """
+        Return bool if SLAVE is enslaved to MASTER
+        :param slave:
+        :param master:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                return self._slaves_master[slave] == master
+        except KeyError:
+            return False
+
+    def get_lower_device_ifname(self, ifname):
+        """
+        Return the lower-device (IFLA_LINK) name or raise KeyError
+        :param ifname:
+        :return: string
+        """
+        try:
+            with self._cache_lock:
+                return self.get_ifname(self._link_cache[ifname].attributes[Link.IFLA_LINK].value)
+        except (NetlinkCacheIfnameNotFoundError, AttributeError, KeyError):
+            return None
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None)
+
+    ##########################################################################
+    # VRF ####################################################################
+    ##########################################################################
+
+    def get_vrf_table_map(self):
+        vrf_table_map = {}
+        try:
+            with self._cache_lock:
+                for ifname, obj in self._link_cache.iteritems():
+                    linkinfo = obj.attributes.get(Link.IFLA_LINKINFO)
+
+                    if linkinfo and linkinfo.value.get(Link.IFLA_INFO_KIND) == "vrf":
+                        vrf_table_map[linkinfo.value[Link.IFLA_INFO_DATA][Link.IFLA_VRF_TABLE]] = ifname
+        except Exception as e:
+            log.debug("get_vrf_table_map: %s" % str(e))
+        return vrf_table_map
+
+    def get_vrf_table(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_VRF_TABLE]
+        except (KeyError, AttributeError):
+            return 0
+
+    ##########################################################################
+    # BOND ###################################################################
+    ##########################################################################
+
+    def bond_exists(self, ifname):
+        """
+        Check if bond 'ifname' exists
+        :param ifname: bond name
+        :return: boolean
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[nlpacket.Link.IFLA_LINKINFO].value[nlpacket.Link.IFLA_INFO_KIND] == 'bond'
+        except (KeyError, AttributeError):
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
+
+    ##########################################################################
+    # BRIDGE PORT ############################################################
+    ##########################################################################
+
+    def get_bridge_port_multicast_router(self, ifname):
+        """
+        Get bridge port multicast_router value - defaults to 1
+
+        :param ifname:
+        :return:
+        """
+        default_value = 1
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA][Link.IFLA_BRPORT_MULTICAST_ROUTER]
+        except (KeyError, AttributeError):
+            # KeyError will be raised if:
+            #   - ifname is missing from the cache (but link_exists should be called prior this call)
+            #   - IFLA_BRPORT_MULTICAST_ROUTER is missing
+            # AttributeError can also be raised if IFLA_LINKINFO is missing (None.value)
+            # default_value is returned.
+            return default_value
+        except TypeError as e:
+            return self.__handle_type_error(
+                inspect.currentframe().f_code.co_name,
+                ifname,
+                str(e),
+                return_value=default_value
+            )
+
+    ##########################################################################
+    # BRIDGE #################################################################
+    ##########################################################################
+
+    def get_bridge_multicast_snooping(self, ifname):
+        """
+        Get bridge multicast_snooping value - defaults to 1
+
+        :param ifname:
+        :return:
+        """
+        default_value = 1
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BR_MCAST_SNOOPING]
+        except (KeyError, AttributeError):
+            # KeyError will be raised if:
+            #   - ifname is missing from the cache (but link_exists should be called prior this call)
+            #   - IFLA_BR_MCAST_SNOOPING is missing
+            # AttributeError can also be raised if IFLA_LINKINFO is missing (None.value)
+            # default_value is returned.
+            return default_value
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default_value)
+
+    def get_bridge_stp(self, ifname):
+        """
+        WARNING: ifname should be a bridge
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BR_STP_STATE]
+        except (KeyError, AttributeError):
+            return 0
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
+
+    def get_brport_cost(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_COST]
+        except (KeyError, AttributeError):
+            return None
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None)
+
+    def get_brport_priority(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_PRIORITY]
+        except (KeyError, AttributeError):
+            return None
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None)
+
+    def get_brport_unicast_flood(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_UNICAST_FLOOD]
+        except (KeyError, AttributeError):
+            return 0
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
+
+    def get_brport_multicast_flood(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_MCAST_FLOOD]
+        except (KeyError, AttributeError):
+            return 0
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
+
+    def get_brport_broadcast_flood(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_BCAST_FLOOD]
+        except (KeyError, AttributeError):
+            return 0
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
+
+    def get_brport_neigh_suppress(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_NEIGH_SUPPRESS]
+        except (KeyError, AttributeError):
+            return 0
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
+
+    def get_brport_learning(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA][Link.IFLA_BRPORT_LEARNING]
+        except (KeyError, AttributeError):
+            return 0
+
+    def get_pvid_and_vids(self, ifname):
+        """
+        vlan-identifiers are stored in:
+
+        self._bridge_vlan_cache = {
+            ifname: [(vlan, flag), (vlan, flag), ...]
+        }
+
+        Those vlans are stored in compressed format (RTEXT_FILTER_BRVLAN_COMPRESSED)
+        We only uncompress the vlan when the user request it.
+
+        :param ifname:
+        :return tuple: pvid, vids = int, [int, ]
+        """
+        pvid = None
+        vlans = []
+        try:
+            range_begin_vlan_id = None
+            range_flag = None
+
+            with self._cache_lock:
+                bridge_vlans_tuples = self._bridge_vlan_cache.get(ifname)
+
+                if bridge_vlans_tuples:
+                    for (vlan_id, vlan_flag) in sorted(bridge_vlans_tuples):
+
+                        if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID:
+                            pvid = vlan_id
+
+                        if vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
+                            range_begin_vlan_id = vlan_id
+                            range_flag = vlan_flag
+
+                        elif vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_END:
+                            range_flag |= vlan_flag
+
+                            if not range_begin_vlan_id:
+                                log.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never "
+                                            "saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id)
+                                range_begin_vlan_id = vlan_id
+
+                            for x in xrange(range_begin_vlan_id, vlan_id + 1):
+                                vlans.append(x)
+
+                            range_begin_vlan_id = None
+                            range_flag = None
+
+                        else:
+                            vlans.append(vlan_id)
+        except:
+            log.exception("get_bridge_vids")
+        return pvid, vlans
+
+    def get_pvid(self, ifname):
+        """
+        Get Port VLAN ID for device 'ifname'
+
+        :param ifname:
+        :return:
+        """
+        pvid = None
+        try:
+            with self._cache_lock:
+                bridge_vlans_tuples = self._bridge_vlan_cache.get(ifname)
+
+                if bridge_vlans_tuples:
+
+                    for (vlan_id, vlan_flag) in sorted(bridge_vlans_tuples):
+
+                        if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID:
+                            return vlan_id
+        except:
+            log.exception("get_pvid")
+        return pvid
+
+    def bridge_exists(self, ifname):
+        """
+        Check if cached device is a bridge
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_KIND] == "bridge"
+        except (KeyError, AttributeError):
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
+
+    def bridge_is_vlan_aware(self, ifname):
+        """
+        Return IFLA_BR_VLAN_FILTERING value
+        :param ifname:
+        :return: boolean
+        """
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BR_VLAN_FILTERING]
+        except (KeyError, AttributeError):
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
+
+    def link_is_bridge_port(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] == "bridge"
+        except (KeyError, AttributeError):
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
+
+    def bridge_port_exists(self, bridge_name, brport_name):
+        try:
+            with self._cache_lock:
+                # we are assuming that bridge_name is a valid bridge?
+                return self._slaves_master[brport_name] == bridge_name
+        except (KeyError, AttributeError):
+            return False
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, bridge_name, str(e), return_value=False)
+
+    def get_bridge_name_from_port(self, bridge_port_name):
+        bridge_name = self.get_master(bridge_port_name)
+        # now that we have the master's name we just need to double check
+        # if the master is really a bridge
+        return bridge_name if self.link_is_bridge(bridge_name) else None
+
+    #def is_link_slave_kind(self, ifname, _type):
+    #    try:
+    #      with self._cache_lock:
+    #            return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] == _type
+    #    except (KeyError, AttributeError):
+    #        return False
+
+    ########################################
+
+    def get_link_ipv6_addrgen_mode(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_ADDR_GEN_MODE]
+        except (KeyError, AttributeError):
+            # default to 0 (eui64)
+            return 0
+        except TypeError as e:
+            return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
+
+    #####################################################
+    #####################################################
+    #####################################################
+    #####################################################
+    #####################################################
+
+    def add_link(self, link):
+        """
+        Cache RTM_NEWLINK packet
+        :param link:
+        :return:
+        """
+        ifindex = link.ifindex
+        ifname = link.get_attribute_value(Link.IFLA_IFNAME)
+
+        # check if this device is registered in the ignore list
+        with self._ignore_rtm_newlinkq_lock:
+            if ifname in self._ignore_rtm_newlinkq:
+                return
+        # check if this device is registered in the nomaster list:
+        # if so we need to remove IFLA_MASTER attribute (if it fails
+        # it means we've received the final notification and we should
+        # unregister the device from our list.
+        with self._rtm_newlink_nomasterq_lock:
+            if ifname in self._rtm_newlink_nomasterq:
+                try:
+                    del link.attributes[Link.IFLA_MASTER]
+                except:
+                    self._rtm_newlink_nomasterq.remove(ifname)
+
+        # we need to check if the device was previously enslaved
+        # so we can update the _masters_and_slaves and _slaves_master
+        # dictionaries if the master has changed or was un-enslaved.
+        old_ifla_master = None
+
+        with self._cache_lock:
+
+            # do we have a wait event registered for RTM_NEWLINK this ifname
+            if self._wait_event and self._wait_event == (ifname, RTM_NEWLINK):
+                self._wait_event_alarm.set()
+
+            try:
+                ifla_master_attr = self._link_cache[ifname].attributes.get(Link.IFLA_MASTER)
+
+                if ifla_master_attr:
+                    old_ifla_master = ifla_master_attr.get_pretty_value()
+            except KeyError:
+                # link is not present in the cache
+                pass
+            except AttributeError:
+                # if this code is ever reached, this is very concerning and
+                # should never happen as _link_cache should always contains
+                # nlpacket.NetlinkPacket... maybe have some extra handling
+                # here just in case?
+                pass
+
+            self._link_cache[ifname] = link
+
+            ######################################################
+            # update helper dictionaries and handle link renamed #
+            ######################################################
+            if ifindex:
+                # ifindex can be None for packet added on ACK, it means
+                # that we are caching the request packet and not the
+                # notification coming from the kernel. We can leave
+                # those data-strctures empty and rely on our try/excepts
+                # in get_ifname/get_ifindex/get_master to do the work.
+
+                self._ifindex_by_ifname[ifname] = ifindex
+
+                rename_detected                 = False
+                old_ifname_entry_for_ifindex    = self._ifname_by_ifindex.get(ifindex)
+
+                if old_ifname_entry_for_ifindex and old_ifname_entry_for_ifindex != ifname:
+                    # The ifindex was reused for a new interface before we got the
+                    # RTM_DELLINK notification or the device using that ifindex was
+                    # renamed. We need to update the cache accordingly.
+                    rename_detected = True
+
+                self._ifname_by_ifindex[ifindex] = ifname
+
+                if rename_detected:
+                    # in this case we detected a rename... It should'nt happen has we should get a RTM_DELLINK before that.
+                    # if we still detect a rename the opti is to get rid of the stale value directly
+                    try:
+                        del self._ifindex_by_ifname[old_ifname_entry_for_ifindex]
+                    except KeyError:
+                        log.debug('update_helper_dicts: del _ifindex_by_ifname[%s]: KeyError ifname: %s'
+                                  % (old_ifname_entry_for_ifindex, old_ifname_entry_for_ifindex))
+                    try:
+                        del self._link_cache[old_ifname_entry_for_ifindex]
+                    except KeyError:
+                        log.debug('update_helper_dicts: del _link_cache[%s]: KeyError ifname: %s'
+                                  % (old_ifname_entry_for_ifindex, old_ifname_entry_for_ifindex))
+            ######################################################
+            ######################################################
+
+            link_ifla_master_attr = link.attributes.get(Link.IFLA_MASTER)
+            if link_ifla_master_attr:
+                link_ifla_master = link_ifla_master_attr.get_pretty_value()
+            else:
+                link_ifla_master = None
+
+            # if the link has a master we need to store it in an helper dictionary, where
+            # the key is the master ifla_ifname and the value is a list of slaves, example:
+            # _masters_slaves_dict = {
+            #       'bond0': ['swp21', 'swp42']
+            # }
+            # this will be useful in the case we need to iterate on all slaves of a specific link
+
+            if old_ifla_master:
+                if old_ifla_master != link_ifla_master:
+                    # the link was previously enslaved but master is now unset on this device
+                    # we need to reflect that on the _masters_and_slaves and _slaves_master dictionaries
+                    try:
+                        self.__unslave_nolock(slave=ifname)
+                    except NetlinkCacheIfindexNotFoundError:
+                        pass
+                else:
+                    # the master status didn't change we can assume that our internal
+                    # masters_slaves dictionary is up to date and return here
+                    return
+
+            if not link_ifla_master:
+                return
+
+            master_ifname = self._ifname_by_ifindex.get(link_ifla_master)
+
+            if not master_ifname:
+                # in this case we have a link object with IFLA_MASTER set to a ifindex
+                # but this ifindex is not in our _ifname_by_ifindex dictionary thus it's
+                # not in the _link_cache yet. This situation may happen when getting the
+                # very first link dump. The kernel dumps device in the "ifindex" order.
+                #
+                # So let's say you have a box with 4 ports (lo, eth0, swp1, swp2), then
+                # manually create a bond (bond0) and enslave swp1 and swp2, bond0 will
+                # have ifindex 5 but when getting the link dump swp1 will be handled first
+                # so at this point the cache has no idea if ifindex 5 is valid or not.
+                # But since we've made it this far we can assume that this is probably a
+                # valid device and will use sysfs to confirm.
+                master_device_path = '/sys/class/net/%s/master' % ifname
+
+                if os.path.exists(master_device_path):
+                    # this check is necessary because realpath doesn't return None on error
+                    # it returns it's input argument...
+                    # >>> os.path.realpath('/sys/class/net/device_not_found')
+                    # '/sys/class/net/device_not_found'
+                    # >>>
+                    master_ifname = os.path.basename(os.path.realpath(master_device_path))
+
+            if master_ifname in self._masters_and_slaves:
+                self._masters_and_slaves[master_ifname].add(ifname)
+            else:
+                self._masters_and_slaves[master_ifname] = set([ifname])
+
+            self._slaves_master[ifname] = master_ifname
+
+    def update_link_info_data(self, ifname, ifla_info_data):
+        """
+        Update specific IFLA_INFO_DATA attributes of an existing cached device
+        ignore all errors
+        """
+        try:
+            with self._cache_lock:
+                self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA].update(ifla_info_data)
+        except:
+            pass
+
+    def add_bridge_vlan(self, msg):
+        """
+        Process AF_BRIDGE family packets (AF_BRIDGE family should be check
+        before calling this function).
+
+        Extract VLAN_INFO (vlan id and flag) and store it in cache.
+
+        :param link:
+        :return:
+        """
+        vlans_list = []
+
+        with self._cache_lock:
+            ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC)
+            ifname = msg.get_attribute_value(Link.IFLA_IFNAME)
+
+            if not ifla_af_spec:
+                return
+
+            try:
+                # We need to check if this object is still in cache, after a bridge
+                # is removed we still receive AF_BRIDGE notifications for it's slave
+                # those notifications should be ignored.
+                ifla_master = msg.get_attribute_value(Link.IFLA_MASTER)
+
+                if not ifla_master or not ifla_master in self._ifname_by_ifindex:
+                    return
+            except:
+                pass
+
+            # Example IFLA_AF_SPEC
+            #  20: 0x1c001a00  ....  Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC
+            #  21: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
+            #  22: 0x00000a00  ....
+            #  23: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
+            #  24: 0x00001000  ....
+            #  25: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
+            #  26: 0x00001400  ....
+            for x_type, x_value in ifla_af_spec.iteritems():
+                if x_type == Link.IFLA_BRIDGE_VLAN_INFO:
+                    for vlan_flag, vlan_id in x_value:
+                        # We store these in the tuple as (vlan, flag) instead
+                        # (flag, vlan) so that we can sort the list of tuples
+                        vlans_list.append((vlan_id, vlan_flag))
+
+            self._bridge_vlan_cache.update({ifname: vlans_list})
+
+    def force_add_slave(self, master, slave):
+        """
+        When calling link_set_master, we don't want to wait for the RTM_GETLINK
+        notification - if the operation return with NL_SUCCESS we can manually
+        update our cache and move on
+        :param master:
+        :param slave:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                master_slaves = self._masters_and_slaves.get(master)
+
+                if not master_slaves:
+                    self._masters_and_slaves[master] = {slave}
+                else:
+                    master_slaves.add(slave)
+
+                # if the slave is already enslaved to another device we should
+                # make sure to remove it from the _masters_and_slaves data
+                # structure as well.
+                old_master = self._slaves_master.get(slave)
+
+                if old_master:
+                    try:
+                        self._masters_and_slaves.get(old_master, []).remove(slave)
+                    except:
+                        pass
+
+                self._slaves_master[slave] = master
+        except:
+            # since this is an optimization function we can ignore all error
+            pass
+
+    def force_add_slave_list(self, master, slave_list):
+        """
+        When calling link_set_master, we don't want to wait for the RTM_GETLINK
+        notification - if the operation return with NL_SUCCESS we can manually
+        update our cache and move on
+        :param master:
+        :param slave:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                master_slaves = self._masters_and_slaves.get(master)
+
+                if not master_slaves:
+                    self._masters_and_slaves[master] = set(slave_list)
+                else:
+                    master_slaves.update(slave_list)
+
+                for slave in slave_list:
+                    self._slaves_master[slave] = master
+        except:
+            # since this is an optimization function we can ignore all error
+            pass
+
+    def force_remove_link(self, ifname):
+        """
+        When calling link_del (RTM_DELLINK) we need to manually remove the
+        associated cache entry - the RTM_DELLINK notification isn't received
+        instantaneously - we don't want to keep stale value in our cache
+        :param ifname:
+        :return:
+        """
+        try:
+            ifindex = self.get_ifindex(ifname)
+        except (KeyError, NetlinkCacheIfnameNotFoundError):
+            ifindex = None
+        self.remove_link(None, link_ifname=ifname, link_ifindex=ifindex)
+
+    def remove_link(self, link, link_ifname=None, link_ifindex=None):
+        """ Process RTM_DELLINK packet and purge the cache accordingly """
+        if link:
+            ifindex = link.ifindex
+            ifname = link.get_attribute_value(Link.IFLA_IFNAME)
+            try:
+                # RTM_DELLINK received - we can now remove ifname from the
+                # ignore_rtm_newlinkq list. We don't bother checkin if the
+                # if name is present in the list (because it's likely in)
+                with self._ignore_rtm_newlinkq_lock:
+                    self._ignore_rtm_newlinkq.remove(ifname)
+            except ValueError:
+                pass
+        else:
+            ifname = link_ifname
+            ifindex = link_ifindex
+
+        link_ifla_master = None
+        # when an enslaved device is removed we receive the RTM_DELLINK
+        # notification without the IFLA_MASTER attribute, we need to
+        # get the previous cached value in order to correctly update the
+        # _masters_and_slaves dictionary
+
+        with self._cache_lock:
+            try:
+                try:
+                    ifla_master_attr = self._link_cache[ifname].attributes.get(Link.IFLA_MASTER)
+                    if ifla_master_attr:
+                        link_ifla_master = ifla_master_attr.get_pretty_value()
+                except KeyError:
+                    # link is not present in the cache
+                    pass
+                except AttributeError:
+                    # if this code is ever reached this is very concerning and
+                    # should never happen as _link_cache should always contains
+                    # nlpacket.NetlinkPacket maybe have some extra handling here
+                    # just in case?
+                    pass
+                finally:
+                    del self._link_cache[ifname]
+            except KeyError:
+                # KeyError means that the link doesn't exists in the cache
+                log.debug('del _link_cache: KeyError ifname: %s' % ifname)
+
+            try:
+                # like in __unslave_nolock() we need to make sure that all deleted link
+                # have their bridge-vlans and _slaves_master entries cleared.
+                for slave in list(self._masters_and_slaves[ifname]):
+                    self.__unslave_nolock(slave, master=ifname)
+            except:
+                pass
+
+            try:
+                del self._bridge_vlan_cache[ifname]
+            except:
+                pass
+
+            try:
+                del self._ifname_by_ifindex[ifindex]
+            except KeyError:
+                log.debug('del _ifname_by_ifindex: KeyError ifindex: %s' % ifindex)
+
+            try:
+                del self._ifindex_by_ifname[ifname]
+            except KeyError:
+                log.debug('del _ifindex_by_ifname: KeyError ifname: %s' % ifname)
+
+            try:
+                del self._addr_cache[ifname]
+            except KeyError:
+                log.debug('del _addr_cache: KeyError ifname: %s' % ifname)
+
+            try:
+                del self._masters_and_slaves[ifname]
+            except KeyError:
+                log.debug('del _masters_and_slaves: KeyError ifname: %s' % ifname)
+
+            # if the device was enslaved to another device we need to remove
+            # it's entry from our _masters_and_slaves dictionary
+            if link_ifla_master > 0:
+                try:
+                    self.__unslave_nolock(slave=ifname)
+                except NetlinkCacheIfindexNotFoundError as e:
+                    log.debug('cache: remove_link: %s: %s' % (ifname, str(e)))
+                except KeyError:
+                    log.debug('_masters_and_slaves[if%s].remove(%s): KeyError' % (link_ifla_master, ifname))
+
+    def _address_get_ifname_and_ifindex(self, addr):
+        ifindex = addr.ifindex
+        label = addr.get_attribute_value(Address.IFA_LABEL)
+
+        if not label:
+            try:
+                label = self.get_ifname(ifindex)
+            except NetlinkCacheIfindexNotFoundError:
+                pass
+
+        return label, ifindex
+
+    def __check_and_replace_address(self, address_list, new_addr):
+        """
+        Check if new_addr is in address_list, if found we replace the occurrence
+        with the new and update object "new_addr"
+
+        address_list should be a valid list (check before calling to improve perf)
+        :param address_list:
+        :param new_addr:
+        :return:
+        """
+        ip_with_prefix = new_addr.get_attribute_value(Address.IFA_ADDRESS).with_prefixlen
+
+        for index, addr in enumerate(address_list):
+            if addr.get_attribute_value(Address.IFA_ADDRESS).with_prefixlen == ip_with_prefix:
+                address_list[index] = new_addr
+                return True
+
+        return False
+
+    def add_address(self, addr):
+        ifname, ifindex = self._address_get_ifname_and_ifindex(addr)
+
+        if not ifname:
+            log.debug('nlcache: add_address: cannot cache addr for ifindex %s' % ifindex)
+            return
+
+        ip_version = addr.get_attribute_value(Address.IFA_ADDRESS).version
+
+        with self._cache_lock:
+
+            if ifname in self._addr_cache:
+                address_list = self._addr_cache[ifname][ip_version]
+                # First check if the address is already cached, if so
+                # we need to update it's entry with the new obj
+                if not address_list or not self.__check_and_replace_address(address_list, addr):
+                    address_list.append(addr)
+            else:
+                self._addr_cache[ifname] = {
+                    4: [],
+                    6: [],
+                    ip_version: [addr]
+                }
+
+    def force_address_flush_family(self, ifname, family):
+        try:
+            with self._cache_lock:
+                self._addr_cache[ifname][family] = []
+        except:
+            pass
+
+    def address_flush_link(self, ifname):
+        """
+        Flush address cache for link 'ifname'
+        :param ifname:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                self._addr_cache[ifname] = {4: [], 6: []}
+        except:
+            pass
+
+    def force_remove_addr(self, ifname, addr):
+        """
+        When calling addr_del (RTM_DELADDR) we need to manually remove the
+        associated cache entry - the RTM_DELADDR notification isn't received
+        instantaneously - we don't want to keep stale value in our cache
+        :param ifname:
+        :param addr:
+        """
+        try:
+            with self._cache_lock:
+                # iterate through the interface addresses
+                # to find which one to remove from the cache
+                obj_to_remove = None
+
+                for cache_addr in self._addr_cache[ifname][addr.version]:
+                    try:
+                        if cache_addr.attributes[Address.IFA_ADDRESS].value.with_prefixlen == addr.with_prefixlen:
+                            obj_to_remove = cache_addr
+                    except:
+                        try:
+                            if cache_addr.attributes[Address.IFA_LOCAL].value.with_prefixlen == addr.with_prefixlen:
+                                obj_to_remove = cache_addr
+                        except:
+                            return
+                if obj_to_remove:
+                    self._addr_cache[ifname][addr.version].remove(obj_to_remove)
+        except:
+            pass
+
+    def remove_address(self, addr_to_remove):
+        ifname, _ = self._address_get_ifname_and_ifindex(addr_to_remove)
+
+        with self._cache_lock:
+            # iterate through the interface addresses
+            # to find which one to remove from the cache
+            try:
+                ip_version = addr_to_remove.get_attribute_value(Address.IFA_ADDRESS).version
+            except:
+                try:
+                    ip_version = addr_to_remove.get_attribute_value(Address.IFA_LOCAL).version
+                except:
+                    # print debug error
+                    return
+
+            addrs_for_interface = self._addr_cache.get(ifname, {}).get(ip_version)
+
+            if not addrs_for_interface:
+                return
+
+            list_addr_to_remove = []
+
+            for addr in addrs_for_interface:
+                # compare each object attribute to see if they match
+                addr_match = False
+
+                for ifa_attr in self._ifa_attributes:
+                    if addr.get_attribute_value(ifa_attr) != addr_to_remove.get_attribute_value(ifa_attr):
+                        addr_match = False
+                        break
+                    addr_match = True
+                    # if the address attribute matches we need to remove this one
+
+                if addr_match:
+                    list_addr_to_remove.append(addr)
+
+            for addr in list_addr_to_remove:
+                try:
+                    addrs_for_interface.remove(addr)
+                except ValueError as e:
+                    log.debug('nlcache: remove_address: exception: %s' % e)
+
+    def get_addresses_list(self, ifname):
+        addresses = []
+        try:
+            with self._cache_lock:
+                intf_addresses = self._addr_cache[ifname]
+                for addr in intf_addresses.get(4, []):
+                    addresses.append(addr.attributes[Address.IFA_ADDRESS].value)
+                for addr in intf_addresses.get(6, []):
+                    addresses.append(addr.attributes[Address.IFA_ADDRESS].value)
+                return addresses
+        except (KeyError, AttributeError):
+            return addresses
+
+    def get_addresses_objects_list(self, ifname):
+        addresses = []
+        try:
+            with self._cache_lock:
+                intf_addresses = self._addr_cache[ifname]
+                for addr in intf_addresses.get(4, []):
+                    addresses.append(addr)
+                for addr in intf_addresses.get(6, []):
+                    addresses.append(addr)
+                return addresses
+        except (KeyError, AttributeError):
+            return addresses
+
+    def link_has_ip(self, ifname):
+        try:
+            with self._cache_lock:
+                intf_addresses = self._addr_cache[ifname]
+                return bool(intf_addresses.get(4, None) or intf_addresses.get(6, None))
+        except:
+            return False
+
+    ############################################################################
+    ############################################################################
+    ############################################################################
+
+    def add_netconf(self, msg):
+        """
+        cache RTM_NEWNETCONF objects
+        {
+            family: {
+                ifindex: RTM_NEWNETCONF
+            }
+        }
+        we currently only support AF_INET, AF_INET6 and AF_MPLS family.
+        """
+        try:
+            with self._netconf_cache_lock:
+                self._netconf_cache[msg.family][msg.get_attribute_value(msg.NETCONFA_IFINDEX)] = msg
+        except:
+            pass
+
+    def remove_netconf(self, msg):
+        """
+        Process RTM_DELNETCONF, remove associated entry in our _netconf_cache
+        """
+        try:
+            with self._netconf_cache_lock:
+                del self._netconf_cache[msg.family][msg.get_attribute_value(msg.NETCONFA_IFINDEX)]
+        except:
+            pass
+
+    def get_netconf_forwarding(self, family, ifname):
+        """
+        Return netconf device forwarding value
+        """
+        try:
+            with self._netconf_cache_lock:
+                return self._netconf_cache[family][self.get_ifindex(ifname)].get_attribute_value(Netconf.NETCONFA_FORWARDING)
+        except:
+            # if KeyError and family == AF_INET6: ipv6 is probably disabled on this device
+            return None
+
+    def get_netconf_mpls_input(self, ifname):
+        """
+        Return netconf device MPLS input value
+        """
+        try:
+            with self._netconf_cache_lock:
+                return self._netconf_cache[AF_MPLS][self.get_ifindex(ifname)].get_attribute_value(Netconf.NETCONFA_INPUT)
+        except:
+            return None
+
+    ############################################################################
+    ############################################################################
+    ############################################################################
+
+    def get_ifupdown2_addresses_list(self, ifaceobj_list, ifname, with_address_virtual=False):
+        """
+            With the new live cache, we store every intf's addresses even if they
+            werent configured by ifupdown2. We need to filter those out to avoid
+            problems
+
+            To do so we look at the previous configuration made by ifupdown2
+            (with the help of the statemanager) together with the addresses
+            specified by the user in /etc/network/interfaces, these addresses
+            are then compared to the running state of the intf
+        """
+        if not ifaceobj_list:
+            ifaceobj_list = []
+
+        config_addrs = set(
+            self.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format(
+                ifaceobj_list,
+                with_address_virtual=with_address_virtual,
+                details=False
+            )
+        )
+
+        for previous_state_addr in self.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format(
+                statemanager.statemanager_api.get_ifaceobjs(ifname),
+                with_address_virtual=with_address_virtual,
+                details=False
+        ):
+            config_addrs.add(previous_state_addr)
+
+        ifupdown2_addresses = []
+
+        for addr in self.get_addresses_objects_list(ifname):
+            ip_addr = addr.attributes[Address.IFA_ADDRESS].value
+            if ip_addr in config_addrs:
+                ifupdown2_addresses.append(ip_addr)
+            elif not addr.scope & Route.RT_SCOPE_LINK:
+                ifupdown2_addresses.append(ip_addr)
+
+        return ifupdown2_addresses
+
+    def get_user_config_ip_addrs_with_attrs_in_ipnetwork_format(self, ifaceobj_list, with_address_virtual=False, details=True):
+        """
+            if details=True:
+                This function will return a OrderedDict of user addresses (from /e/n/i)
+                An OrderedDict is necessary because the addresses order is important (primary etc)
+
+            if details=False:
+                Function will return an ordered list of ip4 followed by ip6 as configured in /e/n/i.
+
+            all of the IP object were created by IPNetwork.
+        """
+        if not ifaceobj_list:
+            return {} if details else []
+
+        ip4 = []
+        ip6 = []
+
+        for ifaceobj in ifaceobj_list:
+            addresses = ifaceobj.get_attr_value('address')
+
+            if addresses:
+                for addr_index, addr in enumerate(addresses):
+                    if '/' in addr:
+                        ip_network_obj = IPNetwork(addr)
+                    else:
+                        # if netmask is specified under the stanza we need to use to
+                        # create the IPNetwork objects, otherwise let IPNetwork figure
+                        # out the correct netmask for ip4 & ip6
+                        netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
+
+                        if netmask:
+                            ip_network_obj = IPNetwork('%s/%s' % (addr, netmask))
+                        else:
+                            ip_network_obj = IPNetwork(addr)
+
+                    if not details:
+                        # if no details=False we don't need to go further and our lists
+                        # will only store the IPNetwork object and nothing else
+                        if ip_network_obj.version == 6:
+                            ip6.append(ip_network_obj)
+                        else:
+                            ip4.append(ip_network_obj)
+                        continue
+
+                    addr_attributes = {}
+
+                    for attr in ['broadcast', 'pointopoint', 'scope', 'preferred-lifetime']:
+                        attr_value = ifaceobj.get_attr_value_n(attr, addr_index)
+
+                        if attr_value:
+                            addr_attributes[attr] = attr_value
+
+                    if ip_network_obj.version == 6:
+                        ip6.append((ip_network_obj, addr_attributes))
+                    else:
+                        ip4.append((ip_network_obj, addr_attributes))
+
+            if not with_address_virtual:
+                continue
+            #
+            # address-virtual and vrrp ips also needs to be accounted for
+            #
+            addresses_virtual = ifaceobj.get_attr_value('address-virtual')
+            vrrp              = ifaceobj.get_attr_value('vrrp')
+
+            for attr_config in (addresses_virtual, vrrp):
+                for addr_virtual_entry in attr_config or []:
+                    for addr in addr_virtual_entry.split():
+                        try:
+                            ip_network_obj = IPNetwork(addr)
+
+                            if ip_network_obj.version == 6:
+                                if not details:
+                                    ip6.append(ip_network_obj)
+                                else:
+                                    ip6.append((ip_network_obj, {}))
+                            else:
+                                if not details:
+                                    ip4.append(ip_network_obj)
+                                else:
+                                    ip4.append((ip_network_obj, {}))
+                        except:
+                            continue
+
+        # always return ip4 first, followed by ip6
+        if not details:
+            return ip4 + ip6
+        else:
+            user_config_addresses = OrderedDict()
+
+            for addr, addr_details in ip4:
+                user_config_addresses[addr] = addr_details
+
+            for addr, addr_details in ip6:
+                user_config_addresses[addr] = addr_details
+
+            return user_config_addresses
+    ############################################################################
+    ############################################################################
+    ############################################################################
+
+    def addr_is_cached(self, ifname, addr):
+        """
+        return True if addr is in cache
+
+        We might need to check if metric/peer and other attribute are also correctly cached.
+        We might also need to add a "force" attribute to skip the cache check
+        :param ifname:
+        :param ifindex:
+        :return:
+        """
+        try:
+            with self._cache_lock:
+                for cache_addr in self._addr_cache[ifname][addr.version]:
+                    try:
+                        ifa_address = cache_addr.attributes[Address.IFA_ADDRESS].value
+                        if ifa_address.ip == addr.ip and ifa_address.prefixlen == addr.prefixlen:
+                            return True
+                    except:
+                        try:
+                            ifa_local = cache_addr.attributes[Address.IFA_LOCAL].value
+                            return ifa_local.ip == addr.ip and ifa_local.prefixlen == addr.prefixlen
+                        except:
+                            pass
+        except (KeyError, AttributeError):
+            pass
+        return False
+
+    # old
+
+    def get_link_obj(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname]
+        except KeyError:
+            return None
+
+    def get_link_info_slave_data(self, ifname):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA]
+        except (KeyError, AttributeError):
+            return {}
+
+    def is_link_kind(self, ifname, _type):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_KIND] == _type
+        except (KeyError, AttributeError):
+            return False
+
+    def is_link_slave_kind(self, ifname, _type):
+        try:
+            with self._cache_lock:
+                return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] == _type
+        except (KeyError, AttributeError):
+            return False
+
+
+class NetlinkListenerWithCache(nllistener.NetlinkManagerWithListener, BaseObject):
+
+    __instance = None
+    VXLAN_UDP_PORT = 4789
+
+    @staticmethod
+    def init(log_level):
+        """
+        Create the singleton via this init function
+        Following calls should use get_instance()
+        :param log_level:
+        :return:
+        """
+        if not NetlinkListenerWithCache.__instance:
+            try:
+                NetlinkListenerWithCache.__instance = NetlinkListenerWithCache(log_level=log_level)
+            except Exception as e:
+                log.error('NetlinkListenerWithCache: init: %s' % e)
+                traceback.print_exc()
+
+    @staticmethod
+    def get_instance():
+        """
+        Use this function to retrieve the active reference to the
+        NetlinkListenerWithCache, make sure you called .init() first
+        :return:
+        """
+        if not NetlinkListenerWithCache.__instance:
+            raise NetlinkListenerWithCacheErrorNotInitialized("NetlinkListenerWithCache not initialized")
+        return NetlinkListenerWithCache.__instance
+
+    def __init__(self, log_level):
+        """
+
+        :param log_level:
+        """
+        if NetlinkListenerWithCache.__instance:
+            raise RuntimeError("NetlinkListenerWithCache: invalid access. Please use NetlinkListenerWithCache.getInstance()")
+        else:
+            NetlinkListenerWithCache.__instance = self
+
+        nllistener.NetlinkManagerWithListener.__init__(
+            self,
+            groups=(
+                nlpacket.RTMGRP_LINK
+                | nlpacket.RTMGRP_IPV4_IFADDR
+                | nlpacket.RTMGRP_IPV6_IFADDR
+                | nlpacket.RTNLGRP_IPV4_NETCONF
+                | nlpacket.RTNLGRP_IPV6_NETCONF
+                | nlpacket.RTNLGRP_MPLS_NETCONF
+            ),
+            start_listener=False,
+            error_notification=True
+        )
+
+        BaseObject.__init__(self)
+
+        signal.signal(signal.SIGTERM, self.signal_term_handler)
+        signal.signal(signal.SIGINT, self.signal_int_handler)
+
+        self.cache = _NetlinkCache()
+
+        # set specific log level to lower-level API
+        nllistener.log.setLevel(log_level)
+        nlpacket.log.setLevel(log_level)
+        nlmanager.log.setLevel(log_level)
+
+        self.IPNetwork_version_to_family = {4: socket.AF_INET, 6: socket.AF_INET6}
+
+        nlpacket.mac_int_to_str = lambda mac_int: ':'.join(('%012x' % mac_int)[i:i + 2] for i in range(0, 12, 2))
+        # Override the nlmanager's mac_int_to_str function
+        # Return an integer in MAC string format: xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
+
+        self.workq_handler = {
+            self.WORKQ_SERVICE_NETLINK_QUEUE: self.service_netlinkq,
+        }
+
+        # NetlinkListenerWithCache first dumps links and addresses then start
+        # a worker thread before returning. The worker thread processes the
+        # workq mainly to service (process) the netlinkq which contains our
+        # netlink packet (notification coming from the Kernel).
+        # When the main thread is making netlin requests (i.e. bridge add etc
+        # ...) the main thread will sleep (thread.event.wait) until we notify
+        # it when receiving an ack associated with the request. The request
+        # may fail and the kernel won't return an ACK but instead return a
+        # NLMSG_ERROR packet. We need to store those packet separatly because:
+        #   - we could have several NLMSG_ERROR for different requests from
+        #     different threads (in a multi-threaded ifupdown2 case)
+        #   - we want to decode the packet and tell the user/log or even raise
+        #     an exception with the appropriate message.
+        #     User must check the return value of it's netlink requests and
+        #     catch any exceptions, for that purpose please use API:
+        #        - tx_nlpacket_get_response_with_error
+        self.errorq = list()
+        self.errorq_lock = threading.Lock()
+        self.errorq_enabled = True
+
+        # when ifupdown2 starts, we need to fill the netlink cache
+        # GET_LINK/ADDR request are asynchronous, we need to block
+        # and wait for the cache to be filled. We are using this one
+        # time netlinkq_notify_event to wait for the cache completion event
+        self.netlinkq_notify_event = None
+
+        # another threading event to make sure that the netlinkq worker thread is ready
+        self.is_ready = threading.Event()
+
+        self.worker = None
+
+    def __str__(self):
+        return "NetlinkListenerWithCache"
+
+    def start(self):
+        """
+        Start NetlinkListener -
+        cache all links, bridges, addresses and netconfs
+        :return:
+        """
+        self.restart_listener()
+
+        # set ifupdown2 specific supported and ignore messages
+        self.listener.supported_messages = (
+            nlpacket.RTM_NEWLINK,
+            nlpacket.RTM_DELLINK,
+            nlpacket.RTM_NEWADDR,
+            nlpacket.RTM_DELADDR,
+            nlpacket.RTM_NEWNETCONF,
+            nlpacket.RTM_DELNETCONF
+        )
+        self.listener.ignore_messages = (
+            nlpacket.RTM_GETLINK,
+            nlpacket.RTM_GETADDR,
+            nlpacket.RTM_GETNEIGH,
+            nlpacket.RTM_GETROUTE,
+            nlpacket.RTM_GETQDISC,
+            nlpacket.RTM_NEWNEIGH,
+            nlpacket.RTM_DELNEIGH,
+            nlpacket.RTM_NEWROUTE,
+            nlpacket.RTM_DELROUTE,
+            nlpacket.RTM_DELNETCONF,
+            nlpacket.RTM_NEWQDISC,
+            nlpacket.RTM_DELQDISC,
+            nlpacket.RTM_GETQDISC,
+            nlpacket.NLMSG_ERROR,  # should be in supported_messages ?
+            nlpacket.NLMSG_DONE  # should be in supported_messages ?
+        )
+
+        # get all links and wait for the cache to be filled
+        self.get_all_links_wait_netlinkq()
+
+        # get all addresses and wait for cache to be filled
+        self.get_all_addresses_wait_netlinkq()
+
+        # get a netconf dump and wait for the cached to be filled
+        self.get_all_netconf_wait_netlinkq()
+
+        # TODO: on ifquery we shoudn't start any thread (including listener in NetlinkListener)
+        # only for standalone code.
+        #import sys
+        #for arg in sys.argv:
+        #    if 'ifquery' in arg:
+        #        self.worker = None
+        #        return
+
+        # start the netlinkq worker thread
+        self.worker = threading.Thread(target=self.main, name='NetlinkListenerWithCache')
+        self.worker.start()
+        self.is_ready.wait()
+
+    def cleanup(self):
+        if not self.__instance:
+            return
+
+        # passing 0, 0 to the handler so it doesn't log.info
+        self.signal_term_handler(0, 0)
+
+        if self.worker:
+            self.worker.join()
+
+    def main(self):
+        self.is_ready.set()
+
+        # This loop has two jobs:
+        # - process items on our workq
+        # - process netlink messages on our netlinkq, messages are placed there via our NetlinkListener
+        try:
+            while True:
+                # Sleep until our alarm goes off...NetlinkListener will set the alarm once it
+                # has placed a NetlinkPacket object on our netlinkq. If someone places an item on
+                # our workq they should also set our alarm...if they don't it is not the end of
+                # the world as we will wake up in 1s anyway to check to see if our shutdown_event
+                # has been set.
+                self.alarm.wait(0.1)
+                # when ifupdown2 is not running we could change the timeout to 1 sec or more (daemon mode)
+                # the daemon can also put a hook (pyinotify) on /etc/network/interfaces
+                # if we detect changes to that file it probably means that ifupdown2 will be called very soon
+                # then we can scale up our ops (or maybe unpause some of them)
+                # lets study the use cases
+                self.alarm.clear()
+                if self.shutdown_event.is_set():
+                    break
+
+                while not self.workq.empty():
+                    (event, options) = self.workq.get()
+
+                    if event == self.WORKQ_SERVICE_NETLINK_QUEUE:
+                        self.service_netlinkq(self.netlinkq_notify_event)
+                    elif event == self.WORKQ_SERVICE_ERROR:
+                        self.logger.error('NetlinkListenerWithCache: WORKQ_SERVICE_ERROR')
+                    else:
+                        raise Exception("Unsupported workq event %s" % event)
+        except:
+            raise
+        finally:
+            # il faut surement mettre un try/except autour de la boucle au dessus
+            # car s'il y a une exception on ne quitte pas le listener thread
+            self.listener.shutdown_event.set()
+            self.listener.join()
+
+    def reset_errorq(self):
+        with self.errorq_lock:
+            self.logger.debug("nlcache: reset errorq")
+            self.errorq = []
+
+    def rx_rtm_newaddr(self, rxed_addr_packet):
+        super(NetlinkListenerWithCache, self).rx_rtm_newaddr(rxed_addr_packet)
+        self.cache.add_address(rxed_addr_packet)
+
+    def rx_rtm_dellink(self, link):
+        # cache only supports AF_UNSPEC for now
+        if link.family != socket.AF_UNSPEC:
+            return
+        super(NetlinkListenerWithCache, self).rx_rtm_dellink(link)
+        self.cache.remove_link(link)
+
+    def rx_rtm_deladdr(self, addr):
+        super(NetlinkListenerWithCache, self).rx_rtm_deladdr(addr)
+        self.cache.remove_address(addr)
+
+    def rx_rtm_newlink(self, rxed_link_packet):
+        # cache only supports AF_UNSPEC for now
+        # we can modify the cache to support more family:
+        # cache {
+        #    intf_name: {
+        #       AF_UNSPEC: NetlinkObj,
+        #       AF_BRIDGE: NetlinkObj
+        #    },
+        # }
+        if rxed_link_packet.family != socket.AF_UNSPEC:
+            # special handling for AF_BRIDGE packets
+            if rxed_link_packet.family == socket.AF_BRIDGE:
+                self.cache.add_bridge_vlan(rxed_link_packet)
+            return
+
+        super(NetlinkListenerWithCache, self).rx_rtm_newlink(rxed_link_packet)
+        self.cache.add_link(rxed_link_packet)
+
+    def rx_rtm_newnetconf(self, msg):
+        super(NetlinkListenerWithCache, self).rx_rtm_newnetconf(msg)
+        self.cache.add_netconf(msg)
+
+    def rx_rtm_delnetconf(self, msg):
+        super(NetlinkListenerWithCache, self).rx_rtm_delnetconf(msg)
+        self.cache.remove_netconf(msg)
+
+    def tx_nlpacket_get_response_with_error(self, nl_packet):
+        """
+            After getting an ACK we need to check if this ACK was in fact an
+            error (NLMSG_ERROR). This function go through the .errorq list to
+            find the error packet associated with our request.
+            If found, we process it and raise an exception with the appropriate
+            information/message.
+
+        :param nl_packet:
+        :return:
+        """
+        self.tx_nlpacket_get_response(nl_packet)
+
+        error_packet = None
+        index = 0
+
+        with self.errorq_lock:
+            for error in self.errorq:
+                if error.seq == nl_packet.seq and error.pid == nl_packet.pid:
+                    error_packet = error
+                    break
+                index += 1
+
+            if error_packet:
+                del self.errorq[index]
+
+        if not error_packet:
+            return True
+
+        error_code = abs(error_packet.negative_errno)
+
+        if error_packet.msgtype == NLMSG_DONE or not error_code:
+            # code NLE_SUCCESS...this is an ACK
+            return True
+
+        if self.debug:
+            error_packet.dump()
+
+        try:
+            # os.strerror might raise ValueError
+            strerror = os.strerror(error_code)
+
+            if strerror:
+                error_str = "operation failed with '%s' (%s)" % (strerror, error_code)
+            else:
+                error_str = "operation failed with code %s" % error_code
+
+        except ValueError:
+            error_str = "operation failed with code %s" % error_code
+
+        raise Exception(error_str)
+
+    def tx_nlpacket_get_response_with_error_and_cache_on_ack(self, packet):
+        """
+            TX packet and manually cache the object
+        """
+        self.tx_nlpacket_get_response_with_error(packet)
+        # When creating a new link via netlink, we don't always wait for the kernel
+        # NEWLINK notification to be cached to continue. If our request is ACKed by
+        # the OS we assume that the link was successfully created. Since we aren't
+        # waiting for the kernel notification to continue we need to manually fill
+        # our cache with the packet we just TX'ed. Once the NEWLINK notification
+        # is received it will simply override the previous entry.
+        # We need to keep track of those manually cached packets. We set a private
+        # flag on the objects via the attribute priv_flags
+        packet.priv_flags |= NLM_F_REQUEST
+        try:
+            # we need to decode the service header so all the attribute are properly
+            # filled in the packet object that we are about to store in cache.
+            # i.e.: packet.flags shouldn't contain NLM_F_* values but IFF_* (in case of Link object)
+            # otherwise call to cache.link_is_up() will probably return True
+            packet.decode_service_header()
+        except:
+            # we can ignore all errors
+            pass
+
+        # Then we can use our normal "add_link" API call to cache the packet
+        # and fill up our additional internal data structures.
+        self.cache.add_link(packet)
+
+    def tx_nlpacket_get_response_with_error_and_wait_for_cache(self, ifname, nl_packet):
+        """
+        The netlink request are asynchronus, but sometimes the main thread/user
+         would like to wait until the result of the request is cached. To do so
+         a cache event for ifname and nl_packet.msgtype is registered. Then the
+         netlink packet is TXed, errors are checked then we sleep until the
+         cache event is set (or we reach the timeout). This allows us to reliably
+         make sure is up to date with newly created/removed devices or addresses.
+        :param nl_packet:
+        :return:
+        """
+        wait_event_registered = self.cache.register_wait_event(ifname, nl_packet.msgtype)
+
+        try:
+            result = self.tx_nlpacket_get_response_with_error(nl_packet)
+        except:
+            # an error was caught, we need to unregister the event and raise again
+            self.cache.unregister_wait_event()
+            raise
+
+        if wait_event_registered:
+            self.cache.wait_event()
+
+        return result
+
+    def get_all_links_wait_netlinkq(self):
+        self.logger.info("requesting link dump")
+        # create netlinkq notify event so we can wait until the links are cached
+        self.netlinkq_notify_event = threading.Event()
+        self.get_all_links()
+        # we also need a dump of all existing bridge vlans
+        self.get_all_br_links(compress_vlans=True)
+        # block until the netlinkq was serviced and cached
+        self.service_netlinkq(self.netlinkq_notify_event)
+        self.netlinkq_notify_event.wait()
+        self.netlinkq_notify_event.clear()
+
+    def get_all_addresses_wait_netlinkq(self):
+        self.logger.info("requesting address dump")
+        self.netlinkq_notify_event = threading.Event()
+        self.get_all_addresses()
+        # block until the netlinkq was serviced and cached
+        self.service_netlinkq(self.netlinkq_notify_event)
+        self.netlinkq_notify_event.wait()
+        self.netlinkq_notify_event.clear()
+        self.netlinkq_notify_event = False
+
+    def get_all_netconf_wait_netlinkq(self):
+        self.logger.info("requesting netconf dump")
+        self.netlinkq_notify_event = threading.Event()
+        self.netconf_dump()
+        # block until the netlinkq was serviced and cached
+        self.service_netlinkq(self.netlinkq_notify_event)
+        self.netlinkq_notify_event.wait()
+        self.netlinkq_notify_event.clear()
+        self.netlinkq_notify_event = False
+
+    def vlan_modify(self, msgtype, ifindex, vlanid_start, vlanid_end=None, bridge_self=False, bridge_master=False, pvid=False, untagged=False):
+        """
+        iproute2 bridge/vlan.c vlan_modify()
+        """
+        assert msgtype in (RTM_SETLINK, RTM_DELLINK), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype
+        assert vlanid_start >= 1 and vlanid_start <= 4096, "Invalid VLAN start %s" % vlanid_start
+
+        if vlanid_end is None:
+            vlanid_end = vlanid_start
+
+        assert vlanid_end >= 1 and vlanid_end <= 4096, "Invalid VLAN end %s" % vlanid_end
+        assert vlanid_start <= vlanid_end, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start, vlanid_end)
+
+        debug = msgtype in self.debug
+        bridge_flags = 0
+        vlan_info_flags = 0
+
+        link = Link(msgtype, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = struct.pack('Bxxxiii', socket.AF_BRIDGE, ifindex, 0, 0)
+
+        if bridge_self:
+            bridge_flags |= Link.BRIDGE_FLAGS_SELF
+
+        if bridge_master:
+            bridge_flags |= Link.BRIDGE_FLAGS_MASTER
+
+        if pvid:
+            vlan_info_flags |= Link.BRIDGE_VLAN_INFO_PVID
+
+        if untagged:
+            vlan_info_flags |= Link.BRIDGE_VLAN_INFO_UNTAGGED
+
+        ifla_af_spec = OrderedDict()
+
+        if bridge_flags:
+            ifla_af_spec[Link.IFLA_BRIDGE_FLAGS] = bridge_flags
+
+        # just one VLAN
+        if vlanid_start == vlanid_end:
+            ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [(vlan_info_flags, vlanid_start), ]
+
+        # a range of VLANs
+        else:
+            ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [
+                (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_BEGIN, vlanid_start),
+                (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_END, vlanid_end)
+            ]
+
+        link.add_attribute(Link.IFLA_AF_SPEC, ifla_af_spec)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response_with_error(link)
+
+    #############################################################################################################
+    # Netlink API ###############################################################################################
+    #############################################################################################################
+
+    def link_add(self, ifname, kind):
+        self.link_add_with_attributes(ifname, kind, {})
+
+    def link_add_with_attributes(self, ifname, kind, ifla):
+        """
+        Build and TX a RTM_NEWLINK message to add the desired interface
+        """
+        if ifla:
+            self.logger.info("%s: netlink: ip link add dev %s type %s (with attributes)" % (ifname, ifname, kind))
+            self.logger.debug("attributes: %s" % ifla)
+        else:
+            self.logger.info("%s: netlink: ip link add dev %s type %s" % (ifname, ifname, kind))
+        try:
+            debug = RTM_NEWLINK in self.debug
+
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+
+            for nl_attr, value in ifla.items():
+                link.add_attribute(nl_attr, value)
+
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+            link.add_attribute(Link.IFLA_LINKINFO, {
+                Link.IFLA_INFO_KIND: kind
+            })
+            link.build_message(self.sequence.next(), self.pid)
+            return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+        except Exception as e:
+            raise Exception("%s: cannot create link %s type %s" % (ifname, ifname, kind))
+
+    def link_add_with_attributes_dry_run(self, ifname, kind, ifla):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type %s" % (ifname, kind))
+        self.logger.debug("attributes: %s" % ifla)
+
+    ###
+
+    def __link_set_flag(self, ifname, flags):
+        """
+        Bring interface 'ifname' up (raises on error)
+        :param ifname:
+        :return:
+        """
+        try:
+            link = Link(RTM_NEWLINK, RTM_NEWLINK in self.debug, use_color=self.use_color)
+            link.flags = NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack("=BxxxiLL", socket.AF_UNSPEC, 0, flags, Link.IFF_UP)
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+            link.build_message(self.sequence.next(), self.pid)
+            result = self.tx_nlpacket_get_response_with_error(link)
+            # if we reach this code it means the operation went through
+            # without exception we can update the cache value this is
+            # needed for the following case (and probably others):
+            #
+            # ifdown bond0 ; ip link set dev bond_slave down
+            # ifup bond0
+            #       at the beginning the slaves are admin down
+            #       ifupdownmain:run_up link up the slave
+            #       the bond addon check if the slave is up or down
+            #           and admin down the slave before enslavement
+            #           but the cache didn't process the UP notification yet
+            #           so the cache has a stale value and we try to enslave
+            #           a port, that is admin up, to a bond resulting
+            #           in an unexpected failure
+            self.cache.override_link_flag(ifname, flags)
+            return result
+        except Exception as e:
+            raise NetlinkError(e, "ip link set dev %s %s" % (ifname, "up" if flags == Link.IFF_UP else "down"), ifname=ifname)
+
+    def link_up(self, ifname):
+        if not self.cache.link_is_up(ifname):
+            self.logger.info("%s: netlink: ip link set dev %s up" % (ifname, ifname))
+            self.__link_set_flag(ifname, flags=Link.IFF_UP)
+
+    def link_up_force(self, ifname):
+        self.logger.info("%s: netlink: ip link set dev %s up" % (ifname, ifname))
+        self.__link_set_flag(ifname, flags=Link.IFF_UP)
+
+    def link_down(self, ifname):
+        if self.cache.link_is_up(ifname):
+            self.logger.info("%s: netlink: ip link set dev %s down" % (ifname, ifname))
+            self.__link_set_flag(ifname, flags=0)
+
+    def link_down_force(self, ifname):
+        self.logger.info("%s: netlink: ip link set dev %s down" % (ifname, ifname))
+        self.__link_set_flag(ifname, flags=0)
+
+    def link_up_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s up" % ifname)
+
+    def link_down_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s down" % ifname)
+
+    def link_down_force_dry_run(self, ifname):
+        self.link_down_dry_run(ifname)
+
+    ###
+
+    def __link_set_protodown(self, ifname, state):
+        debug = RTM_NEWLINK in self.debug
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = struct.pack("=BxxxiLL", socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_PROTO_DOWN, state)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response_with_error(link)
+
+    def link_set_protodown_on(self, ifname):
+        """
+        Bring ifname up by setting IFLA_PROTO_DOWN on
+        """
+        self.logger.info("%s: netlink: set link %s protodown on" % (ifname, ifname))
+        try:
+            self.__link_set_protodown(ifname, 1)
+        except Exception as e:
+            raise NetlinkError(e, "cannot set link %s protodown on" % ifname, ifname=ifname)
+
+    def link_set_protodown_off(self, ifname):
+        """
+        Take ifname down by setting IFLA_PROTO_DOWN off
+        """
+        self.logger.info("%s: netlink: set link %s protodown off" % (ifname, ifname))
+        try:
+            self.__link_set_protodown(ifname, 0)
+        except Exception as e:
+            raise NetlinkError(e, "cannot set link %s protodown off" % ifname, ifname=ifname)
+
+    def link_set_protodown_on_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: set link %s protodown on" % ifname)
+
+    def link_set_protodown_off_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: set link %s protodown off" % ifname)
+
+    ###
+
+    def link_del(self, ifname):
+        """
+        Send RTM_DELLINK request
+        :param ifname:
+        :return:
+        """
+        self.logger.info("%s: netlink: ip link del %s" % (ifname, ifname))
+        try:
+            ifindex = self.cache.get_ifindex(ifname)
+            debug = RTM_DELLINK in self.debug
+
+            link = Link(RTM_DELLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack("Bxxxiii", socket.AF_UNSPEC, ifindex, 0, 0)
+            link.build_message(self.sequence.next(), self.pid)
+
+            try:
+                # We need to register this ifname so the cache can ignore and discard
+                # any further RTM_NEWLINK packet until we receive the associated
+                # RTM_DELLINK notification
+                self.cache.append_to_ignore_rtm_newlinkq(ifname)
+
+                result = self.tx_nlpacket_get_response_with_error(link)
+
+                # Manually purge the cache entry for ifname to make sure we don't have
+                # any stale value in our cache
+                self.cache.force_remove_link(ifname)
+                return result
+            except:
+                # Something went wrong while sending the RTM_DELLINK request
+                # we need to clear ifname from the ignore_rtm_newlinkq list
+                self.cache.remove_from_ignore_rtm_newlinkq(ifname)
+                raise
+        except Exception as e:
+            raise NetlinkError(e, "cannot delete link %s" % ifname, ifname=ifname)
+
+    def link_del_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link del %s" % ifname)
+
+    ###
+
+    def __link_set_master(self, ifname, master_ifindex, master_ifname=None):
+        debug = RTM_NEWLINK in self.debug
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = struct.pack("=BxxxiLL", socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_MASTER, master_ifindex)
+        link.build_message(self.sequence.next(), self.pid)
+        result = self.tx_nlpacket_get_response_with_error(link)
+        # opti:
+        # if we reach this code it means the slave/unslave opreation went through
+        # we can manually update our cache to reflect the change without having
+        # to wait for the netlink notification
+        if master_ifindex:
+            self.cache.force_add_slave(master_ifname, ifname)
+        else:
+            self.cache.override_cache_unslave_link(slave=ifname, master=master_ifname)
+        return result
+
+    def link_set_master(self, ifname, master_ifname):
+        self.logger.info("%s: netlink: ip link set dev %s master %s" % (ifname, ifname, master_ifname))
+        try:
+            self.__link_set_master(ifname, self.cache.get_ifindex(master_ifname), master_ifname=master_ifname)
+        except Exception as e:
+            raise NetlinkError(e, "cannot enslave link %s to %s" % (ifname, master_ifname), ifname=ifname)
+
+    def link_set_nomaster(self, ifname):
+        self.logger.info("%s: netlink: ip link set dev %s nomaster" % (ifname, ifname))
+        try:
+            self.cache.append_to_rtm_newlink_nomasterq(ifname)
+            self.__link_set_master(ifname, 0)
+        except Exception as e:
+            self.cache.remove_from_rtm_newlink_nomasterq(ifname)
+            raise NetlinkError(e, "cannot un-enslave link %s" % ifname, ifname=ifname)
+
+    def link_set_master_dry_run(self, ifname, master_dev):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s master %s" % (ifname, master_dev))
+
+    def link_set_nomaster_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s nomaster" % ifname)
+
+    ###
+
+    def link_set_address_dry_run(self, ifname, hw_address):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s address %s" % (ifname, hw_address))
+
+    def link_set_address(self, ifname, hw_address):
+        is_link_up = self.cache.link_is_up(ifname)
+        # check if the link is already up or not if the link is
+        # up we need to down it then make sure we up it again
+        try:
+            if is_link_up:
+                self.link_down_force(ifname)
+
+            self.logger.info("%s: netlink: ip link set dev %s address %s" % (ifname, ifname, hw_address))
+            debug = RTM_NEWLINK in self.debug
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+            link.add_attribute(Link.IFLA_ADDRESS, hw_address)
+
+            link.build_message(self.sequence.next(), self.pid)
+            return self.tx_nlpacket_get_response_with_error(link)
+        except Exception as e:
+            raise NetlinkError(e, "cannot set dev %s address %s" % (ifname, hw_address), ifname=ifname)
+        finally:
+            if is_link_up:
+                self.link_up_force(ifname)
+
+    ###
+
+    __macvlan_mode = {
+        "private": Link.MACVLAN_MODE_PRIVATE,
+        "vepa": Link.MACVLAN_MODE_VEPA,
+        "bridge": Link.MACVLAN_MODE_BRIDGE,
+        "passthru": Link.MACVLAN_MODE_PASSTHRU,
+        "source": Link.MACVLAN_MODE_SOURCE
+    }
+
+    def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode=None):
+        self.logger.info(
+            "%s: netlink: ip link add link %s name %s type macvlan mode %s"
+            % (ifname, ifname, macvlan_ifname, macvlan_mode if macvlan_mode else "private")
+        )
+        try:
+            ifindex = self.cache.get_ifindex(ifname)
+            debug = RTM_NEWLINK in self.debug
+
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack("Bxxxiii", socket.AF_UNSPEC, 0, 0, 0)
+
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+
+            if ifindex:
+                link.add_attribute(Link.IFLA_LINK, ifindex)
+
+            link.add_attribute(Link.IFLA_LINKINFO, {
+                Link.IFLA_INFO_KIND: "macvlan",
+                Link.IFLA_INFO_DATA: {
+                    Link.IFLA_MACVLAN_MODE: self.__macvlan_mode.get(
+                        macvlan_mode,
+                        Link.MACVLAN_MODE_PRIVATE
+                    )
+                }
+            })
+            link.build_message(self.sequence.next(), self.pid)
+            return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+
+        except Exception as e:
+            raise Exception(
+                "netlink: %s: cannot create macvlan %s: %s"
+                % (ifname, macvlan_ifname, str(e))
+            )
+
+    def link_add_macvlan_dry_run(self, ifname, macvlan_ifame, macvlan_mode=None):
+        self.log_info_ifname_dry_run(
+            ifname,
+            "netlink: ip link add link %s name %s type macvlan mode %s"
+            % (ifname, macvlan_ifame, macvlan_mode if macvlan_mode else "private")
+        )
+        return True
+
+    ###
+
+    def link_add_vrf(self, ifname, vrf_table):
+        self.logger.info("%s: netlink: ip link add dev %s type vrf table %s" % (ifname, ifname, vrf_table))
+
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_LINKINFO, {
+            Link.IFLA_INFO_KIND: "vrf",
+            Link.IFLA_INFO_DATA: {
+                Link.IFLA_VRF_TABLE: int(vrf_table)
+            }
+        })
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+
+    def link_add_vrf_dry_run(self, ifname, vrf_table):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type vrf table %s" % (ifname, vrf_table))
+        return True
+
+    ###
+
+    def link_add_bridge(self, ifname, mtu=None):
+        self.logger.info("%s: netlink: ip link add dev %s type bridge" % (ifname, ifname))
+
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+
+        if mtu:
+            self.logger.info("%s: netlink: set bridge mtu %s" % (ifname, mtu))
+            link.add_attribute(Link.IFLA_MTU, mtu)
+
+        link.add_attribute(Link.IFLA_LINKINFO, {
+            Link.IFLA_INFO_KIND: "bridge",
+        })
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+
+    def link_add_bridge_dry_run(self, ifname):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bridge" % ifname)
+        return True
+
+    def link_set_bridge_info_data(self, ifname, ifla_info_data, link_just_created):
+        self.logger.info(
+            "%s: netlink: ip link set dev %s type bridge (with attributes)"
+            % (ifname, ifname)
+        )
+        self.logger.debug("attributes: %s" % ifla_info_data)
+
+        try:
+            debug = RTM_NEWLINK in self.debug
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+            link.add_attribute(Link.IFLA_LINKINFO, {
+                Link.IFLA_INFO_KIND: "bridge",
+                Link.IFLA_INFO_DATA: ifla_info_data
+            })
+            link.build_message(self.sequence.next(), self.pid)
+            result = self.tx_nlpacket_get_response_with_error(link)
+
+            if link_just_created:
+                self.cache.update_link_info_data(ifname, ifla_info_data)
+
+            return result
+        except Exception as e:
+            raise Exception("%s: netlink: cannot create bridge or set attributes: %s" % (ifname, str(e)))
+
+    def link_set_bridge_info_data_dry_run(self, ifname, ifla_info_data):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bridge (with attributes)" % ifname)
+        self.logger.debug("attributes: %s" % ifla_info_data)
+
+    ###
+
+    def link_add_bridge_vlan(self, ifname, vlan_id):
+        """
+        Add VLAN(s) to a bridge interface
+        """
+        self.logger.info("%s: netlink: bridge vlan add vid %s dev %s" % (ifname, vlan_id, ifname))
+        try:
+            ifindex = self.cache.get_ifindex(ifname)
+            self.vlan_modify(RTM_SETLINK, ifindex, vlan_id, bridge_self=True)
+            # TODO: we should probably fill our internal cache when when the ACK is received.
+        except Exception as e:
+            raise NetlinkError(e, "cannot add bridge vlan %s" % vlan_id, ifname=ifname)
+
+    def link_del_bridge_vlan(self, ifname, vlan_id):
+        """
+        Delete VLAN(s) from a bridge interface
+        """
+        self.logger.info("%s: netlink: bridge vlan del vid %s dev %s" % (ifname, vlan_id, ifname))
+        try:
+            ifindex = self.cache.get_ifindex(ifname)
+            self.vlan_modify(RTM_DELLINK, ifindex, vlan_id, bridge_self=True)
+        except Exception as e:
+            raise NetlinkError(e, "cannot remove bridge vlan %s" % vlan_id, ifname=ifname)
+
+    def link_add_bridge_vlan_dry_run(self, ifname, vlan_id):
+        self.log_info_ifname_dry_run(ifname, "netlink: bridge vlan add vid %s dev %s" % (vlan_id, ifname))
+
+    def link_del_bridge_vlan_dry_run(self, ifname, vlan_id):
+        self.log_info_ifname_dry_run(ifname, "netlink: bridge vlan del vid %s dev %s" % (vlan_id, ifname))
+
+    ###
+
+    def link_add_vlan(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None):
+        """
+        ifindex is the index of the parent interface that this sub-interface
+        is being added to
+
+        If you name an interface swp2.17 but assign it to vlan 12, the kernel
+        will return a very misleading NLE_MSG_OVERFLOW error.  It only does
+        this check if the ifname uses dot notation.
+
+        Do this check here so we can provide a more intuitive error
+        """
+        try:
+            if vlan_protocol:
+                self.logger.info("%s: netlink: ip link add link %s name %s type vlan id %s protocol %s"
+                                 % (ifname, vlan_raw_device, ifname, vlan_id, vlan_protocol))
+
+            else:
+                self.logger.info("%s: netlink: ip link add link %s name %s type vlan id %s"
+                                 % (ifname, vlan_raw_device, ifname, vlan_id))
+
+            if "." in ifname:
+                ifname_vlanid = int(ifname.split(".")[-1])
+
+                if ifname_vlanid != vlan_id:
+                    raise Exception(
+                        "Interface %s must belong to VLAN %d (VLAN %d was requested)"
+                        % (ifname, ifname_vlanid, vlan_id)
+                    )
+
+            ifindex = self.cache.get_ifindex(vlan_raw_device)
+
+            ifla_info_data = {Link.IFLA_VLAN_ID: vlan_id}
+
+            if vlan_protocol:
+                ifla_info_data[Link.IFLA_VLAN_PROTOCOL] = vlan_protocol
+
+            debug = RTM_NEWLINK in self.debug
+
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+            link.add_attribute(Link.IFLA_LINK, ifindex)
+            link.add_attribute(Link.IFLA_LINKINFO, {
+                Link.IFLA_INFO_KIND: "vlan",
+                Link.IFLA_INFO_DATA: ifla_info_data
+            })
+            link.build_message(self.sequence.next(), self.pid)
+            return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+        except Exception as e:
+            raise NetlinkError(e, "cannot create vlan %s %s" % (ifname, vlan_id), ifname=ifname)
+
+    def link_add_vlan_dry_run(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None):
+        """
+        ifindex is the index of the parent interface that this sub-interface
+        is being added to
+
+        If you name an interface swp2.17 but assign it to vlan 12, the kernel
+        will return a very misleading NLE_MSG_OVERFLOW error.  It only does
+        this check if the ifname uses dot notation.
+
+        Do this check here so we can provide a more intuitive error
+        """
+        if vlan_protocol:
+            self.log_info_ifname_dry_run(
+                ifname,
+                "netlink: ip link add link %s name %s type vlan id %s protocol %s"
+                % (vlan_raw_device, ifname, vlan_id, vlan_protocol)
+            )
+
+        else:
+            self.log_info_ifname_dry_run(
+                ifname,
+                "netlink: ip link add link %s name %s type vlan id %s"
+                % (vlan_raw_device, ifname, vlan_id)
+            )
+
+    ###
+
+    def link_add_vxlan_with_info_data(self, ifname, info_data):
+        """
+                cmd = ["ip link add %s type vxlan id %s" % (ifname, id)]
+
+                if port:
+                    cmd.append("dstport %s" % port)
+                    info_data[nlpacket.Link.IFLA_VXLAN_PORT] = int(port)
+
+                if local:
+                    cmd.append("local %s" % local)
+                    info_data[nlpacket.Link.IFLA_VXLAN_LOCAL] = local
+
+                if ageing:
+                    cmd.append("ageing %s" % ageing)
+                    info_data[nlpacket.Link.IFLA_VXLAN_AGEING] = int(ageing)
+
+                if group:
+                    if group.is_multicast:
+                        cmd.append("group %s" % group)
+                    else:
+                        cmd.append("remote %s" % group)
+                    info_data[nlpacket.Link.IFLA_VXLAN_GROUP] = group
+                else:
+                    cmd.append("noremote")
+
+                if not learning:
+                    cmd.append("nolearning")
+                info_data[nlpacket.Link.IFLA_VXLAN_LEARNING] = int(learning)
+
+                if physdev:
+                    cmd.append("dev %s" % physdev)
+                    info_data[nlpacket.Link.IFLA_VXLAN_LINK] = self.cache.get_ifindex(physdev)
+
+                if ttl:
+                    cmd.append("ttl %s" % ttl)
+                    info_data[nlpacket.Link.IFLA_VXLAN_TTL] = ttl
+
+                self.logger.info('%s: netlink: %s' % (ifname, " ".join(cmd)))
+
+        :param ifname:
+        :param info_data:
+        :return:
+        """
+        self.logger.info(
+            "%s: netlink: ip link add dev %s type vxlan id %s (with attributes)"
+            % (ifname, ifname, info_data.get(Link.IFLA_VXLAN_ID))
+        )
+        self.logger.debug("attributes: %s" % info_data)
+
+        debug = RTM_NEWLINK in self.debug
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_LINKINFO, {
+            Link.IFLA_INFO_KIND: "vxlan",
+            Link.IFLA_INFO_DATA: info_data
+        })
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+
+    def link_add_vxlan_with_info_data_dry_run(self, ifname, info_data):
+        self.log_info_ifname_dry_run(
+            ifname,
+            "netlink: ip link add dev %s type vxlan id %s (with attributes)"
+            % (ifname, info_data.get(Link.IFLA_VXLAN_ID))
+        )
+        self.logger.debug("attributes: %s" % info_data)
+        return True
+
+    ###
+
+    def link_add_bond_with_info_data(self, ifname, ifla_info_data):
+        self.logger.info(
+            "%s: netlink: ip link add dev %s type bond (with attributes)"
+            % (ifname, ifname)
+        )
+        self.logger.debug("attributes: %s" % ifla_info_data)
+
+        try:
+            debug = RTM_NEWLINK in self.debug
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+            link.add_attribute(Link.IFLA_LINKINFO, {
+                Link.IFLA_INFO_KIND: "bond",
+                Link.IFLA_INFO_DATA: ifla_info_data
+            })
+            link.build_message(self.sequence.next(), self.pid)
+            return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link)
+        except Exception as e:
+            raise Exception("%s: netlink: cannot create bond with attributes: %s" % (ifname, str(e)))
+
+    def link_add_bond_with_info_data_dry_run(self, ifname, ifla_info_data):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bond (with attributes)" % ifname)
+        self.logger.debug("attributes: %s" % ifla_info_data)
+
+    ###
+
+    def link_set_brport_with_info_slave_data(self, ifname, kind, ifla_info_data, ifla_info_slave_data):
+        """
+        Build and TX a RTM_NEWLINK message to add the desired interface
+        """
+        self.logger.info("%s: netlink: ip link set dev %s: bridge port attributes" % (ifname, ifname))
+        self.logger.debug("attributes: %s" % ifla_info_slave_data)
+
+        try:
+            debug = RTM_NEWLINK in self.debug
+
+            link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+            link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            link.body = struct.pack("Bxxxiii", socket.AF_UNSPEC, 0, 0, 0)
+
+            if ifname:
+                link.add_attribute(Link.IFLA_IFNAME, ifname)
+
+            linkinfo = dict()
+
+            if kind:
+                linkinfo[Link.IFLA_INFO_KIND] = kind
+                linkinfo[Link.IFLA_INFO_DATA] = ifla_info_data
+
+            linkinfo[Link.IFLA_INFO_SLAVE_KIND] = "bridge"
+            linkinfo[Link.IFLA_INFO_SLAVE_DATA] = ifla_info_slave_data
+
+            link.add_attribute(Link.IFLA_LINKINFO, linkinfo)
+            link.build_message(self.sequence.next(), self.pid)
+
+            # the brport already exists and is cached - after this operation we most
+            # likely don't need to do anything about the brport so we don't need to
+            # wait for the new notification to be cached.
+            return self.tx_nlpacket_get_response_with_error(link)
+        except Exception as e:
+            raise Exception("netlink: %s: cannot set %s (bridge slave) with options: %s" % (kind, ifname, str(e)))
+
+    def link_set_brport_with_info_slave_data_dry_run(self, ifname, _, __, ifla_info_slave_data):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s: bridge port attributes" % ifname)
+        self.logger.debug("attributes: %s" % ifla_info_slave_data)
+
+    ############################################################################
+    # ADDRESS
+    ############################################################################
+
+    def addr_add_dry_run(self, ifname, addr, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None):
+        log_msg = ["netlink: ip addr add %s dev %s" % (addr, ifname)]
+
+        if scope:
+            log_msg.append("scope %s" % scope)
+
+        if broadcast:
+            log_msg.append("broadcast %s" % broadcast)
+
+        if preferred_lifetime:
+            log_msg.append("preferred_lft %s" % preferred_lifetime)
+
+        if peer:
+            log_msg.append("peer %s" % peer)
+
+        if metric:
+            log_msg.append("metric %s" % metric)
+
+        self.log_info_ifname_dry_run(ifname, " ".join(log_msg))
+
+    def addr_add(self, ifname, addr, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None):
+        log_msg = ["%s: netlink: ip addr add %s dev %s" % (ifname, addr, ifname)]
+        log_msg_displayed = False
+        try:
+            # We might need to check if metric/peer and other attribute are also
+            # correctly cached.
+            # We might also need to add a "force" attribute to skip the cache check
+            if self.cache.addr_is_cached(ifname, addr):
+                return
+
+            if scope:
+                log_msg.append("scope %s" % scope)
+                scope_value = RT_SCOPES.get(scope, 0)
+            else:
+                scope_value = 0
+
+            debug = RTM_NEWADDR in self.debug
+
+            packet = Address(RTM_NEWADDR, debug, use_color=self.use_color)
+            packet.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+            packet.family = self.IPNetwork_version_to_family.get(addr.version)
+
+            packet.add_attribute(Address.IFA_ADDRESS, addr)
+            packet.add_attribute(Address.IFA_LOCAL, addr)
+
+            if broadcast:
+                log_msg.append("broadcast %s" % broadcast)
+                packet.add_attribute(Address.IFA_BROADCAST, broadcast)
+
+            if preferred_lifetime:
+                # struct ifa_cacheinfo {
+                #    __u32     ifa_prefered;
+                #    __u32     ifa_valid;
+                #    __u32     cstamp; /* created timestamp, hundredths of seconds */
+                #    __u32     tstamp; /* updated timestamp, hundredths of seconds */
+                # };
+                log_msg.append("preferred_lft %s" % preferred_lifetime)
+
+                if preferred_lifetime.lower() == "forever":
+                    preferred_lifetime = INFINITY_LIFE_TIME
+
+                packet.add_attribute(Address.IFA_CACHEINFO, (int(preferred_lifetime), INFINITY_LIFE_TIME, 0, 0))
+
+            if metric:
+                log_msg.append("metric %s" % metric)
+                packet.add_attribute(Address.IFA_RT_PRIORITY, int(metric))
+
+            if peer:
+                log_msg.append("peer %s" % peer)
+                packet.add_attribute(Address.IFA_ADDRESS, peer)
+                packet_prefixlen = peer.prefixlen
+            else:
+                packet_prefixlen = addr.prefixlen
+
+            self.logger.info(" ".join(log_msg))
+            log_msg_displayed = True
+
+            packet.body = struct.pack("=4Bi", packet.family, packet_prefixlen, 0, scope_value, self.cache.get_ifindex(ifname))
+            packet.build_message(self.sequence.next(), self.pid)
+            return self.tx_nlpacket_get_response_with_error(packet)
+        except Exception as e:
+            if not log_msg_displayed:
+                # just in case we get an exception before we reach the log.info
+                # we should display it before we raise the exception
+                log.info(" ".join(log_msg))
+            raise NetlinkError(e, "cannot add address %s dev %s" % (addr, ifname), ifname=ifname)
+
+    ###
+
+    def addr_del_dry_run(self, ifname, addr):
+        self.log_info_ifname_dry_run(ifname, "netlink: ip addr del %s dev %s" % (addr, ifname))
+
+    def addr_del(self, ifname, addr):
+        if not self.cache.addr_is_cached(ifname, addr):
+            return
+        self.logger.info("%s: netlink: ip addr del %s dev %s" % (ifname, addr, ifname))
+        try:
+            debug = RTM_DELADDR in self.debug
+
+            packet = Address(RTM_DELADDR, debug, use_color=self.use_color)
+            packet.flags = NLM_F_REQUEST | NLM_F_ACK
+            packet.family = self.IPNetwork_version_to_family.get(addr.version)
+            packet.body = struct.pack("=4Bi", packet.family, addr.prefixlen, 0, 0, self.cache.get_ifindex(ifname))
+
+            packet.add_attribute(Address.IFA_LOCAL, addr)
+
+            packet.build_message(self.sequence.next(), self.pid)
+            result = self.tx_nlpacket_get_response_with_error(packet)
+
+            # RTM_DELADDR successful, we need to update our cache
+            # to make sure we don't have any stale ip addr cached
+            self.cache.force_remove_addr(ifname, addr)
+
+            return result
+        except Exception as e:
+            raise NetlinkError(e, "cannot delete address %s dev %s" % (addr, ifname), ifname=ifname)
+
+    def addr_flush(self, ifname):
+        """
+        From iproute2/ip/ipaddress.c
+            /*
+             * Note that the kernel may delete multiple addresses for one
+             * delete request (e.g. if ipv4 address promotion is disabled).
+             * Since a flush operation is really a series of delete requests
+             * its possible that we may request an address delete that has
+             * already been done by the kernel. Therefore, ignore EADDRNOTAVAIL
+             * errors returned from a flush request
+             */
+        """
+        for addr in self.cache.get_addresses_list(ifname):
+            try:
+                self.addr_del(ifname, addr)
+            except:
+                pass
+
+    ########################
+    # TEMPORARY DEBUG CODE #
+    ########################
+
+    def DEBUG_ON(self):
+        self.debug_link(True)
+        self.debug_address(True)
+        nllistener.log.setLevel(DEBUG)
+        nlpacket.log.setLevel(DEBUG)
+        nlmanager.log.setLevel(DEBUG)
+
+    def DEBUG_OFF(self):
+        self.debug_address(False)
+        self.debug_link(False)
+        nllistener.log.setLevel(WARNING)
+        nlpacket.log.setLevel(WARNING)
+        nlmanager.log.setLevel(WARNING)
diff --git a/ifupdown2/lib/status.py b/ifupdown2/lib/status.py
new file mode 100644 (file)
index 0000000..3ce9f05
--- /dev/null
@@ -0,0 +1,52 @@
+# Copyright (C) 2019 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+
+
+class Status(object):
+    """
+    Defining client and daemon exit status to better identify
+    client and daemon issue and exceptions.
+    80 > unknown > 90 > client status > 100 > daemon status
+    """
+
+    class Client(object):
+        STATUS_SUCCESS = 0
+        STATUS_INIT = 91
+        STATUS_COULD_NOT_CONNECT = 92
+        STATUS_NO_PID = 93
+        STATUS_EMPTY = 94
+        STATUS_KEYBOARD_INTERRUPT = 95
+        STATUS_NLERROR = 96
+        STATUS_EXCEPTION_MAIN = 99
+
+    class Daemon(object):
+        STATUS_SUCCESS = 0
+        STATUS_INIT = 101
+        STATUS_UNKNOWN = 102
+        STATUS_SOCKET_ERROR = 103
+        STATUS_PROCESS_REQUEST = 104
+        STATUS_KEYBOARD_INTERRUPT = 105
+        STATUS_NLERROR = 106
+
+        STATUS_REQUEST_PARSE_ERROR = 106
+        STATUS_REQUEST_EXCEPTION = 107
+        STATUS_REQUEST_BASE_EXCEPTION = 108
diff --git a/ifupdown2/lib/sysfs.py b/ifupdown2/lib/sysfs.py
new file mode 100644 (file)
index 0000000..ab4b2a7
--- /dev/null
@@ -0,0 +1,248 @@
+# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Author:
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# sysfs -- contains all sysfs related operation
+#
+
+import os
+import glob
+
+try:
+    from ifupdown2.lib.io import IO
+    from ifupdown2.lib.base_objects import Requirements
+
+    from ifupdown2.ifupdown.utils import utils
+
+    from ifupdown2.nlmanager.nlpacket import Link
+except ImportError:
+    from lib.io import IO
+    from lib.base_objects import Requirements
+
+    from ifupdown.utils import utils
+
+    from nlmanager.nlpacket import Link
+
+
+class __Sysfs(IO, Requirements):
+
+    __bond_netlink_to_sysfs_attr_map = {
+        Link.IFLA_BOND_MODE: "mode",
+        Link.IFLA_BOND_MIIMON: "miimon",
+        Link.IFLA_BOND_USE_CARRIER: "use_carrier",
+        Link.IFLA_BOND_AD_LACP_RATE: "lacp_rate",
+        Link.IFLA_BOND_XMIT_HASH_POLICY: "xmit_hash_policy",
+        Link.IFLA_BOND_MIN_LINKS: "min_links",
+        Link.IFLA_BOND_NUM_PEER_NOTIF: "num_grat_arp",
+        Link.IFLA_BOND_AD_ACTOR_SYSTEM: "ad_actor_system",
+        Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: "ad_actor_sys_prio",
+        Link.IFLA_BOND_AD_LACP_BYPASS: "lacp_bypass",
+        Link.IFLA_BOND_UPDELAY: "updelay",
+        Link.IFLA_BOND_DOWNDELAY: "downdelay",
+        Link.IFLA_BOND_PRIMARY: "primary",
+    }
+
+    def __init__(self):
+        IO.__init__(self)
+        Requirements.__init__(self)
+
+        # Temporary work around to solve the circular dependency with nlcache.
+        # Once nlcache is created it will populate sysfs.cache
+        self.cache = None
+
+        # if bridge utils is not installed overrrides specific functions to
+        # avoid constantly checking bridge_utils_is_installed
+        if not Requirements.bridge_utils_is_installed:
+            self.bridge_get_mcqv4src = self.bridge_get_mcqv4src_dry_run
+
+    @staticmethod
+    def link_get_uppers(ifname):
+        try:
+            uppers = glob.glob("/sys/class/net/%s/upper_*" % ifname)
+            if not uppers:
+                return []
+            return [os.path.basename(u)[6:] for u in uppers]
+        except:
+            return []
+
+    @staticmethod
+    def link_get_lowers(ifname):
+        try:
+            lowers = glob.glob("/sys/class/net/%s/lower_*" % ifname)
+            if not lowers:
+                return []
+            return [os.path.basename(l)[6:] for l in lowers]
+        except:
+            return []
+
+    def link_is_up(self, ifname):
+        """
+        Read sysfs operstate file
+        """
+        return "up" == self.read_file_oneline("/sys/class/net/%s/operstate" % ifname)
+
+    def get_link_address(self, ifname):
+        """
+        Read MAC hardware address from sysfs
+        """
+        return self.read_file_oneline("/sys/class/net/%s/address" % ifname)
+
+    #
+    # MTU
+    #
+
+    def link_get_mtu(self, ifname):
+        return int(self.read_file_oneline("/sys/class/net/%s/mtu" % ifname) or 0)
+
+    def link_set_mtu(self, ifname, mtu_str, mtu_int):
+        if self.cache.get_link_mtu(ifname) != mtu_int:
+            if self.write_to_file('/sys/class/net/%s/mtu' % ifname, mtu_str):
+                self.cache.override_link_mtu(ifname, mtu_int)
+
+    def link_set_mtu_dry_run(self, ifname, mtu_str, mtu_int):
+        # we can remove the cache check in DRYRUN mode
+        self.write_to_file('/sys/class/net/%s/mtu' % ifname, mtu_str)
+
+    #
+    # ALIAS
+    #
+
+    def link_set_alias(self, ifname, alias):
+        cached_alias = self.cache.get_link_alias(ifname)
+
+        if cached_alias == alias:
+            return
+
+        if not alias:
+            alias = "\n"
+
+        if self.write_to_file("/sys/class/net/%s/ifalias" % ifname, alias):
+            pass # self.cache.override_link_mtu(ifname, mtu_int)
+
+    def link_set_alias_dry_run(self, ifname, alias):
+        # we can remove the cache check in DRYRUN mode
+        if not alias:
+            alias = ""
+        self.write_to_file("/sys/class/net/%s/ifalias" % ifname, alias)
+
+    ############################################################################
+    # BRIDGE
+    ############################################################################
+
+    def bridge_port_pvids_get(self, bridge_port_name):
+        return self.read_file_oneline("/sys/class/net/%s/brport/pvid" % bridge_port_name)
+
+    def bridge_get_stp(self, bridge):
+        stp_state_path = "/sys/class/net/%s/bridge/stp_state" % bridge
+
+        if not os.path.exists(stp_state_path):
+            return "error"
+
+        stp_state = self.read_file_oneline(stp_state_path)
+
+        if not stp_state:
+            return "error"
+
+        try:
+            stp_state_int = int(stp_state)
+            return "yes" if stp_state_int > 0 else "no"
+        except:
+            return "unknown"
+
+    def bridge_get_mcqv4src(self, bridge):
+        mcqv4src = {}
+        try:
+            filename = "/sys/class/net/%s/bridge/multicast_v4_queriers" % bridge
+            if os.path.exists(filename):
+                for line in self.read_file(filename) or []:
+                    vlan_id, ip = line.split('=')
+                    mcqv4src[vlan_id] = ip.strip()
+            return mcqv4src
+        except Exception as e:
+            self.logger.info("%s showmcqv4src: skipping unsupported command" % utils.brctl_cmd)
+            self.bridge_get_mcqv4src = self.bridge_get_mcqv4src_dry_run
+            return {}
+
+    @staticmethod
+    def bridge_get_mcqv4src_dry_run(bridge):
+        return {}
+
+    ############################################################################
+    # BOND
+    ############################################################################
+
+    def bond_remove_slave(self, bond_name, slave_name):
+        if self.cache.is_link_enslaved_to(slave_name, bond_name):
+            if self.write_to_file("/sys/class/net/%s/bonding/slaves" % bond_name, "-%s" % slave_name):
+                # success we can manually update our cache to make sure we stay up-to-date
+                self.cache.override_cache_unslave_link(slave=slave_name, master=bond_name)
+
+    def bond_remove_slave_dry_run(self, bond_name, slave_name):
+        self.write_to_file("/sys/class/net/%s/bonding/slaves" % bond_name, "-%s" % slave_name)
+
+    ###
+
+    def bond_create(self, bond_name):
+        if self.cache.bond_exists(bond_name):
+            return
+        self.write_to_file("/sys/class/net/bonding_masters", "+%s" % bond_name)
+
+    def bond_create_dry_run(self, bond_name):
+        self.write_to_file("/sys/class/net/bonding_masters", "+%s" % bond_name)
+
+    ###
+
+    def bond_set_attrs_nl(self, bond_name, ifla_info_data):
+        """
+        bond_set_attrs_nl doesn't need a _dry_run handler because each
+        entry in ifla_info_data was checked against the cache already.
+        Here write_to_file already has a dry_run handler.
+        :param bond_name:
+        :param ifla_info_data:
+        :return:
+        """
+        bond_attr_name = 'None'  # for log purpose (in case an exception raised)
+
+        for nl_attr, value in ifla_info_data.items():
+            try:
+                bond_attr_name = self.__bond_netlink_to_sysfs_attr_map.get(nl_attr)
+
+                if bond_attr_name is None:
+                    self.logger.warning(
+                        "%s: sysfs configuration: unknown bond attribute %s (value %s)"
+                        % (bond_name, nl_attr, value)
+                    )
+                    continue
+
+                file_path = "/sys/class/net/%s/bonding/%s" % (bond_name, bond_attr_name)
+                if os.path.exists(file_path):
+                    self.write_to_file(file_path, str(value))
+            except Exception as e:
+                self.logger.warning("%s: %s %s: %s" % (bond_name, bond_attr_name, value, str(e)))
+
+    ############################################################################
+    # /proc/sys/ipv6/conf
+    ############################################################################
+
+    def get_ipv6_conf_disable_ipv6(self, ifname):
+        return int(self.read_file_oneline("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname) or 0)
+
+
+Sysfs = __Sysfs()
index 817c5984d47c6e58e1494b194bed352fce09951d..6fc202c6f0677ed114fab0943a591fb0bcf02fc8 100644 (file)
@@ -104,7 +104,7 @@ OPTIONS
     -p, --print-dependency {list,dot}
                           print iface dependency in list or dot format
 
-    --admin-state, --no-scripts
+    -m, --admin-state, --no-scripts
                           don't run any addon modules/scripts. Only bring
                           the interface administratively up/down
 
index 817c5984d47c6e58e1494b194bed352fce09951d..6fc202c6f0677ed114fab0943a591fb0bcf02fc8 100644 (file)
@@ -104,7 +104,7 @@ OPTIONS
     -p, --print-dependency {list,dot}
                           print iface dependency in list or dot format
 
-    --admin-state, --no-scripts
+    -m, --admin-state, --no-scripts
                           don't run any addon modules/scripts. Only bring
                           the interface administratively up/down
 
index e9c877e3e535956099dda8602662868d66ad1de5..393e511ecdbd9811f807f7004e243365c84eeade 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
+# Copyright (C) 2015-2020 Cumulus Networks, Inc. all rights reserved
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
@@ -32,15 +32,21 @@ from struct import pack, unpack, calcsize
 from threading import Thread, Event, Lock
 from Queue import Queue
 import logging
+import signal
 import socket
 import errno
+import os
 
 log = logging.getLogger(__name__)
 
 
 class NetlinkListener(Thread):
+    # As defined in asm/socket.h
+    _SO_ATTACH_FILTER = 26
 
-    def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000):
+    RECV_BUFFER = 4096  # 1024 * 1024
+
+    def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000, bpf_filter=None):
         """
         groups controls what types of messages we are interested in hearing
         To get everything pass:
@@ -50,12 +56,15 @@ class NetlinkListener(Thread):
             RTMGRP_IPV6_IFADDR | \
             RTMGRP_IPV6_ROUTE
         """
-        Thread.__init__(self)
+        Thread.__init__(self, name='NetlinkListener')
         self.manager = manager
         self.shutdown_event = Event()
         self.groups = groups
         self.pid_offset = pid_offset
         self.rcvbuf_sz = rcvbuf_sz
+        self.bpf_filter = bpf_filter
+        self.rx_socket = None
+        self.rx_socket_prev_seq = {}
 
         # if the app has requested for error notification socket errors will
         # be sent via the SERVICE_ERROR event
@@ -63,7 +72,8 @@ class NetlinkListener(Thread):
 
         self.supported_messages = [RTM_NEWLINK, RTM_DELLINK, RTM_NEWADDR,
                                    RTM_DELADDR, RTM_NEWNEIGH, RTM_DELNEIGH,
-                                   RTM_NEWROUTE, RTM_DELROUTE]
+                                   RTM_NEWROUTE, RTM_DELROUTE,
+                                   RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB]
         self.ignore_messages = [RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH,
                                 RTM_GETROUTE, RTM_GETQDISC, NLMSG_ERROR, NLMSG_DONE]
 
@@ -86,34 +96,92 @@ class NetlinkListener(Thread):
         if msgtype not in self.ignore_messages:
             self.ignore_messages.append(msgtype)
 
+    def __bind_rx_socket(self, pid):
+        """
+        bind rx_socket and retry mechanism in case of failure and collision
+        i.e.: [Errno 98] Address already in use
+
+        We will retry NLMANAGER_BIND_RETRY times (defaults to 4242)
+
+        :param pid:
+        :return:
+        """
+        pid_offset = self.pid_offset
+        for i in xrange(0, int(os.getenv("NLMANAGER_BIND_RETRY", 4242))):
+            try:
+                pid_offset += i
+                self.rx_socket.bind((pid | (pid_offset << 22), self.groups))
+                self.pid_offset = pid_offset
+                return
+            except:
+                pass
+        # if we reach this line it means we've reach NLMANAGER_BIND_RETRY limit
+        # and couldn't successfully bind the rx_socket... We will try one more
+        # time but without catching the related exception.
+        self.rx_socket.bind((pid | (self.pid_offset << 22), self.groups))
+
     def run(self):
         manager = self.manager
-        header_PACK = 'IHHII'
-        header_LEN = calcsize(header_PACK)
-
-        # The RX socket is used to listen to all netlink messages that fly by
-        # as things change in the kernel. We need a very large SO_RCVBUF here
-        # else we tend to miss messages.
-        # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to 
-        # use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
-        # with the netlink manager which always attempts to bind with the pid.
-        self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
-        _SO_RCVBUFFORCE = socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33
-        self.rx_socket.setsockopt(socket.SOL_SOCKET, _SO_RCVBUFFORCE, self.rcvbuf_sz)
-        self.rx_socket.bind((manager.pid | (self.pid_offset << 22), self.groups))
-        self.rx_socket_prev_seq = {}
+        try:
+            header_PACK = 'IHHII'
+            header_LEN = calcsize(header_PACK)
+
+            # The RX socket is used to listen to all netlink messages that fly by
+            # as things change in the kernel. We need a very large SO_RCVBUF here
+            # else we tend to miss messages.
+            # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to
+            # use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
+            # with the netlink manager which always attempts to bind with the pid.
+            self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
+            try:
+                self.rx_socket.setsockopt(
+                    socket.SOL_SOCKET,
+                    socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33,
+                    self.rcvbuf_sz
+                )
+                if self.bpf_filter is not None:
+                    self.rx_socket.setsockopt(
+                        socket.SOL_SOCKET,
+                        NetlinkListener._SO_ATTACH_FILTER,
+                        self.bpf_filter
+                    )
+            except Exception as e:
+                log.debug("nllistener: rx socket: setsockopt: %s" % str(e))
 
-        manager.target_lock.acquire()
-        if not manager.tx_socket:
-            manager.tx_socket_allocate()
-        manager.target_lock.release()
+            self.__bind_rx_socket(manager.pid)
 
-        my_sockets = (manager.tx_socket, self.rx_socket)
+            with manager.target_lock:
+                if not manager.tx_socket:
+                    manager.tx_socket_allocate()
 
-        socket_string = {
-            manager.tx_socket: "TX",
-            self.rx_socket: "RX"
-        }
+            my_sockets = (manager.tx_socket, self.rx_socket)
+
+            socket_string = {
+                manager.tx_socket: "TX",
+                self.rx_socket: "RX"
+            }
+        except Exception as e:
+            if self.rx_socket:
+                self.rx_socket.close()
+                self.rx_socket = None
+
+            # before notifying the main thread we need to set
+            # manager.listener_ready properly to signal the failure
+            manager.listener_ready = False
+            manager.listener_event_ready.set()
+
+            if logging.root.level == logging.DEBUG:
+                # in debug mode we raise the exception so it can be displayed
+                # in the terminal: "Exception in thread NetlinkListener..."
+                raise
+            else:
+                log.error("netlink: listener thread: rx socket: %s" % str(e))
+                return
+
+        # Notify main thread that the NetlinkListener thread
+        # has started and is ready to start processing data
+        manager.listener_ready = True
+        manager.listener_event_ready.set()
 
         while True:
 
@@ -123,7 +191,8 @@ class NetlinkListener(Thread):
 
             # Only block for 1 second so we can wake up to see if shutdown_event is set
             try:
-                (readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 1)
+                (readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 0.1)
+                # when ifupdown2 is not running we could change the timeout to 1 sec or more
             except Exception as e:
                 log.error('select() error: ' + str(e))
                 continue
@@ -139,7 +208,7 @@ class NetlinkListener(Thread):
                 data = []
 
                 try:
-                    data = s.recv(4096)
+                    data = s.recv(self.RECV_BUFFER)
                 except socket.error, e:
                     log.error('recv() error: ' + str(e))
                     data = []
@@ -155,8 +224,15 @@ class NetlinkListener(Thread):
                     # Extract the length, etc from the header
                     (length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
 
+                    msgtype_str = NetlinkPacket.type_to_string.get(msgtype)
+
+                    if not msgtype_str:
+                        data = data[length:]
+                        log.debug('%s %s: RXed unknown/unsupported msg type %s skipping netlink message...' % (self, socket_string[s], msgtype))
+                        continue
+
                     log.debug('%s %s: RXed %s seq %d, pid %d, %d bytes (%d total)' %
-                              (self, socket_string[s], NetlinkPacket.type_to_string[msgtype],
+                              (self, socket_string[s], msgtype_str,
                                seq, pid, length, total_length))
                     possible_ack = False
 
@@ -172,7 +248,13 @@ class NetlinkListener(Thread):
                         msg.decode_packet(length, flags, seq, pid, data)
 
                         if error_code:
-                            log.debug("%s %s: RXed NLMSG_ERROR code %s (%d)" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code))
+                            log.debug("%s %s: RXed NLMSG_ERROR code %s (%d): %s" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code, msg.error_to_human_readable_string.get(error_code)))
+                        else:
+                            log.debug("%s %s: RXed NLMSG_ERROR code %s (%d): %s... this is an ACK" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code, msg.error_to_human_readable_string.get(error_code)))
+
+                        if manager.errorq_enabled:
+                            with manager.errorq_lock:
+                                manager.errorq.append(msg)
 
                     if possible_ack and seq == manager.target_seq and pid == manager.target_pid:
                         log.debug("%s %s: Setting RXed ACK alarm for seq %d, pid %d" %
@@ -213,17 +295,16 @@ class NetlinkListener(Thread):
                     data = data[length:]
 
             if set_tx_socket_rxed_ack_alarm:
-                manager.target_lock.acquire()
-                manager.target_seq = None
-                manager.target_pid = None
-                manager.target_lock.release()
+                with manager.target_lock:
+                    manager.target_seq = None
+                    manager.target_pid = None
                 manager.tx_socket_rxed_ack.set()
 
             if set_alarm:
-                manager.workq.put(('SERVICE_NETLINK_QUEUE', None))
+                manager.workq.put((manager.WORKQ_SERVICE_NETLINK_QUEUE, None))
 
             if set_overrun:
-                manager.workq.put(('SERVICE_ERROR', "OVERFLOW"))
+                manager.workq.put((manager.WORKQ_SERVICE_ERROR, "OVERFLOW"))
 
             if set_alarm or set_overrun:
                 manager.alarm.set()
@@ -233,7 +314,10 @@ class NetlinkListener(Thread):
 
 class NetlinkManagerWithListener(NetlinkManager):
 
-    def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000):
+    WORKQ_SERVICE_NETLINK_QUEUE = 1
+    WORKQ_SERVICE_ERROR         = 2
+
+    def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000, bpf_filter=None):
         NetlinkManager.__init__(self, use_color=use_color, pid_offset=pid_offset)
         self.groups = groups
         self.workq = Queue()
@@ -255,6 +339,14 @@ class NetlinkManagerWithListener(NetlinkManager):
         self.rcvbuf_sz = rcvbuf_sz
         self.error_notification = error_notification
         self.pid_offset = pid_offset
+        self.bpf_filter = bpf_filter
+
+        self.errorq = None
+        self.errorq_lock = None
+        self.errorq_enabled = False
+
+        self.listener_event_ready = None
+        self.listener_ready = None
 
         # Listen to netlink messages
         if start_listener:
@@ -266,11 +358,26 @@ class NetlinkManagerWithListener(NetlinkManager):
         return 'NetlinkManagerWithListener'
 
     def restart_listener(self):
-        self.listener = NetlinkListener(self, self.groups, self.pid_offset+1, self.error_notification, self.rcvbuf_sz)
+        """
+        (re)Start Netlink listener thread and make sure to wait until
+        the newly created thread is ready.
+        :return:
+        """
+        self.listener_event_ready = Event()
+        self.listener_ready = False
+
+        self.listener = NetlinkListener(self, self.groups, self.pid_offset + 1, self.error_notification, self.rcvbuf_sz, self.bpf_filter)
         self.listener.start()
 
-    def signal_term_handler(self, signal, frame):
-        log.info("NetlinkManagerWithListener: Caught SIGTERM")
+        self.listener_event_ready.wait()
+        if not self.listener_ready:
+            self.listener.join()
+            # TODO: add custom exception (easier to ignore and recognize)
+            raise Exception()
+
+    def signal_term_handler(self, sig, frame):
+        if sig == signal.SIGTERM:
+            log.info("NetlinkManagerWithListener: Caught SIGTERM")
 
         if self.listener:
             self.listener.shutdown_event.set()
@@ -290,19 +397,23 @@ class NetlinkManagerWithListener(NetlinkManager):
         self.alarm.set()
 
     def tx_nlpacket_get_response(self, nlpacket):
+        # WARNING: having multiple threads waiting for ACKs might result in
+        # undefined behavior. To make this work we should probably have a
+        # (thread-safe) list of all the target SEQs and PIDs along side a
+        # reference to their alarms (thead.Event) to notify the right thread
+        # of the RXed ACK.
         """
         TX the message and wait for an ack
         """
 
         # NetlinkListener looks at the manager's target_seq and target_pid
         # to know when we've RXed the ack that we want
-        self.target_lock.acquire()
-        self.target_seq = nlpacket.seq
-        self.target_pid = nlpacket.pid
+        with self.target_lock:
+            self.target_seq = nlpacket.seq
+            self.target_pid = nlpacket.pid
 
-        if not self.tx_socket:
-            self.tx_socket_allocate()
-        self.target_lock.release()
+            if not self.tx_socket:
+                self.tx_socket_allocate()
 
         log.debug('%s TX: TXed %s seq %d, pid %d, %d bytes' %
                    (self,  NetlinkPacket.type_to_string[nlpacket.msgtype],
@@ -325,13 +436,29 @@ class NetlinkManagerWithListener(NetlinkManager):
         log.debug("RXed RTM_DELLINK seq %d, pid %d, %d bytes, for %s, state %s" %
                   (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
 
+    def rx_rtm_newnetconf(self, msg):
+        ifindex = msg.get_attribute_value(msg.NETCONFA_IFINDEX)
+        ifname = self.ifname_by_index.get(ifindex)
+
+        if ifname:
+            log.debug("RXed RTM_NEWNETCONF seq %d, pid %d, %d bytes on ifname %s" % (msg.seq, msg.pid, msg.length, ifname))
+        else:
+            log.debug("RXed RTM_NEWNETCONF seq %d, pid %d, %d bytes on ifindex %s" % (msg.seq, msg.pid, msg.length, ifindex))
+
+    def rx_rtm_delnetconf(self, msg):
+        ifindex = msg.get_attribute_value(msg.NETCONFA_IFINDEX)
+        ifname = self.ifname_by_index.get(ifindex)
+
+        if ifname:
+            log.debug("RXed RTM_DELNETCONF seq %d, pid %d, %d bytes on ifname %s" % (msg.seq, msg.pid, msg.length, ifname))
+        else:
+            log.debug("RXed RTM_DELNETCONF seq %d, pid %d, %d bytes on ifindex %s" % (msg.seq, msg.pid, msg.length, ifindex))
+
     def rx_rtm_newaddr(self, msg):
-        log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
+        log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s on %s" % (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), self.ifname_by_index.get(msg.ifindex)))
 
     def rx_rtm_deladdr(self, msg):
-        log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
+        log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s on %s" % (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), self.ifname_by_index.get(msg.ifindex)))
 
     def rx_rtm_newneigh(self, msg):
         log.debug("RXed RTM_NEWNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
@@ -349,6 +476,12 @@ class NetlinkManagerWithListener(NetlinkManager):
         log.debug("RXed RTM_DELROUTE seq %d, pid %d, %d bytes, for %s%s" %
                   (msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
 
+    def rx_rtm_newmdb(self, msg):
+        log.debug("RXed RTM_NEWMDB")
+
+    def rx_rtm_delmdb(self, msg):
+        log.debug("RXed RTM_DELMDB")
+
     def rx_nlmsg_done(self, msg):
         log.debug("RXed NLMSG_DONE seq %d, pid %d, %d bytes" % (msg.seq, msg.pid, msg.length))
 
@@ -552,7 +685,7 @@ class NetlinkManagerWithListener(NetlinkManager):
     def filter_by_nested_attribute(self, add, filter_type, msgtype, attr_filter):
         self._filter_update(add, filter_type, msgtype, ('NESTED_ATTRIBUTE', attr_filter))
 
-    def service_netlinkq(self):
+    def service_netlinkq(self, notify_event=None):
         msg_count = {}
         processed = 0
 
@@ -577,6 +710,12 @@ class NetlinkManagerWithListener(NetlinkManager):
             elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
                 msg = Route(msgtype, debug, use_color=self.use_color)
 
+            elif msgtype in (RTM_GETNETCONF, RTM_NEWNETCONF, RTM_DELNETCONF):
+                msg = Netconf(msgtype, debug, use_color=self.use_color)
+
+            elif msgtype == RTM_NEWMDB or msgtype == RTM_DELMDB:
+                msg = MDB(msgtype, debug, use_color=self.use_color)
+
             elif msgtype == NLMSG_DONE:
                 msg = Done(msgtype, debug, use_color=self.use_color)
 
@@ -630,6 +769,18 @@ class NetlinkManagerWithListener(NetlinkManager):
             elif msg.msgtype == RTM_DELROUTE:
                 self.rx_rtm_delroute(msg)
 
+            elif msg.msgtype == RTM_NEWNETCONF:
+                self.rx_rtm_newnetconf(msg)
+
+            elif msg.msgtype == RTM_DELNETCONF:
+                self.rx_rtm_delnetconf(msg)
+
+            elif msg.msgtype == RTM_NEWMDB:
+                self.rx_rtm_newmdb(msg)
+
+            elif msg.msgtype == RTM_DELMDB:
+                self.rx_rtm_delmdb(msg)
+
             elif msg.msgtype == NLMSG_DONE:
                 self.rx_nlmsg_done(msg)
 
@@ -639,6 +790,9 @@ class NetlinkManagerWithListener(NetlinkManager):
         if processed:
             self.netlinkq = self.netlinkq[processed:]
 
+        if notify_event:
+            notify_event.set()
+
         # too chatty
         # for msgtype in msg_count:
         #     log.debug('RXed %d %s messages' % (msg_count[msgtype], NetlinkPacket.type_to_string[msgtype]))
index deb7ecf99fe0d281f2901e46c4924499fd910475..806b8e6734fef51a62b3c1a193855fe6108b81b1 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
+# Copyright (C) 2015-2020 Cumulus Networks, Inc. all rights reserved
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
@@ -130,7 +130,10 @@ class NetlinkManager(object):
         self._debug_set_clear((RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE), enabled)
 
     def debug_netconf(self, enabled):
-        self._debug_set_clear((RTM_GETNETCONF, RTM_NEWNETCONF), enabled)
+        self._debug_set_clear((RTM_GETNETCONF, RTM_NEWNETCONF, RTM_DELNETCONF), enabled)
+
+    def debug_mdb(self, enabled):
+        self._debug_set_clear((RTM_GETMDB, RTM_NEWMDB, RTM_DELMDB), enabled)
 
     def debug_this_packet(self, mtype):
         if mtype in self.debug:
@@ -353,6 +356,9 @@ class NetlinkManager(object):
                         elif msgtype in (RTM_GETNETCONF, RTM_NEWNETCONF):
                             msg = Netconf(msgtype, nlpacket.debug, use_color=self.use_color)
 
+                        elif msgtype in (RTM_GETMDB, RTM_NEWMDB, RTM_DELMDB):
+                            msg = MDB(msgtype, nlpacket.debug, use_color=self.use_color)
+
                         else:
                             raise Exception("RXed unknown netlink message type %s" % msgtype)
 
@@ -396,6 +402,10 @@ class NetlinkManager(object):
             msg = Route(rtm_type, debug, use_color=self.use_color)
             msg.body = pack('Bxxxii', family, 0, 0)
 
+        elif rtm_type == RTM_GETMDB:
+            msg = MDB(rtm_type, debug, use_color=self.use_color)
+            msg.body = pack('Bxxxii', family, 0, 0)
+
         else:
             log.error("request_dump RTM_GET %s is not supported" % rtm_type)
             return None
@@ -723,24 +733,24 @@ class NetlinkManager(object):
 
         return self._link_add(ifindex, ifname, 'vlan', ifla_info_data)
 
-    def link_add_macvlan(self, ifindex, ifname):
+    def link_add_macvlan(self, ifindex, ifname, macvlan_mode):
         """
         ifindex is the index of the parent interface that this sub-interface
         is being added to
         """
-        return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
-
-    def link_add_xfrm(self, physdev, xfrm_ifname, xfrm_id):
-        """
-        ifindex is the index of the parent interface that this sub-interface
-        is being added to
-        """
-        ifla_info_data = {
-            Link.IFLA_XFRM_IF_ID: int(xfrm_id),
-            Link.IFLA_XFRM_LINK: int(physdev)
-        }
-        
-        return self._link_add(ifindex=None, ifname=xfrm_ifname, kind='xfrm', ifla_info_data=ifla_info_data)
+        return self._link_add(
+            ifindex,
+            ifname,
+            'macvlan',
+            {
+                Link.IFLA_MACVLAN_MODE: {
+                    "private": Link.MACVLAN_MODE_PRIVATE,
+                    "vepa": Link.MACVLAN_MODE_VEPA,
+                    "bridge": Link.MACVLAN_MODE_BRIDGE,
+                    "passthru": Link.MACVLAN_MODE_PASSTHRU
+                }.get(macvlan_mode, Link.MACVLAN_MODE_PRIVATE)
+            }
+        )
 
     def vlan_get(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
         """
@@ -1077,3 +1087,14 @@ class NetlinkManager(object):
         msg.flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
         msg.build_message(self.sequence.next(), self.pid)
         return self.tx_nlpacket_get_response(msg)
+
+    # ===
+    # MDB
+    # ===
+    def mdb_dump(self):
+        debug = RTM_GETMDB in self.debug
+        msg = MDB(RTM_GETMDB, debug, use_color=self.use_color)
+        msg.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0)
+        msg.flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
+        msg.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(msg)
index cd0d332d8a5ddf4e8a2294814b692cba3f0453f7..fcb89fb19759fe62407bb23afa9564033629a2a8 100644 (file)
@@ -1,6 +1,6 @@
 # Copyright (c) 2009-2013, Exa Networks Limited
 # Copyright (c) 2009-2013, Thomas Mangin
-# Copyright (c) 2015-2017 Cumulus Networks, Inc.
+# Copyright (c) 2015-2020 Cumulus Networks, Inc.
 #
 # All rights reserved.
 # Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
 
 import logging
 import struct
-from ipaddr import IPv4Address, IPv6Address, IPAddress
+from ipaddr import IPNetwork, IPv4Address, IPv6Address, IPAddress
 from binascii import hexlify
 from pprint import pformat
 from socket import AF_UNSPEC, AF_INET, AF_INET6, AF_BRIDGE, htons
@@ -40,6 +40,10 @@ from struct import pack, unpack, calcsize
 log = logging.getLogger(__name__)
 SYSLOG_EXTRA_DEBUG = 5
 
+ETH_P_IP = 0x0800
+ETH_P_IPV6 = 0x86DD
+
+INFINITY_LIFE_TIME = 0xFFFFFFFF
 
 # Interface name buffer size #define IFNAMSIZ 16 (kernel source)
 IF_NAME_SIZE = 15 # 15 because python doesn't have \0
@@ -72,8 +76,13 @@ RTM_DELQDISC  = 0x25
 RTM_GETQDISC  = 0x26
 
 RTM_NEWNETCONF = 80
+RTM_DELNETCONF = 81
 RTM_GETNETCONF = 82
 
+RTM_NEWMDB = 84
+RTM_DELMDB = 85
+RTM_GETMDB = 86
+
 # Netlink message flags
 NLM_F_REQUEST = 0x01  # It is query message.
 NLM_F_MULTI   = 0x02  # Multipart message, terminated by NLMSG_DONE
@@ -112,12 +121,41 @@ RTMGRP_IPV6_IFINFO   = 0x800
 RTMGRP_DECnet_IFADDR = 0x1000
 RTMGRP_DECnet_ROUTE  = 0x4000
 RTMGRP_IPV6_PREFIX   = 0x20000
+RTNLGRP_MDB          = 0x1A
+
+
+def nl_mgrp(group):
+    """
+    The api is a reimplementation of "nl_mgrp" function from
+    iproute2/include/utils.h
+    """
+    if group > 31:
+        raise Exception("%d Invalid Group" % group)
+    else:
+        group = (1 << (group - 1)) if group else 0
+        return group
+
+
+RTNLGRP_IPV4_NETCONF = nl_mgrp(24)
+RTNLGRP_IPV6_NETCONF = nl_mgrp(25)
+RTNLGRP_MPLS_NETCONF = nl_mgrp(29)
+
 
 RTMGRP_ALL = (RTMGRP_LINK | RTMGRP_NOTIFY | RTMGRP_NEIGH | RTMGRP_TC |
               RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE |
               RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO |
-              RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE |
-              RTMGRP_IPV6_PREFIX)
+              RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE | nl_mgrp(RTNLGRP_MDB) |
+              RTMGRP_IPV6_PREFIX | RTNLGRP_IPV4_NETCONF | RTNLGRP_IPV6_NETCONF | RTNLGRP_MPLS_NETCONF)
+
+# /etc/iproute2/rt_scopes
+RT_SCOPES = {
+    "global": 0,
+    "universe": 0,
+    "nowhere": 255,
+    "host": 254,
+    "link": 253,
+    "site": 200
+}
 
 AF_MPLS = 28
 
@@ -216,210 +254,1073 @@ def padded_length(length):
     return int((length + 3) / 4) * 4
 
 
-class Attribute(object):
+class NetlinkPacket_IFLA_LINKINFO_Attributes:
 
-    def __init__(self, atype, string, logger):
-        self.atype = atype
-        self.string = string
-        self.HEADER_PACK = '=HH'
-        self.HEADER_LEN = calcsize(self.HEADER_PACK)
-        self.PACK = None
-        self.LEN = None
-        self.value = None
-        self.nested = False
-        self.net_byteorder = False
-        self.log = logger
+    # =========================================
+    # IFLA_LINKINFO attributes
+    # =========================================
+    IFLA_INFO_UNSPEC     = 0
+    IFLA_INFO_KIND       = 1
+    IFLA_INFO_DATA       = 2
+    IFLA_INFO_XSTATS     = 3
+    IFLA_INFO_SLAVE_KIND = 4
+    IFLA_INFO_SLAVE_DATA = 5
+    IFLA_INFO_MAX        = 6
 
-    def __str__(self):
-        return self.string
+    ifla_info_to_string = {
+        IFLA_INFO_UNSPEC     : 'IFLA_INFO_UNSPEC',
+        IFLA_INFO_KIND       : 'IFLA_INFO_KIND',
+        IFLA_INFO_DATA       : 'IFLA_INFO_DATA',
+        IFLA_INFO_XSTATS     : 'IFLA_INFO_XSTATS',
+        IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND',
+        IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA',
+        IFLA_INFO_MAX        : 'IFLA_INFO_MAX'
+    }
 
-    def set_value(self, value):
-        self.value = value
+    # =========================================
+    # IFLA_INFO_DATA attributes for vlan
+    # =========================================
+    IFLA_VLAN_UNSPEC      = 0
+    IFLA_VLAN_ID          = 1
+    IFLA_VLAN_FLAGS       = 2
+    IFLA_VLAN_EGRESS_QOS  = 3
+    IFLA_VLAN_INGRESS_QOS = 4
+    IFLA_VLAN_PROTOCOL    = 5
 
-    def set_nested(self, nested):
-        self.nested = nested
+    ifla_vlan_to_string = {
+        IFLA_VLAN_UNSPEC      : 'IFLA_VLAN_UNSPEC',
+        IFLA_VLAN_ID          : 'IFLA_VLAN_ID',
+        IFLA_VLAN_FLAGS       : 'IFLA_VLAN_FLAGS',
+        IFLA_VLAN_EGRESS_QOS  : 'IFLA_VLAN_EGRESS_QOS',
+        IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS',
+        IFLA_VLAN_PROTOCOL    : 'IFLA_VLAN_PROTOCOL'
+    }
 
-    def set_net_byteorder(self, net_byteorder):
-        self.net_byteorder = net_byteorder
+    ifla_vlan_protocol_dict = {
+        '802.1Q':   0x8100,
+        '802.1q':   0x8100,
 
-    def pad_bytes_needed(self, length):
-        """
-        Return the number of bytes that should be added to align on a 4-byte boundry
-        """
-        remainder = length % 4
+        '802.1ad':  0x88A8,
+        '802.1AD':  0x88A8,
+        '802.1Ad':  0x88A8,
+        '802.1aD':  0x88A8,
 
-        if remainder:
-            return 4 - remainder
+        0x8100:     '802.1Q',
+        0x88A8:     '802.1ad'
+    }
 
-        return 0
+    # =========================================
+    # IFLA_INFO_DATA attributes for macvlan
+    # =========================================
+    IFLA_MACVLAN_UNSPEC = 0
+    IFLA_MACVLAN_MODE   = 1
 
-    def pad(self, length, raw):
-        pad = self.pad_bytes_needed(length)
+    ifla_macvlan_to_string = {
+        IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC',
+        IFLA_MACVLAN_MODE   : 'IFLA_MACVLAN_MODE'
+    }
 
-        if pad:
-            raw += '\0' * pad
+    # enum macvlan_mode
+    MACVLAN_MODE_PRIVATE    = 1  # don't talk to other macvlans */
+    MACVLAN_MODE_VEPA       = 2  # talk to other ports through ext bridge */
+    MACVLAN_MODE_BRIDGE     = 4  # talk to bridge ports directly */
+    MACVLAN_MODE_PASSTHRU   = 8  # take over the underlying device */
+    MACVLAN_MODE_SOURCE     = 16  # use source MAC address list to assign */
 
-        return raw
+    macvlan_mode_to_string = {
+        MACVLAN_MODE_PRIVATE  : 'MACVLAN_MODE_PRIVATE',
+        MACVLAN_MODE_VEPA     : 'MACVLAN_MODE_VEPA',
+        MACVLAN_MODE_BRIDGE   : 'MACVLAN_MODE_BRIDGE',
+        MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU',
+        MACVLAN_MODE_SOURCE   : 'MACVLAN_MODE_SOURCE'
+    }
 
-    def encode(self):
+    # =========================================
+    # IFLA_INFO_DATA attributes for xfrm
+    # =========================================
+    IFLA_XFRM_UNSPEC = 0
+    IFLA_XFRM_LINK   = 1
+    IFLA_XFRM_IF_ID  = 2
 
-        if not self.LEN:
-            raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric')
+    ifla_xfrm_to_string = {
+        IFLA_XFRM_UNSPEC: 'IFLA_XFRM_UNSPEC',
+        IFLA_XFRM_LINK  : 'IFLA_XFRM_LINK',
+        IFLA_XFRM_IF_ID : 'IFLA_XFRM_IF_ID'
+    }
 
-        length = self.HEADER_LEN + self.LEN
-        attr_type_with_flags = self.atype
+    # =========================================
+    # IFLA_INFO_DATA attributes for vxlan
+    # =========================================
+    IFLA_VXLAN_UNSPEC            = 0
+    IFLA_VXLAN_ID                = 1
+    IFLA_VXLAN_GROUP             = 2
+    IFLA_VXLAN_LINK              = 3
+    IFLA_VXLAN_LOCAL             = 4
+    IFLA_VXLAN_TTL               = 5
+    IFLA_VXLAN_TOS               = 6
+    IFLA_VXLAN_LEARNING          = 7
+    IFLA_VXLAN_AGEING            = 8
+    IFLA_VXLAN_LIMIT             = 9
+    IFLA_VXLAN_PORT_RANGE        = 10
+    IFLA_VXLAN_PROXY             = 11
+    IFLA_VXLAN_RSC               = 12
+    IFLA_VXLAN_L2MISS            = 13
+    IFLA_VXLAN_L3MISS            = 14
+    IFLA_VXLAN_PORT              = 15
+    IFLA_VXLAN_GROUP6            = 16
+    IFLA_VXLAN_LOCAL6            = 17
+    IFLA_VXLAN_UDP_CSUM          = 18
+    IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19
+    IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20
+    IFLA_VXLAN_REMCSUM_TX        = 21
+    IFLA_VXLAN_REMCSUM_RX        = 22
+    IFLA_VXLAN_GBP               = 23
+    IFLA_VXLAN_REMCSUM_NOPARTIAL = 24
+    IFLA_VXLAN_COLLECT_METADATA  = 25
+    IFLA_VXLAN_REPLICATION_NODE  = 253
+    IFLA_VXLAN_REPLICATION_TYPE  = 254
 
-        if self.nested:
-            attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED
+    ifla_vxlan_to_string = {
+        IFLA_VXLAN_UNSPEC            : 'IFLA_VXLAN_UNSPEC',
+        IFLA_VXLAN_ID                : 'IFLA_VXLAN_ID',
+        IFLA_VXLAN_GROUP             : 'IFLA_VXLAN_GROUP',
+        IFLA_VXLAN_LINK              : 'IFLA_VXLAN_LINK',
+        IFLA_VXLAN_LOCAL             : 'IFLA_VXLAN_LOCAL',
+        IFLA_VXLAN_TTL               : 'IFLA_VXLAN_TTL',
+        IFLA_VXLAN_TOS               : 'IFLA_VXLAN_TOS',
+        IFLA_VXLAN_LEARNING          : 'IFLA_VXLAN_LEARNING',
+        IFLA_VXLAN_AGEING            : 'IFLA_VXLAN_AGEING',
+        IFLA_VXLAN_LIMIT             : 'IFLA_VXLAN_LIMIT',
+        IFLA_VXLAN_PORT_RANGE        : 'IFLA_VXLAN_PORT_RANGE',
+        IFLA_VXLAN_PROXY             : 'IFLA_VXLAN_PROXY',
+        IFLA_VXLAN_RSC               : 'IFLA_VXLAN_RSC',
+        IFLA_VXLAN_L2MISS            : 'IFLA_VXLAN_L2MISS',
+        IFLA_VXLAN_L3MISS            : 'IFLA_VXLAN_L3MISS',
+        IFLA_VXLAN_PORT              : 'IFLA_VXLAN_PORT',
+        IFLA_VXLAN_GROUP6            : 'IFLA_VXLAN_GROUP6',
+        IFLA_VXLAN_LOCAL6            : 'IFLA_VXLAN_LOCAL6',
+        IFLA_VXLAN_UDP_CSUM          : 'IFLA_VXLAN_UDP_CSUM',
+        IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX',
+        IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX',
+        IFLA_VXLAN_REMCSUM_TX        : 'IFLA_VXLAN_REMCSUM_TX',
+        IFLA_VXLAN_REMCSUM_RX        : 'IFLA_VXLAN_REMCSUM_RX',
+        IFLA_VXLAN_GBP               : 'IFLA_VXLAN_GBP',
+        IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL',
+        IFLA_VXLAN_COLLECT_METADATA  : 'IFLA_VXLAN_COLLECT_METADATA',
+        IFLA_VXLAN_REPLICATION_NODE  : 'IFLA_VXLAN_REPLICATION_NODE',
+        IFLA_VXLAN_REPLICATION_TYPE  : 'IFLA_VXLAN_REPLICATION_TYPE'
+    }
 
-        if self.net_byteorder:
-            attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER
+    # =========================================
+    # IFLA_INFO_DATA attributes for bonds
+    # =========================================
+    IFLA_BOND_UNSPEC                    = 0
+    IFLA_BOND_MODE                      = 1
+    IFLA_BOND_ACTIVE_SLAVE              = 2
+    IFLA_BOND_MIIMON                    = 3
+    IFLA_BOND_UPDELAY                   = 4
+    IFLA_BOND_DOWNDELAY                 = 5
+    IFLA_BOND_USE_CARRIER               = 6
+    IFLA_BOND_ARP_INTERVAL              = 7
+    IFLA_BOND_ARP_IP_TARGET             = 8
+    IFLA_BOND_ARP_VALIDATE              = 9
+    IFLA_BOND_ARP_ALL_TARGETS           = 10
+    IFLA_BOND_PRIMARY                   = 11
+    IFLA_BOND_PRIMARY_RESELECT          = 12
+    IFLA_BOND_FAIL_OVER_MAC             = 13
+    IFLA_BOND_XMIT_HASH_POLICY          = 14
+    IFLA_BOND_RESEND_IGMP               = 15
+    IFLA_BOND_NUM_PEER_NOTIF            = 16
+    IFLA_BOND_ALL_SLAVES_ACTIVE         = 17
+    IFLA_BOND_MIN_LINKS                 = 18
+    IFLA_BOND_LP_INTERVAL               = 19
+    IFLA_BOND_PACKETS_PER_SLAVE         = 20
+    IFLA_BOND_AD_LACP_RATE              = 21
+    IFLA_BOND_AD_SELECT                 = 22
+    IFLA_BOND_AD_INFO                   = 23
+    IFLA_BOND_AD_ACTOR_SYS_PRIO         = 24
+    IFLA_BOND_AD_USER_PORT_KEY          = 25
+    IFLA_BOND_AD_ACTOR_SYSTEM           = 26
+    IFLA_BOND_CL_START                  = 60
+    IFLA_BOND_AD_LACP_BYPASS            = IFLA_BOND_CL_START
 
-        raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value)
-        raw = self.pad(length, raw)
-        return raw
 
-    def decode_length_type(self, data):
-        """
-        The first two bytes of an attribute are the length, the next two bytes are the type
-        """
-        self.data = data
-        prev_atype = self.atype
-        (data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN])
-        self.length = int(data1)
-        self.atype = int(data2)
-        self.attr_end = padded_length(self.length)
+    ifla_bond_to_string = {
+        IFLA_BOND_UNSPEC                    : 'IFLA_BOND_UNSPEC',
+        IFLA_BOND_MODE                      : 'IFLA_BOND_MODE',
+        IFLA_BOND_ACTIVE_SLAVE              : 'IFLA_BOND_ACTIVE_SLAVE',
+        IFLA_BOND_MIIMON                    : 'IFLA_BOND_MIIMON',
+        IFLA_BOND_UPDELAY                   : 'IFLA_BOND_UPDELAY',
+        IFLA_BOND_DOWNDELAY                 : 'IFLA_BOND_DOWNDELAY',
+        IFLA_BOND_USE_CARRIER               : 'IFLA_BOND_USE_CARRIER',
+        IFLA_BOND_ARP_INTERVAL              : 'IFLA_BOND_ARP_INTERVAL',
+        IFLA_BOND_ARP_IP_TARGET             : 'IFLA_BOND_ARP_IP_TARGET',
+        IFLA_BOND_ARP_VALIDATE              : 'IFLA_BOND_ARP_VALIDATE',
+        IFLA_BOND_ARP_ALL_TARGETS           : 'IFLA_BOND_ARP_ALL_TARGETS',
+        IFLA_BOND_PRIMARY                   : 'IFLA_BOND_PRIMARY',
+        IFLA_BOND_PRIMARY_RESELECT          : 'IFLA_BOND_PRIMARY_RESELECT',
+        IFLA_BOND_FAIL_OVER_MAC             : 'IFLA_BOND_FAIL_OVER_MAC',
+        IFLA_BOND_XMIT_HASH_POLICY          : 'IFLA_BOND_XMIT_HASH_POLICY',
+        IFLA_BOND_RESEND_IGMP               : 'IFLA_BOND_RESEND_IGMP',
+        IFLA_BOND_NUM_PEER_NOTIF            : 'IFLA_BOND_NUM_PEER_NOTIF',
+        IFLA_BOND_ALL_SLAVES_ACTIVE         : 'IFLA_BOND_ALL_SLAVES_ACTIVE',
+        IFLA_BOND_MIN_LINKS                 : 'IFLA_BOND_MIN_LINKS',
+        IFLA_BOND_LP_INTERVAL               : 'IFLA_BOND_LP_INTERVAL',
+        IFLA_BOND_PACKETS_PER_SLAVE         : 'IFLA_BOND_PACKETS_PER_SLAVE',
+        IFLA_BOND_AD_LACP_RATE              : 'IFLA_BOND_AD_LACP_RATE',
+        IFLA_BOND_AD_SELECT                 : 'IFLA_BOND_AD_SELECT',
+        IFLA_BOND_AD_INFO                   : 'IFLA_BOND_AD_INFO',
+        IFLA_BOND_AD_ACTOR_SYS_PRIO         : 'IFLA_BOND_AD_ACTOR_SYS_PRIO',
+        IFLA_BOND_AD_USER_PORT_KEY          : 'IFLA_BOND_AD_USER_PORT_KEY',
+        IFLA_BOND_AD_ACTOR_SYSTEM           : 'IFLA_BOND_AD_ACTOR_SYSTEM',
+        IFLA_BOND_CL_START                  : 'IFLA_BOND_CL_START',
+        IFLA_BOND_AD_LACP_BYPASS            : 'IFLA_BOND_AD_LACP_BYPASS'
+    }
 
-        self.nested = True if self.atype & NLA_F_NESTED else False
-        self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False
-        self.atype = self.atype & NLA_TYPE_MASK
-
-        # Should never happen
-        assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype)
+    IFLA_BOND_AD_INFO_UNSPEC            = 0
+    IFLA_BOND_AD_INFO_AGGREGATOR        = 1
+    IFLA_BOND_AD_INFO_NUM_PORTS         = 2
+    IFLA_BOND_AD_INFO_ACTOR_KEY         = 3
+    IFLA_BOND_AD_INFO_PARTNER_KEY       = 4
+    IFLA_BOND_AD_INFO_PARTNER_MAC       = 5
 
-    def dump_first_line(self, dump_buffer, line_number, color):
-        """
-        Add the "Length....Type..." line to the dump buffer
-        """
-        if self.attr_end == self.length:
-            padded_to = ', '
-        else:
-            padded_to = ' padded to %d, ' % self.attr_end
+    ifla_bond_ad_to_string = {
+        IFLA_BOND_AD_INFO_UNSPEC            : 'IFLA_BOND_AD_INFO_UNSPEC',
+        IFLA_BOND_AD_INFO_AGGREGATOR        : 'IFLA_BOND_AD_INFO_AGGREGATOR',
+        IFLA_BOND_AD_INFO_NUM_PORTS         : 'IFLA_BOND_AD_INFO_NUM_PORTS',
+        IFLA_BOND_AD_INFO_ACTOR_KEY         : 'IFLA_BOND_AD_INFO_ACTOR_KEY',
+        IFLA_BOND_AD_INFO_PARTNER_KEY       : 'IFLA_BOND_AD_INFO_PARTNER_KEY',
+        IFLA_BOND_AD_INFO_PARTNER_MAC       : 'IFLA_BOND_AD_INFO_PARTNER_MAC'
+    }
 
-        extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \
-                 (zfilled_hex(self.length, 4), self.length,
-                  padded_to,
-                  zfilled_hex(self.atype, 4),
-                  " (NLA_F_NESTED set)" if self.nested else "",
-                  " (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "",
-                  self.atype,
-                  self)
+    ifla_bond_mode_tbl = {
+        'balance-rr': 0,
+        'active-backup': 1,
+        'balance-xor': 2,
+        'broadcast': 3,
+        '802.3ad': 4,
+        'balance-tlb': 5,
+        'balance-alb': 6,
+        '0': 0,
+        '1': 1,
+        '2': 2,
+        '3': 3,
+        '4': 4,
+        '5': 5,
+        '6': 6,
+        0: 0,
+        1: 1,
+        2: 2,
+        3: 3,
+        4: 4,
+        5: 5,
+        6: 6
+    }
 
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra))
-        return line_number + 1
+    ifla_bond_mode_pretty_tbl = {
+        0: 'balance-rr',
+        1: 'active-backup',
+        2: 'balance-xor',
+        3: 'broadcast',
+        4: '802.3ad',
+        5: 'balance-tlb',
+        6: 'balance-alb'
+    }
 
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
+    ifla_bond_xmit_hash_policy_tbl = {
+        'layer2': 0,
+        'layer3+4': 1,
+        'layer2+3': 2,
+        'encap2+3': 3,
+        'encap3+4': 4,
+        '0': 0,
+        '1': 1,
+        '2': 2,
+        '3': 3,
+        '4': 4,
+        0: 0,
+        1: 1,
+        2: 2,
+        3: 3,
+        4: 4
+    }
 
-        for x in xrange(1, self.attr_end/4):
-            start = x * 4
-            end = start + 4
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], ''))
-            line_number += 1
+    ifla_bond_xmit_hash_policy_pretty_tbl = {
+        0: 'layer2',
+        1: 'layer3+4',
+        2: 'layer2+3',
+        3: 'encap2+3',
+        4: 'encap3+4',
+    }
 
-        return line_number
+    # =========================================
+    # IFLA_INFO_SLAVE_DATA attributes for bonds
+    # =========================================
+    IFLA_BOND_SLAVE_UNSPEC                      = 0
+    IFLA_BOND_SLAVE_STATE                       = 1
+    IFLA_BOND_SLAVE_MII_STATUS                  = 2
+    IFLA_BOND_SLAVE_LINK_FAILURE_COUNT          = 3
+    IFLA_BOND_SLAVE_PERM_HWADDR                 = 4
+    IFLA_BOND_SLAVE_QUEUE_ID                    = 5
+    IFLA_BOND_SLAVE_AD_AGGREGATOR_ID            = 6
+    IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE    = 7
+    IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE  = 8
+    IFLA_BOND_SLAVE_CL_START                    = 50
+    IFLA_BOND_SLAVE_AD_RX_BYPASS                = IFLA_BOND_SLAVE_CL_START
 
-    def get_pretty_value(self, obj=None):
-        if obj and callable(obj):
-            return obj(self.value)
-        return self.value
+    ifla_bond_slave_to_string = {
+        IFLA_BOND_SLAVE_UNSPEC                      : 'IFLA_BOND_SLAVE_UNSPEC',
+        IFLA_BOND_SLAVE_STATE                       : 'IFLA_BOND_SLAVE_STATE',
+        IFLA_BOND_SLAVE_MII_STATUS                  : 'IFLA_BOND_SLAVE_MII_STATUS',
+        IFLA_BOND_SLAVE_LINK_FAILURE_COUNT          : 'IFLA_BOND_SLAVE_LINK_FAILURE_COUNT',
+        IFLA_BOND_SLAVE_PERM_HWADDR                 : 'IFLA_BOND_SLAVE_PERM_HWADDR',
+        IFLA_BOND_SLAVE_QUEUE_ID                    : 'IFLA_BOND_SLAVE_QUEUE_ID',
+        IFLA_BOND_SLAVE_AD_AGGREGATOR_ID            : 'IFLA_BOND_SLAVE_AD_AGGREGATOR_ID',
+        IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE    : 'IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE',
+        IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE  : 'IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE',
+        IFLA_BOND_SLAVE_CL_START                    : 'IFLA_BOND_SLAVE_CL_START',
+        IFLA_BOND_SLAVE_AD_RX_BYPASS                : 'IFLA_BOND_SLAVE_AD_RX_BYPASS'
+    }
 
+    # =========================================
+    # IFLA_PROTINFO attributes for bridge ports
+    # =========================================
+    IFLA_BRPORT_UNSPEC              = 0
+    IFLA_BRPORT_STATE               = 1
+    IFLA_BRPORT_PRIORITY            = 2
+    IFLA_BRPORT_COST                = 3
+    IFLA_BRPORT_MODE                = 4
+    IFLA_BRPORT_GUARD               = 5
+    IFLA_BRPORT_PROTECT             = 6
+    IFLA_BRPORT_FAST_LEAVE          = 7
+    IFLA_BRPORT_LEARNING            = 8
+    IFLA_BRPORT_UNICAST_FLOOD       = 9
+    IFLA_BRPORT_PROXYARP            = 10
+    IFLA_BRPORT_LEARNING_SYNC       = 11
+    IFLA_BRPORT_PROXYARP_WIFI       = 12
+    IFLA_BRPORT_ROOT_ID             = 13
+    IFLA_BRPORT_BRIDGE_ID           = 14
+    IFLA_BRPORT_DESIGNATED_PORT     = 15
+    IFLA_BRPORT_DESIGNATED_COST     = 16
+    IFLA_BRPORT_ID                  = 17
+    IFLA_BRPORT_NO                  = 18
+    IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19
+    IFLA_BRPORT_CONFIG_PENDING      = 20
+    IFLA_BRPORT_MESSAGE_AGE_TIMER   = 21
+    IFLA_BRPORT_FORWARD_DELAY_TIMER = 22
+    IFLA_BRPORT_HOLD_TIMER          = 23
+    IFLA_BRPORT_FLUSH               = 24
+    IFLA_BRPORT_MULTICAST_ROUTER    = 25
+    IFLA_BRPORT_PAD                 = 26
+    IFLA_BRPORT_MCAST_FLOOD         = 27
+    IFLA_BRPORT_MCAST_TO_UCAST      = 28
+    IFLA_BRPORT_VLAN_TUNNEL         = 29
+    IFLA_BRPORT_BCAST_FLOOD         = 30
+    IFLA_BRPORT_GROUP_FWD_MASK      = 31
+    IFLA_BRPORT_NEIGH_SUPPRESS      = 32
+    IFLA_BRPORT_ISOLATED            = 33
+    IFLA_BRPORT_BACKUP_PORT         = 34
 
-class AttributeFourByteList(Attribute):
+    IFLA_BRPORT_PEER_LINK           = 60
+    IFLA_BRPORT_DUAL_LINK           = 61
+    IFLA_BRPORT_GROUP_FWD_MASKHI    = 62
 
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
+    ifla_brport_to_string = {
+        IFLA_BRPORT_UNSPEC              : 'IFLA_BRPORT_UNSPEC',
+        IFLA_BRPORT_STATE               : 'IFLA_BRPORT_STATE',
+        IFLA_BRPORT_PRIORITY            : 'IFLA_BRPORT_PRIORITY',
+        IFLA_BRPORT_COST                : 'IFLA_BRPORT_COST',
+        IFLA_BRPORT_MODE                : 'IFLA_BRPORT_MODE',
+        IFLA_BRPORT_GUARD               : 'IFLA_BRPORT_GUARD',
+        IFLA_BRPORT_PROTECT             : 'IFLA_BRPORT_PROTECT',
+        IFLA_BRPORT_FAST_LEAVE          : 'IFLA_BRPORT_FAST_LEAVE',
+        IFLA_BRPORT_LEARNING            : 'IFLA_BRPORT_LEARNING',
+        IFLA_BRPORT_UNICAST_FLOOD       : 'IFLA_BRPORT_UNICAST_FLOOD',
+        IFLA_BRPORT_PROXYARP            : 'IFLA_BRPORT_PROXYARP',
+        IFLA_BRPORT_LEARNING_SYNC       : 'IFLA_BRPORT_LEARNING_SYNC',
+        IFLA_BRPORT_PROXYARP_WIFI       : 'IFLA_BRPORT_PROXYARP_WIFI',
+        IFLA_BRPORT_ROOT_ID             : 'IFLA_BRPORT_ROOT_ID',
+        IFLA_BRPORT_BRIDGE_ID           : 'IFLA_BRPORT_BRIDGE_ID',
+        IFLA_BRPORT_DESIGNATED_PORT     : 'IFLA_BRPORT_DESIGNATED_PORT',
+        IFLA_BRPORT_DESIGNATED_COST     : 'IFLA_BRPORT_DESIGNATED_COST',
+        IFLA_BRPORT_ID                  : 'IFLA_BRPORT_ID',
+        IFLA_BRPORT_NO                  : 'IFLA_BRPORT_NO',
+        IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK',
+        IFLA_BRPORT_CONFIG_PENDING      : 'IFLA_BRPORT_CONFIG_PENDING',
+        IFLA_BRPORT_MESSAGE_AGE_TIMER   : 'IFLA_BRPORT_MESSAGE_AGE_TIMER',
+        IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER',
+        IFLA_BRPORT_HOLD_TIMER          : 'IFLA_BRPORT_HOLD_TIMER',
+        IFLA_BRPORT_FLUSH               : 'IFLA_BRPORT_FLUSH',
+        IFLA_BRPORT_MULTICAST_ROUTER    : 'IFLA_BRPORT_MULTICAST_ROUTER',
+        IFLA_BRPORT_PAD                 : 'IFLA_BRPORT_PAD',
+        IFLA_BRPORT_MCAST_FLOOD         : 'IFLA_BRPORT_MCAST_FLOOD',
+        IFLA_BRPORT_MCAST_TO_UCAST      : 'IFLA_BRPORT_MCAST_TO_UCAST',
+        IFLA_BRPORT_VLAN_TUNNEL         : 'IFLA_BRPORT_VLAN_TUNNEL',
+        IFLA_BRPORT_BCAST_FLOOD         : 'IFLA_BRPORT_BCAST_FLOOD',
+        IFLA_BRPORT_GROUP_FWD_MASK      : 'IFLA_BRPORT_GROUP_FWD_MASK',
+        IFLA_BRPORT_NEIGH_SUPPRESS      : 'IFLA_BRPORT_NEIGH_SUPPRESS',
+        IFLA_BRPORT_ISOLATED            : 'IFLA_BRPORT_ISOLATED',
+        IFLA_BRPORT_BACKUP_PORT         : 'IFLA_BRPORT_BACKUP_PORT',
+        IFLA_BRPORT_PEER_LINK           : 'IFLA_BRPORT_PEER_LINK',
+        IFLA_BRPORT_DUAL_LINK           : 'IFLA_BRPORT_DUAL_LINK',
+        IFLA_BRPORT_GROUP_FWD_MASKHI    : 'IFLA_BRPORT_GROUP_FWD_MASKHI'
+    }
 
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        wordcount = (self.attr_end - 4)/4
-        self.PACK = '=%dL' % wordcount
-        self.LEN = calcsize(self.PACK)
+    IFLA_BR_UNSPEC                     =  0
+    IFLA_BR_FORWARD_DELAY              =  1
+    IFLA_BR_HELLO_TIME                 =  2
+    IFLA_BR_MAX_AGE                    =  3
+    IFLA_BR_AGEING_TIME                =  4
+    IFLA_BR_STP_STATE                  =  5
+    IFLA_BR_PRIORITY                   =  6
+    IFLA_BR_VLAN_FILTERING             =  7
+    IFLA_BR_VLAN_PROTOCOL              =  8
+    IFLA_BR_GROUP_FWD_MASK             =  9
+    IFLA_BR_ROOT_ID                    = 10
+    IFLA_BR_BRIDGE_ID                  = 11
+    IFLA_BR_ROOT_PORT                  = 12
+    IFLA_BR_ROOT_PATH_COST             = 13
+    IFLA_BR_TOPOLOGY_CHANGE            = 14
+    IFLA_BR_TOPOLOGY_CHANGE_DETECTED   = 15
+    IFLA_BR_HELLO_TIMER                = 16
+    IFLA_BR_TCN_TIMER                  = 17
+    IFLA_BR_TOPOLOGY_CHANGE_TIMER      = 18
+    IFLA_BR_GC_TIMER                   = 19
+    IFLA_BR_GROUP_ADDR                 = 20
+    IFLA_BR_FDB_FLUSH                  = 21
+    IFLA_BR_MCAST_ROUTER               = 22
+    IFLA_BR_MCAST_SNOOPING             = 23
+    IFLA_BR_MCAST_QUERY_USE_IFADDR     = 24
+    IFLA_BR_MCAST_QUERIER              = 25
+    IFLA_BR_MCAST_HASH_ELASTICITY      = 26
+    IFLA_BR_MCAST_HASH_MAX             = 27
+    IFLA_BR_MCAST_LAST_MEMBER_CNT      = 28
+    IFLA_BR_MCAST_STARTUP_QUERY_CNT    = 29
+    IFLA_BR_MCAST_LAST_MEMBER_INTVL    = 30
+    IFLA_BR_MCAST_MEMBERSHIP_INTVL     = 31
+    IFLA_BR_MCAST_QUERIER_INTVL        = 32
+    IFLA_BR_MCAST_QUERY_INTVL          = 33
+    IFLA_BR_MCAST_QUERY_RESPONSE_INTVL = 34
+    IFLA_BR_MCAST_STARTUP_QUERY_INTVL  = 35
+    IFLA_BR_NF_CALL_IPTABLES           = 36
+    IFLA_BR_NF_CALL_IP6TABLES          = 37
+    IFLA_BR_NF_CALL_ARPTABLES          = 38
+    IFLA_BR_VLAN_DEFAULT_PVID          = 39
+    IFLA_BR_PAD                        = 40
+    IFLA_BR_VLAN_STATS_ENABLED         = 41
+    IFLA_BR_MCAST_STATS_ENABLED        = 42
+    IFLA_BR_MCAST_IGMP_VERSION         = 43
+    IFLA_BR_MCAST_MLD_VERSION          = 44
 
-        try:
-            self.value = unpack(self.PACK, self.data[4:])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
+    ifla_br_to_string = {
+        IFLA_BR_UNSPEC                     : 'IFLA_BR_UNSPEC',
+        IFLA_BR_FORWARD_DELAY              : 'IFLA_BR_FORWARD_DELAY',
+        IFLA_BR_HELLO_TIME                 : 'IFLA_BR_HELLO_TIME',
+        IFLA_BR_MAX_AGE                    : 'IFLA_BR_MAX_AGE',
+        IFLA_BR_AGEING_TIME                : 'IFLA_BR_AGEING_TIME',
+        IFLA_BR_STP_STATE                  : 'IFLA_BR_STP_STATE',
+        IFLA_BR_PRIORITY                   : 'IFLA_BR_PRIORITY',
+        IFLA_BR_VLAN_FILTERING             : 'IFLA_BR_VLAN_FILTERING',
+        IFLA_BR_VLAN_PROTOCOL              : 'IFLA_BR_VLAN_PROTOCOL',
+        IFLA_BR_GROUP_FWD_MASK             : 'IFLA_BR_GROUP_FWD_MASK',
+        IFLA_BR_ROOT_ID                    : 'IFLA_BR_ROOT_ID',
+        IFLA_BR_BRIDGE_ID                  : 'IFLA_BR_BRIDGE_ID',
+        IFLA_BR_ROOT_PORT                  : 'IFLA_BR_ROOT_PORT',
+        IFLA_BR_ROOT_PATH_COST             : 'IFLA_BR_ROOT_PATH_COST',
+        IFLA_BR_TOPOLOGY_CHANGE            : 'IFLA_BR_TOPOLOGY_CHANGE',
+        IFLA_BR_TOPOLOGY_CHANGE_DETECTED   : 'IFLA_BR_TOPOLOGY_CHANGE_DETECTED',
+        IFLA_BR_HELLO_TIMER                : 'IFLA_BR_HELLO_TIMER',
+        IFLA_BR_TCN_TIMER                  : 'IFLA_BR_TCN_TIMER',
+        IFLA_BR_TOPOLOGY_CHANGE_TIMER      : 'IFLA_BR_TOPOLOGY_CHANGE_TIMER',
+        IFLA_BR_GC_TIMER                   : 'IFLA_BR_GC_TIMER',
+        IFLA_BR_GROUP_ADDR                 : 'IFLA_BR_GROUP_ADDR',
+        IFLA_BR_FDB_FLUSH                  : 'IFLA_BR_FDB_FLUSH',
+        IFLA_BR_MCAST_ROUTER               : 'IFLA_BR_MCAST_ROUTER',
+        IFLA_BR_MCAST_SNOOPING             : 'IFLA_BR_MCAST_SNOOPING',
+        IFLA_BR_MCAST_QUERY_USE_IFADDR     : 'IFLA_BR_MCAST_QUERY_USE_IFADDR',
+        IFLA_BR_MCAST_QUERIER              : 'IFLA_BR_MCAST_QUERIER',
+        IFLA_BR_MCAST_HASH_ELASTICITY      : 'IFLA_BR_MCAST_HASH_ELASTICITY',
+        IFLA_BR_MCAST_HASH_MAX             : 'IFLA_BR_MCAST_HASH_MAX',
+        IFLA_BR_MCAST_LAST_MEMBER_CNT      : 'IFLA_BR_MCAST_LAST_MEMBER_CNT',
+        IFLA_BR_MCAST_STARTUP_QUERY_CNT    : 'IFLA_BR_MCAST_STARTUP_QUERY_CNT',
+        IFLA_BR_MCAST_LAST_MEMBER_INTVL    : 'IFLA_BR_MCAST_LAST_MEMBER_INTVL',
+        IFLA_BR_MCAST_MEMBERSHIP_INTVL     : 'IFLA_BR_MCAST_MEMBERSHIP_INTVL',
+        IFLA_BR_MCAST_QUERIER_INTVL        : 'IFLA_BR_MCAST_QUERIER_INTVL',
+        IFLA_BR_MCAST_QUERY_INTVL          : 'IFLA_BR_MCAST_QUERY_INTVL',
+        IFLA_BR_MCAST_QUERY_RESPONSE_INTVL : 'IFLA_BR_MCAST_QUERY_RESPONSE_INTVL',
+        IFLA_BR_MCAST_STARTUP_QUERY_INTVL  : 'IFLA_BR_MCAST_STARTUP_QUERY_INTVL',
+        IFLA_BR_NF_CALL_IPTABLES           : 'IFLA_BR_NF_CALL_IPTABLES',
+        IFLA_BR_NF_CALL_IP6TABLES          : 'IFLA_BR_NF_CALL_IP6TABLES',
+        IFLA_BR_NF_CALL_ARPTABLES          : 'IFLA_BR_NF_CALL_ARPTABLES',
+        IFLA_BR_VLAN_DEFAULT_PVID          : 'IFLA_BR_VLAN_DEFAULT_PVID',
+        IFLA_BR_PAD                        : 'IFLA_BR_PAD',
+        IFLA_BR_VLAN_STATS_ENABLED         : 'IFLA_BR_VLAN_STATS_ENABLED',
+        IFLA_BR_MCAST_STATS_ENABLED        : 'IFLA_BR_MCAST_STATS_ENABLED',
+        IFLA_BR_MCAST_IGMP_VERSION         : 'IFLA_BR_MCAST_IGMP_VERSION',
+        IFLA_BR_MCAST_MLD_VERSION          : 'IFLA_BR_MCAST_MLD_VERSION'
+    }
 
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        idx = 1
-        for val in self.value:
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[4*idx:4*(idx+1)], val))
-            line_number += 1
-            idx += 1
-        return line_number
+    # =========================================
+    # IFLA_INFO_DATA attributes for vrfs
+    # =========================================
+    IFLA_VRF_UNSPEC                         = 0
+    IFLA_VRF_TABLE                          = 1
 
+    ifla_vrf_to_string = {
+        IFLA_VRF_UNSPEC                     : 'IFLA_VRF_UNSPEC',
+        IFLA_VRF_TABLE                      : 'IFLA_VRF_TABLE'
+    }
 
-class AttributeFourByteValue(Attribute):
+    # ================================================================
+    # IFLA_INFO_DATA attributes for (ip6)gre, (ip6)gretap, (ip6)erspan
+    # ================================================================
+    IFLA_GRE_UNSPEC             = 0
+    IFLA_GRE_LINK               = 1
+    IFLA_GRE_IFLAGS             = 2
+    IFLA_GRE_OFLAGS             = 3
+    IFLA_GRE_IKEY               = 4
+    IFLA_GRE_OKEY               = 5
+    IFLA_GRE_LOCAL              = 6
+    IFLA_GRE_REMOTE             = 7
+    IFLA_GRE_TTL                = 8
+    IFLA_GRE_TOS                = 9
+    IFLA_GRE_PMTUDISC           = 10
+    IFLA_GRE_ENCAP_LIMIT        = 11
+    IFLA_GRE_FLOWINFO           = 12
+    IFLA_GRE_FLAGS              = 13
+    IFLA_GRE_ENCAP_TYPE         = 14
+    IFLA_GRE_ENCAP_FLAGS        = 15
+    IFLA_GRE_ENCAP_SPORT        = 16
+    IFLA_GRE_ENCAP_DPORT        = 17
+    IFLA_GRE_COLLECT_METADATA   = 18
+    IFLA_GRE_IGNORE_DF          = 19
+    IFLA_GRE_FWMARK             = 20
+    IFLA_GRE_ERSPAN_INDEX       = 21
+    IFLA_GRE_ERSPAN_VER         = 22
+    IFLA_GRE_ERSPAN_DIR         = 23
+    IFLA_GRE_ERSPAN_HWID        = 24
 
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = '=L'
-        self.LEN = calcsize(self.PACK)
+    ifla_gre_to_string = {
+        IFLA_GRE_UNSPEC             : "IFLA_GRE_UNSPEC",
+        IFLA_GRE_LINK               : "IFLA_GRE_LINK",
+        IFLA_GRE_IFLAGS             : "IFLA_GRE_IFLAGS",
+        IFLA_GRE_OFLAGS             : "IFLA_GRE_OFLAGS",
+        IFLA_GRE_IKEY               : "IFLA_GRE_IKEY",
+        IFLA_GRE_OKEY               : "IFLA_GRE_OKEY",
+        IFLA_GRE_LOCAL              : "IFLA_GRE_LOCAL",
+        IFLA_GRE_REMOTE             : "IFLA_GRE_REMOTE",
+        IFLA_GRE_TTL                : "IFLA_GRE_TTL",
+        IFLA_GRE_TOS                : "IFLA_GRE_TOS",
+        IFLA_GRE_PMTUDISC           : "IFLA_GRE_PMTUDISC",
+        IFLA_GRE_ENCAP_LIMIT        : "IFLA_GRE_ENCAP_LIMIT",
+        IFLA_GRE_FLOWINFO           : "IFLA_GRE_FLOWINFO",
+        IFLA_GRE_FLAGS              : "IFLA_GRE_FLAGS",
+        IFLA_GRE_ENCAP_TYPE         : "IFLA_GRE_ENCAP_TYPE",
+        IFLA_GRE_ENCAP_FLAGS        : "IFLA_GRE_ENCAP_FLAGS",
+        IFLA_GRE_ENCAP_SPORT        : "IFLA_GRE_ENCAP_SPORT",
+        IFLA_GRE_ENCAP_DPORT        : "IFLA_GRE_ENCAP_DPORT",
+        IFLA_GRE_COLLECT_METADATA   : "IFLA_GRE_COLLECT_METADATA",
+        IFLA_GRE_IGNORE_DF          : "IFLA_GRE_IGNORE_DF",
+        IFLA_GRE_FWMARK             : "IFLA_GRE_FWMARK",
+        IFLA_GRE_ERSPAN_INDEX       : "IFLA_GRE_ERSPAN_INDEX",
+        IFLA_GRE_ERSPAN_VER         : "IFLA_GRE_ERSPAN_VER",
+        IFLA_GRE_ERSPAN_DIR         : "IFLA_GRE_ERSPAN_DIR",
+        IFLA_GRE_ERSPAN_HWID        : "IFLA_GRE_ERSPAN_HWID",
+    }
 
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+    # ===============================================
+    # IFLA_INFO_DATA attributes for ipip, sit, ip6tnl
+    # ===============================================
+    IFLA_IPTUN_UNSPEC                       = 0
+    IFLA_IPTUN_LINK                         = 1
+    IFLA_IPTUN_LOCAL                        = 2
+    IFLA_IPTUN_REMOTE                       = 3
+    IFLA_IPTUN_TTL                          = 4
+    IFLA_IPTUN_TOS                          = 5
+    IFLA_IPTUN_ENCAP_LIMIT                  = 6
+    IFLA_IPTUN_FLOWINFO                     = 7
+    IFLA_IPTUN_FLAGS                        = 8
+    IFLA_IPTUN_PROTO                        = 9
+    IFLA_IPTUN_PMTUDISC                     = 10
+    IFLA_IPTUN_6RD_PREFIX                   = 11
+    IFLA_IPTUN_6RD_RELAY_PREFIX             = 12
+    IFLA_IPTUN_6RD_PREFIXLEN                = 13
+    IFLA_IPTUN_6RD_RELAY_PREFIXLEN          = 14
+    IFLA_IPTUN_ENCAP_TYPE                   = 15
+    IFLA_IPTUN_ENCAP_FLAGS                  = 16
+    IFLA_IPTUN_ENCAP_SPORT                  = 17
+    IFLA_IPTUN_ENCAP_DPORT                  = 18
+    IFLA_IPTUN_COLLECT_METADATA             = 19
+    IFLA_IPTUN_FWMARK                       = 20
 
-        try:
-            self.value = int(unpack(self.PACK, self.data[4:])[0])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
+    ifla_iptun_to_string = {
+        IFLA_IPTUN_UNSPEC                   : "IFLA_IPTUN_UNSPEC",
+        IFLA_IPTUN_LINK                     : "IFLA_IPTUN_LINK",
+        IFLA_IPTUN_LOCAL                    : "IFLA_IPTUN_LOCAL",
+        IFLA_IPTUN_REMOTE                   : "IFLA_IPTUN_REMOTE",
+        IFLA_IPTUN_TTL                      : "IFLA_IPTUN_TTL",
+        IFLA_IPTUN_TOS                      : "IFLA_IPTUN_TOS",
+        IFLA_IPTUN_ENCAP_LIMIT              : "IFLA_IPTUN_ENCAP_LIMIT",
+        IFLA_IPTUN_FLOWINFO                 : "IFLA_IPTUN_FLOWINFO",
+        IFLA_IPTUN_FLAGS                    : "IFLA_IPTUN_FLAGS",
+        IFLA_IPTUN_PROTO                    : "IFLA_IPTUN_PROTO",
+        IFLA_IPTUN_PMTUDISC                 : "IFLA_IPTUN_PMTUDISC",
+        IFLA_IPTUN_6RD_PREFIX               : "IFLA_IPTUN_6RD_PREFIX",
+        IFLA_IPTUN_6RD_RELAY_PREFIX         : "IFLA_IPTUN_6RD_RELAY_PREFIX",
+        IFLA_IPTUN_6RD_PREFIXLEN            : "IFLA_IPTUN_6RD_PREFIXLEN",
+        IFLA_IPTUN_6RD_RELAY_PREFIXLEN      : "IFLA_IPTUN_6RD_RELAY_PREFIXLEN",
+        IFLA_IPTUN_ENCAP_TYPE               : "IFLA_IPTUN_ENCAP_TYPE",
+        IFLA_IPTUN_ENCAP_FLAGS              : "IFLA_IPTUN_ENCAP_FLAGS",
+        IFLA_IPTUN_ENCAP_SPORT              : "IFLA_IPTUN_ENCAP_SPORT",
+        IFLA_IPTUN_ENCAP_DPORT              : "IFLA_IPTUN_ENCAP_DPORT",
+        IFLA_IPTUN_COLLECT_METADATA         : "IFLA_IPTUN_COLLECT_METADATA",
+        IFLA_IPTUN_FWMARK                   : "IFLA_IPTUN_FWMARK",
+    }
 
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-        return line_number + 1
+    # =========================================
+    # IFLA_INFO_DATA attributes for vti, vti6
+    # =========================================
+    IFLA_VTI_UNSPEC     = 0
+    IFLA_VTI_LINK       = 1
+    IFLA_VTI_IKEY       = 2
+    IFLA_VTI_OKEY       = 3
+    IFLA_VTI_LOCAL      = 4
+    IFLA_VTI_REMOTE     = 5
+    IFLA_VTI_FWMARK     = 6
 
+    ifla_vti_to_string = {
+        IFLA_VTI_UNSPEC     : "IFLA_VTI_UNSPEC",
+        IFLA_VTI_LINK       : "IFLA_VTI_LINK",
+        IFLA_VTI_IKEY       : "IFLA_VTI_IKEY",
+        IFLA_VTI_OKEY       : "IFLA_VTI_OKEY",
+        IFLA_VTI_LOCAL      : "IFLA_VTI_LOCAL",
+        IFLA_VTI_REMOTE     : "IFLA_VTI_REMOTE",
+        IFLA_VTI_FWMARK     : "IFLA_VTI_FWMARK",
+    }
 
-class AttributeTwoByteValue(Attribute):
 
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = '=Hxx'
-        self.LEN = calcsize(self.PACK)
+class Attribute(object):
 
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+    def __init__(self, atype, string, logger):
+        self.atype = atype
+        self.string = string
+        self.HEADER_PACK = '=HH'
+        self.HEADER_LEN = calcsize(self.HEADER_PACK)
+        self.PACK = None
+        self.LEN = None
+        self.raw = None  # raw value (i.e. int for mac address)
+        self.value = None
+        self.nested = False
+        self.net_byteorder = False
+        self.log = logger
 
-        try:
-            self.value = int(unpack(self.PACK, self.data[4:8])[0])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:6])))
-            raise
+    def __str__(self):
+        return self.string
 
-    def encode(self):
-        length = self.HEADER_LEN + self.LEN
-        raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, self.value)
-        raw = self.pad(length, raw)
-        return raw
+    def set_value(self, value):
+        self.value = value
 
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-        return line_number + 1
+    def set_nested(self, nested):
+        self.nested = nested
 
+    def set_net_byteorder(self, net_byteorder):
+        self.net_byteorder = net_byteorder
 
-class AttributeString(Attribute):
+    @staticmethod
+    def pad_bytes_needed(length):
+        """
+        Return the number of bytes that should be added to align on a 4-byte boundry
+        """
+        remainder = length % 4
 
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = None
-        self.LEN = None
+        if remainder:
+            return 4 - remainder
+
+        return 0
+
+    def pad(self, length, raw):
+        pad = self.pad_bytes_needed(length)
+
+        if pad:
+            raw += '\0' * pad
+
+        return raw
+
+    def encode(self):
+
+        if not self.LEN:
+            raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric')
+
+        length = self.HEADER_LEN + self.LEN
+        attr_type_with_flags = self.atype
+
+        if self.nested:
+            attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED
+
+        if self.net_byteorder:
+            attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER
+
+        raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value)
+        raw = self.pad(length, raw)
+        return raw
+
+    def decode_length_type(self, data):
+        """
+        The first two bytes of an attribute are the length, the next two bytes are the type
+        """
+        self.data = data
+        prev_atype = self.atype
+        (data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN])
+        self.length = int(data1)
+        self.atype = int(data2)
+        self.attr_end = padded_length(self.length)
+
+        self.nested = True if self.atype & NLA_F_NESTED else False
+        self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False
+        self.atype = self.atype & NLA_TYPE_MASK
+
+        # Should never happen
+        assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype)
+
+    def dump_first_line(self, dump_buffer, line_number, color):
+        """
+        Add the "Length....Type..." line to the dump buffer
+        """
+        if self.attr_end == self.length:
+            padded_to = ', '
+        else:
+            padded_to = ' padded to %d, ' % self.attr_end
+
+        extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \
+                 (zfilled_hex(self.length, 4), self.length,
+                  padded_to,
+                  zfilled_hex(self.atype, 4),
+                  " (NLA_F_NESTED set)" if self.nested else "",
+                  " (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "",
+                  self.atype,
+                  self)
+
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra))
+        return line_number + 1
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+
+        for x in xrange(1, self.attr_end/4):
+            start = x * 4
+            end = start + 4
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], ''))
+            line_number += 1
+
+        return line_number
+
+    def get_pretty_value(self, obj=None):
+        if obj and callable(obj):
+            return obj(self.value)
+        return self.value
+
+    @staticmethod
+    def decode_one_byte_attribute(data, _=None):
+        return unpack("=B", data[4])[0]
+
+    @staticmethod
+    def decode_two_bytes_attribute(data, _=None):
+        return unpack("=H", data[4:6])[0]
+
+    @staticmethod
+    def decode_two_bytes_network_byte_order_attribute(data, _=None):
+        # The form '!' is available for those poor souls who claim they can't
+        # remember whether network byte order is big-endian or little-endian.
+        return unpack("!H", data[4:6])[0]
+
+    @staticmethod
+    def decode_four_bytes_attribute(data, _=None):
+        return unpack("=L", data[4:8])[0]
+
+    @staticmethod
+    def decode_eight_bytes_attribute(data, _=None):
+        return unpack("=Q", data[4:12])[0]
+
+    @staticmethod
+    def decode_mac_address_attribute(data, _=None):
+        (data1, data2) = unpack(">LHxx", data[4:12])
+        return mac_int_to_str(data1 << 16 | data2)
+
+    @staticmethod
+    def decode_ipv4_address_attribute(data, _=None):
+        return IPv4Address(unpack(">L", data[4:8])[0])
+
+    @staticmethod
+    def decode_ipv6_address_attribute(data, _=None):
+        (data1, data2) = unpack(">QQ", data[4:20])
+        return IPv6Address(data1 << 64 | data2)
+
+    @staticmethod
+    def decode_bond_ad_info_attribute(data, info_data_end):
+        ifla_bond_ad_info = {}
+        ad_attr_data = data[4:info_data_end]
+
+        while ad_attr_data:
+            (ad_data_length, ad_data_type) = unpack("=HH", ad_attr_data[:4])
+            ad_data_end = padded_length(ad_data_length)
+
+            if ad_data_type == Link.IFLA_BOND_AD_INFO_PARTNER_MAC:
+                (data1, data2) = unpack(">LHxx", ad_attr_data[4:12])
+                ifla_bond_ad_info[ad_data_type] = mac_int_to_str(data1 << 16 | data2)
+
+            ad_attr_data = ad_attr_data[ad_data_end:]
+
+        return ifla_bond_ad_info
+
+    @staticmethod
+    def decode_vlan_protocol_attribute(data, _=None):
+        return Link.ifla_vlan_protocol_dict.get(int("0x%s" % data[4:6].encode("hex"), base=16))
+
+    ############################################################################
+    # encode methods
+    ############################################################################
+
+    @staticmethod
+    def encode_one_byte_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(5)  # length
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("B")
+        sub_attr_payload.append(info_data_value)
+
+        # pad 3 bytes
+        sub_attr_pack_layout.extend("xxx")
+
+    @staticmethod
+    def encode_one_byte_attribute_from_string_boolean(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        return Attribute.encode_one_byte_attribute(
+            sub_attr_pack_layout, sub_attr_payload, info_data_type, value_to_bool_dict.get(info_data_value)
+        )
+
+    @staticmethod
+    def encode_bond_xmit_hash_policy_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        return Attribute.encode_one_byte_attribute(
+            sub_attr_pack_layout,
+            sub_attr_payload,
+            info_data_type,
+            Link.ifla_bond_xmit_hash_policy_tbl.get(info_data_value, 0),
+        )
+
+    @staticmethod
+    def encode_bond_mode_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        return Attribute.encode_one_byte_attribute(
+            sub_attr_pack_layout,
+            sub_attr_payload,
+            info_data_type,
+            Link.ifla_bond_mode_tbl.get(info_data_value, 0),
+        )
+
+    @staticmethod
+    def encode_two_bytes_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(6)  # length
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("H")
+        sub_attr_payload.append(info_data_value)
+
+        # pad 2 bytes
+        sub_attr_pack_layout.extend("xx")
+
+    @staticmethod
+    def encode_four_bytes_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(8)  # length
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("L")
+        sub_attr_payload.append(info_data_value)
+
+    @staticmethod
+    def encode_eight_bytes_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(12)  # length
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("Q")
+        sub_attr_payload.append(info_data_value)
+
+    @staticmethod
+    def encode_ipv4_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(8)  # length
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("L")
+
+        if info_data_value:
+            reorder = unpack("<L", IPv4Address(info_data_value).packed)[0]
+            sub_attr_payload.append(IPv4Address(reorder))
+        else:
+            sub_attr_payload.append(0)
+
+    @staticmethod
+    def encode_ipv6_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        raise NotImplementedError("ipv6 tunnel local and remote not supported yet")
+
+    @staticmethod
+    def encode_vlan_protocol_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(6)  # length
+        sub_attr_payload.append(info_data_type)
+
+        # vlan protocol
+        vlan_protocol = NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vlan_protocol_dict.get(info_data_value)
+        if not vlan_protocol:
+            raise NotImplementedError('vlan protocol %s not implemented' % info_data_value)
+
+        sub_attr_pack_layout.append("H")
+        sub_attr_payload.append(htons(vlan_protocol))
+
+        # pad 2 bytes
+        sub_attr_pack_layout.extend("xx")
+
+    @staticmethod
+    def encode_vxlan_port_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(6)
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("H")
+
+        # byte swap
+        swaped = pack(">H", info_data_value)
+        sub_attr_payload.append(unpack("<H", swaped)[0])
+
+        # pad 2 bytes
+        sub_attr_pack_layout.extend("xx")
+
+    @staticmethod
+    def encode_mac_address_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+        sub_attr_pack_layout.append("HH")
+        sub_attr_payload.append(10)  # length
+        sub_attr_payload.append(info_data_type)
+
+        sub_attr_pack_layout.append("6B")
+        for mbyte in info_data_value.replace(".", " ").replace(":", " ").split():
+            sub_attr_payload.append(int(mbyte, 16))
+
+        # pad 2 bytes
+        sub_attr_pack_layout.extend('xx')
+
+
+class AttributeCACHEINFO(Attribute):
+    """
+        struct ifa_cacheinfo {
+            __u32      ifa_prefered;
+            __u32      ifa_valid;
+            __u32      cstamp; /* created timestamp, hundredths of seconds */
+            __u32      tstamp; /* updated timestamp, hundredths of seconds */
+        };
+    """
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = "=IIII"
+        self.LEN = calcsize(self.PACK)
+
+    def encode(self):
+        ifa_prefered, ifa_valid, cstamp, tstamp = self.value
+        return pack(self.HEADER_PACK, self.HEADER_LEN + self.LEN , self.atype) + pack(self.PACK, ifa_prefered, ifa_valid, cstamp, tstamp)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        try:
+            self.value = unpack(self.PACK, self.data[4:])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+
+
+class AttributeFourByteList(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        wordcount = (self.attr_end - 4)/4
+        self.PACK = '=%dL' % wordcount
+        self.LEN = calcsize(self.PACK)
+
+        try:
+            self.value = unpack(self.PACK, self.data[4:])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        idx = 1
+        for val in self.value:
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[4*idx:4*(idx+1)], val))
+            line_number += 1
+            idx += 1
+        return line_number
+
+
+class AttributeFourByteValue(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = '=L'
+        self.LEN = calcsize(self.PACK)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+
+        try:
+            self.value = int(unpack(self.PACK, self.data[4:])[0])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+        return line_number + 1
+
+
+class AttributeTwoByteValue(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = '=Hxx'
+        self.LEN = calcsize(self.PACK)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+
+        try:
+            self.value = int(unpack(self.PACK, self.data[4:8])[0])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:6])))
+            raise
+
+    def encode(self):
+        length = self.HEADER_LEN + self.LEN
+        raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, self.value)
+        raw = self.pad(length, raw)
+        return raw
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+        return line_number + 1
+
+
+class AttributeString(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = None
+        self.LEN = None
 
     def encode(self):
         # some interface names come from JSON as unicode strings
@@ -440,7 +1341,7 @@ class AttributeString(Attribute):
         self.LEN = calcsize(self.PACK)
 
         try:
-            self.value = remove_trailing_null(unpack(self.PACK, self.data[4:self.length])[0])
+            self.value = str(remove_trailing_null(unpack(self.PACK, self.data[4:self.length])[0]))
         except struct.error:
             self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:self.length])))
             raise
@@ -483,21 +1384,29 @@ class AttributeIPAddress(Attribute):
         if value is None:
             self.value = None
         else:
-            self.value = IPAddress(value)
+            self.value = IPNetwork(value)
 
     def decode(self, parent_msg, data):
         self.decode_length_type(data)
 
         try:
-            if self.family == AF_INET:
+            if self.family in (AF_INET, AF_BRIDGE):
                 self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
 
             elif self.family == AF_INET6:
                 (data1, data2) = unpack(self.PACK, self.data[4:])
                 self.value = IPv6Address(data1 << 64 | data2)
 
-            elif self.family == AF_BRIDGE:
-                self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
+            if isinstance(parent_msg, Route):
+                if self.atype == Route.RTA_SRC:
+                    self.value = IPNetwork('%s/%s' % (self.value, parent_msg.src_len))
+                elif self.atype == Route.RTA_DST:
+                    self.value = IPNetwork('%s/%s' % (self.value, parent_msg.dst_len))
+            else:
+                try:
+                    self.value = IPNetwork('%s/%s' % (self.value, parent_msg.prefixlen))
+                except AttributeError:
+                    self.value = IPNetwork('%s' % self.value)
 
             self.value_int = int(self.value)
             self.value_int_str = str(self.value_int)
@@ -550,9 +1459,9 @@ class AttributeMACAddress(Attribute):
 
     def decode(self, parent_msg, data):
         self.decode_length_type(data)
-        
+
         # IFLA_ADDRESS and IFLA_BROADCAST attributes for all interfaces has been a
-        # 6-byte MAC address. But the GRE interface uses a 4-byte IP address and 
+        # 6-byte MAC address. But the GRE interface uses a 4-byte IP address and
         # GREv6 uses a 16-byte IPv6 address for this attribute.
         try:
             # GRE interface uses a 4-byte IP address for this attribute
@@ -560,11 +1469,12 @@ class AttributeMACAddress(Attribute):
                 self.value = IPv4Address(unpack('>L', self.data[4:])[0])
                 self.value_int = int(self.value)
                 self.value_int_str = str(self.value_int)
-            # MAC Address 
+            # MAC Address
             elif self.length == 10:
                 (data1, data2) = unpack(self.PACK, self.data[4:])
-                self.value = mac_int_to_str(data1 << 16 | data2)
-            # GREv6 interface uses a 16-byte IP address for this attribute 
+                self.raw = data1 << 16 | data2
+                self.value = mac_int_to_str(self.raw)
+            # GREv6 interface uses a 16-byte IP address for this attribute
             elif self.length == 20:
                 self.value = IPv6Address(unpack('>L', self.data[16:])[0])
                 self.value_int = int(self.value)
@@ -1094,9 +2004,777 @@ class AttributeIFLA_LINKINFO(Attribute):
         }
     }
     """
+    decode_ifla_info_nested_data_handlers = {
+        NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_DATA: {
+            "bridge": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_FILTERING: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE_DETECTED: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_ROUTER: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_SNOOPING: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_USE_IFADDR: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IPTABLES: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IP6TABLES: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_ARPTABLES: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_STATS_ENABLED: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STATS_ENABLED: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_IGMP_VERSION: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MLD_VERSION: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_PRIORITY: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_GROUP_FWD_MASK: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_DEFAULT_PVID: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_AGEING_TIME: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_FORWARD_DELAY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_HELLO_TIME: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MAX_AGE: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_STP_STATE: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PATH_COST: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_ELASTICITY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_MAX: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_CNT: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_CNT: Attribute.decode_four_bytes_attribute,
+
+                # 8 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MEMBERSHIP_INTVL: Attribute.decode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER_INTVL: Attribute.decode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_INTVL: Attribute.decode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_INTVL: Attribute.decode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: Attribute.decode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: Attribute.decode_eight_bytes_attribute,
+
+                # vlan-protocol attribute ######################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_PROTOCOL: Attribute.decode_vlan_protocol_attribute
+            },
+            "bond": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MODE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_USE_CARRIER: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_RATE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_BYPASS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_XMIT_HASH_POLICY: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_NUM_PEER_NOTIF: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYS_PRIO: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIIMON: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_UPDELAY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_DOWNDELAY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIN_LINKS: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_PRIMARY: Attribute.decode_four_bytes_attribute,
+
+                # mac address attributes #######################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYSTEM: Attribute.decode_mac_address_attribute,
+
+                # bond ad info attribute #######################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_INFO: Attribute.decode_bond_ad_info_attribute,
+            },
+            "vlan": {
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_ID: Attribute.decode_two_bytes_attribute,
+
+                # vlan-protocol attribute ######################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_PROTOCOL: Attribute.decode_vlan_protocol_attribute
+            },
+            "macvlan": {
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_MACVLAN_MODE: Attribute.decode_four_bytes_attribute,
+            },
+            "vxlan": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LEARNING: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PROXY: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_RSC: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L2MISS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L3MISS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_CSUM: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_TX: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_RX: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_TX: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_RX: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REPLICATION_TYPE: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes network byte order attributes ########################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT: Attribute.decode_two_bytes_network_byte_order_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_ID: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_AGEING: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LIMIT: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT_RANGE: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_GROUP: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LOCAL: Attribute.decode_ipv4_address_attribute,
+            },
+            "vrf": {
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VRF_TABLE: Attribute.decode_four_bytes_attribute
+            },
+            "xfrm": {
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_XFRM_IF_ID: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_XFRM_LINK: Attribute.decode_four_bytes_attribute
+            },
+
+            # Tunnels:
+            # There's is a lot of copy paste here because most of the tunnels
+            # share the same attribute key / attribute index value, but ipv6
+            # tunnels needs special handling for their ipv6 attributes.
+            #
+            # "gre", "gretap", "erspan", "ip6gre", "ip6gretap", "ip6erspan" are
+            # identical as well with special handling for LOCAL and REMOTE for
+            # the ipv6 tunnels
+            "gre": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv4_address_attribute
+            },
+            "gretap": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv4_address_attribute
+            },
+            "erspan": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv4_address_attribute
+            },
+            "ip6gre": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv6_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv6_address_attribute
+            },
+            "ip6gretap": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv6_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv6_address_attribute
+            },
+            "ip6erspan": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv6_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv6_address_attribute
+            },
+
+            # "ipip", "sit", "ip6tnl" are identical as well except for some
+            # special ipv6 handling for LOCAL and REMOTE.
+            "ipip": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LINK: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LOCAL: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_REMOTE: Attribute.decode_ipv4_address_attribute,
+            },
+            "sit": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LINK: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LOCAL: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_REMOTE: Attribute.decode_ipv4_address_attribute,
+            },
+            "ip6tnl": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TTL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TOS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_PMTUDISC: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_TYPE: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_SPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_DPORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LINK: Attribute.decode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LOCAL: Attribute.decode_ipv6_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_REMOTE: Attribute.decode_ipv6_address_attribute,
+            },
+
+            # Same story with "vti", "vti6"...
+            "vti": {
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LOCAL: Attribute.decode_ipv4_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_REMOTE: Attribute.decode_ipv4_address_attribute
+            },
+            "vti6": {
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LINK: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_IKEY: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_OKEY: Attribute.decode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LOCAL: Attribute.decode_ipv6_address_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_REMOTE: Attribute.decode_ipv6_address_attribute
+            }
+        },
+        NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_SLAVE_DATA: {
+            "bridge": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_STATE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MODE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GUARD: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROTECT: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_FAST_LEAVE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_UNICAST_FLOOD: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING_SYNC: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP_WIFI: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_CONFIG_PENDING: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MULTICAST_ROUTER: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_FLOOD: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BCAST_FLOOD: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_TO_UCAST: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_VLAN_TUNNEL: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PEER_LINK: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DUAL_LINK: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NEIGH_SUPPRESS: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PRIORITY: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_PORT: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_COST: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_ID: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NO: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASK: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASKHI: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_COST: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BACKUP_PORT: Attribute.decode_four_bytes_attribute,
+            },
+            "bond": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_STATE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_MII_STATUS: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE: Attribute.decode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_AD_RX_BYPASS: Attribute.decode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_QUEUE_ID: Attribute.decode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID: Attribute.decode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_PERM_HWADDR: Attribute.decode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT: Attribute.decode_four_bytes_attribute,
+            }
+        }
+    }
+
+    encode_ifla_info_nested_data_handlers = {
+        NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_DATA: {
+            "vlan": {
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_ID: Attribute.encode_two_bytes_attribute,
+
+                # vlan-protocol attribute ######################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_PROTOCOL: Attribute.encode_vlan_protocol_attribute,
+            },
+            "macvlan": {
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_MACVLAN_MODE: Attribute.encode_four_bytes_attribute,
+            },
+            "vxlan": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LEARNING: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PROXY: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_RSC: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L2MISS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L3MISS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_CSUM: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_TX: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_RX: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_TX: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_RX: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REPLICATION_TYPE: Attribute.encode_one_byte_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_ID: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_AGEING: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LIMIT: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT_RANGE: Attribute.encode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_GROUP: Attribute.encode_ipv4_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LOCAL: Attribute.encode_ipv4_attribute,
+
+                # vxlan-port attribute #########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT: Attribute.encode_vxlan_port_attribute,
+            },
+            "bond": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_NUM_PEER_NOTIF: Attribute.encode_one_byte_attribute,
+
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_USE_CARRIER: Attribute.encode_one_byte_attribute_from_string_boolean,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_BYPASS: Attribute.encode_one_byte_attribute_from_string_boolean,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_RATE: Attribute.encode_one_byte_attribute_from_string_boolean,
+
+                # bond-mode attribute ##########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MODE: Attribute.encode_bond_mode_attribute,
+
+                # bond-xmit-hash-policy attribute ##############################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_XMIT_HASH_POLICY: Attribute.encode_bond_xmit_hash_policy_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYS_PRIO: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIIMON: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_UPDELAY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_DOWNDELAY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIN_LINKS: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_PRIMARY: Attribute.encode_four_bytes_attribute,
+
+                # mac address attribute ########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYSTEM: Attribute.encode_mac_address_attribute
+            },
+            "vrf": {
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VRF_TABLE: Attribute.encode_four_bytes_attribute
+            },
+            "bridge": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_FILTERING: Attribute.encode_one_byte_attribute_from_string_boolean,
+
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE_DETECTED: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_ROUTER: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_SNOOPING: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_USE_IFADDR: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IPTABLES: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IP6TABLES: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_ARPTABLES: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_STATS_ENABLED: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STATS_ENABLED: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_IGMP_VERSION: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MLD_VERSION: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_PRIORITY: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_GROUP_FWD_MASK: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_DEFAULT_PVID: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_FORWARD_DELAY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_HELLO_TIME: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MAX_AGE: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_AGEING_TIME: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_STP_STATE: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PATH_COST: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_ELASTICITY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_MAX: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_CNT: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_CNT: Attribute.encode_four_bytes_attribute,
+
+                # 8 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_INTVL: Attribute.encode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MEMBERSHIP_INTVL: Attribute.encode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER_INTVL: Attribute.encode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_INTVL: Attribute.encode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: Attribute.encode_eight_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: Attribute.encode_eight_bytes_attribute,
+
+                # vlan-protocol attribute ######################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_PROTOCOL: Attribute.encode_vlan_protocol_attribute
+            },
+            # Tunnels:
+            # There's is a lot of copy paste here because most of the tunnels
+            # share the same attribute key / attribute index value, but ipv6
+            # tunnels needs special handling for their ipv6 attributes.
+            #
+            # "gre", "gretap", "erspan", "ip6gre", "ip6gretap", "ip6erspan" are
+            # identical as well with special handling for LOCAL and REMOTE for
+            # the ipv6 tunnels
+            "gre": {  # == ("gre", "gretap", "erspan")
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv4_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv4_attribute,
+            },
+            "gretap": {  # == ("gre", "gretap", "erspan")
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv4_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv4_attribute,
+            },
+            "erspan": {  # == ("gre", "gretap", "erspan")
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute,
+
+                # ipv4 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv4_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv4_attribute,
+            },
+            "ip6gre": {  # == ("ip6gre", "ip6gretap", "ip6erspan")
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv6_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv6_attribute,
+            },
+            "ip6gretap": {  # == ("ip6gre", "ip6gretap", "ip6erspan")
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv6_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv6_attribute,
+            },
+            "ip6erspan": {  # == ("ip6gre", "ip6gretap", "ip6erspan")
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute,
+
+                # ipv6 attributes ##############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv6_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv6_attribute,
+            }
+        },
+        NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_SLAVE_DATA: {
+            "bridge": {
+                # 1 byte attributes ############################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_STATE: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MODE: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GUARD: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROTECT: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_FAST_LEAVE: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_UNICAST_FLOOD: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING_SYNC: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP_WIFI: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_CONFIG_PENDING: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MULTICAST_ROUTER: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_FLOOD: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_TO_UCAST: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_VLAN_TUNNEL: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BCAST_FLOOD: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PEER_LINK: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DUAL_LINK: Attribute.encode_one_byte_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NEIGH_SUPPRESS: Attribute.encode_one_byte_attribute,
+
+                # 2 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PRIORITY: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_PORT: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_COST: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_ID: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NO: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASK: Attribute.encode_two_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASKHI: Attribute.encode_two_bytes_attribute,
+
+                # 4 bytes attributes ###########################################
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_COST: Attribute.encode_four_bytes_attribute,
+                NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BACKUP_PORT: Attribute.encode_four_bytes_attribute,
+            },
+        }
+    }
+
+    ifla_info_nested_data_attributes_to_string_dict = {
+        NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_DATA: {
+            "bond": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_bond_to_string,
+            "vlan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vlan_to_string,
+            "vxlan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vxlan_to_string,
+            "bridge": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_br_to_string,
+            "macvlan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_macvlan_to_string,
+            "vrf": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vrf_to_string,
+            "gre": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string,
+            "gretap": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string,
+            "erspan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string,
+            "ip6gre": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string,
+            "ip6gretap": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string,
+            "ip6erspan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string,
+            "vti": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vti_to_string,
+            "vti6": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vti_to_string,
+            "ipip": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_iptun_to_string,
+            "sit": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_iptun_to_string,
+            "ip6tnl": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_iptun_to_string
+        },
+        NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_SLAVE_DATA: {
+            "bridge": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_brport_to_string,
+            "bond": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_bond_slave_to_string
+        }
+    }
+
     def __init__(self, atype, string, family, logger):
         Attribute.__init__(self, atype, string, logger)
 
+    def encode_ifla_info_nested_data(self, sub_attr_pack_layout, sub_attr_type, sub_attr_type_string, sub_attr_value, encode_handlers, ifla_info_nested_attr_to_str_dict, kind):
+        sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED]
+
+        if not encode_handlers:
+            self.log.log(
+                SYSLOG_EXTRA_DEBUG,
+                "Add support for encoding %s for %s link kind" % (sub_attr_type_string, kind)
+            )
+        else:
+            for (info_data_type, info_data_value) in sub_attr_value.iteritems():
+                encode_handler = encode_handlers.get(info_data_type)
+
+                if encode_handler:
+                    encode_handler(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value)
+                else:
+                    self.log.log(
+                        SYSLOG_EXTRA_DEBUG,
+                        "Add support for encoding %s %s sub-attribute %s (%d)"
+                        % (
+                            sub_attr_type_string,
+                            kind,
+                            ifla_info_nested_attr_to_str_dict.get(info_data_type),
+                            info_data_type
+                        )
+                    )
+
+        return sub_attr_payload
+
     def encode(self):
         pack_layout = [self.HEADER_PACK]
         payload = [0, self.atype | NLA_F_NESTED]
@@ -1105,8 +2783,18 @@ class AttributeIFLA_LINKINFO(Attribute):
         kind        = self.value.get(Link.IFLA_INFO_KIND)
         slave_kind  = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
 
-        if not slave_kind and kind not in ('vlan', 'macvlan', 'vxlan', 'bond', 'bridge', 'xfrm'):
-            raise Exception('Unsupported IFLA_INFO_KIND %s' % kind)
+        if not slave_kind and kind not in (
+            "vrf",
+            "vlan",
+            "vxlan",
+            "bond",
+            "dummy",
+            "bridge",
+            "macvlan",
+        ):
+            self.log.debug('Unsupported IFLA_INFO_KIND %s' % kind)
+            return
+
         elif not kind and slave_kind != 'bridge':
             # only support brport for now.
             raise Exception('Unsupported IFLA_INFO_SLAVE_KIND %s' % slave_kind)
@@ -1115,436 +2803,69 @@ class AttributeIFLA_LINKINFO(Attribute):
         # order (=). If a field is added that needs to be packed via network
         # order (>) then some smarts will need to be added to split the pack_layout
         # string at the >, split the payload and make the needed pack() calls.
-        #
         # Until we cross that bridge though we will keep things nice and simple and
         # pack everything via a single pack() call.
+
         for (sub_attr_type, sub_attr_value) in self.value.iteritems():
             sub_attr_pack_layout = ['=', 'HH']
             sub_attr_payload = [0, sub_attr_type]
             sub_attr_length_index = 0
 
-            if sub_attr_type == Link.IFLA_INFO_KIND:
+            if sub_attr_type in (Link.IFLA_INFO_KIND, Link.IFLA_INFO_SLAVE_KIND):
                 sub_attr_pack_layout.append('%ds' % len(sub_attr_value))
                 sub_attr_payload.append(sub_attr_value)
 
             elif sub_attr_type == Link.IFLA_INFO_DATA:
+                sub_attr_payload = self.encode_ifla_info_nested_data(
+                    sub_attr_pack_layout,
+                    sub_attr_type,
+                    "IFLA_INFO_DATA",
+                    sub_attr_value,
+                    self.encode_ifla_info_nested_data_handlers.get(Link.IFLA_INFO_DATA, {}).get(kind),
+                    self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_DATA, {}).get(kind),
+                    kind
+                )
 
-                sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED]
+            elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA:
+                sub_attr_payload = self.encode_ifla_info_nested_data(
+                    sub_attr_pack_layout,
+                    sub_attr_type,
+                    "IFLA_INFO_SLAVE_DATA",
+                    sub_attr_value,
+                    self.encode_ifla_info_nested_data_handlers.get(Link.IFLA_INFO_SLAVE_DATA, {}).get(slave_kind),
+                    self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_SLAVE_DATA, {}).get(slave_kind),
+                    slave_kind
+                )
 
-                for (info_data_type, info_data_value) in sub_attr_value.iteritems():
+            else:
+                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_LINKINFO sub-attribute type %d' % sub_attr_type)
+                continue
 
-                    if kind == 'vlan':
-                        if info_data_type == Link.IFLA_VLAN_ID:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_data_type)
+            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
+            sub_attr_payload[sub_attr_length_index] = sub_attr_length
 
-                            # The vlan-id
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(info_data_value)
+            # add padding
+            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
+                sub_attr_pack_layout.append('x')
 
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
+            # The [1:] is to remove the leading = so that when we do the ''.join() later
+            # we do not end up with an = in the middle of the pack layout string. There
+            # will be an = at the beginning via self.HEADER_PACK
+            sub_attr_pack_layout = sub_attr_pack_layout[1:]
 
-                        elif info_data_type == Link.IFLA_VLAN_PROTOCOL:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_data_type)
+            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
+            pack_layout.extend(sub_attr_pack_layout)
+            payload.extend(sub_attr_payload)
 
-                            # vlan protocol
-                            vlan_protocol = Link.ifla_vlan_protocol_dict.get(info_data_value)
-                            if not vlan_protocol:
-                                raise NotImplementedError('vlan protocol %s not implemented' % info_data_value)
+        pack_layout = ''.join(pack_layout)
 
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(htons(vlan_protocol))
+        # Fill in the length field
+        length = calcsize(pack_layout)
+        payload[attr_length_index] = length
 
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
-                        else:
-                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA vlan sub-attribute type %d' % info_data_type)
-
-                    elif kind == 'macvlan':
-                        if info_data_type == Link.IFLA_MACVLAN_MODE:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # macvlan mode
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(info_data_value)
-
-                        else:
-                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA macvlan sub-attribute type %d' % info_data_type)
-
-                    elif kind == 'xfrm':
-                        if info_data_type in (Link.IFLA_XFRM_IF_ID, Link.IFLA_XFRM_LINK):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(info_data_value)
-
-                    elif kind == 'vxlan':
-                        if info_data_type in (Link.IFLA_VXLAN_ID,
-                                              Link.IFLA_VXLAN_LINK,
-                                              Link.IFLA_VXLAN_AGEING,
-                                              Link.IFLA_VXLAN_LIMIT,
-                                              Link.IFLA_VXLAN_PORT_RANGE):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(info_data_value)
-
-                        elif info_data_type in (Link.IFLA_VXLAN_GROUP,
-                                                Link.IFLA_VXLAN_LOCAL):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-
-                            reorder = unpack('<L', IPv4Address(info_data_value).packed)[0]
-                            sub_attr_payload.append(IPv4Address(reorder))
-
-                        elif info_data_type in (Link.IFLA_VXLAN_PORT,):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('H')
-
-                            # byte swap
-                            swaped = pack(">H", info_data_value)
-                            sub_attr_payload.append(unpack("<H", swaped)[0])
-
-                            sub_attr_pack_layout.extend('xx')
-
-                        elif info_data_type in (Link.IFLA_VXLAN_TTL,
-                                                Link.IFLA_VXLAN_TOS,
-                                                Link.IFLA_VXLAN_LEARNING,
-                                                Link.IFLA_VXLAN_PROXY,
-                                                Link.IFLA_VXLAN_RSC,
-                                                Link.IFLA_VXLAN_L2MISS,
-                                                Link.IFLA_VXLAN_L3MISS,
-                                                Link.IFLA_VXLAN_UDP_CSUM,
-                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
-                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
-                                                Link.IFLA_VXLAN_REMCSUM_TX,
-                                                Link.IFLA_VXLAN_REMCSUM_RX,
-                                                Link.IFLA_VXLAN_REPLICATION_TYPE):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(info_data_value)
-                            sub_attr_pack_layout.extend('xxx')
-
-                        else:
-                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA vxlan sub-attribute type %d' % info_data_type)
-
-                    elif kind == 'bond':
-                        if info_data_type in (Link.IFLA_BOND_AD_ACTOR_SYSTEM, ):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(10)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('6B')
-                            for mbyte in info_data_value.replace('.', ' ').replace(':', ' ').split():
-                                sub_attr_payload.append(int(mbyte, 16))
-                            sub_attr_pack_layout.extend('xx')
-
-                        elif info_data_type == Link.IFLA_BOND_AD_ACTOR_SYS_PRIO:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 2 bytes
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(int(info_data_value))
-
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
-
-                        elif info_data_type == Link.IFLA_BOND_NUM_PEER_NOTIF:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 1 byte
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(int(info_data_value))
-
-                            # pad 3 bytes
-                            sub_attr_pack_layout.extend('xxx')
-
-
-                        elif info_data_type in (Link.IFLA_BOND_AD_LACP_RATE,
-                                                Link.IFLA_BOND_AD_LACP_BYPASS,
-                                                Link.IFLA_BOND_USE_CARRIER):
-                            # converts yes/no/on/off/0/1 strings to boolean value
-                            bool_value = self.get_bool_value(info_data_value)
-
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 1 byte
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(bool_value)
-
-                            # pad 3 bytes
-                            sub_attr_pack_layout.extend('xxx')
-
-                        elif info_data_type == Link.IFLA_BOND_XMIT_HASH_POLICY:
-                            index = self.get_index(Link.ifla_bond_xmit_hash_policy_tbl,
-                                                   'bond xmit hash policy',
-                                                   info_data_value)
-
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 1 byte
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(index)
-
-                            # pad 3 bytes
-                            sub_attr_pack_layout.extend('xxx')
-
-                        elif info_data_type == Link.IFLA_BOND_MODE:
-                            index = self.get_index(Link.ifla_bond_mode_tbl,
-                                                   'bond mode',
-                                                   info_data_value)
-
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 1 byte
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(index)
-
-                            # pad 3 bytes
-                            sub_attr_pack_layout.extend('xxx')
-
-                        elif info_data_type in (Link.IFLA_BOND_MIIMON,
-                                                Link.IFLA_BOND_UPDELAY,
-                                                Link.IFLA_BOND_DOWNDELAY,
-                                                Link.IFLA_BOND_MIN_LINKS):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(int(info_data_value))
-
-                        else:
-                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA bond sub-attribute type %d' % info_data_type)
-
-                    elif kind == 'bridge':
-                        if info_data_type == Link.IFLA_BR_VLAN_PROTOCOL:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # vlan protocol
-                            vlan_protocol = Link.ifla_vlan_protocol_dict.get(info_data_value)
-                            if not vlan_protocol:
-                                raise NotImplementedError('vlan protocol %s not implemented' % info_data_value)
-
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(htons(vlan_protocol))
-
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
-
-                        # 1 byte
-                        elif info_data_type in (Link.IFLA_BR_VLAN_FILTERING,
-                                              Link.IFLA_BR_TOPOLOGY_CHANGE,
-                                              Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
-                                              Link.IFLA_BR_MCAST_ROUTER,
-                                              Link.IFLA_BR_MCAST_SNOOPING,
-                                              Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
-                                              Link.IFLA_BR_MCAST_QUERIER,
-                                              Link.IFLA_BR_NF_CALL_IPTABLES,
-                                              Link.IFLA_BR_NF_CALL_IP6TABLES,
-                                              Link.IFLA_BR_NF_CALL_ARPTABLES,
-                                              Link.IFLA_BR_VLAN_STATS_ENABLED,
-                                              Link.IFLA_BR_MCAST_STATS_ENABLED,
-                                              Link.IFLA_BR_MCAST_IGMP_VERSION,
-                                              Link.IFLA_BR_MCAST_MLD_VERSION):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 1 byte
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(int(info_data_value))
-
-                            # pad 3 bytes
-                            sub_attr_pack_layout.extend('xxx')
-
-                        # 2 bytes
-                        elif info_data_type in (Link.IFLA_BR_PRIORITY,
-                                              Link.IFLA_BR_GROUP_FWD_MASK,
-                                              Link.IFLA_BR_ROOT_PORT,
-                                              Link.IFLA_BR_VLAN_DEFAULT_PVID):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # 2 bytes
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(int(info_data_value))
-
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
-
-                        # 4 bytes
-                        elif info_data_type in (Link.IFLA_BR_FORWARD_DELAY,
-                                                Link.IFLA_BR_HELLO_TIME,
-                                                Link.IFLA_BR_MAX_AGE,
-                                                Link.IFLA_BR_AGEING_TIME,
-                                                Link.IFLA_BR_STP_STATE,
-                                                Link.IFLA_BR_ROOT_PATH_COST,
-                                                Link.IFLA_BR_MCAST_QUERIER,
-                                                Link.IFLA_BR_MCAST_HASH_ELASTICITY,
-                                                Link.IFLA_BR_MCAST_HASH_MAX,
-                                                Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
-                                                Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(int(info_data_value))
-
-                        # 8 bytes
-                        elif info_data_type in (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
-                                                Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
-                                                Link.IFLA_BR_MCAST_QUERIER_INTVL,
-                                                Link.IFLA_BR_MCAST_QUERY_INTVL,
-                                                Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
-                                                Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(12)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('Q')
-                            sub_attr_payload.append(int(info_data_value))
-
-                        else:
-                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA bridge sub-attribute type %d' % info_data_type)
-
-            elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA:
-
-                sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED]
-
-                for (info_slave_data_type, info_slave_data_value) in sub_attr_value.iteritems():
-
-                    if slave_kind == 'bridge':
-
-                        # 1 byte
-                        if info_slave_data_type in (Link.IFLA_BRPORT_STATE,
-                                                    Link.IFLA_BRPORT_MODE,
-                                                    Link.IFLA_BRPORT_GUARD,
-                                                    Link.IFLA_BRPORT_PROTECT,
-                                                    Link.IFLA_BRPORT_FAST_LEAVE,
-                                                    Link.IFLA_BRPORT_LEARNING,
-                                                    Link.IFLA_BRPORT_UNICAST_FLOOD,
-                                                    Link.IFLA_BRPORT_PROXYARP,
-                                                    Link.IFLA_BRPORT_LEARNING_SYNC,
-                                                    Link.IFLA_BRPORT_PROXYARP_WIFI,
-                                                    Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
-                                                    Link.IFLA_BRPORT_CONFIG_PENDING,
-                                                    Link.IFLA_BRPORT_MULTICAST_ROUTER,
-                                                    Link.IFLA_BRPORT_MCAST_FLOOD,
-                                                    Link.IFLA_BRPORT_MCAST_TO_UCAST,
-                                                    Link.IFLA_BRPORT_VLAN_TUNNEL,
-                                                    Link.IFLA_BRPORT_BCAST_FLOOD,
-                                                    Link.IFLA_BRPORT_PEER_LINK,
-                                                    Link.IFLA_BRPORT_DUAL_LINK,
-                                                    Link.IFLA_BRPORT_ARP_SUPPRESS,
-                                                    Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(5)  # length
-                            sub_attr_payload.append(info_slave_data_type)
-
-                            # 1 byte
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(int(info_slave_data_value))
-
-                            # pad 3 bytes
-                            sub_attr_pack_layout.extend('xxx')
-
-                        # 2 bytes
-                        elif info_slave_data_type in (Link.IFLA_BRPORT_PRIORITY,
-                                                      Link.IFLA_BRPORT_DESIGNATED_PORT,
-                                                      Link.IFLA_BRPORT_DESIGNATED_COST,
-                                                      Link.IFLA_BRPORT_ID,
-                                                      Link.IFLA_BRPORT_NO,
-                                                      Link.IFLA_BRPORT_GROUP_FWD_MASK,
-                                                      Link.IFLA_BRPORT_GROUP_FWD_MASKHI):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_slave_data_type)
-
-                            # 2 bytes
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(int(info_slave_data_value))
-
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
-
-                        # 4 bytes
-                        elif info_slave_data_type == Link.IFLA_BRPORT_COST:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_slave_data_type)
-
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(int(info_slave_data_value))
-
-                        else:
-                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_SLAVE_DATA bond sub-attribute type %d' % info_slave_data_type)
-
-                    else:
-                        self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_LINKINFO kind %s' % slave_kind)
-
-            else:
-                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_LINKINFO sub-attribute type %d' % sub_attr_type)
-                continue
-
-            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
-            sub_attr_payload[sub_attr_length_index] = sub_attr_length
-
-            # add padding
-            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
-                sub_attr_pack_layout.append('x')
-
-            # The [1:] is to remove the leading = so that when we do the ''.join() later
-            # we do not end up with an = in the middle of the pack layout string. There
-            # will be an = at the beginning via self.HEADER_PACK
-            sub_attr_pack_layout = sub_attr_pack_layout[1:]
-
-            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
-            pack_layout.extend(sub_attr_pack_layout)
-            payload.extend(sub_attr_payload)
-
-        pack_layout = ''.join(pack_layout)
-
-        # Fill in the length field
-        length = calcsize(pack_layout)
-        payload[attr_length_index] = length
-
-        raw = pack(pack_layout, *payload)
-        raw = self.pad(length, raw)
-        return raw
+        raw = pack(pack_layout, *payload)
+        raw = self.pad(length, raw)
+        return raw
 
     def get_bool_value(self, value, default=None):
         try:
@@ -1560,6 +2881,53 @@ class AttributeIFLA_LINKINFO(Attribute):
             self.log.debug('unsupported %s value %s (%s)' % (attr, value, tbl.keys()))
             return default
 
+    def decode_ifla_info_nested_data(self, kind, ifla_info_nested_kind_str, sub_attr_type, sub_attr_type_str, data, sub_attr_end):
+        sub_attr_data = data[4:sub_attr_end]
+        ifla_info_nested_data = {}
+
+        if not kind:
+            self.log.warning("%s is not known...we cannot parse %s" % (ifla_info_nested_kind_str, sub_attr_type_str))
+        else:
+            ifla_info_nested_data_handlers = self.decode_ifla_info_nested_data_handlers.get(sub_attr_type, {}).get(kind)
+            ifla_info_nested_attr_to_str_dict = self.ifla_info_nested_data_attributes_to_string_dict.get(sub_attr_type, {}).get(kind)
+
+            if not ifla_info_nested_data_handlers or not ifla_info_nested_attr_to_str_dict:
+                self.log.log(SYSLOG_EXTRA_DEBUG, "%s: decode: unsupported %s %s" % (sub_attr_type_str, ifla_info_nested_kind_str, kind))
+            else:
+                while sub_attr_data:
+                    (info_nested_data_length, info_nested_data_type) = unpack("=HH", sub_attr_data[:4])
+                    info_nested_data_end = padded_length(info_nested_data_length)
+                    handler = ifla_info_nested_data_handlers.get(info_nested_data_type)
+                    try:
+                        if handler:
+                            ifla_info_nested_data[info_nested_data_type] = handler(sub_attr_data, info_nested_data_end)
+                        else:
+                            self.log.log(
+                                SYSLOG_EXTRA_DEBUG,
+                                "Add support for decoding %s %s, attribute %s (%s)"
+                                % (
+                                    ifla_info_nested_kind_str,
+                                    kind,
+                                    ifla_info_nested_attr_to_str_dict.get(info_nested_data_type, "UNKNOWN"),
+                                    info_nested_data_type
+                                )
+                            )
+                    except Exception as e:
+                        self.log.warning(
+                            "%s: %s: attribute %s: %s (%s)"
+                            % (
+                                ifla_info_nested_kind_str,
+                                kind,
+                                ifla_info_nested_attr_to_str_dict.get(info_nested_data_type, "UNKNOWN"),
+                                info_nested_data_type,
+                                str(e)
+                            )
+                        )
+
+                    sub_attr_data = sub_attr_data[info_nested_data_end:]
+
+        return ifla_info_nested_data
+
     def decode(self, parent_msg, data):
         """
         value is a dictionary such as:
@@ -1584,6 +2952,9 @@ class AttributeIFLA_LINKINFO(Attribute):
             (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
             sub_attr_end = padded_length(sub_attr_length)
 
+            if sub_attr_type & NLA_F_NESTED:
+                sub_attr_type ^= NLA_F_NESTED
+
             if not sub_attr_length:
                 self.log.error('parsed a zero length sub-attr')
                 return
@@ -1591,388 +2962,29 @@ class AttributeIFLA_LINKINFO(Attribute):
             if sub_attr_type in (Link.IFLA_INFO_KIND, Link.IFLA_INFO_SLAVE_KIND):
                 self.value[sub_attr_type] = remove_trailing_null(unpack('%ds' % (sub_attr_length - 4), data[4:sub_attr_length])[0])
 
-            elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA:
-                sub_attr_data = data[4:sub_attr_end]
-
-                ifla_info_slave_data = dict()
-                ifla_info_slave_kind = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
-
-                if not ifla_info_slave_kind:
-                    self.log.warning('IFLA_INFO_SLAVE_KIND is not known...we cannot parse IFLA_INFO_SLAVE_DATA')
-                else:
-                    while sub_attr_data:
-                        (info_data_length, info_data_type) = unpack('=HH', sub_attr_data[:4])
-                        info_data_end = padded_length(info_data_length)
-                        try:
-                            if ifla_info_slave_kind == 'bridge':
-                                # 1 byte
-                                if info_data_type in (Link.IFLA_BRPORT_STATE,
-                                                      Link.IFLA_BRPORT_MODE,
-                                                      Link.IFLA_BRPORT_GUARD,
-                                                      Link.IFLA_BRPORT_PROTECT,
-                                                      Link.IFLA_BRPORT_FAST_LEAVE,
-                                                      Link.IFLA_BRPORT_LEARNING,
-                                                      Link.IFLA_BRPORT_UNICAST_FLOOD,
-                                                      Link.IFLA_BRPORT_PROXYARP,
-                                                      Link.IFLA_BRPORT_LEARNING_SYNC,
-                                                      Link.IFLA_BRPORT_PROXYARP_WIFI,
-                                                      Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
-                                                      Link.IFLA_BRPORT_CONFIG_PENDING,
-                                                      Link.IFLA_BRPORT_MULTICAST_ROUTER,
-                                                      Link.IFLA_BRPORT_MCAST_FLOOD,
-                                                      Link.IFLA_BRPORT_MCAST_TO_UCAST,
-                                                      Link.IFLA_BRPORT_VLAN_TUNNEL,
-                                                      Link.IFLA_BRPORT_PEER_LINK,
-                                                      Link.IFLA_BRPORT_DUAL_LINK,
-                                                      Link.IFLA_BRPORT_ARP_SUPPRESS,
-                                                      Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
-                                    ifla_info_slave_data[info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                                # 2 bytes
-                                elif info_data_type in (Link.IFLA_BRPORT_PRIORITY,
-                                                        Link.IFLA_BRPORT_DESIGNATED_PORT,
-                                                        Link.IFLA_BRPORT_DESIGNATED_COST,
-                                                        Link.IFLA_BRPORT_ID,
-                                                        Link.IFLA_BRPORT_NO,
-                                                        Link.IFLA_BRPORT_GROUP_FWD_MASK,
-                                                        Link.IFLA_BRPORT_GROUP_FWD_MASKHI):
-                                    ifla_info_slave_data[info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                                # 4 bytes
-                                elif info_data_type == Link.IFLA_BRPORT_COST:
-                                    ifla_info_slave_data[info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                            elif ifla_info_slave_kind == 'bond':
-
-                                # 1 byte
-                                if info_data_type in (
-                                        Link.IFLA_BOND_SLAVE_STATE,
-                                        Link.IFLA_BOND_SLAVE_MII_STATUS,
-                                        Link.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
-                                        Link.IFLA_BOND_SLAVE_AD_RX_BYPASS,
-                                ):
-                                    ifla_info_slave_data[info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                                # 2 bytes
-                                elif info_data_type in (
-                                        Link.IFLA_BOND_SLAVE_QUEUE_ID,
-                                        Link.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID
-                                ):
-                                    ifla_info_slave_data[info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                                # 4 bytes
-                                elif info_data_type == (
-                                        Link.IFLA_BOND_SLAVE_PERM_HWADDR,
-                                        Link.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT
-                                ):
-                                    ifla_info_slave_data[info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                        except Exception as e:
-                            self.log.debug('%s: attribute %s: %s'
-                                            % (self.value[Link.IFLA_INFO_SLAVE_KIND],
-                                               info_data_type,
-                                               str(e)))
-                        sub_attr_data = sub_attr_data[info_data_end:]
-
-                self.value[Link.IFLA_INFO_SLAVE_DATA] = ifla_info_slave_data
-
             elif sub_attr_type == Link.IFLA_INFO_DATA:
-                sub_attr_data = data[4:sub_attr_end]
-                self.value[Link.IFLA_INFO_DATA] = {}
-
-                ifla_info_kind = self.value.get(Link.IFLA_INFO_KIND)
-                if not ifla_info_kind:
-                    self.log.warning('IFLA_INFO_KIND is not known...we cannot parse IFLA_INFO_DATA')
-                else:
-                    while sub_attr_data:
-                        (info_data_length, info_data_type) = unpack('=HH', sub_attr_data[:4])
-                        info_data_end = padded_length(info_data_length)
-                        try:
-                            if ifla_info_kind == 'vlan':
-                                if info_data_type == Link.IFLA_VLAN_ID:
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                                elif info_data_type == Link.IFLA_VLAN_PROTOCOL:
-                                    hex_value = '0x%s' % sub_attr_data[4:6].encode('hex')
-                                    vlan_protocol = Link.ifla_vlan_protocol_dict.get(int(hex_value, base=16))
-
-                                    if vlan_protocol:
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = vlan_protocol
-                                    else:
-                                        self.log.warning('IFLA_VLAN_PROTOCOL: cannot decode vlan protocol %s' % hex_value)
-
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND vlan type %s (%d), length %d, padded to %d' %
-                                                (parent_msg.get_ifla_vlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                            elif ifla_info_kind == 'macvlan':
-                                if info_data_type == Link.IFLA_MACVLAN_MODE:
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND macvlan type %s (%d), length %d, padded to %d' %
-                                                (parent_msg.get_ifla_macvlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                            elif ifla_info_kind == 'xfrm':
-                                # 4-byte int
-                                if info_data_type in (Link.IFLA_XFRM_IF_ID, Link.IFLA_XFRM_LINK):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                            elif ifla_info_kind == 'vxlan':
-
-                                # IPv4Address
-                                if info_data_type in (Link.IFLA_VXLAN_GROUP,
-                                                      Link.IFLA_VXLAN_LOCAL):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack('>L', sub_attr_data[4:8])[0])
-
-                                # 4-byte int
-                                elif info_data_type in (Link.IFLA_VXLAN_ID,
-                                                        Link.IFLA_VXLAN_LINK,
-                                                        Link.IFLA_VXLAN_AGEING,
-                                                        Link.IFLA_VXLAN_LIMIT,
-                                                        Link.IFLA_VXLAN_PORT_RANGE):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                                # 2-byte int
-                                elif info_data_type in (Link.IFLA_VXLAN_PORT, ):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('!H', sub_attr_data[4:6])[0]
-                                    # The form '!' is available for those poor souls who claim they can't
-                                    # remember whether network byte order is big-endian or little-endian.
-
-                                # 1-byte int
-                                elif info_data_type in (Link.IFLA_VXLAN_TTL,
-                                                        Link.IFLA_VXLAN_TOS,
-                                                        Link.IFLA_VXLAN_LEARNING,
-                                                        Link.IFLA_VXLAN_PROXY,
-                                                        Link.IFLA_VXLAN_RSC,
-                                                        Link.IFLA_VXLAN_L2MISS,
-                                                        Link.IFLA_VXLAN_L3MISS,
-                                                        Link.IFLA_VXLAN_UDP_CSUM,
-                                                        Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
-                                                        Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
-                                                        Link.IFLA_VXLAN_REMCSUM_TX,
-                                                        Link.IFLA_VXLAN_REMCSUM_RX,
-                                                        Link.IFLA_VXLAN_REPLICATION_TYPE):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                                else:
-                                    # sub_attr_end = padded_length(sub_attr_length)
-                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND vxlan type %s (%d), length %d, padded to %d' %
-                                                (parent_msg.get_ifla_vxlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                            elif ifla_info_kind == 'bond':
-
-                                if info_data_type in (Link.IFLA_BOND_AD_INFO, ):
-                                    ad_attr_data = sub_attr_data[4:info_data_end]
-                                    self.value[Link.IFLA_INFO_DATA][Link.IFLA_BOND_AD_INFO] = {}
-
-                                    while ad_attr_data:
-                                        (ad_data_length, ad_data_type) = unpack('=HH', ad_attr_data[:4])
-                                        ad_data_end = padded_length(ad_data_length)
-
-                                        if ad_data_type in (Link.IFLA_BOND_AD_INFO_PARTNER_MAC,):
-                                            (data1, data2) = unpack('>LHxx', ad_attr_data[4:12])
-                                            self.value[Link.IFLA_INFO_DATA][Link.IFLA_BOND_AD_INFO][ad_data_type] = mac_int_to_str(data1 << 16 | data2)
-
-                                        ad_attr_data = ad_attr_data[ad_data_end:]
-
-                                # 1-byte int
-                                elif info_data_type in (Link.IFLA_BOND_MODE,
-                                                        Link.IFLA_BOND_USE_CARRIER,
-                                                        Link.IFLA_BOND_AD_LACP_RATE,
-                                                        Link.IFLA_BOND_AD_LACP_BYPASS,
-                                                        Link.IFLA_BOND_XMIT_HASH_POLICY,
-                                                        Link.IFLA_BOND_NUM_PEER_NOTIF):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                                # 2-bytes int
-                                elif info_data_type == Link.IFLA_BOND_AD_ACTOR_SYS_PRIO:
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                                # 4-bytes int
-                                elif info_data_type in (Link.IFLA_BOND_MIIMON,
-                                                        Link.IFLA_BOND_UPDELAY,
-                                                        Link.IFLA_BOND_DOWNDELAY,
-                                                        Link.IFLA_BOND_MIN_LINKS):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                                # mac address
-                                elif info_data_type in (Link.IFLA_BOND_AD_ACTOR_SYSTEM, ):
-                                    (data1, data2) = unpack('>LHxx', sub_attr_data[4:12])
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = mac_int_to_str(data1 << 16 | data2)
-
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND bond type %s (%d), length %d, padded to %d' %
-                                                (parent_msg.get_ifla_bond_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                            elif ifla_info_kind == 'bridge':
-                                # 4 bytes
-                                if info_data_type in (Link.IFLA_BR_AGEING_TIME,
-                                                      Link.IFLA_BR_FORWARD_DELAY,
-                                                      Link.IFLA_BR_HELLO_TIME,
-                                                      Link.IFLA_BR_MAX_AGE,
-                                                      Link.IFLA_BR_STP_STATE,
-                                                      Link.IFLA_BR_ROOT_PATH_COST,
-                                                      Link.IFLA_BR_MCAST_HASH_ELASTICITY,
-                                                      Link.IFLA_BR_MCAST_HASH_MAX,
-                                                      Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
-                                                      Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                                # 2 bytes
-                                elif info_data_type in (Link.IFLA_BR_PRIORITY,
-                                                        Link.IFLA_BR_GROUP_FWD_MASK,
-                                                        Link.IFLA_BR_ROOT_PORT,
-                                                        Link.IFLA_BR_VLAN_DEFAULT_PVID):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                                elif info_data_type == Link.IFLA_BR_VLAN_PROTOCOL:
-                                    hex_value = '0x%s' % sub_attr_data[4:6].encode('hex')
-                                    vlan_protocol = Link.ifla_vlan_protocol_dict.get(int(hex_value, base=16))
-
-                                    if vlan_protocol:
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = vlan_protocol
-                                    else:
-                                        self.log.warning('IFLA_VLAN_PROTOCOL: cannot decode vlan protocol %s' % hex_value)
-
-                                # 8 bytes
-                                elif info_data_type in (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
-                                                        Link.IFLA_BR_MCAST_QUERIER_INTVL,
-                                                        Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
-                                                        Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
-                                                        Link.IFLA_BR_MCAST_QUERIER_INTVL,
-                                                        Link.IFLA_BR_MCAST_QUERY_INTVL,
-                                                        Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
-                                                        Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=Q', sub_attr_data[4:12])[0]
-
-                                # 1 bytes
-                                elif info_data_type in (Link.IFLA_BR_VLAN_FILTERING,
-                                                        Link.IFLA_BR_TOPOLOGY_CHANGE,
-                                                        Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
-                                                        Link.IFLA_BR_MCAST_ROUTER,
-                                                        Link.IFLA_BR_MCAST_SNOOPING,
-                                                        Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
-                                                        Link.IFLA_BR_MCAST_QUERIER,
-                                                        Link.IFLA_BR_NF_CALL_IPTABLES,
-                                                        Link.IFLA_BR_NF_CALL_IP6TABLES,
-                                                        Link.IFLA_BR_NF_CALL_ARPTABLES,
-                                                        Link.IFLA_BR_VLAN_STATS_ENABLED,
-                                                        Link.IFLA_BR_MCAST_STATS_ENABLED,
-                                                        Link.IFLA_BR_MCAST_IGMP_VERSION,
-                                                        Link.IFLA_BR_MCAST_MLD_VERSION):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND bridge type %s (%d), length %d, padded to %d' %
-                                                (parent_msg.get_ifla_br_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                            elif ifla_info_kind == 'vrf':
-
-                                if info_data_type in (Link.IFLA_VRF_TABLE,):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                            elif ifla_info_kind in ("gre", "gretap", "erspan", "ip6gre", "ip6gretap", "ip6erspan"):
-
-                                # 1-byte
-                                if info_data_type in (Link.IFLA_GRE_TTL, Link.IFLA_GRE_TOS, Link.IFLA_GRE_PMTUDISC):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                                # 2-bytes
-                                elif info_data_type in (
-                                        Link.IFLA_GRE_IFLAGS,
-                                        Link.IFLA_GRE_OFLAGS,
-                                        Link.IFLA_GRE_ENCAP_TYPE,
-                                        Link.IFLA_GRE_ENCAP_SPORT,
-                                        Link.IFLA_GRE_ENCAP_DPORT,
-                                        Link.IFLA_GRE_ENCAP_FLAGS
-                                ):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                                # 4 bytes
-                                elif info_data_type in (Link.IFLA_GRE_LINK, Link.IFLA_GRE_IKEY, Link.IFLA_GRE_OKEY):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                                # ip addr
-                                elif info_data_type in (Link.IFLA_GRE_LOCAL, Link.IFLA_GRE_REMOTE):
-                                    if ifla_info_kind in ("ip6gre", "ip6gretap", "ip6erspan"):
-                                        (data1, data2) = unpack(">QQ", sub_attr_data[4:20])
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv6Address(data1 << 64 | data2)
-                                    else:
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack(">L", sub_attr_data[4:8])[0])
-
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG,
-                                                 'Add support for decoding IFLA_INFO_KIND %s type %s (%d), length %d, padded to %d' %
-                                                 (ifla_info_kind, parent_msg.get_ifla_gre_string(info_data_type), info_data_type,
-                                                  info_data_length, info_data_end))
-
-                            elif ifla_info_kind in ("ipip", "sit", "ip6tnl"):
-
-                                # 1-byte
-                                if info_data_type in (Link.IFLA_IPTUN_TTL, Link.IFLA_IPTUN_TOS, Link.IFLA_IPTUN_PMTUDISC):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                                # 2-bytes
-                                elif info_data_type in (Link.IFLA_IPTUN_ENCAP_TYPE, Link.IFLA_IPTUN_ENCAP_SPORT, Link.IFLA_IPTUN_ENCAP_DPORT, Link.IFLA_IPTUN_ENCAP_FLAGS):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[
-                                        0]
-
-                                # 4 bytes
-                                elif info_data_type == Link.IFLA_IPTUN_LINK:
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[
-                                        0]
-
-                                # ip addr
-                                elif info_data_type in (Link.IFLA_IPTUN_LOCAL, Link.IFLA_IPTUN_REMOTE):
-                                    if ifla_info_kind == "ip6tnl":
-                                        (data1, data2) = unpack(">QQ", sub_attr_data[4:20])
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv6Address(data1 << 64 | data2)
-                                    else:
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack(">L", sub_attr_data[4:8])[0])
-
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG,
-                                             'Add support for decoding IFLA_INFO_KIND %s type %s (%d), length %d, padded to %d' %
-                                             (ifla_info_kind, parent_msg.get_ifla_iptun_string(info_data_type), info_data_type,
-                                              info_data_length, info_data_end))
-
-                            elif ifla_info_kind in ("vti", "vti6"):
-                                # 4 bytes
-                                if info_data_type in (Link.IFLA_VTI_LINK, Link.IFLA_VTI_IKEY, Link.IFLA_VTI_OKEY):
-                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                                # ip addr
-                                elif info_data_type in (Link.IFLA_VTI_LOCAL, Link.IFLA_VTI_REMOTE):
-                                    if ifla_info_kind == "vti6":
-                                        (data1, data2) = unpack(">QQ", sub_attr_data[4:20])
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv6Address(data1 << 64 | data2)
-                                    else:
-                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack(">L", sub_attr_data[4:8])[0])
-
-                                else:
-                                    self.log.log(SYSLOG_EXTRA_DEBUG,
-                                             'Add support for decoding IFLA_INFO_KIND %s type %s (%d), length %d, padded to %d' %
-                                             (ifla_info_kind, parent_msg.get_ifla_vti_string(info_data_type), info_data_type,
-                                              info_data_length, info_data_end))
+                self.value[Link.IFLA_INFO_DATA] = self.decode_ifla_info_nested_data(
+                    self.value.get(Link.IFLA_INFO_KIND), "IFLA_INFO_KIND",
+                    Link.IFLA_INFO_DATA, "IFLA_INFO_DATA",
+                    data, sub_attr_end,
+                )
 
-                            else:
-                                self.log.log(SYSLOG_EXTRA_DEBUG, "Add support for decoding IFLA_INFO_KIND %s (%d), length %d, padded to %d" %
-                                            (ifla_info_kind, info_data_type, info_data_length, info_data_end))
-
-                        except Exception as e:
-                            self.log.debug('%s: attribute %s: %s'
-                                           % (self.value[Link.IFLA_INFO_KIND],
-                                              info_data_type,
-                                              str(e)))
-                        sub_attr_data = sub_attr_data[info_data_end:]
+            elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA:
+                self.value[Link.IFLA_INFO_SLAVE_DATA] = self.decode_ifla_info_nested_data(
+                    self.value.get(Link.IFLA_INFO_SLAVE_KIND), "IFLA_INFO_SLAVE_KIND",
+                    Link.IFLA_INFO_SLAVE_DATA, "IFLA_INFO_SLAVE_DATA",
+                    data, sub_attr_end,
+                )
 
             else:
-                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d' %
-                            (parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
+                self.log.log(
+                    SYSLOG_EXTRA_DEBUG,
+                    'Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d'
+                    % (parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end)
+                )
 
             data = data[sub_attr_end:]
 
-        # self.log.info('IFLA_LINKINFO values %s' % pformat(self.value))
-
     def dump_lines(self, dump_buffer, line_number, color):
         line_number = self.dump_first_line(dump_buffer, line_number, color)
         extra = ''
@@ -2000,11 +3012,14 @@ class AttributeIFLA_LINKINFO(Attribute):
                 else:
                     padded_to = ' padded to %d, ' % sub_attr_end
 
-                extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
+                if sub_attr_type & NLA_F_NESTED:
+                    sub_attr_type ^= NLA_F_NESTED
+
+                extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s (%s)' % \
                         (zfilled_hex(sub_attr_length, 4), sub_attr_length,
                          padded_to,
                          zfilled_hex(sub_attr_type, 4), sub_attr_type,
-                         Link.ifla_info_to_string.get(sub_attr_type))
+                         Link.ifla_info_to_string.get(sub_attr_type), sub_attr_type)
             else:
                 extra = ''
 
@@ -2022,38 +3037,10 @@ class AttributeIFLA_LINKINFO(Attribute):
         ifla_info_kind          = self.value.get(Link.IFLA_INFO_KIND)
         ifla_info_slave_kind    = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
 
-        kind_dict = dict()
-
-        # We do this so we can print a more human readable dictionary
-        # with the names of the nested keys instead of their numbers
-
-        # Most of these are placeholders...we need to add support
-        # for more human readable dictionaries for bond, bridge, etc
-        kind_dict[Link.IFLA_INFO_DATA] = {
-            'bond':         Link.ifla_bond_to_string,
-            'vlan':         Link.ifla_vlan_to_string,
-            'vxlan':        Link.ifla_vxlan_to_string,
-            'bridge':       Link.ifla_br_to_string,
-            'macvlan':      Link.ifla_macvlan_to_string,
-            'gre':          Link.ifla_gre_to_string,
-            'gretap':       Link.ifla_gre_to_string,
-            'erspan':       Link.ifla_gre_to_string,
-            'ip6gre':       Link.ifla_gre_to_string,
-            'ip6gretap':    Link.ifla_gre_to_string,
-            'ip6erspan':    Link.ifla_gre_to_string,
-            'vti':          Link.ifla_vti_to_string,
-            'vti6':         Link.ifla_vti_to_string,
-            'ipip':         Link.ifla_iptun_to_string,
-            'sit':          Link.ifla_iptun_to_string,
-            'ip6tnl':       Link.ifla_iptun_to_string,
-            'xfrm':       Link.ifla_xfrm_to_string
-        }.get(ifla_info_kind, {})
-
-        kind_dict[Link.IFLA_INFO_SLAVE_DATA] = {
-            'bridge': Link.ifla_brport_to_string,
-            'bond': Link.ifla_bond_slave_to_string
-        }.get(ifla_info_slave_kind, {})
-
+        kind_dict = {
+            Link.IFLA_INFO_DATA: self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_DATA, {}).get(ifla_info_kind),
+            Link.IFLA_INFO_SLAVE_DATA: self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_SLAVE_DATA, {}).get(ifla_info_slave_kind)
+        }
         if ifla_info_kind or ifla_info_slave_kind:
             value_pretty = {}
 
@@ -2120,11 +3107,9 @@ class AttributeIFLA_PROTINFO(Attribute):
                                      Link.IFLA_BRPORT_MULTICAST_ROUTER,
                                      Link.IFLA_BRPORT_PEER_LINK,
                                      Link.IFLA_BRPORT_DUAL_LINK,
-                                     Link.IFLA_BRPORT_ARP_SUPPRESS,
-                                     Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
+                                     Link.IFLA_BRPORT_NEIGH_SUPPRESS):
                     sub_attr_pack_layout.append('B')
                     sub_attr_payload.append(sub_attr_value)
-                    sub_attr_pack_layout.extend('xxx')
 
                 # 2 Byte attributes
                 elif sub_attr_type in (Link.IFLA_BRPORT_PRIORITY,
@@ -2134,10 +3119,9 @@ class AttributeIFLA_PROTINFO(Attribute):
                                        Link.IFLA_BRPORT_NO):
                     sub_attr_pack_layout.append('H')
                     sub_attr_payload.append(sub_attr_value)
-                    sub_attr_pack_layout.extend('xx')
 
                 # 4 Byte attributes
-                elif sub_attr_type in (Link.IFLA_BRPORT_COST,):
+                elif sub_attr_type in (Link.IFLA_BRPORT_COST, Link.IFLA_BRPORT_BACKUP_PORT):
                     sub_attr_pack_layout.append('L')
                     sub_attr_payload.append(sub_attr_value)
 
@@ -2219,8 +3203,7 @@ class AttributeIFLA_PROTINFO(Attribute):
                                      Link.IFLA_BRPORT_MULTICAST_ROUTER,
                                      Link.IFLA_BRPORT_PEER_LINK,
                                      Link.IFLA_BRPORT_DUAL_LINK,
-                                     Link.IFLA_BRPORT_ARP_SUPPRESS,
-                                     Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
+                                     Link.IFLA_BRPORT_NEIGH_SUPPRESS):
                     self.value[sub_attr_type] = unpack('=B', data[4])[0]
 
                 # 2 Byte attributes
@@ -2232,7 +3215,7 @@ class AttributeIFLA_PROTINFO(Attribute):
                     self.value[sub_attr_type] = unpack('=H', data[4:6])[0]
 
                 # 4 Byte attributes
-                elif sub_attr_type in (Link.IFLA_BRPORT_COST,):
+                elif sub_attr_type in (Link.IFLA_BRPORT_COST, Link.IFLA_BRPORT_BACKUP_PORT):
                     self.value[sub_attr_type] = unpack('=L', data[4:8])[0]
 
                 # 8 Byte attributes
@@ -2347,7 +3330,11 @@ class NetlinkPacket(object):
         RTM_DELQDISC  : 'RTM_DELQDISC',
         RTM_GETQDISC  : 'RTM_GETQDISC',
         RTM_NEWNETCONF: 'RTM_NEWNETCONF',
-        RTM_GETNETCONF: 'RTM_GETNETCONF'
+        RTM_GETNETCONF: 'RTM_GETNETCONF',
+        RTM_DELNETCONF: 'RTM_DELNETCONF',
+        RTM_NEWMDB    : 'RTM_NEWMDB',
+        RTM_DELMDB    : 'RTM_DELMDB',
+        RTM_GETMDB    : 'RTM_GETMDB'
     }
 
     af_family_to_string = {
@@ -2355,7 +3342,7 @@ class NetlinkPacket(object):
         AF_INET6    : 'inet6'
     }
 
-    def __init__(self, msgtype, debug, owner_logger=None, use_color=True):
+    def __init__(self, msgtype, debug, owner_logger=None, use_color=True, rx=False, tx=False):
         self.msgtype     = msgtype
         self.attributes  = {}
         self.dump_buffer = ['']
@@ -2364,6 +3351,9 @@ class NetlinkPacket(object):
         self.message     = None
         self.use_color   = use_color
         self.family      = None
+        self.rx          = rx
+        self.tx          = tx
+        self.priv_flags  = 0
 
         if owner_logger:
             self.log = owner_logger
@@ -2427,7 +3417,7 @@ class NetlinkPacket(object):
             foo.append('NLM_F_ECHO')
 
         # Modifiers to GET query
-        if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC, RTM_GETNETCONF):
+        if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC, RTM_GETNETCONF, RTM_GETMDB):
             if flags & NLM_F_DUMP:
                 foo.append('NLM_F_DUMP')
             else:
@@ -2441,7 +3431,7 @@ class NetlinkPacket(object):
                 foo.append('NLM_F_ATOMIC')
 
         # Modifiers to NEW query
-        elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC):
+        elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC, RTM_NEWMDB):
             if flags & NLM_F_REPLACE:
                 foo.append('NLM_F_REPLACE')
 
@@ -2561,7 +3551,7 @@ class NetlinkPacket(object):
             take the family into account. For now we'll handle this as a special case for
             MPLS but long term we may need to make key a tuple of the attr_type and family.
             '''
-            if self.msgtype not in (RTM_NEWNETCONF, RTM_GETNETCONF) and attr_type == Route.RTA_DST and self.family == AF_MPLS:
+            if self.msgtype not in (RTM_NEWNETCONF, RTM_GETNETCONF, RTM_DELNETCONF) and attr_type == Route.RTA_DST and self.family == AF_MPLS:
                 attr_string = 'RTA_DST'
                 attr_class = AttributeMplsLabel
 
@@ -2675,6 +3665,7 @@ class Address(NetlinkPacket):
     IFA_CACHEINFO = 0x06
     IFA_MULTICAST = 0x07
     IFA_FLAGS     = 0x08
+    IFA_RT_PRIORITY = 0x09  # 32, priority / metricfor prefix route
 
     attribute_to_class = {
         IFA_UNSPEC    : ('IFA_UNSPEC', AttributeGeneric),
@@ -2683,9 +3674,10 @@ class Address(NetlinkPacket):
         IFA_LABEL     : ('IFA_LABEL', AttributeString),
         IFA_BROADCAST : ('IFA_BROADCAST', AttributeIPAddress),
         IFA_ANYCAST   : ('IFA_ANYCAST', AttributeIPAddress),
-        IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeGeneric),
+        IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeCACHEINFO),
         IFA_MULTICAST : ('IFA_MULTICAST', AttributeIPAddress),
-        IFA_FLAGS     : ('IFA_FLAGS', AttributeGeneric)
+        IFA_FLAGS     : ('IFA_FLAGS', AttributeGeneric),
+        IFA_RT_PRIORITY : ('IFA_RT_PRIORITY', AttributeFourByteValue)
     }
 
     # Address flags
@@ -2787,6 +3779,7 @@ class Error(NetlinkPacket):
     NLE_NODEV             = 0x1F
     NLE_IMMUTABLE         = 0x20
     NLE_DUMP_INTR         = 0x21
+    NLE_ATTRSIZE          = 0x22
 
     error_to_string = {
         NLE_SUCCESS           : 'NLE_SUCCESS',
@@ -2822,7 +3815,46 @@ class Error(NetlinkPacket):
         NLE_PARSE_ERR         : 'NLE_PARSE_ERR',
         NLE_NODEV             : 'NLE_NODEV',
         NLE_IMMUTABLE         : 'NLE_IMMUTABLE',
-        NLE_DUMP_INTR         : 'NLE_DUMP_INTR'
+        NLE_DUMP_INTR         : 'NLE_DUMP_INTR',
+        NLE_ATTRSIZE          : 'NLE_ATTRSIZE'
+    }
+
+    error_to_human_readable_string = {
+        NLE_SUCCESS:           "Success",
+        NLE_FAILURE:           "Unspecific failure",
+        NLE_INTR:              "Interrupted system call",
+        NLE_BAD_SOCK:          "Bad socket",
+        NLE_AGAIN:             "Try again",
+        NLE_NOMEM:             "Out of memory",
+        NLE_EXIST:             "Object exists",
+        NLE_INVAL:             "Invalid input data or parameter",
+        NLE_RANGE:             "Input data out of range",
+        NLE_MSGSIZE:           "Message size not sufficient",
+        NLE_OPNOTSUPP:         "Operation not supported",
+        NLE_AF_NOSUPPORT:      "Address family not supported",
+        NLE_OBJ_NOTFOUND:      "Object not found",
+        NLE_NOATTR:            "Attribute not available",
+        NLE_MISSING_ATTR:      "Missing attribute",
+        NLE_AF_MISMATCH:       "Address family mismatch",
+        NLE_SEQ_MISMATCH:      "Message sequence number mismatch",
+        NLE_MSG_OVERFLOW:      "Kernel reported message overflow",
+        NLE_MSG_TRUNC:         "Kernel reported truncated message",
+        NLE_NOADDR:            "Invalid address for specified address family",
+        NLE_SRCRT_NOSUPPORT:   "Source based routing not supported",
+        NLE_MSG_TOOSHORT:      "Netlink message is too short",
+        NLE_MSGTYPE_NOSUPPORT: "Netlink message type is not supported",
+        NLE_OBJ_MISMATCH:      "Object type does not match cache",
+        NLE_NOCACHE:           "Unknown or invalid cache type",
+        NLE_BUSY:              "Object busy",
+        NLE_PROTO_MISMATCH:    "Protocol mismatch",
+        NLE_NOACCESS:          "No Access",
+        NLE_PERM:              "Operation not permitted",
+        NLE_PKTLOC_FILE:       "Unable to open packet location file",
+        NLE_PARSE_ERR:         "Unable to parse object",
+        NLE_NODEV:             "No such device",
+        NLE_IMMUTABLE:         "Immutable attribute",
+        NLE_DUMP_INTR:         "Dump inconsistency detected, interrupted",
+        NLE_ATTRSIZE:          "Attribute max length exceeded",
     }
 
     def __init__(self, msgtype, debug=False, logger=None, use_color=True):
@@ -2849,7 +3881,8 @@ class Error(NetlinkPacket):
             for x in range(0, self.LEN/4):
 
                 if self.line_number == 5:
-                    extra = "Error Number %s is %s" % (self.negative_errno, self.error_to_string.get(abs(self.negative_errno)))
+                    error_number = abs(self.negative_errno)
+                    extra = "Error Number %s is %s (%s)" % (self.negative_errno, self.error_to_string.get(error_number), self.error_to_human_readable_string.get(error_number))
                     # zfilled_hex(self.negative_errno, 2)
 
                 elif self.line_number == 6:
@@ -2875,7 +3908,7 @@ class Error(NetlinkPacket):
                 self.line_number += 1
 
 
-class Link(NetlinkPacket):
+class Link(NetlinkPacket, NetlinkPacket_IFLA_LINKINFO_Attributes):
     """
     Service Header
 
@@ -2936,6 +3969,16 @@ class Link(NetlinkPacket):
     IFLA_PROTO_DOWN      = 39
     IFLA_GSO_MAX_SEGS    = 40
     IFLA_GSO_MAX_SIZE    = 41
+    IFLA_PAD             = 42
+    IFLA_XDP             = 43
+    IFLA_EVENT           = 44
+    IFLA_NEW_NETNSID     = 45
+    IFLA_IF_NETNSID      = 46
+    IFLA_CARRIER_UP_COUNT   = 47
+    IFLA_CARRIER_DOWN_COUNT = 48
+    IFLA_NEW_IFINDEX        = 49
+    IFLA_MIN_MTU            = 50
+    IFLA_MAX_MTU            = 51
 
     attribute_to_class = {
         IFLA_UNSPEC          : ('IFLA_UNSPEC', AttributeGeneric),
@@ -2958,7 +4001,7 @@ class Link(NetlinkPacket):
         IFLA_LINKMODE        : ('IFLA_LINKMODE', AttributeOneByteValue),
         IFLA_LINKINFO        : ('IFLA_LINKINFO', AttributeIFLA_LINKINFO),
         IFLA_NET_NS_PID      : ('IFLA_NET_NS_PID', AttributeGeneric),
-        IFLA_IFALIAS         : ('IFLA_IFALIAS', AttributeGeneric),
+        IFLA_IFALIAS         : ('IFLA_IFALIAS', AttributeString),
         IFLA_NUM_VF          : ('IFLA_NUM_VF', AttributeGeneric),
         IFLA_VFINFO_LIST     : ('IFLA_VFINFO_LIST', AttributeGeneric),
         IFLA_STATS64         : ('IFLA_STATS64', AttributeGeneric),
@@ -2979,7 +4022,17 @@ class Link(NetlinkPacket):
         IFLA_PHYS_PORT_NAME  : ('IFLA_PHYS_PORT_NAME', AttributeGeneric),
         IFLA_PROTO_DOWN      : ('IFLA_PROTO_DOWN', AttributeOneByteValue),
         IFLA_GSO_MAX_SEGS    : ('IFLA_GSO_MAX_SEGS', AttributeFourByteValue),
-        IFLA_GSO_MAX_SIZE    : ('IFLA_GSO_MAX_SIZE', AttributeFourByteValue)
+        IFLA_GSO_MAX_SIZE    : ('IFLA_GSO_MAX_SIZE', AttributeFourByteValue),
+        IFLA_PAD             : ('IFLA_PAD', AttributeGeneric),
+        IFLA_XDP             : ('IFLA_XDP', AttributeGeneric),
+        IFLA_EVENT           : ('IFLA_EVENT', AttributeFourByteValue),
+        IFLA_NEW_NETNSID     : ('IFLA_NEW_NETNSID', AttributeFourByteValue),
+        IFLA_IF_NETNSID      : ('IFLA_IF_NETNSID', AttributeFourByteValue),
+        IFLA_CARRIER_UP_COUNT   : ('IFLA_CARRIER_UP_COUNT', AttributeFourByteValue),
+        IFLA_CARRIER_DOWN_COUNT : ('IFLA_CARRIER_DOWN_COUNT', AttributeFourByteValue),
+        IFLA_NEW_IFINDEX        : ('IFLA_NEW_IFINDEX', AttributeFourByteValue),
+        IFLA_MIN_MTU            : ('IFLA_MIN_MTU', AttributeFourByteValue),
+        IFLA_MAX_MTU            : ('IFLA_MAX_MTU', AttributeFourByteValue),
     }
 
     # Link flags
@@ -3115,469 +4168,67 @@ class Link(NetlinkPacket):
     ARPHRD_NONE               = 0xFFFE  # zero header length
 
     link_type_to_string = {
-        ARPHRD_NETROM             : 'ARPHRD_NETROM',
-        ARPHRD_ETHER              : 'ARPHRD_ETHER',
-        ARPHRD_EETHER             : 'ARPHRD_EETHER',
-        ARPHRD_AX25               : 'ARPHRD_AX25',
-        ARPHRD_PRONET             : 'ARPHRD_PRONET',
-        ARPHRD_CHAOS              : 'ARPHRD_CHAOS',
-        ARPHRD_IEEE802            : 'ARPHRD_IEEE802',
-        ARPHRD_ARCNET             : 'ARPHRD_ARCNET',
-        ARPHRD_APPLETLK           : 'ARPHRD_APPLETLK',
-        ARPHRD_DLCI               : 'ARPHRD_DLCI',
-        ARPHRD_ATM                : 'ARPHRD_ATM',
-        ARPHRD_METRICOM           : 'ARPHRD_METRICOM',
-        ARPHRD_IEEE1394           : 'ARPHRD_IEEE1394',
-        ARPHRD_EUI64              : 'ARPHRD_EUI64',
-        ARPHRD_INFINIBAND         : 'ARPHRD_INFINIBAND',
-        ARPHRD_SLIP               : 'ARPHRD_SLIP',
-        ARPHRD_CSLIP              : 'ARPHRD_CSLIP',
-        ARPHRD_SLIP6              : 'ARPHRD_SLIP6',
-        ARPHRD_CSLIP6             : 'ARPHRD_CSLIP6',
-        ARPHRD_RSRVD              : 'ARPHRD_RSRVD',
-        ARPHRD_ADAPT              : 'ARPHRD_ADAPT',
-        ARPHRD_ROSE               : 'ARPHRD_ROSE',
-        ARPHRD_X25                : 'ARPHRD_X25',
-        ARPHRD_HWX25              : 'ARPHRD_HWX25',
-        ARPHRD_CAN                : 'ARPHRD_CAN',
-        ARPHRD_PPP                : 'ARPHRD_PPP',
-        ARPHRD_CISCO              : 'ARPHRD_CISCO',
-        ARPHRD_HDLC               : 'ARPHRD_HDLC',
-        ARPHRD_LAPB               : 'ARPHRD_LAPB',
-        ARPHRD_DDCMP              : 'ARPHRD_DDCMP',
-        ARPHRD_RAWHDLC            : 'ARPHRD_RAWHDLC',
-        ARPHRD_TUNNEL             : 'ARPHRD_TUNNEL',
-        ARPHRD_TUNNEL6            : 'ARPHRD_TUNNEL6',
-        ARPHRD_FRAD               : 'ARPHRD_FRAD',
-        ARPHRD_SKIP               : 'ARPHRD_SKIP',
-        ARPHRD_LOOPBACK           : 'ARPHRD_LOOPBACK',
-        ARPHRD_LOCALTLK           : 'ARPHRD_LOCALTLK',
-        ARPHRD_FDDI               : 'ARPHRD_FDDI',
-        ARPHRD_BIF                : 'ARPHRD_BIF',
-        ARPHRD_SIT                : 'ARPHRD_SIT',
-        ARPHRD_IPDDP              : 'ARPHRD_IPDDP',
-        ARPHRD_IPGRE              : 'ARPHRD_IPGRE',
-        ARPHRD_PIMREG             : 'ARPHRD_PIMREG',
-        ARPHRD_HIPPI              : 'ARPHRD_HIPPI',
-        ARPHRD_ASH                : 'ARPHRD_ASH',
-        ARPHRD_ECONET             : 'ARPHRD_ECONET',
-        ARPHRD_IRDA               : 'ARPHRD_IRDA',
-        ARPHRD_FCPP               : 'ARPHRD_FCPP',
-        ARPHRD_FCAL               : 'ARPHRD_FCAL',
-        ARPHRD_FCPL               : 'ARPHRD_FCPL',
-        ARPHRD_FCFABRIC           : 'ARPHRD_FCFABRIC',
-        ARPHRD_IEEE802_TR         : 'ARPHRD_IEEE802_TR',
-        ARPHRD_IEEE80211          : 'ARPHRD_IEEE80211',
-        ARPHRD_IEEE80211_PRISM    : 'ARPHRD_IEEE80211_PRISM',
-        ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP',
-        ARPHRD_IEEE802154         : 'ARPHRD_IEEE802154',
-        ARPHRD_PHONET             : 'ARPHRD_PHONET',
-        ARPHRD_PHONET_PIPE        : 'ARPHRD_PHONET_PIPE',
-        ARPHRD_CAIF               : 'ARPHRD_CAIF',
-        ARPHRD_VOID               : 'ARPHRD_VOID',
-        ARPHRD_NONE               : 'ARPHRD_NONE'
-    }
-
-    # =========================================
-    # IFLA_LINKINFO attributes
-    # =========================================
-    IFLA_INFO_UNSPEC     = 0
-    IFLA_INFO_KIND       = 1
-    IFLA_INFO_DATA       = 2
-    IFLA_INFO_XSTATS     = 3
-    IFLA_INFO_SLAVE_KIND = 4
-    IFLA_INFO_SLAVE_DATA = 5
-    IFLA_INFO_MAX        = 6
-
-    ifla_info_to_string = {
-        IFLA_INFO_UNSPEC     : 'IFLA_INFO_UNSPEC',
-        IFLA_INFO_KIND       : 'IFLA_INFO_KIND',
-        IFLA_INFO_DATA       : 'IFLA_INFO_DATA',
-        IFLA_INFO_XSTATS     : 'IFLA_INFO_XSTATS',
-        IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND',
-        IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA',
-        IFLA_INFO_MAX        : 'IFLA_INFO_MAX'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for vlan
-    # =========================================
-    IFLA_VLAN_UNSPEC      = 0
-    IFLA_VLAN_ID          = 1
-    IFLA_VLAN_FLAGS       = 2
-    IFLA_VLAN_EGRESS_QOS  = 3
-    IFLA_VLAN_INGRESS_QOS = 4
-    IFLA_VLAN_PROTOCOL    = 5
-
-    ifla_vlan_to_string = {
-        IFLA_VLAN_UNSPEC      : 'IFLA_VLAN_UNSPEC',
-        IFLA_VLAN_ID          : 'IFLA_VLAN_ID',
-        IFLA_VLAN_FLAGS       : 'IFLA_VLAN_FLAGS',
-        IFLA_VLAN_EGRESS_QOS  : 'IFLA_VLAN_EGRESS_QOS',
-        IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS',
-        IFLA_VLAN_PROTOCOL    : 'IFLA_VLAN_PROTOCOL'
-    }
-
-    ifla_vlan_protocol_dict = {
-        '802.1Q':   0x8100,
-        '802.1q':   0x8100,
-
-        '802.1ad':  0x88A8,
-        '802.1AD':  0x88A8,
-        '802.1Ad':  0x88A8,
-        '802.1aD':  0x88A8,
-
-        0x8100:     '802.1Q',
-        0x88A8:     '802.1ad'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for macvlan
-    # =========================================
-    IFLA_MACVLAN_UNSPEC = 0
-    IFLA_MACVLAN_MODE   = 1
-
-    ifla_macvlan_to_string = {
-        IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC',
-        IFLA_MACVLAN_MODE   : 'IFLA_MACVLAN_MODE'
-    }
-
-    # macvlan modes
-    MACVLAN_MODE_PRIVATE  = 1
-    MACVLAN_MODE_VEPA     = 2
-    MACVLAN_MODE_BRIDGE   = 3
-    MACVLAN_MODE_PASSTHRU = 4
-
-    macvlan_mode_to_string = {
-        MACVLAN_MODE_PRIVATE  : 'MACVLAN_MODE_PRIVATE',
-        MACVLAN_MODE_VEPA     : 'MACVLAN_MODE_VEPA',
-        MACVLAN_MODE_BRIDGE   : 'MACVLAN_MODE_BRIDGE',
-        MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for xfrm
-    # =========================================
-    IFLA_XFRM_UNSPEC = 0
-    IFLA_XFRM_LINK   = 1
-    IFLA_XFRM_IF_ID  = 2
-
-    ifla_xfrm_to_string = {
-        IFLA_XFRM_UNSPEC : 'IFLA_XFRM_UNSPEC',
-        IFLA_XFRM_LINK   : 'IFLA_XFRM_LINK',
-        IFLA_XFRM_IF_ID   : 'IFLA_XFRM_IF_ID'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for vxlan
-    # =========================================
-    IFLA_VXLAN_UNSPEC            = 0
-    IFLA_VXLAN_ID                = 1
-    IFLA_VXLAN_GROUP             = 2
-    IFLA_VXLAN_LINK              = 3
-    IFLA_VXLAN_LOCAL             = 4
-    IFLA_VXLAN_TTL               = 5
-    IFLA_VXLAN_TOS               = 6
-    IFLA_VXLAN_LEARNING          = 7
-    IFLA_VXLAN_AGEING            = 8
-    IFLA_VXLAN_LIMIT             = 9
-    IFLA_VXLAN_PORT_RANGE        = 10
-    IFLA_VXLAN_PROXY             = 11
-    IFLA_VXLAN_RSC               = 12
-    IFLA_VXLAN_L2MISS            = 13
-    IFLA_VXLAN_L3MISS            = 14
-    IFLA_VXLAN_PORT              = 15
-    IFLA_VXLAN_GROUP6            = 16
-    IFLA_VXLAN_LOCAL6            = 17
-    IFLA_VXLAN_UDP_CSUM          = 18
-    IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19
-    IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20
-    IFLA_VXLAN_REMCSUM_TX        = 21
-    IFLA_VXLAN_REMCSUM_RX        = 22
-    IFLA_VXLAN_GBP               = 23
-    IFLA_VXLAN_REMCSUM_NOPARTIAL = 24
-    IFLA_VXLAN_COLLECT_METADATA  = 25
-    IFLA_VXLAN_REPLICATION_NODE  = 253
-    IFLA_VXLAN_REPLICATION_TYPE  = 254
-
-    ifla_vxlan_to_string = {
-        IFLA_VXLAN_UNSPEC            : 'IFLA_VXLAN_UNSPEC',
-        IFLA_VXLAN_ID                : 'IFLA_VXLAN_ID',
-        IFLA_VXLAN_GROUP             : 'IFLA_VXLAN_GROUP',
-        IFLA_VXLAN_LINK              : 'IFLA_VXLAN_LINK',
-        IFLA_VXLAN_LOCAL             : 'IFLA_VXLAN_LOCAL',
-        IFLA_VXLAN_TTL               : 'IFLA_VXLAN_TTL',
-        IFLA_VXLAN_TOS               : 'IFLA_VXLAN_TOS',
-        IFLA_VXLAN_LEARNING          : 'IFLA_VXLAN_LEARNING',
-        IFLA_VXLAN_AGEING            : 'IFLA_VXLAN_AGEING',
-        IFLA_VXLAN_LIMIT             : 'IFLA_VXLAN_LIMIT',
-        IFLA_VXLAN_PORT_RANGE        : 'IFLA_VXLAN_PORT_RANGE',
-        IFLA_VXLAN_PROXY             : 'IFLA_VXLAN_PROXY',
-        IFLA_VXLAN_RSC               : 'IFLA_VXLAN_RSC',
-        IFLA_VXLAN_L2MISS            : 'IFLA_VXLAN_L2MISS',
-        IFLA_VXLAN_L3MISS            : 'IFLA_VXLAN_L3MISS',
-        IFLA_VXLAN_PORT              : 'IFLA_VXLAN_PORT',
-        IFLA_VXLAN_GROUP6            : 'IFLA_VXLAN_GROUP6',
-        IFLA_VXLAN_LOCAL6            : 'IFLA_VXLAN_LOCAL6',
-        IFLA_VXLAN_UDP_CSUM          : 'IFLA_VXLAN_UDP_CSUM',
-        IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX',
-        IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX',
-        IFLA_VXLAN_REMCSUM_TX        : 'IFLA_VXLAN_REMCSUM_TX',
-        IFLA_VXLAN_REMCSUM_RX        : 'IFLA_VXLAN_REMCSUM_RX',
-        IFLA_VXLAN_GBP               : 'IFLA_VXLAN_GBP',
-        IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL',
-        IFLA_VXLAN_COLLECT_METADATA  : 'IFLA_VXLAN_COLLECT_METADATA',
-        IFLA_VXLAN_REPLICATION_NODE  : 'IFLA_VXLAN_REPLICATION_NODE',
-        IFLA_VXLAN_REPLICATION_TYPE  : 'IFLA_VXLAN_REPLICATION_TYPE'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for bonds
-    # =========================================
-    IFLA_BOND_UNSPEC                    = 0
-    IFLA_BOND_MODE                      = 1
-    IFLA_BOND_ACTIVE_SLAVE              = 2
-    IFLA_BOND_MIIMON                    = 3
-    IFLA_BOND_UPDELAY                   = 4
-    IFLA_BOND_DOWNDELAY                 = 5
-    IFLA_BOND_USE_CARRIER               = 6
-    IFLA_BOND_ARP_INTERVAL              = 7
-    IFLA_BOND_ARP_IP_TARGET             = 8
-    IFLA_BOND_ARP_VALIDATE              = 9
-    IFLA_BOND_ARP_ALL_TARGETS           = 10
-    IFLA_BOND_PRIMARY                   = 11
-    IFLA_BOND_PRIMARY_RESELECT          = 12
-    IFLA_BOND_FAIL_OVER_MAC             = 13
-    IFLA_BOND_XMIT_HASH_POLICY          = 14
-    IFLA_BOND_RESEND_IGMP               = 15
-    IFLA_BOND_NUM_PEER_NOTIF            = 16
-    IFLA_BOND_ALL_SLAVES_ACTIVE         = 17
-    IFLA_BOND_MIN_LINKS                 = 18
-    IFLA_BOND_LP_INTERVAL               = 19
-    IFLA_BOND_PACKETS_PER_SLAVE         = 20
-    IFLA_BOND_AD_LACP_RATE              = 21
-    IFLA_BOND_AD_SELECT                 = 22
-    IFLA_BOND_AD_INFO                   = 23
-    IFLA_BOND_AD_ACTOR_SYS_PRIO         = 24
-    IFLA_BOND_AD_USER_PORT_KEY          = 25
-    IFLA_BOND_AD_ACTOR_SYSTEM           = 26
-    IFLA_BOND_AD_LACP_BYPASS            = 100
-
-    ifla_bond_to_string = {
-        IFLA_BOND_UNSPEC                    : 'IFLA_BOND_UNSPEC',
-        IFLA_BOND_MODE                      : 'IFLA_BOND_MODE',
-        IFLA_BOND_ACTIVE_SLAVE              : 'IFLA_BOND_ACTIVE_SLAVE',
-        IFLA_BOND_MIIMON                    : 'IFLA_BOND_MIIMON',
-        IFLA_BOND_UPDELAY                   : 'IFLA_BOND_UPDELAY',
-        IFLA_BOND_DOWNDELAY                 : 'IFLA_BOND_DOWNDELAY',
-        IFLA_BOND_USE_CARRIER               : 'IFLA_BOND_USE_CARRIER',
-        IFLA_BOND_ARP_INTERVAL              : 'IFLA_BOND_ARP_INTERVAL',
-        IFLA_BOND_ARP_IP_TARGET             : 'IFLA_BOND_ARP_IP_TARGET',
-        IFLA_BOND_ARP_VALIDATE              : 'IFLA_BOND_ARP_VALIDATE',
-        IFLA_BOND_ARP_ALL_TARGETS           : 'IFLA_BOND_ARP_ALL_TARGETS',
-        IFLA_BOND_PRIMARY                   : 'IFLA_BOND_PRIMARY',
-        IFLA_BOND_PRIMARY_RESELECT          : 'IFLA_BOND_PRIMARY_RESELECT',
-        IFLA_BOND_FAIL_OVER_MAC             : 'IFLA_BOND_FAIL_OVER_MAC',
-        IFLA_BOND_XMIT_HASH_POLICY          : 'IFLA_BOND_XMIT_HASH_POLICY',
-        IFLA_BOND_RESEND_IGMP               : 'IFLA_BOND_RESEND_IGMP',
-        IFLA_BOND_NUM_PEER_NOTIF            : 'IFLA_BOND_NUM_PEER_NOTIF',
-        IFLA_BOND_ALL_SLAVES_ACTIVE         : 'IFLA_BOND_ALL_SLAVES_ACTIVE',
-        IFLA_BOND_MIN_LINKS                 : 'IFLA_BOND_MIN_LINKS',
-        IFLA_BOND_LP_INTERVAL               : 'IFLA_BOND_LP_INTERVAL',
-        IFLA_BOND_PACKETS_PER_SLAVE         : 'IFLA_BOND_PACKETS_PER_SLAVE',
-        IFLA_BOND_AD_LACP_RATE              : 'IFLA_BOND_AD_LACP_RATE',
-        IFLA_BOND_AD_SELECT                 : 'IFLA_BOND_AD_SELECT',
-        IFLA_BOND_AD_INFO                   : 'IFLA_BOND_AD_INFO',
-        IFLA_BOND_AD_ACTOR_SYS_PRIO         : 'IFLA_BOND_AD_ACTOR_SYS_PRIO',
-        IFLA_BOND_AD_USER_PORT_KEY          : 'IFLA_BOND_AD_USER_PORT_KEY',
-        IFLA_BOND_AD_ACTOR_SYSTEM           : 'IFLA_BOND_AD_ACTOR_SYSTEM',
-        IFLA_BOND_AD_LACP_BYPASS            : 'IFLA_BOND_AD_LACP_BYPASS'
-    }
-
-    IFLA_BOND_AD_INFO_UNSPEC            = 0
-    IFLA_BOND_AD_INFO_AGGREGATOR        = 1
-    IFLA_BOND_AD_INFO_NUM_PORTS         = 2
-    IFLA_BOND_AD_INFO_ACTOR_KEY         = 3
-    IFLA_BOND_AD_INFO_PARTNER_KEY       = 4
-    IFLA_BOND_AD_INFO_PARTNER_MAC       = 5
-
-    ifla_bond_ad_to_string = {
-        IFLA_BOND_AD_INFO_UNSPEC            : 'IFLA_BOND_AD_INFO_UNSPEC',
-        IFLA_BOND_AD_INFO_AGGREGATOR        : 'IFLA_BOND_AD_INFO_AGGREGATOR',
-        IFLA_BOND_AD_INFO_NUM_PORTS         : 'IFLA_BOND_AD_INFO_NUM_PORTS',
-        IFLA_BOND_AD_INFO_ACTOR_KEY         : 'IFLA_BOND_AD_INFO_ACTOR_KEY',
-        IFLA_BOND_AD_INFO_PARTNER_KEY       : 'IFLA_BOND_AD_INFO_PARTNER_KEY',
-        IFLA_BOND_AD_INFO_PARTNER_MAC       : 'IFLA_BOND_AD_INFO_PARTNER_MAC'
-    }
-
-    ifla_bond_mode_tbl = {
-        'balance-rr': 0,
-        'active-backup': 1,
-        'balance-xor': 2,
-        'broadcast': 3,
-        '802.3ad': 4,
-        'balance-tlb': 5,
-        'balance-alb': 6,
-        '0': 0,
-        '1': 1,
-        '2': 2,
-        '3': 3,
-        '4': 4,
-        '5': 5,
-        '6': 6,
-        0: 0,
-        1: 1,
-        2: 2,
-        3: 3,
-        4: 4,
-        5: 5,
-        6: 6
-    }
-
-    ifla_bond_mode_pretty_tbl = {
-        0: 'balance-rr',
-        1: 'active-backup',
-        2: 'balance-xor',
-        3: 'broadcast',
-        4: '802.3ad',
-        5: 'balance-tlb',
-        6: 'balance-alb'
-    }
-
-    ifla_bond_xmit_hash_policy_tbl = {
-        'layer2': 0,
-        'layer3+4': 1,
-        'layer2+3': 2,
-        'encap2+3': 3,
-        'encap3+4': 4,
-        '0': 0,
-        '1': 1,
-        '2': 2,
-        '3': 3,
-        '4': 4,
-        0: 0,
-        1: 1,
-        2: 2,
-        3: 3,
-        4: 4
-    }
-
-    ifla_bond_xmit_hash_policy_pretty_tbl = {
-        0: 'layer2',
-        1: 'layer3+4',
-        2: 'layer2+3',
-        3: 'encap2+3',
-        4: 'encap3+4',
-    }
-
-    # =========================================
-    # IFLA_INFO_SLAVE_DATA attributes for bonds
-    # =========================================
-    IFLA_BOND_SLAVE_UNSPEC                      = 0
-    IFLA_BOND_SLAVE_STATE                       = 1
-    IFLA_BOND_SLAVE_MII_STATUS                  = 2
-    IFLA_BOND_SLAVE_LINK_FAILURE_COUNT          = 3
-    IFLA_BOND_SLAVE_PERM_HWADDR                 = 4
-    IFLA_BOND_SLAVE_QUEUE_ID                    = 5
-    IFLA_BOND_SLAVE_AD_AGGREGATOR_ID            = 6
-    IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE    = 7
-    IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE  = 8
-    IFLA_BOND_SLAVE_CL_START                    = 50
-    IFLA_BOND_SLAVE_AD_RX_BYPASS                = IFLA_BOND_SLAVE_CL_START
-
-    ifla_bond_slave_to_string = {
-        IFLA_BOND_SLAVE_UNSPEC                      : 'IFLA_BOND_SLAVE_UNSPEC',
-        IFLA_BOND_SLAVE_STATE                       : 'IFLA_BOND_SLAVE_STATE',
-        IFLA_BOND_SLAVE_MII_STATUS                  : 'IFLA_BOND_SLAVE_MII_STATUS',
-        IFLA_BOND_SLAVE_LINK_FAILURE_COUNT          : 'IFLA_BOND_SLAVE_LINK_FAILURE_COUNT',
-        IFLA_BOND_SLAVE_PERM_HWADDR                 : 'IFLA_BOND_SLAVE_PERM_HWADDR',
-        IFLA_BOND_SLAVE_QUEUE_ID                    : 'IFLA_BOND_SLAVE_QUEUE_ID',
-        IFLA_BOND_SLAVE_AD_AGGREGATOR_ID            : 'IFLA_BOND_SLAVE_AD_AGGREGATOR_ID',
-        IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE    : 'IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE',
-        IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE  : 'IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE',
-        IFLA_BOND_SLAVE_CL_START                    : 'IFLA_BOND_SLAVE_CL_START',
-        IFLA_BOND_SLAVE_AD_RX_BYPASS                : 'IFLA_BOND_SLAVE_AD_RX_BYPASS'
-    }
-
-    # =========================================
-    # IFLA_PROTINFO attributes for bridge ports
-    # =========================================
-    IFLA_BRPORT_UNSPEC              = 0
-    IFLA_BRPORT_STATE               = 1
-    IFLA_BRPORT_PRIORITY            = 2
-    IFLA_BRPORT_COST                = 3
-    IFLA_BRPORT_MODE                = 4
-    IFLA_BRPORT_GUARD               = 5
-    IFLA_BRPORT_PROTECT             = 6
-    IFLA_BRPORT_FAST_LEAVE          = 7
-    IFLA_BRPORT_LEARNING            = 8
-    IFLA_BRPORT_UNICAST_FLOOD       = 9
-    IFLA_BRPORT_PROXYARP            = 10
-    IFLA_BRPORT_LEARNING_SYNC       = 11
-    IFLA_BRPORT_PROXYARP_WIFI       = 12
-    IFLA_BRPORT_ROOT_ID             = 13
-    IFLA_BRPORT_BRIDGE_ID           = 14
-    IFLA_BRPORT_DESIGNATED_PORT     = 15
-    IFLA_BRPORT_DESIGNATED_COST     = 16
-    IFLA_BRPORT_ID                  = 17
-    IFLA_BRPORT_NO                  = 18
-    IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19
-    IFLA_BRPORT_CONFIG_PENDING      = 20
-    IFLA_BRPORT_MESSAGE_AGE_TIMER   = 21
-    IFLA_BRPORT_FORWARD_DELAY_TIMER = 22
-    IFLA_BRPORT_HOLD_TIMER          = 23
-    IFLA_BRPORT_FLUSH               = 24
-    IFLA_BRPORT_MULTICAST_ROUTER    = 25
-    IFLA_BRPORT_PAD                 = 26
-    IFLA_BRPORT_MCAST_FLOOD         = 27
-    IFLA_BRPORT_MCAST_TO_UCAST      = 28
-    IFLA_BRPORT_VLAN_TUNNEL         = 29
-    IFLA_BRPORT_BCAST_FLOOD         = 30
-    IFLA_BRPORT_GROUP_FWD_MASK      = 31
-    IFLA_BRPORT_ARP_SUPPRESS        = 32
-    IFLA_BRPORT_PEER_LINK           = 150
-    IFLA_BRPORT_DUAL_LINK           = 151
-    IFLA_BRPORT_GROUP_FWD_MASKHI    = 153
-    IFLA_BRPORT_DOWN_PEERLINK_REDIRECT = 154
-
-    ifla_brport_to_string = {
-        IFLA_BRPORT_UNSPEC              : 'IFLA_BRPORT_UNSPEC',
-        IFLA_BRPORT_STATE               : 'IFLA_BRPORT_STATE',
-        IFLA_BRPORT_PRIORITY            : 'IFLA_BRPORT_PRIORITY',
-        IFLA_BRPORT_COST                : 'IFLA_BRPORT_COST',
-        IFLA_BRPORT_MODE                : 'IFLA_BRPORT_MODE',
-        IFLA_BRPORT_GUARD               : 'IFLA_BRPORT_GUARD',
-        IFLA_BRPORT_PROTECT             : 'IFLA_BRPORT_PROTECT',
-        IFLA_BRPORT_FAST_LEAVE          : 'IFLA_BRPORT_FAST_LEAVE',
-        IFLA_BRPORT_LEARNING            : 'IFLA_BRPORT_LEARNING',
-        IFLA_BRPORT_UNICAST_FLOOD       : 'IFLA_BRPORT_UNICAST_FLOOD',
-        IFLA_BRPORT_PROXYARP            : 'IFLA_BRPORT_PROXYARP',
-        IFLA_BRPORT_LEARNING_SYNC       : 'IFLA_BRPORT_LEARNING_SYNC',
-        IFLA_BRPORT_PROXYARP_WIFI       : 'IFLA_BRPORT_PROXYARP_WIFI',
-        IFLA_BRPORT_ROOT_ID             : 'IFLA_BRPORT_ROOT_ID',
-        IFLA_BRPORT_BRIDGE_ID           : 'IFLA_BRPORT_BRIDGE_ID',
-        IFLA_BRPORT_DESIGNATED_PORT     : 'IFLA_BRPORT_DESIGNATED_PORT',
-        IFLA_BRPORT_DESIGNATED_COST     : 'IFLA_BRPORT_DESIGNATED_COST',
-        IFLA_BRPORT_ID                  : 'IFLA_BRPORT_ID',
-        IFLA_BRPORT_NO                  : 'IFLA_BRPORT_NO',
-        IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK',
-        IFLA_BRPORT_CONFIG_PENDING      : 'IFLA_BRPORT_CONFIG_PENDING',
-        IFLA_BRPORT_MESSAGE_AGE_TIMER   : 'IFLA_BRPORT_MESSAGE_AGE_TIMER',
-        IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER',
-        IFLA_BRPORT_HOLD_TIMER          : 'IFLA_BRPORT_HOLD_TIMER',
-        IFLA_BRPORT_FLUSH               : 'IFLA_BRPORT_FLUSH',
-        IFLA_BRPORT_MULTICAST_ROUTER    : 'IFLA_BRPORT_MULTICAST_ROUTER',
-        IFLA_BRPORT_PAD                 : 'IFLA_BRPORT_PAD',
-        IFLA_BRPORT_MCAST_FLOOD         : 'IFLA_BRPORT_MCAST_FLOOD',
-        IFLA_BRPORT_MCAST_TO_UCAST      : 'IFLA_BRPORT_MCAST_TO_UCAST',
-        IFLA_BRPORT_VLAN_TUNNEL         : 'IFLA_BRPORT_VLAN_TUNNEL',
-        IFLA_BRPORT_BCAST_FLOOD         : 'IFLA_BRPORT_BCAST_FLOOD',
-        IFLA_BRPORT_GROUP_FWD_MASK      : 'IFLA_BRPORT_GROUP_FWD_MASK',
-        IFLA_BRPORT_PEER_LINK           : 'IFLA_BRPORT_PEER_LINK',
-        IFLA_BRPORT_DUAL_LINK           : 'IFLA_BRPORT_DUAL_LINK',
-        IFLA_BRPORT_ARP_SUPPRESS        : 'IFLA_BRPORT_ARP_SUPPRESS',
-        IFLA_BRPORT_GROUP_FWD_MASKHI    : 'IFLA_BRPORT_GROUP_FWD_MASKHI',
-        IFLA_BRPORT_DOWN_PEERLINK_REDIRECT : 'IFLA_BRPORT_DOWN_PEERLINK_REDIRECT'
+        ARPHRD_NETROM             : 'ARPHRD_NETROM',
+        ARPHRD_ETHER              : 'ARPHRD_ETHER',
+        ARPHRD_EETHER             : 'ARPHRD_EETHER',
+        ARPHRD_AX25               : 'ARPHRD_AX25',
+        ARPHRD_PRONET             : 'ARPHRD_PRONET',
+        ARPHRD_CHAOS              : 'ARPHRD_CHAOS',
+        ARPHRD_IEEE802            : 'ARPHRD_IEEE802',
+        ARPHRD_ARCNET             : 'ARPHRD_ARCNET',
+        ARPHRD_APPLETLK           : 'ARPHRD_APPLETLK',
+        ARPHRD_DLCI               : 'ARPHRD_DLCI',
+        ARPHRD_ATM                : 'ARPHRD_ATM',
+        ARPHRD_METRICOM           : 'ARPHRD_METRICOM',
+        ARPHRD_IEEE1394           : 'ARPHRD_IEEE1394',
+        ARPHRD_EUI64              : 'ARPHRD_EUI64',
+        ARPHRD_INFINIBAND         : 'ARPHRD_INFINIBAND',
+        ARPHRD_SLIP               : 'ARPHRD_SLIP',
+        ARPHRD_CSLIP              : 'ARPHRD_CSLIP',
+        ARPHRD_SLIP6              : 'ARPHRD_SLIP6',
+        ARPHRD_CSLIP6             : 'ARPHRD_CSLIP6',
+        ARPHRD_RSRVD              : 'ARPHRD_RSRVD',
+        ARPHRD_ADAPT              : 'ARPHRD_ADAPT',
+        ARPHRD_ROSE               : 'ARPHRD_ROSE',
+        ARPHRD_X25                : 'ARPHRD_X25',
+        ARPHRD_HWX25              : 'ARPHRD_HWX25',
+        ARPHRD_CAN                : 'ARPHRD_CAN',
+        ARPHRD_PPP                : 'ARPHRD_PPP',
+        ARPHRD_CISCO              : 'ARPHRD_CISCO',
+        ARPHRD_HDLC               : 'ARPHRD_HDLC',
+        ARPHRD_LAPB               : 'ARPHRD_LAPB',
+        ARPHRD_DDCMP              : 'ARPHRD_DDCMP',
+        ARPHRD_RAWHDLC            : 'ARPHRD_RAWHDLC',
+        ARPHRD_TUNNEL             : 'ARPHRD_TUNNEL',
+        ARPHRD_TUNNEL6            : 'ARPHRD_TUNNEL6',
+        ARPHRD_FRAD               : 'ARPHRD_FRAD',
+        ARPHRD_SKIP               : 'ARPHRD_SKIP',
+        ARPHRD_LOOPBACK           : 'ARPHRD_LOOPBACK',
+        ARPHRD_LOCALTLK           : 'ARPHRD_LOCALTLK',
+        ARPHRD_FDDI               : 'ARPHRD_FDDI',
+        ARPHRD_BIF                : 'ARPHRD_BIF',
+        ARPHRD_SIT                : 'ARPHRD_SIT',
+        ARPHRD_IPDDP              : 'ARPHRD_IPDDP',
+        ARPHRD_IPGRE              : 'ARPHRD_IPGRE',
+        ARPHRD_PIMREG             : 'ARPHRD_PIMREG',
+        ARPHRD_HIPPI              : 'ARPHRD_HIPPI',
+        ARPHRD_ASH                : 'ARPHRD_ASH',
+        ARPHRD_ECONET             : 'ARPHRD_ECONET',
+        ARPHRD_IRDA               : 'ARPHRD_IRDA',
+        ARPHRD_FCPP               : 'ARPHRD_FCPP',
+        ARPHRD_FCAL               : 'ARPHRD_FCAL',
+        ARPHRD_FCPL               : 'ARPHRD_FCPL',
+        ARPHRD_FCFABRIC           : 'ARPHRD_FCFABRIC',
+        ARPHRD_IEEE802_TR         : 'ARPHRD_IEEE802_TR',
+        ARPHRD_IEEE80211          : 'ARPHRD_IEEE80211',
+        ARPHRD_IEEE80211_PRISM    : 'ARPHRD_IEEE80211_PRISM',
+        ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP',
+        ARPHRD_IEEE802154         : 'ARPHRD_IEEE802154',
+        ARPHRD_PHONET             : 'ARPHRD_PHONET',
+        ARPHRD_PHONET_PIPE        : 'ARPHRD_PHONET_PIPE',
+        ARPHRD_CAIF               : 'ARPHRD_CAIF',
+        ARPHRD_VOID               : 'ARPHRD_VOID',
+        ARPHRD_NONE               : 'ARPHRD_NONE'
     }
 
     # Subtype attributes for IFLA_AF_SPEC
@@ -3615,298 +4266,66 @@ class Link(NetlinkPacket):
         IN6_ADDR_GEN_MODE_NONE: "none",
         IN6_ADDR_GEN_MODE_STABLE_PRIVACY: "stable_secret",
         IN6_ADDR_GEN_MODE_RANDOM: "random"
-    }
-
-    # Subtype attrbutes AF_INET
-    IFLA_INET_UNSPEC    = 0
-    IFLA_INET_CONF      = 1
-    __IFLA_INET_MAX     = 2
-
-    ifla_inet_af_spec_to_string = {
-        IFLA_INET_UNSPEC    : 'IFLA_INET_UNSPEC',
-        IFLA_INET_CONF      : 'IFLA_INET_CONF',
-    }
-
-    # BRIDGE IFLA_AF_SPEC attributes
-    IFLA_BRIDGE_FLAGS     = 0
-    IFLA_BRIDGE_MODE      = 1
-    IFLA_BRIDGE_VLAN_INFO = 2
-
-    ifla_bridge_af_spec_to_string = {
-        IFLA_BRIDGE_FLAGS     : 'IFLA_BRIDGE_FLAGS',
-        IFLA_BRIDGE_MODE      : 'IFLA_BRIDGE_MODE',
-        IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO'
-    }
-
-    # BRIDGE_VLAN_INFO flags
-    BRIDGE_VLAN_INFO_MASTER      = 1 << 0  # Operate on Bridge device as well
-    BRIDGE_VLAN_INFO_PVID        = 1 << 1  # VLAN is PVID, ingress untagged
-    BRIDGE_VLAN_INFO_UNTAGGED    = 1 << 2  # VLAN egresses untagged
-    BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3  # VLAN is start of vlan range
-    BRIDGE_VLAN_INFO_RANGE_END   = 1 << 4  # VLAN is end of vlan range
-    BRIDGE_VLAN_INFO_BRENTRY     = 1 << 5  # Global bridge VLAN entry
-
-    bridge_vlan_to_string = {
-        BRIDGE_VLAN_INFO_MASTER      : 'BRIDGE_VLAN_INFO_MASTER',
-        BRIDGE_VLAN_INFO_PVID        : 'BRIDGE_VLAN_INFO_PVID',
-        BRIDGE_VLAN_INFO_UNTAGGED    : 'BRIDGE_VLAN_INFO_UNTAGGED',
-        BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN',
-        BRIDGE_VLAN_INFO_RANGE_END   : 'BRIDGE_VLAN_INFO_RANGE_END',
-        BRIDGE_VLAN_INFO_BRENTRY     : 'BRIDGE_VLAN_INFO_BRENTRY'
-    }
-
-    # Bridge flags
-    BRIDGE_FLAGS_MASTER = 1
-    BRIDGE_FLAGS_SELF   = 2
-
-    bridge_flags_to_string = {
-        BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER',
-        BRIDGE_FLAGS_SELF   : 'BRIDGE_FLAGS_SELF'
-    }
-
-    # filters for IFLA_EXT_MASK
-    RTEXT_FILTER_VF                = 1 << 0
-    RTEXT_FILTER_BRVLAN            = 1 << 1
-    RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2
-    RTEXT_FILTER_SKIP_STATS        = 1 << 3
-
-    rtext_to_string = {
-        RTEXT_FILTER_VF                : 'RTEXT_FILTER_VF',
-        RTEXT_FILTER_BRVLAN            : 'RTEXT_FILTER_BRVLAN',
-        RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED',
-        RTEXT_FILTER_SKIP_STATS        : 'RTEXT_FILTER_SKIP_STATS'
-    }
-
-    IFLA_BR_UNSPEC                     =  0
-    IFLA_BR_FORWARD_DELAY              =  1
-    IFLA_BR_HELLO_TIME                 =  2
-    IFLA_BR_MAX_AGE                    =  3
-    IFLA_BR_AGEING_TIME                =  4
-    IFLA_BR_STP_STATE                  =  5
-    IFLA_BR_PRIORITY                   =  6
-    IFLA_BR_VLAN_FILTERING             =  7
-    IFLA_BR_VLAN_PROTOCOL              =  8
-    IFLA_BR_GROUP_FWD_MASK             =  9
-    IFLA_BR_ROOT_ID                    = 10
-    IFLA_BR_BRIDGE_ID                  = 11
-    IFLA_BR_ROOT_PORT                  = 12
-    IFLA_BR_ROOT_PATH_COST             = 13
-    IFLA_BR_TOPOLOGY_CHANGE            = 14
-    IFLA_BR_TOPOLOGY_CHANGE_DETECTED   = 15
-    IFLA_BR_HELLO_TIMER                = 16
-    IFLA_BR_TCN_TIMER                  = 17
-    IFLA_BR_TOPOLOGY_CHANGE_TIMER      = 18
-    IFLA_BR_GC_TIMER                   = 19
-    IFLA_BR_GROUP_ADDR                 = 20
-    IFLA_BR_FDB_FLUSH                  = 21
-    IFLA_BR_MCAST_ROUTER               = 22
-    IFLA_BR_MCAST_SNOOPING             = 23
-    IFLA_BR_MCAST_QUERY_USE_IFADDR     = 24
-    IFLA_BR_MCAST_QUERIER              = 25
-    IFLA_BR_MCAST_HASH_ELASTICITY      = 26
-    IFLA_BR_MCAST_HASH_MAX             = 27
-    IFLA_BR_MCAST_LAST_MEMBER_CNT      = 28
-    IFLA_BR_MCAST_STARTUP_QUERY_CNT    = 29
-    IFLA_BR_MCAST_LAST_MEMBER_INTVL    = 30
-    IFLA_BR_MCAST_MEMBERSHIP_INTVL     = 31
-    IFLA_BR_MCAST_QUERIER_INTVL        = 32
-    IFLA_BR_MCAST_QUERY_INTVL          = 33
-    IFLA_BR_MCAST_QUERY_RESPONSE_INTVL = 34
-    IFLA_BR_MCAST_STARTUP_QUERY_INTVL  = 35
-    IFLA_BR_NF_CALL_IPTABLES           = 36
-    IFLA_BR_NF_CALL_IP6TABLES          = 37
-    IFLA_BR_NF_CALL_ARPTABLES          = 38
-    IFLA_BR_VLAN_DEFAULT_PVID          = 39
-    IFLA_BR_PAD                        = 40
-    IFLA_BR_VLAN_STATS_ENABLED         = 41
-    IFLA_BR_MCAST_STATS_ENABLED        = 42
-    IFLA_BR_MCAST_IGMP_VERSION         = 43
-    IFLA_BR_MCAST_MLD_VERSION          = 44
-
-    ifla_br_to_string = {
-        IFLA_BR_UNSPEC                     : 'IFLA_BR_UNSPEC',
-        IFLA_BR_FORWARD_DELAY              : 'IFLA_BR_FORWARD_DELAY',
-        IFLA_BR_HELLO_TIME                 : 'IFLA_BR_HELLO_TIME',
-        IFLA_BR_MAX_AGE                    : 'IFLA_BR_MAX_AGE',
-        IFLA_BR_AGEING_TIME                : 'IFLA_BR_AGEING_TIME',
-        IFLA_BR_STP_STATE                  : 'IFLA_BR_STP_STATE',
-        IFLA_BR_PRIORITY                   : 'IFLA_BR_PRIORITY',
-        IFLA_BR_VLAN_FILTERING             : 'IFLA_BR_VLAN_FILTERING',
-        IFLA_BR_VLAN_PROTOCOL              : 'IFLA_BR_VLAN_PROTOCOL',
-        IFLA_BR_GROUP_FWD_MASK             : 'IFLA_BR_GROUP_FWD_MASK',
-        IFLA_BR_ROOT_ID                    : 'IFLA_BR_ROOT_ID',
-        IFLA_BR_BRIDGE_ID                  : 'IFLA_BR_BRIDGE_ID',
-        IFLA_BR_ROOT_PORT                  : 'IFLA_BR_ROOT_PORT',
-        IFLA_BR_ROOT_PATH_COST             : 'IFLA_BR_ROOT_PATH_COST',
-        IFLA_BR_TOPOLOGY_CHANGE            : 'IFLA_BR_TOPOLOGY_CHANGE',
-        IFLA_BR_TOPOLOGY_CHANGE_DETECTED   : 'IFLA_BR_TOPOLOGY_CHANGE_DETECTED',
-        IFLA_BR_HELLO_TIMER                : 'IFLA_BR_HELLO_TIMER',
-        IFLA_BR_TCN_TIMER                  : 'IFLA_BR_TCN_TIMER',
-        IFLA_BR_TOPOLOGY_CHANGE_TIMER      : 'IFLA_BR_TOPOLOGY_CHANGE_TIMER',
-        IFLA_BR_GC_TIMER                   : 'IFLA_BR_GC_TIMER',
-        IFLA_BR_GROUP_ADDR                 : 'IFLA_BR_GROUP_ADDR',
-        IFLA_BR_FDB_FLUSH                  : 'IFLA_BR_FDB_FLUSH',
-        IFLA_BR_MCAST_ROUTER               : 'IFLA_BR_MCAST_ROUTER',
-        IFLA_BR_MCAST_SNOOPING             : 'IFLA_BR_MCAST_SNOOPING',
-        IFLA_BR_MCAST_QUERY_USE_IFADDR     : 'IFLA_BR_MCAST_QUERY_USE_IFADDR',
-        IFLA_BR_MCAST_QUERIER              : 'IFLA_BR_MCAST_QUERIER',
-        IFLA_BR_MCAST_HASH_ELASTICITY      : 'IFLA_BR_MCAST_HASH_ELASTICITY',
-        IFLA_BR_MCAST_HASH_MAX             : 'IFLA_BR_MCAST_HASH_MAX',
-        IFLA_BR_MCAST_LAST_MEMBER_CNT      : 'IFLA_BR_MCAST_LAST_MEMBER_CNT',
-        IFLA_BR_MCAST_STARTUP_QUERY_CNT    : 'IFLA_BR_MCAST_STARTUP_QUERY_CNT',
-        IFLA_BR_MCAST_LAST_MEMBER_INTVL    : 'IFLA_BR_MCAST_LAST_MEMBER_INTVL',
-        IFLA_BR_MCAST_MEMBERSHIP_INTVL     : 'IFLA_BR_MCAST_MEMBERSHIP_INTVL',
-        IFLA_BR_MCAST_QUERIER_INTVL        : 'IFLA_BR_MCAST_QUERIER_INTVL',
-        IFLA_BR_MCAST_QUERY_INTVL          : 'IFLA_BR_MCAST_QUERY_INTVL',
-        IFLA_BR_MCAST_QUERY_RESPONSE_INTVL : 'IFLA_BR_MCAST_QUERY_RESPONSE_INTVL',
-        IFLA_BR_MCAST_STARTUP_QUERY_INTVL  : 'IFLA_BR_MCAST_STARTUP_QUERY_INTVL',
-        IFLA_BR_NF_CALL_IPTABLES           : 'IFLA_BR_NF_CALL_IPTABLES',
-        IFLA_BR_NF_CALL_IP6TABLES          : 'IFLA_BR_NF_CALL_IP6TABLES',
-        IFLA_BR_NF_CALL_ARPTABLES          : 'IFLA_BR_NF_CALL_ARPTABLES',
-        IFLA_BR_VLAN_DEFAULT_PVID          : 'IFLA_BR_VLAN_DEFAULT_PVID',
-        IFLA_BR_PAD                        : 'IFLA_BR_PAD',
-        IFLA_BR_VLAN_STATS_ENABLED         : 'IFLA_BR_VLAN_STATS_ENABLED',
-        IFLA_BR_MCAST_STATS_ENABLED        : 'IFLA_BR_MCAST_STATS_ENABLED',
-        IFLA_BR_MCAST_IGMP_VERSION         : 'IFLA_BR_MCAST_IGMP_VERSION',
-        IFLA_BR_MCAST_MLD_VERSION          : 'IFLA_BR_MCAST_MLD_VERSION'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for vrfs
-    # =========================================
-    IFLA_VRF_UNSPEC                         = 0
-    IFLA_VRF_TABLE                          = 1
-
-    ifla_vrf_to_string = {
-        IFLA_VRF_UNSPEC                     : 'IFLA_VRF_UNSPEC',
-        IFLA_VRF_TABLE                      : 'IFLA_VRF_TABLE'
-    }
-
-    # ================================================================
-    # IFLA_INFO_DATA attributes for (ip6)gre, (ip6)gretap, (ip6)erspan
-    # ================================================================
-    IFLA_GRE_UNSPEC             = 0
-    IFLA_GRE_LINK               = 1
-    IFLA_GRE_IFLAGS             = 2
-    IFLA_GRE_OFLAGS             = 3
-    IFLA_GRE_IKEY               = 4
-    IFLA_GRE_OKEY               = 5
-    IFLA_GRE_LOCAL              = 6
-    IFLA_GRE_REMOTE             = 7
-    IFLA_GRE_TTL                = 8
-    IFLA_GRE_TOS                = 9
-    IFLA_GRE_PMTUDISC           = 10
-    IFLA_GRE_ENCAP_LIMIT        = 11
-    IFLA_GRE_FLOWINFO           = 12
-    IFLA_GRE_FLAGS              = 13
-    IFLA_GRE_ENCAP_TYPE         = 14
-    IFLA_GRE_ENCAP_FLAGS        = 15
-    IFLA_GRE_ENCAP_SPORT        = 16
-    IFLA_GRE_ENCAP_DPORT        = 17
-    IFLA_GRE_COLLECT_METADATA   = 18
-    IFLA_GRE_IGNORE_DF          = 19
-    IFLA_GRE_FWMARK             = 20
-    IFLA_GRE_ERSPAN_INDEX       = 21
-    IFLA_GRE_ERSPAN_VER         = 22
-    IFLA_GRE_ERSPAN_DIR         = 23
-    IFLA_GRE_ERSPAN_HWID        = 24
+    }
 
-    ifla_gre_to_string = {
-        IFLA_GRE_UNSPEC             : "IFLA_GRE_UNSPEC",
-        IFLA_GRE_LINK               : "IFLA_GRE_LINK",
-        IFLA_GRE_IFLAGS             : "IFLA_GRE_IFLAGS",
-        IFLA_GRE_OFLAGS             : "IFLA_GRE_OFLAGS",
-        IFLA_GRE_IKEY               : "IFLA_GRE_IKEY",
-        IFLA_GRE_OKEY               : "IFLA_GRE_OKEY",
-        IFLA_GRE_LOCAL              : "IFLA_GRE_LOCAL",
-        IFLA_GRE_REMOTE             : "IFLA_GRE_REMOTE",
-        IFLA_GRE_TTL                : "IFLA_GRE_TTL",
-        IFLA_GRE_TOS                : "IFLA_GRE_TOS",
-        IFLA_GRE_PMTUDISC           : "IFLA_GRE_PMTUDISC",
-        IFLA_GRE_ENCAP_LIMIT        : "IFLA_GRE_ENCAP_LIMIT",
-        IFLA_GRE_FLOWINFO           : "IFLA_GRE_FLOWINFO",
-        IFLA_GRE_FLAGS              : "IFLA_GRE_FLAGS",
-        IFLA_GRE_ENCAP_TYPE         : "IFLA_GRE_ENCAP_TYPE",
-        IFLA_GRE_ENCAP_FLAGS        : "IFLA_GRE_ENCAP_FLAGS",
-        IFLA_GRE_ENCAP_SPORT        : "IFLA_GRE_ENCAP_SPORT",
-        IFLA_GRE_ENCAP_DPORT        : "IFLA_GRE_ENCAP_DPORT",
-        IFLA_GRE_COLLECT_METADATA   : "IFLA_GRE_COLLECT_METADATA",
-        IFLA_GRE_IGNORE_DF          : "IFLA_GRE_IGNORE_DF",
-        IFLA_GRE_FWMARK             : "IFLA_GRE_FWMARK",
-        IFLA_GRE_ERSPAN_INDEX       : "IFLA_GRE_ERSPAN_INDEX",
-        IFLA_GRE_ERSPAN_VER         : "IFLA_GRE_ERSPAN_VER",
-        IFLA_GRE_ERSPAN_DIR         : "IFLA_GRE_ERSPAN_DIR",
-        IFLA_GRE_ERSPAN_HWID        : "IFLA_GRE_ERSPAN_HWID",
+    # Subtype attrbutes AF_INET
+    IFLA_INET_UNSPEC    = 0
+    IFLA_INET_CONF      = 1
+    __IFLA_INET_MAX     = 2
+
+    ifla_inet_af_spec_to_string = {
+        IFLA_INET_UNSPEC    : 'IFLA_INET_UNSPEC',
+        IFLA_INET_CONF      : 'IFLA_INET_CONF',
     }
 
-    # ===============================================
-    # IFLA_INFO_DATA attributes for ipip, sit, ip6tnl
-    # ===============================================
-    IFLA_IPTUN_UNSPEC                       = 0
-    IFLA_IPTUN_LINK                         = 1
-    IFLA_IPTUN_LOCAL                        = 2
-    IFLA_IPTUN_REMOTE                       = 3
-    IFLA_IPTUN_TTL                          = 4
-    IFLA_IPTUN_TOS                          = 5
-    IFLA_IPTUN_ENCAP_LIMIT                  = 6
-    IFLA_IPTUN_FLOWINFO                     = 7
-    IFLA_IPTUN_FLAGS                        = 8
-    IFLA_IPTUN_PROTO                        = 9
-    IFLA_IPTUN_PMTUDISC                     = 10
-    IFLA_IPTUN_6RD_PREFIX                   = 11
-    IFLA_IPTUN_6RD_RELAY_PREFIX             = 12
-    IFLA_IPTUN_6RD_PREFIXLEN                = 13
-    IFLA_IPTUN_6RD_RELAY_PREFIXLEN          = 14
-    IFLA_IPTUN_ENCAP_TYPE                   = 15
-    IFLA_IPTUN_ENCAP_FLAGS                  = 16
-    IFLA_IPTUN_ENCAP_SPORT                  = 17
-    IFLA_IPTUN_ENCAP_DPORT                  = 18
-    IFLA_IPTUN_COLLECT_METADATA             = 19
-    IFLA_IPTUN_FWMARK                       = 20
+    # BRIDGE IFLA_AF_SPEC attributes
+    IFLA_BRIDGE_FLAGS     = 0
+    IFLA_BRIDGE_MODE      = 1
+    IFLA_BRIDGE_VLAN_INFO = 2
 
-    ifla_iptun_to_string = {
-        IFLA_IPTUN_UNSPEC                   : "IFLA_IPTUN_UNSPEC",
-        IFLA_IPTUN_LINK                     : "IFLA_IPTUN_LINK",
-        IFLA_IPTUN_LOCAL                    : "IFLA_IPTUN_LOCAL",
-        IFLA_IPTUN_REMOTE                   : "IFLA_IPTUN_REMOTE",
-        IFLA_IPTUN_TTL                      : "IFLA_IPTUN_TTL",
-        IFLA_IPTUN_TOS                      : "IFLA_IPTUN_TOS",
-        IFLA_IPTUN_ENCAP_LIMIT              : "IFLA_IPTUN_ENCAP_LIMIT",
-        IFLA_IPTUN_FLOWINFO                 : "IFLA_IPTUN_FLOWINFO",
-        IFLA_IPTUN_FLAGS                    : "IFLA_IPTUN_FLAGS",
-        IFLA_IPTUN_PROTO                    : "IFLA_IPTUN_PROTO",
-        IFLA_IPTUN_PMTUDISC                 : "IFLA_IPTUN_PMTUDISC",
-        IFLA_IPTUN_6RD_PREFIX               : "IFLA_IPTUN_6RD_PREFIX",
-        IFLA_IPTUN_6RD_RELAY_PREFIX         : "IFLA_IPTUN_6RD_RELAY_PREFIX",
-        IFLA_IPTUN_6RD_PREFIXLEN            : "IFLA_IPTUN_6RD_PREFIXLEN",
-        IFLA_IPTUN_6RD_RELAY_PREFIXLEN      : "IFLA_IPTUN_6RD_RELAY_PREFIXLEN",
-        IFLA_IPTUN_ENCAP_TYPE               : "IFLA_IPTUN_ENCAP_TYPE",
-        IFLA_IPTUN_ENCAP_FLAGS              : "IFLA_IPTUN_ENCAP_FLAGS",
-        IFLA_IPTUN_ENCAP_SPORT              : "IFLA_IPTUN_ENCAP_SPORT",
-        IFLA_IPTUN_ENCAP_DPORT              : "IFLA_IPTUN_ENCAP_DPORT",
-        IFLA_IPTUN_COLLECT_METADATA         : "IFLA_IPTUN_COLLECT_METADATA",
-        IFLA_IPTUN_FWMARK                   : "IFLA_IPTUN_FWMARK",
+    ifla_bridge_af_spec_to_string = {
+        IFLA_BRIDGE_FLAGS     : 'IFLA_BRIDGE_FLAGS',
+        IFLA_BRIDGE_MODE      : 'IFLA_BRIDGE_MODE',
+        IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO'
     }
 
-    # =========================================
-    # IFLA_INFO_DATA attributes for vti, vti6
-    # =========================================
-    IFLA_VTI_UNSPEC     = 0
-    IFLA_VTI_LINK       = 1
-    IFLA_VTI_IKEY       = 2
-    IFLA_VTI_OKEY       = 3
-    IFLA_VTI_LOCAL      = 4
-    IFLA_VTI_REMOTE     = 5
-    IFLA_VTI_FWMARK     = 6
+    # BRIDGE_VLAN_INFO flags
+    BRIDGE_VLAN_INFO_MASTER      = 1 << 0  # Operate on Bridge device as well
+    BRIDGE_VLAN_INFO_PVID        = 1 << 1  # VLAN is PVID, ingress untagged
+    BRIDGE_VLAN_INFO_UNTAGGED    = 1 << 2  # VLAN egresses untagged
+    BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3  # VLAN is start of vlan range
+    BRIDGE_VLAN_INFO_RANGE_END   = 1 << 4  # VLAN is end of vlan range
+    BRIDGE_VLAN_INFO_BRENTRY     = 1 << 5  # Global bridge VLAN entry
 
-    ifla_vti_to_string = {
-        IFLA_VTI_UNSPEC     : "IFLA_VTI_UNSPEC",
-        IFLA_VTI_LINK       : "IFLA_VTI_LINK",
-        IFLA_VTI_IKEY       : "IFLA_VTI_IKEY",
-        IFLA_VTI_OKEY       : "IFLA_VTI_OKEY",
-        IFLA_VTI_LOCAL      : "IFLA_VTI_LOCAL",
-        IFLA_VTI_REMOTE     : "IFLA_VTI_REMOTE",
-        IFLA_VTI_FWMARK     : "IFLA_VTI_FWMARK",
+    bridge_vlan_to_string = {
+        BRIDGE_VLAN_INFO_MASTER      : 'BRIDGE_VLAN_INFO_MASTER',
+        BRIDGE_VLAN_INFO_PVID        : 'BRIDGE_VLAN_INFO_PVID',
+        BRIDGE_VLAN_INFO_UNTAGGED    : 'BRIDGE_VLAN_INFO_UNTAGGED',
+        BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN',
+        BRIDGE_VLAN_INFO_RANGE_END   : 'BRIDGE_VLAN_INFO_RANGE_END',
+        BRIDGE_VLAN_INFO_BRENTRY     : 'BRIDGE_VLAN_INFO_BRENTRY'
+    }
+
+    # Bridge flags
+    BRIDGE_FLAGS_MASTER = 1
+    BRIDGE_FLAGS_SELF   = 2
+
+    bridge_flags_to_string = {
+        BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER',
+        BRIDGE_FLAGS_SELF   : 'BRIDGE_FLAGS_SELF'
+    }
+
+    # filters for IFLA_EXT_MASK
+    RTEXT_FILTER_VF                = 1 << 0
+    RTEXT_FILTER_BRVLAN            = 1 << 1
+    RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2
+    RTEXT_FILTER_SKIP_STATS        = 1 << 3
+
+    rtext_to_string = {
+        RTEXT_FILTER_VF                : 'RTEXT_FILTER_VF',
+        RTEXT_FILTER_BRVLAN            : 'RTEXT_FILTER_BRVLAN',
+        RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED',
+        RTEXT_FILTER_SKIP_STATS        : 'RTEXT_FILTER_SKIP_STATS'
     }
 
     def __init__(self, msgtype, debug=False, logger=None, use_color=True):
@@ -3935,12 +4354,15 @@ class Link(NetlinkPacket):
     def get_ifla_vxlan_string(self, index):
         return self.get_string(self.ifla_vxlan_to_string, index)
 
+    def get_ifla_vrf_string(self, index):
+        return self.get_string(self.ifla_vrf_to_string, index)
+
+    def get_ifla_bond_slave_string(self, index):
+        return self.get_string(self.ifla_bond_slave_to_string, index)
+
     def get_ifla_macvlan_string(self, index):
         return self.get_string(self.ifla_macvlan_to_string, index)
 
-    def get_ifla_xfrm_string(self, index):
-        return self.get_string(self.ifla_xfrm_to_string, index)
-
     def get_macvlan_mode_string(self, index):
         return self.get_string(self.macvlan_mode_to_string, index)
 
@@ -4014,6 +4436,386 @@ class Link(NetlinkPacket):
         return False
 
 
+class AttributeMDBA_MDB(Attribute):
+    """
+    /* Bridge multicast database attributes
+     * [MDBA_MDB] = {
+     *     [MDBA_MDB_ENTRY] = {
+     *         [MDBA_MDB_ENTRY_INFO] {
+     *                struct br_mdb_entry
+     *                [MDBA_MDB_EATTR attributes]
+     *         }
+     *     }
+     * }
+    """
+    """
+    Current we support only MDB Dump and no MDB_GET.
+    The code has been written to handle multiple entries in a single msg.
+    data -- alignment
+    MDBA_MDB ===> data[0:4]
+    MDBA_MDB_ENTRY ===> data[4:8]
+    MDBA_MDB_ENTRY_INFO ===> data[8:12]
+    br_mdb_entry -- ===> ifindex data[12:16]
+                 -- ===> state,flags,vide data[16:20]
+                  -- ===> ip_addr data[20:36]
+                 -- ===> proto data[36:40]
+                 -- ===> MDB_MDB_EATTR_TIMER data[40:44]
+                 -- Timer Value data[44:48]
+    """
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+
+        data = self.data[4:]
+        if parent_msg.msgtype == RTM_GETMDB:
+            self.value = []
+            while data:
+                (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+                sub_attr_end = padded_length(sub_attr_length)
+                sub_attr_data = data[4:sub_attr_end]
+
+                mdb_entry = {}
+                mdb_entry[MDB.MDBA_MDB_ENTRY] = []
+                if not sub_attr_length:
+                    self.log.error('parsed a zero length sub-attr')
+                    return
+
+                if sub_attr_type == MDB.MDBA_MDB_ENTRY:
+                    while sub_attr_data:
+                        nested_mdb_entry = {}
+                        (nested_attr_length,nested_attr_type) = unpack('=HH',sub_attr_data[:4])
+                        nested_attr_end = padded_length(nested_attr_length)
+                        if nested_attr_type == MDB.MDBA_MDB_ENTRY_INFO:
+                            (ifindex, state, flags, vid) = unpack('=LBBH',sub_attr_data[4:12])
+                            info = [ifindex,state,flags,vid]
+                            proto = unpack('=H',sub_attr_data[28:30])[0]
+                            if proto == htons(ETH_P_IP):
+                                ip_addr = IPv4Address(unpack('>L', sub_attr_data[12:16])[0])
+                            else:
+                                (data1, data2) = unpack('>QQ',sub_attr_data[12:28])
+                                ip_addr        =  IPv6Address(data1 << 64 | data2)
+
+                            info.append(ip_addr)
+
+                            try:
+                                (timer_attr_length,timer_attr_type) = unpack('=HH',sub_attr_data[32:36])
+                                if(timer_attr_type ) == MDB.MDBA_MDB_EATTR_TIMER:
+                                    info.append({MDB.MDBA_MDB_EATTR_TIMER: (unpack('=I',sub_attr_data[36:40])[0])*0.01})
+                            except struct.error:
+                                self.log.error('No TimerAttribute')
+                            nested_mdb_entry[MDB.MDBA_MDB_ENTRY] = info
+                            mdb_entry[MDB.MDBA_MDB_ENTRY].append(nested_mdb_entry)
+                        sub_attr_data = sub_attr_data[nested_attr_end:]
+                self.value.append(mdb_entry)
+                data = data[sub_attr_end:]
+
+        else:
+            self.value = {}
+            while data:
+                (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+                sub_attr_end = padded_length(sub_attr_length)
+                sub_attr_data = data[4:]
+                if not sub_attr_length:
+                    self.log.error('parsed a zero length sub-attr')
+                    return
+
+                if sub_attr_type == MDB.MDBA_MDB_ENTRY:
+                    (nested_attr_length,nested_attr_type) = unpack('=HH',sub_attr_data[:4])
+                    if nested_attr_type == MDB.MDBA_MDB_ENTRY_INFO:
+                        self.value[MDB.MDBA_MDB_ENTRY] = {}
+                        (ifindex,state,flags,vid) = unpack('=LBBH',sub_attr_data[4:12])
+                        info = (ifindex,state,flags,vid)
+                        info = list(info)
+                        proto = unpack('=H',sub_attr_data[28:30])[0]
+                        if proto == 8:
+                            ip_addr = IPv4Address(unpack('>L', sub_attr_data[12:16])[0])
+                        else:
+                            (data1, data2) = unpack('>QQ',sub_attr_data[12:28])
+                            ip_addr        =  IPv6Address(data1 << 64 | data2)
+
+                        info.append(ip_addr)
+                        self.value[MDB.MDBA_MDB_ENTRY][MDB.MDBA_MDB_ENTRY_INFO] = info
+                data = data[sub_attr_end:]
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], self.value))
+        return line_number + 1
+
+
+
+class AttributeMDBA_ROUTER(Attribute):
+    """
+    /*
+     * [MDBA_ROUTER] = {
+     *    [MDBA_ROUTER_PORT] = {
+     *        u32 ifindex
+     *        [MDBA_ROUTER_PATTR attributes]
+     *    }
+     * }
+     */
+     """
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        data = self.data[4:]
+        if parent_msg.msgtype == RTM_GETMDB:
+            self.value = []
+            while data:
+                (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+                sub_attr_end = padded_length(sub_attr_length)
+                sub_attr_data = data[4:sub_attr_end]
+                router_entry = {}
+
+                if not sub_attr_length:
+                    self.log.error('parsed a zero length sub-attr')
+                    return
+
+                if sub_attr_type == MDB.MDBA_ROUTER_PORT:
+                    ifindex = unpack('=I',sub_attr_data[:4])[0]
+                    sub_attr_data = sub_attr_data[4:]
+                    timer_info = {}
+                    type_info  = {}
+                    while sub_attr_data:
+                        (nested_sub_attr_length, nested_sub_attr_type) = unpack('=HH',sub_attr_data[:4])
+                        nested_sub_attr_data = sub_attr_data[4:]
+                        nested_sub_attr_end = padded_length(nested_sub_attr_length)
+                        if nested_sub_attr_type == MDB.MDBA_ROUTER_PATTR_TIMER:
+                            timer_info[MDB.MDBA_ROUTER_PATTR_TIMER] = (unpack('=L',nested_sub_attr_data[ :4])[0])*0.01
+                        elif nested_sub_attr_type == MDB.MDBA_ROUTER_PATTR_TYPE:
+                            type_info[MDB.MDBA_ROUTER_PATTR_TYPE] = unpack('=B',nested_sub_attr_data[:1])[0]
+                        else:
+                            raise Exception("Invalid Router Port Attribute")
+                        sub_attr_data = sub_attr_data[nested_sub_attr_end:]
+                    router_entry[MDB.MDBA_ROUTER_PORT] = [ifindex,timer_info,type_info]
+
+                self.value.append(router_entry)
+                data = data[sub_attr_end:]
+
+        else:
+            self.value = {}
+            while data:
+                (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+                sub_attr_end = padded_length(sub_attr_length)
+                sub_attr_data = data[4:]
+
+                if not sub_attr_length:
+                    self.log.error('parsed a zero length sub-attr')
+                    return
+
+                if sub_attr_type == MDB.MDBA_ROUTER_PORT:
+                    ifindex = unpack('=L',sub_attr_data[:4])[0]
+                self.value[MDB.MDBA_ROUTER_PORT] = ifindex
+                data = data[sub_attr_end:]
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], self.value))
+        return line_number + 1
+
+
+class AttributeMDBA_SET_ENTRY(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = None
+        self.LEN  = None
+
+    def set_value(self, value):
+        self.value = value
+
+
+    def encode(self):
+        if self.value:
+            (ifindex, flags, state,vid, ip, proto) = self.value
+            if proto == htons(ETH_P_IP):
+                self.PACK = '=IBBHLxxxxxxxxxxxxHxx'
+                reorder = unpack('<L', ip.packed)[0]
+                ip = IPv4Address(reorder)
+
+                self.LEN = calcsize(self.PACK)
+                length = self.HEADER_LEN + self.LEN
+                #TODO Please check the encoding for Ipv6
+                raw = pack(self.HEADER_PACK, length, self.atype) + pack(self.PACK, ifindex, flags, state, vid, ip, proto)
+            elif proto == htons(ETH_P_IPV6):
+                self.PACK = '=IBBHQQHxx'
+                (data1, data2) = unpack('<QQ', ip.packed)
+                self.LEN = calcsize(self.PACK)
+                length = self.HEADER_LEN + self.LEN
+                raw = pack(self.HEADER_PACK, length, self.atype) + pack(self.PACK, ifindex, flags, state, vid, data1,data2, proto)
+
+            else:
+                raise Exception("%d Invalid Proto" % proto)
+            raw = self.pad(length, raw)
+            return raw
+
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        if self.length == 32:
+            proto = unpack('=H', data[28:30])[0]
+            if proto == htons(ETH_P_IP):
+                self.PACK = '=IBBHLxxxxxxxxxxxxHxx'
+                (ifindex, flags, state,vid, ip, proto) = unpack(self.PACK, self.data[4:])
+            elif proto == htons(ETH_P_IPV6):
+                self.PACK = '=IBBHQQHxx'
+                (ifindex, flags, state,vid, data1,data2, proto) = unpack(self.PACK, self.data[4:])
+                ip = IPv6Address(data1 << 64 | data2)
+            else:
+                raise Exception("%d Invalid Proto" % proto)
+            self.LEN = calcsize(self.PACK)
+            self.value = (ifindex, flags, state,vid, ip, proto)
+        else:
+            raise Exception("Invalid Attribute Length")
+
+
+
+class MDB(NetlinkPacket):
+    """
+    Service Header
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   Family    |    Reserved1  |           Reserved2           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                     Interface Index                         |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    struct br_port_msg {
+    __u8  family;
+    __u32 ifindex;
+    };
+
+    RTM_GETMDB - Service Header
+
+    /* Bridge multicast database attributes
+     * [MDBA_MDB] = {
+     *     [MDBA_MDB_ENTRY] = {
+     *         [MDBA_MDB_ENTRY_INFO] {
+     *                struct br_mdb_entry
+     *                [MDBA_MDB_EATTR attributes]
+     *         }
+     *     }
+     * }
+     * [MDBA_ROUTER] = {
+     *    [MDBA_ROUTER_PORT] = {
+     *        u32 ifindex
+     *        [MDBA_ROUTER_PATTR attributes]
+     *    }
+     * }
+     */
+
+     struct br_mdb_entry {
+            __u32 ifindex;
+        #define MDB_TEMPORARY 0
+        #define MDB_PERMANENT 1
+            __u8 state;
+        #define MDB_FLAGS_OFFLOAD      (1 << 0)
+        #define MDB_FLAGS_FAST_LEAVE   (1 << 1)
+            __u8 flags;
+            __u16 vid;
+            struct {
+                union {
+                    __be32    ip4;
+                    struct in6_addr ip6;
+                } u;
+                __be16        proto;
+            } addr;
+        };
+    """
+    MDBA_UNSPEC = 0
+    MDBA_MDB    = 1
+    MDBA_ROUTER = 2
+    __MDBA_MAX  = 3
+    MDBA_MAX    = (__MDBA_MAX - 1)
+
+    #MDBA Set Attributes
+    MDBA_SET_ENTRY_UNSPEC  = 0
+    MDBA_SET_ENTRY         = 1
+    __MDBA_SET_ENTRY_MAX   = 2
+    MDBA_SET_ENTRY_MAX = (__MDBA_SET_ENTRY_MAX - 1)
+
+    #MDBA flags
+    MDB_FLAGS_OFFLOAD       = 1 << 0
+    MDB_FLAGS_FAST_LEAVE    = 1 << 1
+
+    #MDBA Attributes
+    MDBA_MDB_UNSPEC = 0
+    MDBA_MDB_ENTRY  = 1
+    __MDBA_MDB_MAX  = 2
+    MDBA_MDB_MAX    = (__MDBA_MDB_MAX - 1)
+
+    MDBA_MDB_ENTRY_UNSPEC   = 0
+    MDBA_MDB_ENTRY_INFO     = 1
+    __MDBA_MDB_ENTRY_MAX    = 2
+    MDBA_MDB_ENTRY_MAX      = (__MDBA_MDB_ENTRY_MAX - 1)
+
+    MDBA_MDB_EATTR_UNSPEC   = 0
+    MDBA_MDB_EATTR_TIMER    = 1
+    __MDBA_MDB_EATTR_MAX    = 2
+    MDBA_MDB_ENTRY_MAX            = (__MDBA_MDB_EATTR_MAX -1)
+
+    # router port attributes
+    MDBA_ROUTER_UNSPEC  = 0
+    MDBA_ROUTER_PORT    = 1
+    __MDBA_ROUTER_MAX   = 2
+    MDBA_ROUTER_MAX     = (__MDBA_ROUTER_MAX - 1)
+
+
+    MDBA_ROUTER_PATTR_UNSPEC    = 0
+    MDBA_ROUTER_PATTR_TIMER     = 1
+    MDBA_ROUTER_PATTR_TYPE      = 2
+    __MDBA_ROUTER_PATTR_MAX     = 3
+    MDBA_ROUTER_PATTR_MAX       = (__MDBA_ROUTER_PATTR_MAX - 1)
+
+    MDB_RTR_TYPE_DISABLED = 0
+    MDB_RTR_TYPE_TEMP_QUERY = 1
+    MDB_RTR_TYPE_PERM = 2
+    MDB_RTR_TYPE_TEMP = 3
+
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True, rx=False, tx=False):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color, rx, tx)
+        if self.tx and msgtype in (RTM_NEWMDB, RTM_DELMDB):
+            self.attribute_to_class = {
+                self.MDBA_UNSPEC: ('MDBA_SET_ENTRY_UNSPEC', AttributeGeneric),
+                self.MDBA_SET_ENTRY: ('MDBA_SET_ENTRY', AttributeMDBA_SET_ENTRY),
+        }
+        else:
+            self.attribute_to_class = {
+                self.MDBA_UNSPEC: ('MDBA_UNSPEC', AttributeGeneric),
+                self.MDBA_MDB: ('MDBA_MDB', AttributeMDBA_MDB),
+                self.MDBA_ROUTER: ('MDBA_ROUTER', AttributeMDBA_ROUTER),
+            }
+
+        self.PACK = 'Bxxxi'
+        self.LEN  = calcsize(self.PACK)
+
+    def decode_service_header(self):
+        # Nothing to do if the message did not contain a service header
+        if self.length == self.header_LEN:
+            return
+
+        (self.family,self.ifindex) = unpack(self.PACK, self.msg_data[:self.LEN])
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+            self.dump_buffer.append(self.msg_data)
+            self.dump_buffer.append(data_to_color_text(1, color, bytearray(struct.pack('!I', self.family)),
+                                              "Family %s (%d)" % (zfilled_hex(self.family, 2), self.family)))
+            self.dump_buffer.append(data_to_color_text(2, color, bytearray(struct.pack('i', self.ifindex)),
+                                              "Ifindex %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)))
+
 class Netconf(Link):
     """
     RTM_NEWNETCONF - Service Header
@@ -4048,7 +4850,8 @@ class Netconf(Link):
     NETCONFA_PROXY_NEIGH                    = 5
     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN    = 6
     NETCONFA_INPUT                          = 7
-    __NETCONFA_MAX                          = 8
+    NETCONFA_BC_FORWARDING                  = 8
+    __NETCONFA_MAX                          = 9
 
     NETCONFA_MAX                            = (__NETCONFA_MAX - 1)
 
@@ -4073,6 +4876,7 @@ class Netconf(Link):
         NETCONFA_PROXY_NEIGH                    : ('NETCONFA_PROXY_NEIGH', AttributeFourByteValue),
         NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN    : ('NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN', AttributeFourByteValue),
         NETCONFA_INPUT                          : ('NETCONFA_INPUT', AttributeFourByteValue),
+        NETCONFA_BC_FORWARDING                  : ('NETCONFA_BC_FORWARDING', AttributeFourByteValue),
     }
 
     def __init__(self, msgtype, debug=False, logger=None, use_color=True):
@@ -4080,7 +4884,9 @@ class Netconf(Link):
         if msgtype == RTM_GETNETCONF:  # same as RTM_GETLINK
             self.PACK = 'BxHiII'
             self.LEN  = calcsize(self.PACK)
-        elif msgtype == RTM_NEWNETCONF:
+        else:
+            # RTM_NEWNETCONF
+            # RTM_DELNETCONF
             self.PACK = 'Bxxx'
             self.LEN  = calcsize(self.PACK)
 
@@ -4092,7 +4898,9 @@ class Netconf(Link):
         if self.msgtype == RTM_GETNETCONF:
             super(Netconf, self).decode_service_header()
 
-        elif self.msgtype == RTM_NEWNETCONF:
+        else:
+            # RTM_NEWNETCONF
+            # RTM_DELNETCONF
             (self.family,) = unpack(self.PACK, self.msg_data[:self.LEN])
 
             if self.debug:
@@ -4586,6 +5394,7 @@ class Route(NetlinkPacket):
                 self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
                 self.line_number += 1
 
+
 class Done(NetlinkPacket):
     """
     NLMSG_DONE
diff --git a/ifupdown2/sbin/ifupdown2d b/ifupdown2/sbin/ifupdown2d
deleted file mode 100755 (executable)
index d223b80..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2017 Cumulus Networks, Inc. All rights reserved.
-# Authors:
-#           Roopa Prabhu, roopa@cumulusnetworks.com
-#           Julien Fortin, julien@cumulusnetworks.com
-#
-# ifupdown2 --
-#    tool to configure network interfaces
-#
-
-import os
-import re
-import json
-import fcntl
-import struct
-import signal
-import socket
-import daemon
-import select
-import datetime
-import threading
-
-try:
-    import ifupdown2.ifupdown.argv
-
-    from ifupdown2.ifupdown.log import log
-    from ifupdown2.ifupdown.main import Ifupdown2
-except ImportError:
-    import ifupdown.argv
-
-    from ifupdown.log import log
-    from ifupdown.main import Ifupdown2
-
-
-class Daemon:
-    shutdown_event = threading.Event()
-
-    def __init__(self):
-        self.uds = None
-        self.context = None
-        self.working_directory = '/var/run/ifupdown2d/'
-        self.server_address = '/var/run/ifupdown2d/uds'
-
-        if not os.path.exists(self.working_directory):
-            log.info('creating %s' % self.working_directory)
-            os.makedirs(self.working_directory, mode=0755)
-
-        if os.path.exists(self.server_address):
-            log.info('removing uds %s' % self.server_address)
-            os.remove(self.server_address)
-
-        self.context = daemon.DaemonContext(
-            working_directory=self.working_directory,
-            signal_map={
-                signal.SIGINT: self.signal_handler,
-                signal.SIGTERM: self.signal_handler,
-                signal.SIGQUIT: self.signal_handler,
-            },
-            umask=0o22
-        )
-
-        try:
-            self.SO_PEERCRED = socket.SO_PEERCRED
-        except AttributeError:
-            # powerpc is the only non-generic we care about. alpha, mips,
-            # sparc, and parisc also have non-generic values.
-            machine = os.uname()[4]
-            if re.search(r'^(ppc|powerpc)', machine):
-                self.SO_PASSCRED = 20
-                self.SO_PEERCRED = 21
-            else:
-                self.SO_PASSCRED = 16
-                self.SO_PEERCRED = 17
-
-        log.info('daemonizing ifupdown2d...')
-        self.context.open()
-
-        log.info('preloading all necessary modules')
-        self.preload_imports()
-
-        try:
-            log.info('opening UNIX socket')
-            self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            fcntl.fcntl(self.uds.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
-        except Exception as e:
-            raise Exception('socket: %s' % str(e))
-        try:
-            self.uds.bind(self.server_address)
-        except Exception as e:
-            raise Exception('bind: %s' % str(e))
-        try:
-            self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
-        except Exception as e:
-            raise Exception('setsockopt: %s' % str(e))
-        try:
-            self.uds.listen(1)
-        except Exception as e:
-            raise Exception('listen: %s' % str(e))
-        os.chmod(self.server_address, 0777)
-
-    def __del__(self):
-        if self.context:
-            self.context.close()
-        if self.uds:
-            self.uds.close()
-
-    @staticmethod
-    def preload_imports():
-        """
-            preloading all the necessary modules
-            at first will increase performances
-        """
-        try:
-            import io
-            import pdb
-            import imp
-            import sets
-            import json
-            import glob
-            import time
-            import copy
-            import errno
-            import pprint
-            import atexit
-            import ipaddr
-            import cPickle
-            import logging
-            import argparse
-            import StringIO
-            import datetime
-            import traceback
-            import itertools
-            import subprocess
-            import argcomplete
-            import collections
-            import ConfigParser
-            import pkg_resources
-
-            import ifupdown2.ifupdown.exceptions
-            import ifupdown2.ifupdown.graph
-            import ifupdown2.ifupdown.iface
-            import ifupdown2.ifupdown.iff
-            import ifupdown2.ifupdown.ifupdownbase
-            import ifupdown2.ifupdown.ifupdownbase
-            import ifupdown2.ifupdown.ifupdownconfig
-            import ifupdown2.ifupdown.ifupdownflags
-            import ifupdown2.ifupdown.ifupdownmain
-            import ifupdown2.ifupdown.netlink
-            import ifupdown2.ifupdown.networkinterfaces
-            import ifupdown2.ifupdown.policymanager
-            import ifupdown2.ifupdown.scheduler
-            import ifupdown2.ifupdown.statemanager
-            import ifupdown2.ifupdown.template
-            import ifupdown2.ifupdown.utils
-
-            import ifupdown2.ifupdownaddons.cache
-            import ifupdown2.ifupdownaddons.dhclient
-            import ifupdown2.ifupdownaddons.mstpctlutil
-            import ifupdown2.ifupdownaddons.LinkUtils
-            import ifupdown2.ifupdownaddons.modulebase
-            import ifupdown2.ifupdownaddons.systemutils
-            import ifupdown2.ifupdownaddons.utilsbase
-        except ImportError, e:
-            raise ImportError('%s - required module not found' % str(e))
-
-    @staticmethod
-    def signal_handler(sig, frame):
-        log.info('received %s' % 'SIGINT' if sig == signal.SIGINT else 'SIGTERM')
-        Daemon.shutdown_event.set()
-
-    @staticmethod
-    def user_waiting_for_reply():
-        return not log.is_syslog()
-
-    def run(self):
-        try:
-            while True:
-                if Daemon.shutdown_event.is_set():
-                    log.info("shutdown signal RXed, breaking out loop")
-                    break
-
-                try:
-                    (client_socket, client_address) = self.uds.accept()
-                except socket.error as e:
-                    log.error(str(e))
-                    break
-
-                pid = os.fork()
-                if pid == 0:
-                    exit(self.ifupdown2(client_socket))
-                else:
-                    log.tx_data(json.dumps({'pid': pid}), socket=client_socket)
-
-                    start = datetime.datetime.now()
-                    status = os.WEXITSTATUS(os.waitpid(pid, 0)[1])
-                    end = datetime.datetime.now()
-
-                    log.tx_data(json.dumps({'status': status}), socket=client_socket)
-                    client_socket.close()
-
-                    log.info('exit status %d - in %ssecs'
-                             % (status, (end - start).total_seconds()))
-
-        except Exception as e:
-            log.error(e)
-        self.uds.close()
-
-    def get_client_uid(self, client_socket):
-        creds = client_socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize('3i'))
-        (pid, uid, gid) = struct.unpack('3i', creds)
-        log.debug('client uid %d' % uid)
-        return uid
-
-    @staticmethod
-    def get_client_request(client_socket):
-        """
-            This function handles requests of any length.
-
-                if the received json is longer than 65k it will be truncated
-                several calls to recv will be needed, we store the data until
-                we can decode them with the json library.
-        """
-        data = []
-        while True:
-            log.debug('waiting for request on client socket')
-            ready = select.select([client_socket], [], [])
-
-            if ready and ready[0] and ready[0][0] == client_socket:
-                # data available start reading
-                raw_data = client_socket.recv(65536)
-
-                try:
-                    return json.loads(raw_data)
-                except ValueError:
-                    # the json is incomplete
-                    data.append(raw_data)
-
-                    if len(data) > 1:
-                        try:
-                            return json.loads(''.join(data))
-                        except ValueError:
-                            pass
-
-    def ifupdown2(self, client_socket):
-        try:
-            fcntl.fcntl(client_socket.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
-
-            ifupdown2 = Ifupdown2(daemon=True, uid=self.get_client_uid(client_socket))
-            ifupdown2.set_signal_handlers()
-
-            request = self.get_client_request(client_socket)
-            log.info('request: %s' % request['argv'])
-
-            ifupdown2.parse_argv(request['argv'])
-            # adjust the logger with argv
-            ifupdown2.update_logger(socket=client_socket)
-
-            try:
-                status = ifupdown2.main(request['stdin'])
-            except Exception as e:
-                log.error(str(e))
-                status = 1
-
-        except ifupdown2.ifupdown.argv.ArgvParseError as e:
-            log.update_current_logger(syslog=False, verbose=True, debug=False)
-            log.set_socket(client_socket)
-            e.log_error()
-            status = 1
-        except Exception as e:
-            log.error(e)
-            status = 1
-
-        log.flush()
-        log.set_socket(None)
-        client_socket.close()
-        return status
-
-
-if __name__ == '__main__':
-    try:
-        Daemon().run()
-    except Exception as e:
-        print e
-        log.error(str(e))
-        import traceback
-        log.error(traceback.format_exc())
-        exit(1)
diff --git a/ifupdown2/sbin/start-networking b/ifupdown2/sbin/start-networking
deleted file mode 100755 (executable)
index da6f9d0..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/bin/bash
-
-# This replaces the old init.d script, and is run from the networking.service
-# Only has start, stop, reload, because that's all systemd has.
-# restart is implemented in systemd by stop then start.
-
-RUN_DIR="/run/network"
-IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
-
-STATE_DIR="/var/tmp/network"
-IFSTATE_FILE="${STATE_DIR}/ifstatenew"
-
-NAME=networking
-
-[ -x /sbin/ifup ] || exit 0
-[ -x /sbin/ifdown ] || exit 0
-
-CONFIGURE_INTERFACES=yes
-
-EXTRA_ARGS=
-
-[ -f /etc/default/networking ] && . /etc/default/networking
-
-[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
-[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
-[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
-
-perf_options() {
-    # At bootup lets set perfmode
-    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
-
-    echo -n "--perfmode"
-}
-
-process_exclusions() {
-    set -- $EXCLUDE_INTERFACES
-    exclusions=""
-    for d
-    do
-       exclusions="-X $d $exclusions"
-    done
-    echo $exclusions
-}
-
-check_network_file_systems() {
-    [ -e /proc/mounts ] || return 0
-
-    if [ -e /etc/iscsi/iscsi.initramfs ]; then
-       echo ${NAME}':' "not deconfiguring network interfaces: iSCSI root is mounted."
-       exit 0
-    fi
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           echo ${NAME}':' "not deconfiguring network interfaces: network devices still mounted."
-           exit 0
-           ;;
-       esac
-       case $FSTYPE in
-       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
-           echo ${NAME}':' "not deconfiguring network interfaces: network file systems still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/mounts
-}
-
-check_network_swap() {
-    [ -e /proc/swaps ] || return 0
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           echo ${NAME}':' "not deconfiguring network interfaces: network swap still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/swaps
-}
-
-ifup_hotplug () {
-    if [ -d /sys/class/net ]
-    then
-           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
-                           do
-                                   link=${iface##:*}
-                                   link=${link##.*}
-                                   if [ -e "/sys/class/net/$link" ]
-                                   then
-                                       echo "$iface"
-                                   fi
-                           done)
-           if [ -n "$ifaces" ]
-           then
-               ifup $ifaces "$@" || true
-           fi
-    fi
-}
-
-ifup_mgmt () {
-       ifaces=$(ifquery --list --allow=mgmt 2>/dev/null)
-       if [ -n "$ifaces" ]; then
-               echo "bringing up mgmt class interfaces"
-               ifup --allow=mgmt
-       fi
-}
-
-ifupdown_init() {
-       # remove state file at boot
-       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
-
-       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
-       [ ! -e /etc/network/run ] && \
-               ln -sf /run/network /etc/network/run &>/dev/null
-}
-
-case "$1" in
-start)
-       ifupdown_init
-       if [ "$CONFIGURE_INTERFACES" = no ]
-       then
-           echo ${NAME}':' "Not configuring network interfaces, see /etc/default/networking"
-           exit 0
-       fi
-       set -f
-       exclusions=$(process_exclusions)
-       perfoptions=$(perf_options)
-       echo ${NAME}':' "Configuring network interfaces"
-       ifup_mgmt
-       ifup -a $EXTRA_ARGS $exclusions $perfoptions
-       ifup_hotplug $HOTPLUG_ARGS $EXTRA_ARGS $exclusions
-       ;;
-stop)
-       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
-        SYSRESET=0
-        systemctl list-jobs | egrep -q '(shutdown|reboot|halt|poweroff)\.target'
-        [ $? -eq 0 ] && SYSRESET=1
-        if [ $SYSRESET -eq 1 ]; then
-            echo ${NAME}':' "Skipping deconfiguring network interfaces"
-            exit 0
-        fi
-       fi
-       ifupdown_init
-       check_network_file_systems
-       check_network_swap
-       exclusions=$(process_exclusions)
-
-       echo ${NAME}':' "Deconfiguring network interfaces"
-       ifdown -a $EXTRA_ARGS $exclusions
-       ;;
-
-reload)
-
-       ifupdown_init
-       exclusions=$(process_exclusions)
-
-       echo ${NAME}':' "Reloading network interfaces configuration"
-       ifreload -a $EXTRA_ARGS $exclusions
-       ;;
-
-*)
-       echo ${NAME}':' "Usage: $0 {start|stop|reload}"
-       exit 1
-       ;;
-esac
-
-exit 0
index c3d61b3f6915ac152ae3b68162655869309c69f2..c25e0a1696c0d5754727c968da0e305ab4fee153 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,6 @@ INSTALL_REQUIRES = [
 ]
 
 DATA_FILES = [
-    ('/etc/default/', ['etc/default/networking']),
     ('/etc/network/ifupdown2/', ['etc/network/ifupdown2/addons.conf']),
     ('/etc/network/ifupdown2/', ['etc/network/ifupdown2/ifupdown2.conf']),
 ]
@@ -25,13 +24,12 @@ ENTRY_POINTS = {}
 def build_deb_package():
     try:
         return sys.argv[sys.argv.index('--root') + 1].endswith('/debian/ifupdown2')
-    except:
-        return False
+    except Exception:
+        pass
+    return False
 
 
-if build_deb_package():
-    DATA_FILES.append(('/usr/share/ifupdown2/sbin/', ['ifupdown2/sbin/start-networking']))
-else:
+if not build_deb_package():
     ENTRY_POINTS = {
         'console_scripts': [
             'ifup = ifupdown2.__main__:main',
@@ -42,8 +40,8 @@ else:
     }
 
 setup(
-    author='Roopa Prabhu',
-    author_email='roopa@cumulusnetworks.com',
+    author='Julien Fortin',
+    author_email='julien@cumulusnetworks.com',
     maintainer='Julien Fortin',
     maintainer_email='julien@cumulusnetworks.com',
     classifiers=[
@@ -65,7 +63,7 @@ setup(
     name='ifupdown2',
     packages=find_packages(),
     url='https://github.com/CumulusNetworks/ifupdown2',
-    version='1.2.9',
+    version='2.0.0',
     data_files=DATA_FILES,
     setup_requires=['setuptools'],
     scripts=SCRIPTS,