RHEL7 Networking

From trapsink.com
Jump to: navigation, search


Overview

The release of RHEL7 - and by extension CentOS7 - has changed the way core networking is managed on the system. In general the new and preferred method is to use nmcli to generate all configs; however it's possible to mix and match the old style and new style - sometimes with undesired results.

There are now two parts to Networking:

  • NetworkManager.service - this is a systemd unit which is started first. It will bring interfaces online if configured to do so and perform all the additional configuration work. It relies on the methodology deployed using nmcli commands.
  • network SysV service - this is a traditional shim in /etc/rc3.d/ that can also be used to bring up additional networking, such as the static-routes file. The service uses NetworkManager internally, and also the traditional route command.


General Design

The systemd unit NetworkManager.service is preconfigured in /etc/NetworkManager/NetworkManager.conf with this:

[main]
plugins=ifcfg-rh

This plugin is fully detailed in the man page nm-settings-ifcfg-rh which is part of the NetworkManager RPM. It provides a comprehensive set of tables of the old FOO=bar values to the new settings along with the nmcli man page.

  1. The /etc/sysconfig/network file will respect a global GATEWAY and GATEWAYDEV setting, however this is not how Red Hat would like for it to be done now; they prefer the values be set as part of the interface. The file is empty by default; if it is populated with GATEWAY, any new interface created with nmcli will inherit and use this GATEWAY in the new config file.
  2. Determining the default route methodology: https://mail.gnome.org/archives/networkmanager-list/2014-July/msg00080.html
    NM automatically sets the default route based on two things:
    
    (1) interface priority - all interfaces have a priority and if two
    interfaces are active, and *not* prevented from getting the default
    route (see #2), the one with the highest priority wins.  Right now,
    that's a static ordering but we're exploring how to make that dynamic.
    
    (2) the "never-default" option: you can prevent connections (and thus
    their interface when that connection is active) from ever getting the
    default route by setting this option.  With nmcli:
    
      nmcli c mod eth0 ipv4.never-default true
      nmcli c mod eth0 ipv6.never-default true
    
    would prevent connection "eth0" from ever receiving the IPv4 or IPv6
    default route.  This would allow some other connection/interface to
    receive the default route, when active.
  3. Unlike earlier releases, leave NM_CONTROLLED alone and do not set it to 'no'; the NetworkManager service runs along with the traditional /etc/init.d/network script
  4. The route, netstat and other tools part of RPM net-tools is not installed on a default Minimal installation; the ifenslave binary is now part of RPM iputils, so no longer a unique RPM
  5. The new ipv4.ignore-auto-dns config is what causes NM to write out the DNS*= lines from ifcfg-? to /etc/resolv.conf


Additionally, Red Hat's online documentation and the man pages have a few important design features to understand:

  • The default gateway is determined by the network scripts which parse the /etc/sysconfig/network file first and then the network interface ifcfg files for interfaces that are "up". The ifcfg files are parsed in numerically ascending order, and the last GATEWAY directive to be read is used to compose a default route in the routing table.
  • The default route can thus be indicated by means of the GATEWAY directive and can be specified either globally or in interface-specific configuration files. Specifying the gateway globally has certain advantages in static networking environments, especially if more than one network interface is present. It can make fault finding simpler if applied consistently.
  • Each NetworkManager connection maps to one ifcfg-* file, with possible usage of keys-* for passwords, route-* for static IPv4 routes and route6-* for static IPv6 routes.
  • The UUID values in the config files must be unique. You can use uuidgen command line tool to generate such values. Alternatively, you can leave out UUID entirely. In that case NetworkManager will generate a UUID based on the file name.

Other important concepts are outlined beyond what's presented above for other types of networking (bridges, etc.) - check the man pages for further information.

Services Required

The NetworkManager.service systemd unit should be enabled, and the network SysV initscript should be enabled for full network collaboration between traditional methods and the new style.

systemctl enable NetworkManager.service
chkconfig network on

UUID Configuration

The nmcli tool will automatically generate the UUID for an interface when created; if copying a config from one server to another, simply comment out the existing UUID and NetworkManager will dynamically assign a new one when brought online. Do not bring up two interfaces with the same UUID - it is the same hazard as using the same IP on two interfaces.

NM_CONTROLLED

In previous releases it was common to add or set the NM_CONTROLLED variable to "no" - do not do this in RHEL7. This setting should be either not in the config (the default) or set to yes if it needs to be in the config file. It is recommended to not use this setting on RHEL7 and let the system self-manage.

DNS Servers

The standard way for DNS to be configured is using nmcli and the server IPs are inside the ifcfg-* files; when the interface is brought online NetworkManager will add/remove the values in /etc/resolv.conf. To disable this behaviour and use traditional static DNS configuration, the property .ignore-auto-dns is manipulated to yes:

# nmcli con mod eno1 ipv4.ignore-auto-dns yes ipv6.ignore-auto-dns yes
# nmcli -f ipv4.ignore-auto-dns,ipv6.ignore-auto-dns con show eno1
ipv4.ignore-auto-dns:                   yes
ipv6.ignore-auto-dns:                   yes

# grep PEERDNS /etc/sysconfig/network-scripts/ifcfg-eno1
PEERDNS=no
IPV6_PEERDNS=no

Changing the nmcli property method sets the PEERDNS variable, they are one in the same result.


Device Names

There are 3 possible ways a network device name will appear in RHEL7, depending on the kernel parameters used at boot (grub2):

  1. With no parameters or net.ifnames=1 used, the names will use systemd Predictable Network Interface Names
  2. With biosdevname=1 used, the names will be managed by udev (technically systemd-udev) using Consistent Network Device Naming
  3. With net.ifnames=0 and biosdevname=0 both specified, traditional ("kernel names") ethX names will be used; if udev rules are also added they can rename the interfaces to anything except ethX

When method #3 is used, the renaming from ethX to ethY (using udev rules or otherwise) is not supported by Red Hat in RHEL7. There was a commit to systemd code in 2012 that removed the ability to shuffle ethX kernel names properly, resulting in errors on boot if it is attempted with RHEL7. Renaming kernel names is not a feature provided (or declared) by RHEL7.

net.ifnames Naming

Two character prefixes based on the type of interface:

en ethernet
sl serial line IP (slip)
wl wlan
ww wwan

Type of names:

b<number> BCMA bus core number
ccw<name> CCW bus group name
o<index>[d<dev_port>] on-board device index number
s<slot>[f<function>][d<dev_port>] hotplug slot index number
x<MAC> MAC address
[P<domain>]p<bus>s<slot>[f<function>][d<dev_port>] PCI geographical location
[P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>] USB port number chain
  • All multi-function PCI devices will carry the [f<function>] number in the device name, including the function 0 device.
  • When using PCI geography, The PCI domain is only prepended when it is not 0.
  • For USB devices the full chain of port numbers of hubs is composed. If the name gets longer than the maximum number of 15 characters, the name is not exported.
  • The usual USB configuration == 1 and interface == 0 values are suppressed., including the function 0 device.

biosdevname Naming

The naming convention is as follows:

em[1-N] on-board (embedded) NICs (# matches chassis labels)
p<slot_number>p<port_number> cards in PCI slots, ports starting at 1 (not zero)
_<vf> suffix NPAR and SR-IOV devices from 0..N depending on the number of Partitions or Virtual Functions exposed on each port
.<vlan> and :<alias> suffixes Other Linux conventions remain unchanged and are still applicable

The algorithm:

  • If system BIOS exposes the new PCI Firmware Specification 3.1 ACPI _DSM method, we get the interface label and index from ACPI, and use those.
  • Else if system BIOS exposes an index and label in SMBIOS 2.6 types 9 and 41, use the index value.
  • Else if system BIOS exposes index via the HP proprietary SMBIOS extension, use that.
  • Else fall back to using the legacy PCI IRQ Routing Table to figure out which slots devices are in, sort the PCI device list in breadth-first order, and assign index values.

Example Mappings

Given a HP DL380 G9 chassis with 4x onboard, 4x offboard, 2x 10G onboard (FlexiLOM) and 2x 10G offboard (Brocade) NICs this is what the name relationship looks like in it's natural state (no udev rules in place or specific HWADDR= lines in the ifcfg-* files):

Hardware Device MAC Address Traditional net.ifnames=1 biosdevname=1
HP 1G port 1 38:63:bb:44:89:e4 eth0 eno1 em1
HP 1G port 2 38:63:bb:44:89:e5 eth1 eno2 em2
HP 1G port 3 38:63:bb:44:89:e6 eth3 eno3 em3
HP 1G port 4 38:63:bb:44:89:e7 eth4 eno4 em4
Intel 1G port 1 a0:36:9f:63:2a:dc eth2 ens6f0 p6p1
Intel 1G port 2 a0:36:9f:63:2a:dd eth5 ens6f1 p6p2
Intel 1G port 3 a0:36:9f:63:2a:de eth6 ens6f2 p6p3
Intel 1G port 4 a0:36:9f:63:2a:df eth7 ens6f3 p6p4
HP 10G port 1 8c:dc:d4:a9:ea:74 eth8 eno49 em49
HP 10G port 2 8c:dc:d4:a9:ea:75 eth10 eno50 em50
Brocade 10G #1 00:24:ff:a2:a7:00 eth9 ens1f0 p1p1
Brocade 10G #2 00:24:ff:a2:ab:00 eth11 ens4f0 p4p1

The net.ifnames=1 systemd derived names are a mixture of base-0 and base-1 names that does not happen with udev derived names which are all base-1. The use of biosdevname=1 creates the most consistently named device interfaces regardless if they are onboard or offboard NICs, all using a base-1 for the first port name as shown above.

Example udev rules

Using the same MAC addresses from the table above, an example of renaming all the network interfaces with netX as the pattern when booting (grub2) with net.ifnames=0 biosdevname=0 kernel parameters - these names will match the /etc/sysconfig/network-scripts/ifcfg-netX configuration files that need to be created as well.

/etc/udev/rules.d/70-persistent-net.rules
# Onboard quad-port (HP)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="38:63:bb:44:89:e4", ATTR{type}=="1", KERNEL=="eth*", NAME="net0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="38:63:bb:44:89:e5", ATTR{type}=="1", KERNEL=="eth*", NAME="net1"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="38:63:bb:44:89:e6", ATTR{type}=="1", KERNEL=="eth*", NAME="net2"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="38:63:bb:44:89:e7", ATTR{type}=="1", KERNEL=="eth*", NAME="net3"

# Offboard quad-port (Intel)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="a0:36:9f:63:2a:dc", ATTR{type}=="1", KERNEL=="eth*", NAME="net4"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="a0:36:9f:63:2a:dd", ATTR{type}=="1", KERNEL=="eth*", NAME="net5"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="a0:36:9f:63:2a:de", ATTR{type}=="1", KERNEL=="eth*", NAME="net6"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="a0:36:9f:63:2a:df", ATTR{type}=="1", KERNEL=="eth*", NAME="net7"

# Onboard 10G 2-port (HP FlexiLOM)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="8c:dc:d4:a9:ea:74", ATTR{type}=="1", KERNEL=="eth*", NAME="net8"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="8c:dc:d4:a9:ea:75", ATTR{type}=="1", KERNEL=="eth*", NAME="net9"

# Offboard 10G 2x cards (Brocade HBAs)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:24:ff:a2:a7:00", ATTR{type}=="1", KERNEL=="eth*", NAME="net10"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:24:ff:a2:ab:00", ATTR{type}=="1", KERNEL=="eth*", NAME="net11"


Using nmcli

Remember: Tab-completion is your friend! Additionally, the nmcli command will allow you to shorten the commands to a minimal level as long as it's unique; for example these are all functionally equivalent to get the same information:

nmcli connection show eno1
nmcli con show eno1
nmcli c s eno1

Show connections

nmcli connection show
nmcli connection show eno1

Configure an IP address

nmcli connection modify eno1 ipv4.addresses 192.168.0.5/24

Configure an IP address with default gateway

nmcli connection modify eno1 ipv4.addresses 192.168.0.5/24 \
 ipv4.gateway 192.168.0.1 ipv4.never-default no

Configure an additional IP address

nmcli connection modify eno1 +ipv4.addresses 192.168.0.6

Add a static route

These will be saved in the route-<interface> file in /etc/sysconfig/network-scripts/ :

nmcli connection modify eno1 +ipv4.routes '192.168.111.0/24 192.168.99.1'

Modify DNS servers

# replace - notice no "+"
nmcli connection modify eno1 ipv4.dns '172.20.20.8,172.20.20.9'

# add - notice "+"
nmcli connection modify eno1 +ipv4.dns '192.168.100.123'

# remove - notice "-"
nmcli connection modify eno1 -ipv4.dns '192.168.100.123'

# also the search domains
nmcli connection modify eno1 +ipv4.dns-search 'mydomain.com'

Activate changes

Logically in the same area as "ifup" and "ifdown" which also work, as these commands are NetworkManager aware in RHEL7.

nmcli connection up eno1

Reload changes

Used when manually editing the configuration file and you need to update the daemon's cache info:

nmcli connection reload eno2

Delete an interface

This will erase the config file entirely, make a backup first if needed!

nmcli connection delete eno2

Create new interface

Note the use of "ipv4.never-default yes ipv4.ignore-auto-dns yes" – this prevents this interface from ever being the default route, and prevents it from updating DNS in /etc/resolv.conf. These would be set to "no" for a primary interface where you do want to specify a DNS, gateway and make it the default.

# add the primary connection itself -- the IPv4 parameters are not allowed with "add"
nmcli con add autoconnect yes type ethernet con-name eno2 ifname eno2

# now configure the specifics for this interface
nmcli con mod eno2 ipv4.method manual \
 ipv4.addresses 10.128.161.98/27 \
 ipv4.routes "10.191.192.0/18 10.128.161.97" \
 ipv4.never-default yes ipv4.ignore-auto-dns yes ipv6.method link-local


Well Formed Configuration

Based on the Red Hat default methodology a few items are different that previous versions.

/etc/hostname

The hostname is now part of standard systemd configuration and is managed via hostnamectl command; it is no longer configured in the networking config files.

/etc/resolv.conf

This acts as in the traditional manner, however it's content is managed by the NetworkManager infrastructure. Specify the DNS information in the primary interface config and let NetworkManager update this file as needed, or alter the interface configuration properties as outlined above to disable NetworkManager from updating it for traditional static behaviour.

/etc/sysconfig/network

By default this file should be empty - but it is possible to specify GATEWAY and GATEWAYDEV in this file as before.

Any gateway values present in this file will automatically get added as the gateway for new interfaces created (eno2, etc.) - leave it blank for best results.

/etc/sysconfig/static-routes

By default this file should be empty - but it is possible to specify routes as before. It is recommended to instead use route-eth? files instead.

/etc/sysconfig/network-scripts/ifcfg-eno1

An example of a nmcli based well-formed example of this config file; note the DOMAIN, DEFROUTE and others.

/etc/sysconfig/network-scripts/ifcfg-eno1
TYPE=Ethernet
BOOTPROTO=none
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
NAME=eno1
UUID=90363eef-bd9c-4b1e-86d6-b6893f9aa04d
DEVICE=eno1
ONBOOT=yes
IPADDR=172.20.20.135
PREFIX=22
GATEWAY=172.20.20.1
DNS1=172.20.20.8
DNS2=172.20.20.9
DOMAIN=mydomain.com
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_PRIVACY=no

The interface has other properties seen via nmcli con show eno1 style commands:

nmcli con show eno1
connection.id:                          eno1
connection.uuid:                        90363eef-bd9c-4b1e-86d6-b6893f9aa04d
connection.interface-name:              eno1
connection.type:                        802-3-ethernet
connection.autoconnect:                 yes
connection.autoconnect-priority:        0
connection.timestamp:                   1431380830
connection.read-only:                   no
connection.permissions:                 
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --
connection.secondaries:                 
connection.gateway-ping-timeout:        0
802-3-ethernet.port:                    --
802-3-ethernet.speed:                   0
802-3-ethernet.duplex:                  --
802-3-ethernet.auto-negotiate:          yes
802-3-ethernet.mac-address:             --
802-3-ethernet.cloned-mac-address:      --
802-3-ethernet.mac-address-blacklist:   
802-3-ethernet.mtu:                     auto
802-3-ethernet.s390-subchannels:        
802-3-ethernet.s390-nettype:            --
802-3-ethernet.s390-options:            
ipv4.method:                            manual
ipv4.dns:                               172.20.20.8,172.20.20.9
ipv4.dns-search:                        mydomain.com
ipv4.addresses:                         172.20.20.135/22
ipv4.gateway:                           172.20.20.1
ipv4.routes:                            
ipv4.route-metric:                      -1
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   no
ipv4.dhcp-client-id:                    --
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.never-default:                     no
ipv4.may-fail:                          yes
ipv6.method:                            auto
ipv6.dns:                               
ipv6.dns-search:                        
ipv6.addresses:                         
ipv6.gateway:                           --
ipv6.routes:                            
ipv6.route-metric:                      -1
ipv6.ignore-auto-routes:                no
ipv6.ignore-auto-dns:                   no
ipv6.never-default:                     no
ipv6.may-fail:                          yes
ipv6.ip6-privacy:                       0 (disabled)
ipv6.dhcp-send-hostname:                yes
ipv6.dhcp-hostname:                     --
GENERAL.NAME:                           eno1
GENERAL.UUID:                           90363eef-bd9c-4b1e-86d6-b6893f9aa04d
GENERAL.DEVICES:                        eno1
GENERAL.STATE:                          activated
GENERAL.DEFAULT:                        yes
GENERAL.DEFAULT6:                       no
GENERAL.VPN:                            no
GENERAL.ZONE:                           --
GENERAL.DBUS-PATH:                      /org/freedesktop/NetworkManager/ActiveConnection/0
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/Settings/0
GENERAL.SPEC-OBJECT:                    /
GENERAL.MASTER-PATH:                    --
IP4.ADDRESS[1]:                         172.20.20.135/22
IP4.GATEWAY:                            172.20.20.1
IP4.DNS[1]:                             172.20.20.8
IP4.DNS[2]:                             172.20.20.9
IP6.ADDRESS[1]:                         fe80::3a63:bbff:fe40:f708/64
IP6.GATEWAY:                            

/etc/sysconfig/network-scripts/ifcfg-eno2

A secondary interface that doesn't route; for example the commands to create an interface:

nmcli con add autoconnect yes type ethernet con-name eno2 ifname eno2
nmcli con mod eno2 ipv4.method manual \
 ipv4.addresses 10.128.161.98/27 \
 ipv4.routes "10.191.192.0/18 10.128.161.97" \
 ipv4.never-default yes ipv4.ignore-auto-dns yes ipv6.method link-local

...results in this ifcfg-eno2:

TYPE=Ethernet
BOOTPROTO=none
DEFROUTE=no
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6_DEFROUTE=no
IPV6_FAILURE_FATAL=no
NAME=eno2
UUID=a92aedd0-3dae-4648-85c8-1e4aac835146
DEVICE=eno2
ONBOOT=yes
DOMAIN=mydomain.com
IPADDR=10.128.161.98
PREFIX=27
IPV6_PRIVACY=no

...and the full nmcli con show eno2:

connection.id:                          eno2
connection.uuid:                        a92aedd0-3dae-4648-85c8-1e4aac835146
connection.interface-name:              eno2
connection.type:                        802-3-ethernet
connection.autoconnect:                 yes
connection.autoconnect-priority:        0
connection.timestamp:                   1431381130
connection.read-only:                   no
connection.permissions:                 
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --
connection.secondaries:                 
connection.gateway-ping-timeout:        0
802-3-ethernet.port:                    --
802-3-ethernet.speed:                   0
802-3-ethernet.duplex:                  --
802-3-ethernet.auto-negotiate:          yes
802-3-ethernet.mac-address:             --
802-3-ethernet.cloned-mac-address:      --
802-3-ethernet.mac-address-blacklist:   
802-3-ethernet.mtu:                     auto
802-3-ethernet.s390-subchannels:        
802-3-ethernet.s390-nettype:            --
802-3-ethernet.s390-options:            
ipv4.method:                            manual
ipv4.dns:                               
ipv4.dns-search:                        mydomain.com
ipv4.addresses:                         10.128.161.98/27
ipv4.gateway:                           --
ipv4.routes:                            { ip = 10.191.192.0/18, nh = 10.128.161.97 }
ipv4.route-metric:                      -1
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   yes
ipv4.dhcp-client-id:                    --
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.never-default:                     yes
ipv4.may-fail:                          yes
ipv6.method:                            link-local
ipv6.dns:                               
ipv6.dns-search:                        
ipv6.addresses:                         
ipv6.gateway:                           --
ipv6.routes:                            
ipv6.route-metric:                      -1
ipv6.ignore-auto-routes:                yes
ipv6.ignore-auto-dns:                   yes
ipv6.never-default:                     yes
ipv6.may-fail:                          yes
ipv6.ip6-privacy:                       0 (disabled)
ipv6.dhcp-send-hostname:                yes
ipv6.dhcp-hostname:                     --
GENERAL.NAME:                           eno2
GENERAL.UUID:                           a92aedd0-3dae-4648-85c8-1e4aac835146
GENERAL.DEVICES:                        eno2
GENERAL.STATE:                          activated
GENERAL.DEFAULT:                        no
GENERAL.DEFAULT6:                       no
GENERAL.VPN:                            no
GENERAL.ZONE:                           --
GENERAL.DBUS-PATH:                      /org/freedesktop/NetworkManager/ActiveConnection/4
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/Settings/5
GENERAL.SPEC-OBJECT:                    /
GENERAL.MASTER-PATH:                    --
IP4.ADDRESS[1]:                         10.128.161.98/27
IP4.GATEWAY:                            
IP4.ROUTE[1]:                           dst = 10.191.192.0/18, nh = 10.128.161.97, mt = 100
IP6.ADDRESS[1]:                         fe80::3a63:bbff:fe40:f709/64
IP6.GATEWAY:                            

/etc/sysconfig/network-scripts/route-eno2

This file accepts the old style and new style at the same time which is unique for a config file.

If using nmcli with the NetworkManager.service systemd unit it will have a format like so:

ADDRESS0=10.191.192.0
NETMASK0=255.255.192.0
GATEWAY0=10.128.161.97

...however it has a traditional format for route via the network SysV script:

10.191.192.0/18 via 10.128.161.97

The nmcli method is preferred for full compatibility.


Bonding Configuration

Given two interfaces on the same VLAN - eno1 and eno2 used here - the information can be pulled from the running eno1 and a bond0 build from it using a small amount of scripting; this is a proof of concept.

#!/bin/bash
#
# Create a bond interface on RHEL7 / CentOS7 using pure nmcli methods
# on a fresh installation (or vanilla networking) that was built using
# NetworkManager. IPv4 only.
#
# Required: first parameter passed is the fully configured interface with
# IP(s)/CIDR, Gateway and DNS; these values will be extracted and applied
# to the new bond0 and set as a default route
#
# BACK UP YOUR CONFIGS FIRST

if [ $# -ne 3 ]; then
  echo "Usage:   $0 <bond name> <1st slave> <2nd slave>"
  echo "Example: $0 bond0 eno1 eno2"
  exit 1
fi

BOND=$1
SLAVE1=$2
SLAVE2=$3

# Build the basic bond in HA mode (most common)
nmcli con add autoconnect yes type bond con-name ${BOND} ifname ${BOND} \
  mode active-backup miimon 100

# Set a high priority to keep gateway as the default route
nmcli con mod ${BOND} connection.autoconnect-priority 99 \
  ipv6.method link-local

# Pull existing IPv4
IPV4=($(nmcli -f \
  ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,ipv4.dns-search \
  con show ${SLAVE1} | awk '{print $2}'))

# Configure the bond
nmcli con mod ${BOND} ipv4.method ${IPV4[0]} \
  ipv4.addresses ${IPV4[1]} ipv4.gateway ${IPV4[2]} \
  ipv4.dns ${IPV4[3]} ipv4.dns-search ${IPV4[4]} \
  ipv4.never-default no ipv4.ignore-auto-dns no

# Remove then add the interfaces as slaves
nmcli con del ${SLAVE1}
nmcli con add autoconnect yes type bond-slave \
  con-name ${SLAVE1} ifname ${SLAVE1} master ${BOND}

nmcli con del ${SLAVE2}
nmcli con add autoconnect yes type bond-slave \
  con-name ${SLAVE2} ifname ${SLAVE2} master ${BOND}

exit 0


References