Script oficial da Oracle Cloud que detecta e configura automaticamente todas as VNICs secundárias anexadas a uma VM Linux.
- Sobe as interfaces correspondentes às VNICs secundárias;
- Obtém o IP (via DHCP) ou aplica o IP estático já definido;
- Cria rotas e regras de policy routing por interface, isolando o tráfego por sub-rede;
- Persiste a configuração para sobreviver a reinicializações;
- É idempotente (pode ser executado várias vezes) e dispensa reboot após anexar a VNIC.
Ideal para multihoming, separação de redes (app/backup), rotas dedicadas e cenários de alta disponibilidade.
Faça o Download do script abaixo :
#!/usr/bin/env bash
# Copyright (c) 2018, Oracle and/or its affiliates.
# The Universal Permissive License (UPL), Version 1.0
#
# Oracle OCI Virtual Cloud Networks IP configuration script
#
# 2017-10-24 initial release
# 2017-11-21 filter out VLANs if VM
# 2017-11-21 inhibit namespaces for ubuntu 16
# 2018-02-12 fix sshd typo in help
# 2018-02-20 update copyright notice
# 2018-03-21 fix to add src routing on primary VNIC when not using namespaces
# 2018-04-05 add note on multiple VCNs
# 2018-05-10 fix to delete config by deleting default route before deleting table, added -v option
# 2018-05-22 fix to ignore interfaces enslaved to a master device
# 2018-09-14 allow namespaces for Ubuntu 16 since problems with DHCP are fixed, and inhibit for all OL/Centos 6
# 2018-09-19 fix os name and version setting for centos 6 images
# 2018-09-19 fix ip addr parsing to remove MAC VLAN interfaces
# 2018-10-25 fix secondary private ip src routing
# 2018-10-31 fix misc routing setup, fix multiple ifaces on same subnet
declare -r THIS=$(basename "$0")
declare -r MD_URL='http://169.254.169.254/opc/v1/vnics/'
declare -r NA='-'
declare -r RTS_FILE='/etc/iproute2/rt_tables'
declare -ir RT_ID_MIN=10 # in case lower ones are reserved
declare -ir RT_ID_MAX=255
declare -r RT_FORMAT_BM='ort${nic}vl${vltag}'
declare -r RT_FORMAT_VM='ort${nic}'
declare -r DEF_NS_FORMAT_BM='ons${nic}vl${vltag}'
declare -r DEF_NS_FORMAT_VM='ons${nic}'
declare -r MACVLAN_FORMAT='${iface}.${vltag}' # note awk script looks for this (max 15 chars)
declare -r VLAN_FORMAT='${iface}v${vltag}' # (max 15 chars)
declare -ir MTU=9000
declare -r ADD='ADD'
declare -r DELETE='DELETE'
declare -r YES='YES'
declare -r CURL=$(which curl)
declare -r IP=$(which ip)
declare -r SSHD=$(which sshd)
declare -r MODPROBE=$(which modprobe)
declare -r OS_RELEASE='/etc/os-release'
if [ -f "$OS_RELEASE" ]; then
declare -r OS_ID=$(grep -ws ID $OS_RELEASE | cut -f 2 -d '=' | tr -d '"' | tr '[:upper:]' '[:lower:]')
declare -r OS_VERSION=$(grep -ws VERSION_ID $OS_RELEASE | cut -f 2 -d '=' | tr -d '"')
else
declare -r OS_RELEASE_alt='/etc/redhat-release'
if [ -f "$OS_RELEASE_alt" ]; then
declare -r OS_ID=$(cat $OS_RELEASE_alt|cut -f 1 -d ' ' | tr '[:upper:]' '[:lower:]')
declare -r OS_VERSION=$(cat $OS_RELEASE_alt | cut -f 3 -d ' ')
fi
fi
if [ -n "$OS_VERSION" ]; then
declare -r OS_MAJ_VERSION=$(echo $OS_VERSION | cut -f 1 -d '.')
fi
declare -r SYS_CLASS_NET='/sys/class/net'
declare -A VIRTUAL_IFACES
declare IS_VM=''
declare -a MACS # all (unique) macs
declare -A MD_I_BY_MAC # index into arrays by MAC
declare -a MD_MACS
declare -a MD_ADDRS
declare -a MD_VLTAGS
declare -a MD_SCIDRS
declare -a MD_SPREFIXS
declare -a MD_SBITSS
declare -a MD_VIRTRTS
declare -a MD_VNICS
declare -a MD_NIC_IS # not set at all if vm
declare -a MD_CONFIGS # $ADD if vnic added
declare -A DUP_ADDRS # hash of addrs that appear more than once
declare -A DUP_SADDRS # hash of subnet addrs that appear more than once
# items use $NA to mean null
declare -A IP_I_BY_MAC # index into arrays by MAC
declare -a IP_MACS # runs of dups if sec addrs
declare -a IP_NSS
declare -a IP_IFACES
declare -a IP_ADDRS
declare -a IP_SADDRS
declare -a IP_SBITSS
declare -a IP_VIRTRTS
declare -a IP_STATES
declare -a IP_VLANS
declare -a IP_VLTAGS # vltag (0 if phys iface)
declare -a IP_SECADS # set to $YES if secondary addr
declare -a IP_SRCS # set to $YES if src hint
declare -a IP_NIC_IS # nic index of iface
declare -a IP_CONFIGS # $DELETE if vnic deleted
declare -a NIC_IP_IS # index of physical iface for nic index
declare -A NIC_I_BY_PHYS_IP_I # nic index for physical iface ip_i
# be sure to clear any IP_ arrays above in the read function
# options:
declare QUIET=''
declare DEBUG=''
declare START_SSHD=''
declare USE_NS=''
declare NS_FORMAT=''
declare -a SEC_ADDRS
declare -a SEC_VNICS
declare -r IFACE_AWK_SCRIPT='/tmp/oci_vcn_iface.awk'
cat >$IFACE_AWK_SCRIPT <<'EOF'
function prtiface(mac, iface, addr, sbits, state, vlan, vltag, secad) {
if (iface != "") printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", mac, iface, addr, sbits, state, vlan, vltag, secad
}
BEGIN { iface = ""; addr = "-" }
/^[0-9]/ {
# if not the first and prev was not a mac vlan then print previous (note print at end too)
if (addr == "-") prtiface(mac, iface, addr, sbits, state, vlan, vltag, secad)
addr = "-"
sbits = "-"
state = "-"
macvlan = "-"
vlan = "-"
vltag = "-"
secad = "-"
if ($0 ~ /BROADCAST/ && $0 !~ /UNKNOWN/ && $0 !~ /NO-CARRIER/ && $0 !~ /master /) {
i = index($2, "@")
if (1 < i) {
j = index($2, ".")
if (j < i) { # mac vlan (not used, no addrs)
macvlan = substr($2, 1, i - 1)
iface = substr($2, i + 1, length($2) - i - 1) # skip : at end
addr = "" # skip the mac vlan iface
} else { # vlan
vlan = substr($2, 1, i - 1)
# extract iface/vltag from macvlan
iface = substr($2, i + 1, j - i - 1)
vltag = substr($2, j + 1, length($2) - j - 1) # skip : at end
}
} else {
i = index($2, ":")
if (i <= 1) { print "cannot find interface name"; exit 1 }
iface = substr($2, 1, i - 1)
}
if ($0 ~ /LOWER_UP/) state = "UP"
else state = "DOWN"
} else iface = ""
next
}
/ link\/ether / { mac = tolower($2) }
/ inet [0-9]/ {
i = index($2, "/")
if (i <= 1) { print "cannot find interface inet address"; exit 1 }
if (addr != "-") secad = "YES"
addr = substr($2, 0, i - 1)
sbits = substr($2, i + 1, length($2) - i)
prtiface(mac, iface, addr, sbits, state, vlan, vltag, secad)
}
END { if (addr == "-") prtiface(mac, iface, addr, sbits, state, vlan, vltag, secad) }
EOF
oci_vcn_err() {
echo "Error: $1" >&2
exit 1
}
oci_vcn_warn() {
echo "Warning: $1" >&2
}
oci_vcn_info() {
[ -n "$QUIET" ] || echo "Info: $1" >&2
}
oci_vcn_debug() {
[ -z "$DEBUG" ] || echo "Debug: $1" >&2
}
oci_vcn_virtual_ifaces_read() {
VIRTUAL_IFACES=()
local iface
for iface in $(ls $SYS_CLASS_NET); do
if ls -l $SYS_CLASS_NET/$iface | grep -wq virtual; then
VIRTUAL_IFACES[$iface]='t'
fi
done
}
oci_vcn_md_read() {
# sets all MD data arrays and their index I_BY_MAC
local -r tmpfile=$(mktemp /tmp/oci_vcn_md.XXXXX)
# MD notes:
# vnic order: primary first, then time created (and therefore vltag/nic)
# BM notes: (1) may be interleaved wrt nic index (i.e. all nic 0 not guaranteed before all nic 1)
# (2) nicIndex was supported starting around 8/23/17, but will not appear on previously
# launched instances unless refreshed by a vnic attach or detach after that date
# parse: force json fields on separate lines
# WARNING: assumes no string values with commas or double quotes
# WARNING: assumes no sub-objects with identical field names
[ -n "$CURL" ] || oci_vcn_err "cannot find curl command"
$CURL -s $MD_URL | tr , '\n' >"$tmpfile" || oci_vcn_err "cannot read metadata"
MD_MACS=($(grep -w macAddr "$tmpfile" | cut -f 4 -d '"')) || exit $? # string
local -i i
for i in $(seq 0 $((${#MD_MACS[@]} - 1))); do
MD_MACS[$i]="${MD_MACS[$i],,}"
done
MD_ADDRS=($(grep -w privateIp "$tmpfile" | cut -f 4 -d '"')) # string
MD_VLTAGS=($(grep -w vlanTag "$tmpfile" | cut -f 2 -d ':' | tr -d ' ')) # integer
MD_VIRTRTS=($(grep -w virtualRouterIp "$tmpfile" | cut -f 4 -d '"')) # string
local s
for s in $(grep -w subnetCidrBlock "$tmpfile" | cut -f 4 -d '"'); do # string
MD_SCIDRS+=(${s})
MD_SPREFIXS+=(${s%/*})
MD_SBITSS+=(${s#*/})
done
MD_VNICS=($(grep -w vnicId "$tmpfile" | cut -f 4 -d '"'))
MD_NIC_IS=($(grep -w nicIndex "$tmpfile" | cut -f 2 -d ':' | tr -d ' '))
# do some validity checks on md data
[ ${#MD_MACS[@]} -eq ${#MD_ADDRS[@]} ] || oci_vcn_err "invalid metadata: MAC or IP addresses are missing"
[ ${#MD_MACS[@]} -eq ${#MD_VLTAGS[@]} ] || oci_vcn_err "invalid metadata: MAC or VLAN tags are missing"
[ ${#MD_MACS[@]} -eq ${#MD_VIRTRTS[@]} ] || oci_vcn_err "invalid metadata: MAC or virtual router addresses are missing"
[ ${#MD_MACS[@]} -eq ${#MD_SPREFIXS[@]} ] || oci_vcn_err "invalid metadata: MAC or subnets are missing"
[ ${#MD_MACS[@]} -eq ${#MD_VNICS[@]} ] || oci_vcn_err "invalid metadata: MAC or VNIC ids are missing"
for i in $(seq 0 $((${#MD_MACS[@]} - 1))); do
[[ ${MD_ADDRS[$i]} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || oci_vcn_err "invalid metadata: address IP format incorrect: ${MD_ADDRS[$i]}"
[[ ${MD_VLTAGS[$i]} =~ ^[0-9]+$ ]] || oci_vcn_err "invalid metadata: VLAN tag incorrect: ${MD_VLTAGS[$i]}"
[[ ${MD_VIRTRTS[$i]} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || oci_vcn_err "invalid metadata: virtual router address format incorrect: ${MD_VIRTRTS[$i]}"
[[ ${MD_SPREFIXS[$i]} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || oci_vcn_err "invalid metadata: subnet prefix format incorrect: ${MD_SPREFIXS[$i]}"
done
# set vm flag based on existence of nic index (see override in oci_vcn_ip_read)
# get the virtual interfaces if VM
if [ ${#MD_NIC_IS[@]} -eq 0 ]; then
IS_VM='t'
oci_vcn_virtual_ifaces_read
fi
# create reverse lookup
for i in "${!MD_MACS[@]}"; do
MD_I_BY_MAC[${MD_MACS[$i]}]=$i
done
rm "$tmpfile"
# find the duplicate addrs, if any
local -A addrs=()
local addr
for addr in "${MD_ADDRS[@]}"; do
if [ -n "${addrs[$addr]}" ]; then DUP_ADDRS[$addr]='t'; fi
addrs[$addr]='t'
done
# find the duplicate subnet addrs, if any
local -A saddrs=()
for addr in "${MD_SPREFIXS[@]}"; do
if [ -n "${saddrs[$addr]}" ]; then DUP_SADDRS[addr]='t'; fi
saddrs[addr]='t'
done
}
oci_vcn_ip_route_table_name() {
local -ir nic=$1 # format looks for "${nic}", note this is really $nic_i
local -ir vltag=$2
local format="$RT_FORMAT_VM"
[ -n "$IS_VM" ] || format="$RT_FORMAT_BM"
eval echo "$format"
}
oci_vcn_ip_route_table_name_ip_i() {
# use only when ip already setup
local -ir ip_i=$1
local -ir nic_i=${IP_NIC_IS[$ip_i]}
local -ir vltag=${IP_VLTAGS[$ip_i]}
oci_vcn_ip_route_table_name $nic_i $vltag
}
oci_vcn_ip_route_table_exists() {
local -r rt_name=$1
if grep -qsw $rt_name $RTS_FILE; then
echo "$rt_name"
fi
}
oci_vcn_ip_route_table_find_unused_id() {
# read all the current route table id/name pairs
# mapfile will create array with each line an element
local lines
mapfile -t lines < <(cat $RTS_FILE | grep -E '^[0-9]' | tr '\t' ' ' | tr -s ' ' ' ') || oci_vcn_err "cannot read route tables file $RTS_FILE"
local line
local -A rt_by_id
for line in "${lines[@]}"; do
local -a pair=($line)
local id=${pair[0]}
local name=${pair[1]}
rt_by_id[$id]=$name
done
# find first id not used
local -i unused=-1
local -i i
for i in $(seq $RT_ID_MIN $RT_ID_MAX); do
if [ -z "${rt_by_id[$i]}" ]; then
unused=$i
break
fi
done
[ $unused -ne -1 ] || oci_vcn_err "cannot find unused id in route tables file $RTS_FILE"
echo $unused
}
oci_vcn_ip_route_table_create() {
local -ir nic_i=$1
local -ir vltag=$2
local -r skip_if_exists=$3
local -r rt_name=$(oci_vcn_ip_route_table_name $nic_i $vltag)
# Check if the route table exists
local rt_exists
rt_exists=$(oci_vcn_ip_route_table_exists $rt_name) || exit $?
if [ -n "$rt_exists" ]; then # already exists
if [ -n "$skip_if_exists" ]; then
oci_vcn_debug "create route table $rt_name already exists, skipping"
return
fi
oci_vcn_warn "route table $rt_name already exists, reusing"
else # create
local -i rt_id
rt_id=$(oci_vcn_ip_route_table_find_unused_id) || exit $?
oci_vcn_debug "create route table $rt_name ($rt_id)"
echo "$rt_id $rt_name" >> $RTS_FILE
fi
echo "$rt_name"
}
oci_vcn_ip_route_table_del() {
local -r rt_name=$1
local -r tmpfile=$(mktemp /tmp/oci_vcn_rt_tables.XXXXX)
if ! grep -qsw $rt_name $RTS_FILE; then
oci_vcn_debug "rt $rt_name not in $RTS_FILE"
return
fi
oci_vcn_debug "rt $rt_name delete"
cp -p $RTS_FILE "$tmpfile" # in case grep fails
grep -vw $rt_name $RTS_FILE > "$tmpfile"
mv "$tmpfile" $RTS_FILE
echo 't'
}
oci_vcn_ip_routing_add() {
local -ir md_i=$1
local -ir nic_i=$2
local -r iface=$3
local -r ns=$4
local -r skip_if_exists=$5
local -ir vltag="${MD_VLTAGS[$md_i]}"
local -r addr="${MD_ADDRS[$md_i]}"
local -r sprefix="${MD_SPREFIXS[$md_i]}"
local -r virtrt="${MD_VIRTRTS[$md_i]}"
local -r scidr="${MD_SCIDRS[$md_i]}"
# set a default route to the gateway/virtual router
if [ -n "$ns" ]; then
local -r nscmd="netns exec $ns $IP"
# using a namespace: iface is assumed only one in namespace, so just set a
# default route to the gateway in the main route table
oci_vcn_debug "default route add"
$IP $nscmd route add default via $virtrt || oci_vcn_err "cannot add namespace $ns default gateway $virtrt"
oci_vcn_info "added namespace $ns default route to gateway $virtrt"
else
# the default route (in main route table) already defined through primary vnic.
# this adds a rule to lookup a route table for any packets sourced from addr
# and then the route table has the default route for that addr.
# this allows packets from protocol services that reply with src addr
# set to route back out through the iface (prevents asymmetric routing).
# check for dup addrs and subnet addrs
if [ -n "${DUP_ADDRS[$addr]}" ]; then
oci_vcn_warn "IP address $addr is a duplicate, skipping creating source route rule"
return
fi
[ -z "${DUP_SADDRS[$sprefix]}" ] || oci_vcn_warn "duplicate subnet prefix $sprefix"
# create route table and add a default route via the gateway, and subnet route
local rt_name
rt_name=$(oci_vcn_ip_route_table_create $nic_i $vltag $skip_if_exists) || exit $?
[ -n "$rt_name" ] || return
oci_vcn_debug "route table add rules"
$IP route add default via $virtrt dev $iface table $rt_name || oci_vcn_err "cannot add default route via $virtrt on $iface to table $rt_name"
$IP route add $scidr dev $iface table $rt_name || oci_vcn_err "cannot add subnet $scidr route on $iface to table $rt_name"
# create source-based rule to use table
oci_vcn_debug "src rule add"
$IP rule add from $addr lookup $rt_name || oci_vcn_err "cannot add rule from $addr use table $rt_name"
oci_vcn_info "added rule for routing from $addr lookup $rt_name with default via $virtrt"
fi
}
oci_vcn_ip_routing_del() {
local -ir ip_i=$1
local -r ns="${IP_NSS[$ip_i]#$NA}"
local -r iface="${IP_IFACES[$ip_i]}"
# for namespaces the subnet and default routes will be auto deleted with the namespace
if [ -z "$ns" ]; then
# delete rule
local -r rt_name=$(oci_vcn_ip_route_table_name_ip_i $ip_i)
local had_rule=''
if $IP rule | grep -qsw $rt_name; then # rule(s) exists
# note that there may be secondary private ips rules using the same table
oci_vcn_debug "rules to table $rt_name delete"
while $IP rule del lookup $rt_name 2>/dev/null; do true; done
had_rule='t'
fi
# delete route table
local had_table
had_table=$(oci_vcn_ip_route_table_del $rt_name) || exit $?
([ -z "$had_rule" ] && [ -z "$had_table" ]) || oci_vcn_info "removed routing on interface $iface"
fi
}
oci_vcn_ip_routing_sec_addr_add() {
local -ir iface_ip_i=$1
local -r addr=$2
local -r ns="${IP_NSS[$iface_ip_i]#$NA}"
# need to add source rule so reuse the routing table from the primary private ip
# not needed if in namespace
# not needed if on pri iface (uses main route table for dest and default route)
if [ -z "$ns" ] && [ $iface_ip_i -ne 0 ]; then
# create source-based rule to use iface's routing table
local -r rt_name=$(oci_vcn_ip_route_table_name_ip_i $iface_ip_i)
oci_vcn_debug "sec addr src rule add"
$IP rule add from $addr lookup $rt_name || oci_vcn_err "cannot add rule from secondary $addr use table $rt_name"
oci_vcn_info "added rule for routing from secondary IP address $addr lookup $rt_name"
fi
}
oci_vcn_ip_routing_sec_addr_del() {
local -ir ip_i=$1
local -r addr=$2
local -r ns="${IP_NSS[$ip_i]#$NA}"
# deconfig the routing (see comments in add)
if [ -z "$ns" ] && [ $ip_i -ne 0 ]; then
$IP rule del from $addr 2>/dev/null # ok if already deleted
oci_vcn_info "deleted rule for routing from secondary IP address $addr"
fi
}
oci_vcn_ip_routes_read() {
#ip r list 192.168.1.0/24
local -ir ip_i=$1
local -r ns="${IP_NSS[$ip_i]#$NA}"
local -r iface="${IP_IFACES[$ip_i]}"
local nscmd=''
local virtrt="$NA"
if [ -n "$ns" ]; then
nscmd="netns exec $ns $IP"
else
# no namespace: check for route table
local -r rt_name=$(oci_vcn_ip_route_table_name_ip_i $ip_i)
local rt_exists
rt_exists=$(oci_vcn_ip_route_table_exists $rt_name) || exit $?
if [ -n "$rt_exists" ]; then # exists
# check for rule
if $IP rule | grep -qsw $rt_name; then # rule exists that uses route table
# look for default route in table, note table may exist but be empty
# "default via 10.0.0.1 dev ens3"
local -a def_entry
def_entry=($($IP route show table $rt_name | grep -sw ^default))
if [ -n "${def_entry[2]}" ]; then
virtrt="${def_entry[2]}"
oci_vcn_debug "default route: $virtrt"
else # emtpy table: delete rule and table
virtrt="$NA"
oci_vcn_ip_routing_del $ip_i
fi
else # clean up route table since no rule uses it
$(oci_vcn_ip_route_table_del $rt_name)
fi
fi
fi
# read the routes
local sprefix="$NA"
local sbits="$NA"
local src="$NA"
# mapfile will create array with each line an element
mapfile -t routes < <($IP $nscmd route | grep -w $iface) || oci_vcn_err "cannot read IP routes for interface $iface"
local line
for line in "${routes[@]}"; do
local -a route=($line)
if [ "${route[0]}" = 'default' ]; then
# "default via 10.0.0.1 dev ens3"
virtrt="${route[2]}"
oci_vcn_debug "default route via: $virtrt"
elif [ "${route[0]#169.}" = "${route[0]}" ]; then # not cavium route
# "10.0.0.0/24 dev ens3 proto kernel scope link src 10.0.0.2"
local -i i
for i in $(seq 0 $((${#route[@]} - 1))); do
local x="${route[$i]}"
if [ $i -eq 0 ]; then
sprefix=${x%/*}
sbits=${x#*/}
if [ "$sprefix" = "$sbits" ]; then # not valid line
sprefix="$NA"
sbits="$NA"
break
else
oci_vcn_debug "subnet route: $sprefix/$sbits"
fi
elif [ "$x" = 'src' ]; then src="$YES"
fi
done
fi
done
IP_VIRTRTS[$ip_i]="$virtrt"
IP_SADDRS[$ip_i]="$sprefix"
IP_SBITSS[$ip_i]="$sbits"
IP_SRCS[$ip_i]="$src"
}
oci_vcn_macvlan_name() {
local -r iface=$1
local -r vltag=$2
eval echo "$MACVLAN_FORMAT"
}
oci_vcn_vlan_name() {
local -r iface=$1
local -r vltag=$2
eval echo "$VLAN_FORMAT"
}
oci_vcn_ip_ns_name() {
local -ir nic=$1 # format looks for "${nic}"
local -ir vltag=$2
if [ -n "$USE_NS" ] && [ -z "$NS_FORMAT" ]; then
if [ -n "$IS_VM" ]; then NS_FORMAT="$DEF_NS_FORMAT_VM"
else NS_FORMAT="$DEF_NS_FORMAT_BM"; fi
fi
eval echo "$NS_FORMAT"
}
oci_vcn_ip_ns_svcs_stop() {
local -r ns=$1
local pids
pids=$($IP netns pids $ns) || oci_vcn_err "cannot get ids for processes in namespace $ns"
if [ -n "$pids" ]; then
kill -TERM $pids || oci_vcn_err "cannot terminate namespace $ns processes: $pids"
oci_vcn_info "terminated namespace $ns processes: $pids"
fi
}
oci_vcn_ip_ns_svcs_start() {
local -r ns=$1
if [ -n "$START_SSHD" ]; then # start SSH daemon
$IP netns exec $ns $SSHD || oci_vcn_err "cannot start ssh daemon"
oci_vcn_info "started namespace $ns ssh daemon"
fi
}
oci_vcn_ip_ns_del() {
local -r ns=$1
# note also deletes vlans and routes
$IP netns del $ns || oci_vcn_err "cannot delete namespace $ns"
oci_vcn_info "deleted namespace $ns"
}
oci_vcn_ip_ns_create() {
local -ir nic_i=$1
local -ir vltag=$2
[ -n "$MODPROBE" ] || oci_vcn_err "cannot find modprobe command"
$MODPROBE 8021q || oci_vcn_err "failed to load 8021q module"
local ns
ns=$(oci_vcn_ip_ns_name $nic_i $vltag) || exit $?
$IP netns add $ns || oci_vcn_err "cannot create namespace $ns"
oci_vcn_info "created namespace $ns"
echo "$ns"
}
oci_vcn_ip_addr_add_iface() {
local -ir md_i=$1
local -ir ip_i=$2 # index of physical iface/nic
local -r ns=$3
local iface="${IP_IFACES[$ip_i]}"
local -r physns="${IP_NSS[$ip_i]#$NA}"
local -r mac="${MD_MACS[$md_i]}"
local -ir vltag="${MD_VLTAGS[$md_i]}"
local -r addr="${MD_ADDRS[$md_i]}"
local -r sbits="${MD_SBITSS[$md_i]}"
local vlan=''
# must be adding to physical iface/nic
[ -z "${IP_VLANS[$ip_i]#$NA}" ] || oci_vcn_err "cannot add IP address $addr to virtual interface ${IP_VLANS[$ip_i]}"
# create virtual interface if needed (bm cases)
local macvlan=''
if [ -z "$IS_VM" ] && [ $vltag -ne 0 ]; then
# bm vnics need a virtual iface except for vltag=0
# if physical iface/nic is in a namespace we must go there to create
local physnscmd=''
if [ -n "$physns" ]; then
physnscmd="netns exec $physns $IP"
fi
# create a mac vlan from physical iface/nic
oci_vcn_debug "macvlan link add $macvlan"
macvlan=$(oci_vcn_macvlan_name $iface $vltag) || exit $?
$IP $physnscmd link add link $iface name $macvlan address $mac type macvlan \
|| oci_vcn_err "cannot create MAC VLAN interface $macvlan for MAC address $mac"
# if physical iface/nic is in a namespace pull out the created mac vlan
if [ -n "$physns" ]; then
$IP $physnscmd link set $macvlan netns 1
fi
# create an ip vlan on top of the mac vlan
oci_vcn_debug "vlan link add"
vlan=$(oci_vcn_vlan_name $iface $vltag) || exit $?
$IP link add link $macvlan name $vlan type vlan id $vltag \
|| oci_vcn_err "cannot create VLAN $vlan on MAC VLAN $macvlan"
fi
# use namespace, if option
local nscmd=''
local -r dev="${vlan:-$iface}"
if [ -n "$ns" ]; then
nscmd="netns exec $ns $IP"
# move the iface(s) to the target namespace
if [ -n "$macvlan" ]; then
oci_vcn_debug "macvlan link move $ns"
$IP link set dev $macvlan netns $ns || oci_vcn_err "cannot move MAC VLAN $macvlan into namespace $ns"
fi
oci_vcn_debug "$dev link move $ns"
$IP link set dev $dev netns $ns || oci_vcn_err "cannot move interface $dev into namespace $ns"
fi
# add IP address to iface:
# the netmask is specified to create a route in the main route table
# if this iface is on the same subnet as another iface then the route will not have an effect
# (until the other iface is removed)
oci_vcn_debug "addr $addr/$sbits add on $dev ns '$ns'"
$IP $nscmd addr add $addr/$sbits dev $dev || oci_vcn_err "cannot add IP address $addr/$sbits on interface $dev"
if [ -n "$macvlan" ]; then # set vlans up
oci_vcn_debug "vlans set up"
$IP $nscmd link set dev $macvlan mtu $MTU up || oci_vcn_err "cannot set MAC VLAN $macvlan up"
$IP $nscmd link set dev $vlan mtu $MTU up || oci_vcn_err "cannot set VLAN $vlan up"
else
oci_vcn_debug "$iface set up"
$IP $nscmd link set dev $iface mtu $MTU up || oci_vcn_err "cannot set interface $iface MTU"
fi
oci_vcn_info "added IP address $addr on interface $dev with MTU $MTU"
echo "$dev"
}
oci_vcn_ip_addr_del_iface() {
local -ir ip_i=$1
local -r iface="${IP_IFACES[$ip_i]}"
local -r ns="${IP_NSS[$ip_i]#$NA}"
local -r vlan="${IP_VLANS[$ip_i]#$NA}"
local -r secad="${IP_SECADS[$ip_i]#$NA}"
local nscmd=''
if [ -n "$ns" ]; then
nscmd="netns exec $ns $IP"
fi
if [ "$secad" != "$YES" ] && [ -n "$vlan" ]; then
# delete vlan and macvlan, removes the addrs (pri and sec) as well
oci_vcn_debug "$ns link delete"
local -ir vltag="${IP_VLTAGS[$ip_i]}"
local macvlan
macvlan=$(oci_vcn_macvlan_name $iface $vltag) || exit $?
$IP $nscmd link del link $vlan dev $macvlan || oci_vcn_err "cannot remove VLAN $vlan"
oci_vcn_info "removed VLAN $vlan"
else
# delete addr from phys iface
# deleting namespace will move phys iface back to main
# note that we may be deleting sec addr from a vlan here
local -r addr="${IP_ADDRS[$ip_i]#$NA}"
local -r dev="${vlan:-$iface}"
local bits="${IP_SBITSS[$ip_i]#$NA}"
[ "$secad" != "$YES" ] || bits=32
oci_vcn_debug "addr $addr del ns '$ns' dev $dev"
$IP $nscmd addr del $addr/$bits dev $dev || oci_vcn_err "cannot remove IP address $addr/$bits from interface $dev"
oci_vcn_info "removed IP address $addr from interface $dev"
fi
}
oci_vcn_ip_addr_add() {
local -ir md_i=$1
local -r mac="${MD_MACS[$md_i]}"
local -r addr="${MD_ADDRS[$md_i]}"
local ns=''
local iface=''
local -i nic_i
local -i vltag
# note that when adding an addr to a physical iface ip_i will be the index of the
# addr, but when creating a vlan for addr ip_i will not be the vlan iface but its phys one
local -i ip_i
if [ -z "$IS_VM" ]; then
# bm vnics' physical ifaces are identified by nic index
nic_i=${MD_NIC_IS[$md_i]}
[ $nic_i -lt ${#NIC_IP_IS[@]} ] || oci_vcn_err "cannot find interface for NIC $nic_i"
ip_i=${NIC_IP_IS[$nic_i]}
iface="${IP_IFACES[$ip_i]}"
vltag=${MD_VLTAGS[$md_i]}
else
# vm vnics' physical ifaces are identified by matching mac
local found=''
ip_i=0
local ip_mac
for ip_mac in "${IP_MACS[@]}"; do
if [ "$ip_mac" = "$mac" ]; then
found='t'
break
fi
ip_i+=1
done
[ -n "$found" ] || oci_vcn_err "cannot find interface matching VNIC MAC $mac"
iface="${IP_IFACES[$ip_i]}"
nic_i=${IP_NIC_IS[$ip_i]}
vltag=0
fi
# check that there is no current addr on iface
if [ -n "$IS_VM" ] || [ "${MD_VLTAGS[$md_i]}" = "0" ]; then # putting addr directly on iface
local -r ip_addr="${IP_ADDRS[$ip_i]#$NA}"
[ -z "$ip_addr" ] || oci_vcn_err "IP address $ip_addr already added on interface $iface"
fi
# make sure physical iface/nic is up
if [ "${IP_STATES[$ip_i]}" != "UP" ]; then
$IP link set dev $iface up || oci_vcn_err "cannot set interface $iface up"
fi
# create namespace if requested
local ns=''
if [ -n "$USE_NS" ]; then
ns=$(oci_vcn_ip_ns_create $nic_i $vltag) || exit $?
# if working on a physical iface we need to set its namespace so that any
# vlan created subsequently will know how to find it
[ $vltag -ne 0 ] || IP_NSS[$ip_i]="$ns"
fi
# add addr to iface (possibly creating vlan)
local dev
dev=$(oci_vcn_ip_addr_add_iface $md_i $ip_i $ns) || exit $?
# setup routes
oci_vcn_ip_routing_add $md_i $nic_i $dev $ns
# if namespace then wait for changes and start services
if [ -n "$ns" ]; then
sleep 1 # namespace changes seem to take time
oci_vcn_ip_ns_svcs_start $ns
fi
}
oci_vcn_ip_sec_addr_add() {
local -ir iface_ip_i=$1
local -r addr=$2
local -r iface="${IP_IFACES[$iface_ip_i]}"
local -r vlan="${IP_VLANS[$iface_ip_i]#$NA}"
local -r dev="${vlan:-$iface}"
local -r ns="${IP_NSS[$iface_ip_i]#$NA}"
local nscmd=''
local nsinfo=''
# config routing
oci_vcn_ip_routing_sec_addr_add $iface_ip_i $addr
# config the addr on the iface
if [ -n "$ns" ]; then
nscmd="netns exec $ns $IP"
nsinfo=" in namespace $ns"
fi
oci_vcn_info "adding secondary IP address $addr to interface (or VLAN) $dev$nsinfo"
$IP $nscmd addr add $addr/32 dev $dev || oci_vcn_err "cannot add secondary IP address $addr on interface $dev$nsinfo"
}
oci_vcn_ip_sec_addr_del() {
local -ir ip_i=$1
local -r deconfig_all=$2
local -r addr=${IP_ADDRS[$ip_i]}
local -r iface=${IP_IFACES[$ip_i]}
local -r vlan="${IP_VLANS[$ip_i]#$NA}"
local -r dev="${vlan:-$iface}"
local -r ns="${IP_NSS[$ip_i]#$NA}"
local nscmd=''
local nsinfo=''
[ "${IP_SECADS[$ip_i]}" = "$YES" ] || oci_vcn_err "not a secondary IP address: $addr"
# remove the addr on the iface (see comments in add)
# no need if deconfig all and on a vlan or in namespace
if [ -z "$deconfig_all" ] || ([ -z "$vlan" ] && [ -z "$ns" ]); then
if [ -z "$deconfig_all" ] && [ -n "$ns" ]; then
nscmd="netns exec $ns $IP"
nsinfo=" in namespace $ns"
fi
oci_vcn_info "removing secondary IP address $addr from interface (or VLAN) $dev$nsinfo"
$IP $nscmd addr del $addr/32 dev $dev || oci_vcn_err "cannot remove IP address $addr on interface $dev"
fi
# remove the routing (see comments in add)
oci_vcn_ip_routing_sec_addr_del $ip_i $addr
}
oci_vcn_sec_addr_is_provisioned() {
local -r find_addr=$1
local -r find_vnic=$2
local -i i
local found=''
for i in $(seq 0 $((${#SEC_ADDRS[@]} - 1))); do
local addr=${SEC_ADDRS[$i]}
local vnic=${SEC_VNICS[$i]}
if [ "$find_addr" = "$addr" ] && [ "$find_vnic" = "$vnic" ]; then
found='t'
break
fi
done
echo "$found"
}
oci_vcn_ip_addr_del() {
local -ir ip_i=$1
local -r ns="${IP_NSS[$ip_i]#$NA}"
local -r secad="${IP_SECADS[$ip_i]#$NA}"
[ $ip_i -ne 0 ] || oci_vcn_err "cannot remove primary VNIC"
if [ "$secad" != "$YES" ]; then
if [ -n "$ns" ]; then
# stop services in namespace
oci_vcn_ip_ns_svcs_stop $ns
fi
# remove routing
oci_vcn_ip_routing_del $ip_i
fi
# remove addr
oci_vcn_ip_addr_del_iface $ip_i
if [ "$secad" != "$YES" ] && [ -n "$ns" ]; then
# delete namespace
oci_vcn_ip_ns_del $ns
sleep 1 # namespace changes seem to take time
fi
}
oci_vcn_ip_ifaces_read() {
local -r ns="$1"
local nscmd=''
local -a iface_datas
if [ -n "$ns" ]; then # change ip command to use namespace
nscmd="netns exec $ns $IP"
fi
# read the interfaces in namespace (if any)
# mapfile will create array with each line an element
mapfile -t iface_datas < <($IP $nscmd addr show | awk -f $IFACE_AWK_SCRIPT) || oci_vcn_err "cannot read IP addresses"
if [ ${#iface_datas[@]} -eq 0 ]; then
# if reading physical ifaces, must be at least 1
[ -n "$ns" ] || oci_vcn_err "cannot locate interfaces"
# empty namespace: probably result of a VM VNIC delete
# note empty namespaces do not survive reboot
$IP netns del $ns || oci_vcn_err "cannot delete empty namespace $ns"
oci_vcn_warn "deleted empty namespace $ns"
else
local -r nsna="${ns:-$NA}"
local -i ip_i=${#IP_MACS[@]} # continue from previous ns (if any)
local line
for line in "${iface_datas[@]}"; do
# line items are in order printed by awk script print
# note that $NA is used to mean null (i.e. not set)
oci_vcn_debug "iface line: $line"
local -a iface_data=($line)
local iface="${iface_data[1]}"
# filter out virtual interfaces if VM (assume created by user for other purpose)
if [ -z "$IS_VM" ] || [ -z "${VIRTUAL_IFACES[$iface]}" ]; then
local mac="${iface_data[0]}"
IP_MACS+=("$mac")
IP_NSS+=("$nsna")
IP_IFACES+=("$iface")
IP_ADDRS+=("${iface_data[2]}")
IP_SBITSS+=("${iface_data[3]}")
IP_STATES+=("${iface_data[4]}")
IP_VLANS+=("${iface_data[5]}")
IP_VLTAGS+=("${iface_data[6]}")
local secad="${iface_data[7]}"
IP_SECADS+=("$secad")
[ "$secad" = "$YES" ] || IP_I_BY_MAC["$mac"]=$ip_i # primary addrs only
ip_i+=1
fi
done
fi
}
oci_vcn_ip_read() {
IP_I_BY_MAC=()
IP_MACS=()
IP_NSS=()
IP_IFACES=()
IP_ADDRS=()
IP_SADDRS=()
IP_SBITSS=()
IP_VIRTRTS=()
IP_STATES=()
IP_VLANS=()
IP_VLTAGS=()
IP_SECADS=()
IP_SRCS=()
IP_NIC_IS=()
NIC_IP_IS=()
NIC_I_BY_PHYS_IP_I=()
# read the non-namespace ifaces and any addrs on them
oci_vcn_ip_ifaces_read
# read ifaces in all the namespaces (if any), note some os's don't support netns
mapfile -t nss < <($IP netns 2>/dev/null)
local ns
for ns in "${nss[@]}"; do
oci_vcn_ip_ifaces_read $ns
done
# set vltag 0 (for phys ifaces) and read os routes for all ifaces
local -i ip_i
for ip_i in $(seq 0 $((${#IP_MACS[@]} - 1))); do
# any iface w/o vltag has tag 0
[ -n "${IP_VLTAGS[$ip_i]#$NA}" ] || IP_VLTAGS[$ip_i]=0
oci_vcn_ip_routes_read $ip_i
done
# find the "physical" iface indices: vltag 0 and not secondary addr
# note vm ifaces are considered physical
# these may not be in nic index order due to inclusion in namespaces (see next)
local -r tmpfile=$(mktemp /tmp/oci_vcn_ifaces.XXXXX)
local -A ip_i_by_phys_iface
for ip_i in $(seq 0 $((${#IP_MACS[@]} - 1))); do
if [ ${IP_VLTAGS[$ip_i]} -eq 0 ] && [ "${IP_SECADS[$ip_i]}" != "$YES" ]; then
# physical iface/nic
local iface=${IP_IFACES[$ip_i]}
ip_i_by_phys_iface[$iface]=$ip_i
echo "$iface" >> "$tmpfile"
fi
NIC_I_BY_PHYS_IP_I[$ip_i]=-1
done
# sort physical ifaces by first number (either at end or in middle) in iface name
# this will provide the nic index
local -i nic_i=0
local iface
for iface in $(cat "$tmpfile" | awk -- '{ match($1, /[0-9]+/); print substr($1, RSTART, RLENGTH), $1 }' | sort -n | cut -f 2 -d ' '); do
local ip_i=${ip_i_by_phys_iface[$iface]}
NIC_IP_IS+=($ip_i)
NIC_I_BY_PHYS_IP_I[$ip_i]=$nic_i
nic_i+=1
done
rm "$tmpfile"
# for each iface (phys or vlan) get nic index
for ip_i in $(seq 0 $((${#IP_MACS[@]} - 1))); do
local iface=${IP_IFACES[$ip_i]}
local -i phys_ip_i=${ip_i_by_phys_iface[$iface]}
IP_NIC_IS[$ip_i]=${NIC_I_BY_PHYS_IP_I[$phys_ip_i]}
done
# fix up missing nic index for bms (see also oci_vcn_md_read): bms will be missing nic index metadata
# if they were created before and have not had a vnic attached or detached since 8/23/17.
# if there is a secondary vnic created and configured (before 8/23/17) we can tell it is a bm
# because there will either be configured vlans or more vnics than physical ifaces.
# note that gen 2 shapes are post 8/23/17 and, hence, will have nic indices already set.
# a missing nic index for an old bm will not matter if there are no vnics to configure.
if [ ${#MD_NIC_IS[@]} -eq 0 ] && [ ${#NIC_IP_IS[@]} -eq 1 ] && \
([ ${#IP_MACS[@]} -gt 1 ] || [ ${#MD_MACS[@]} -gt 1 ]); then # configured or new secondaries
local -i md_i
for md_i in $(seq 0 $((${#MD_MACS[@]} - 1))); do
MD_NIC_IS+=(0)
done
oci_vcn_info "legacy BM instance detected"
IS_VM=''
fi
}
oci_vcn_read() {
# assumes md info is already read
# reads ip configs and creates single array of all macs
# (fixes vm interfaces if random mac)
[ -n "$IP" ] || oci_vcn_err "cannot find ip command"
local warn_ifaces=''
local -i attempt
for attempt in 1 2; do
# initialize md/ip shared arrays
MACS=("${MD_MACS[@]}")
# read all of ip config info and see if it matches md info
oci_vcn_ip_read
MD_CONFIGS=()
local -i md_i=0
local mac
for mac in "${MD_MACS[@]}"; do
MD_CONFIGS[$md_i]="$NA"
local -i ip_i=${IP_I_BY_MAC[$mac]:--1}
if [ $ip_i -lt 0 ]; then
# if there is no corresponding iface mac: add
# note ifaces with random macs will be detected below and this will be retried
MD_CONFIGS[$md_i]="$ADD"
else
local addr="${IP_ADDRS[$ip_i]#$NA}"
if [ -z "$addr" ]; then
# matching mac iface does not have address: add
# make sure it is not a (corrupted) vlan that had been configured previously
[ ${IP_VLTAGS[$ip_i]} -eq 0 ] || oci_vcn_err "VLAN (with MAC $mac) configured but missing IP address (must manually fix)"
MD_CONFIGS[$md_i]="$ADD"
fi
fi
md_i+=1
done
local -i ip_i=0
IP_CONFIGS=()
local retry=''
warn_ifaces=''
local -A new_macs=() # for deduping secondary addr macs
for mac in "${IP_MACS[@]}"; do
IP_CONFIGS[$ip_i]="$NA"
local addr="${IP_ADDRS[$ip_i]#$NA}"
# note that the primary vnic will be matched up (permanently)
local -i md_i=${MD_I_BY_MAC[$mac]:--1}
if [ $md_i -lt 0 ]; then
# no metadata mac corresponding to ip mac
local iface="${IP_IFACES[$ip_i]}"
if [ -n "$addr" ]; then
# addr is configured: should be deleted
# bm case (in vm case ifaces are auto-deleted when vnic is detached)
IP_CONFIGS[$ip_i]="$DELETE"
elif [ -z "$IS_VM" ]; then
# bm iface mac w/o addr and w/o md mac:
# skip if phys iface, else addr deleted w/o deleting vlan?
if [ ${IP_VLTAGS[$ip_i]} -ne 0 ]; then
IP_CONFIGS[$ip_i]="$DELETE"
warn_ifaces="$warn_ifaces $iface"
fi
else
# vm iface mac w/o addr and w/o md mac: assume random mac
([ $attempt -eq 1 ] && [ "${IP_STATES[$ip_i]}" = 'DOWN' ]) || oci_vcn_err "interface $iface with MAC $mac does not have corresponding metadata"
# assume vm case 1st since less likely addr was deleted
# attempt mac fix by turning iface up, then retry
# this is probably an Intel driver problem
# could look at /sys/class/net/<iface>/addr_assign_type
$IP link set dev $iface up || oci_vcn_err "cannot set interface $iface up"
retry='t'
fi
if [ -z "${new_macs[$mac]}" ]; then
new_macs[$mac]='t'
MACS+=("$mac") # accumulate all unique macs
fi
# TODO else validate for consistency?: addr, vltag, subnet, virtrt
elif [ "${IP_SECADS[$ip_i]}" = "$YES" ]; then
local is_prov=$(oci_vcn_sec_addr_is_provisioned $addr "${MD_VNICS[$md_i]}")
if [ -z "$is_prov" ]; then
IP_CONFIGS[$ip_i]="$DELETE"
fi
fi
ip_i+=1
done
[ -n "$retry" ] || break
done
if [ -n "$warn_ifaces" ]; then
oci_vcn_warn "no VNIC (or MAC does not match) and no address, interfaces will be marked for delete:$warn_ifaces"
fi
}
oci_vcn_config_or_deconfig_sec_addrs() {
local -r do_config="$1" # config if not empty, else deconfig
local found=''
# vnics must be configured, whether config or deconfig secondary addrs
local -i i
for i in $(seq 0 $((${#SEC_ADDRS[@]} - 1))); do
local addr=${SEC_ADDRS[$i]}
local vnic=${SEC_VNICS[$i]}
# find vnic's mac
local mac=''
local -i md_i
for md_i in $(seq 0 $((${#MD_MACS[@]} - 1))); do
if [ "$vnic" = "${MD_VNICS[$md_i]}" ]; then
mac="${MD_MACS[$md_i]}"
break
fi
done
[ -n "$mac" ] || oci_vcn_err "cannot find VNIC for secondary IP address $addr on $vnic"
# find mac in ip config
local -i iface_ip_i=-1
local already_config=''
local -i ip_i
for ip_i in $(seq 0 $((${#IP_MACS[@]} - 1))); do
if [ "$mac" = "${IP_MACS[$ip_i]}" ]; then # put on this iface if not already configured
[ $iface_ip_i -ge 0 ] || iface_ip_i=$ip_i # 1st is interface (secondary addrs come after)
if [ "$addr" = "${IP_ADDRS[$ip_i]}" ]; then # already configured
already_config='t'
break
fi
fi
done
[ $iface_ip_i -ge 0 ] || oci_vcn_err "cannot find interface for secondary IP address $addr on $vnic"
if [ -n "$do_config" ] && [ -z "$already_config" ]; then # configure
oci_vcn_ip_sec_addr_add $iface_ip_i $addr
found='t'
elif [ -z "$do_config" ] && [ -n "$already_config" ]; then # deconfigure
# note this path is only if deconfiguring just the secondaries
oci_vcn_ip_sec_addr_del $ip_i
found='t'
fi
done
echo "$found"
}
oci_vcn_config() {
local found=''
local mac
local -A configed
# fix up config: md will indicate adds, ip deletes
local -i md_i
for md_i in $(seq 0 $((${#MD_CONFIGS[@]} - 1))); do
local config="${MD_CONFIGS[$md_i]#$NA}"
if [ "$config" = "$ADD" ]; then
oci_vcn_info "adding IP config for VNIC MAC ${MD_MACS[$md_i]} with id ${MD_VNICS[$md_i]}"
oci_vcn_ip_addr_add $md_i
found='t'
fi
done
local del_vmac=''
local -i ip_i
for ip_i in $(seq 0 $((${#IP_CONFIGS[@]} - 1))); do
local config="${IP_CONFIGS[$ip_i]#$NA}"
if [ "$config" = "$DELETE" ]; then
# del all pri addrs and sec addrs (unless its vlan is being deleted)
local mac="${IP_MACS[$ip_i]}"
local secad="${IP_SECADS[$ip_i]#$NA}"
if [ "$secad" != "$YES" ] || [ "$del_vmac" != "$mac" ]; then
local addr="${IP_ADDRS[$ip_i]}"
oci_vcn_info "removing IP config of address $addr from MAC $mac"
oci_vcn_ip_addr_del $ip_i
found='t'
# keep track of last deleted vlan
[ "${IP_VLTAGS[$ip_i]}" -eq 0 ] || del_vmac=$mac
fi
fi
done
# config secondary addrs, if any
local sec_addrs_found=''
if [ ${#SEC_ADDRS[@]} -gt 0 ]; then
# reread config if there were changes so that secondaries are put in the correct place
if [ -n "$found" ]; then
sleep 2 # wait for newly created ifaces to settle
oci_vcn_read
fi
sec_addrs_found=$(oci_vcn_config_or_deconfig_sec_addrs 't') || exit $?
fi
[ -n "$found" ] || [ -n "$sec_addrs_found" ] || oci_vcn_info "no changes, IP configuration is up-to-date"
}
oci_vcn_deconfig_all() {
local -i ip_i=0
local found=''
local mac
for mac in "${IP_MACS[@]}"; do
local addr="${IP_ADDRS[$ip_i]#$NA}"
if [ -n "$addr" ]; then # ip is configured
# note that primaries are encountered first
if [ "${IP_SECADS[$ip_i]}" != "$YES" ]; then # primary addr
if [ $ip_i -gt 0 ]; then # skip pri vnic, pri addr
local -i md_i=${MD_I_BY_MAC[$mac]:--1}
local missing=" missing"
local vnicmsg=''
if [ $md_i -ge 0 ]; then vnicmsg=" with id ${MD_VNICS[$md_i]}"; missing=""; fi
oci_vcn_info "removing IP config of address $addr for$missing VNIC MAC $mac$vnicmsg"
oci_vcn_ip_addr_del $ip_i
found='t'
fi
else # secondary addr
oci_vcn_ip_sec_addr_del $ip_i 't'
found='t'
fi
fi
ip_i+=1
done
if [ -z "$found" ]; then
oci_vcn_info "no changes, no IP configuration to delete"
fi
}
oci_vcn_show() {
local -r fmt="%-6s %-15s %-15s %-5s %-15s %-10s %-3s %-10s %-5s %-11s %-5s %-17s %s\n"
printf "$fmt" CONFIG ADDR SPREFIX SBITS VIRTRT NS IND IFACE VLTAG VLAN STATE MAC VNIC
local mac
for mac in "${MACS[@]}"; do # all known macs
local config="$NA"
local addr="$NA"
local nic_i="$NA"
local vltag="$NA"
local sprefix="$NA"
local sbits="$NA"
local virtrt="$NA"
local ns="$NA"
local iface="$NA"
local vlan="$NA"
local state="$NA"
local vnic="$NA"
local -i md_i=${MD_I_BY_MAC[$mac]:--1}
if [ $md_i -ge 0 ]; then # in md: ADD, or no change depending on ip info
config="${MD_CONFIGS[$md_i]}"
nic_i="${MD_NIC_IS[$md_i]:-$NA}"
addr="${MD_ADDRS[$md_i]}"
[ -n "$IS_VM" ] || vltag="${MD_VLTAGS[$md_i]}" # not used in vms
sprefix="${MD_SPREFIXS[$md_i]}"
sbits="${MD_SBITSS[$md_i]}"
virtrt="${MD_VIRTRTS[$md_i]}"
vnic="${MD_VNICS[$md_i]}"
fi
# find the ip info on this mac, note that there could be none, one,
# or multiple addrs if secondaries addrs exist (they will come after primary)
local -i ip_i=${IP_I_BY_MAC[$mac]:--1} # index of primary addr, if any
if [ $ip_i -ge 0 ]; then
local -i pri_ip_i=$ip_i
local secad=''
while true; do
secad="${IP_SECADS[$ip_i]#$NA}"
[ $pri_ip_i -eq $ip_i ] || [ -n "$secad" ] || break
vlan="${IP_VLANS[$ip_i]:-$NA}"
iface="${IP_IFACES[$ip_i]}"
ns="${IP_NSS[$ip_i]}"
state="${IP_STATES[$ip_i]}"
local cfg="${IP_CONFIGS[$ip_i]#$NA}"
[ -z "$cfg" ] || config="$cfg"
if [ $md_i -lt 0 ]; then # not in md, fill with ip info
addr="${IP_ADDRS[$ip_i]}"
sbits="${IP_SBITSS[$ip_i]}"
virtrt="${IP_VIRTRTS[$ip_i]}"
[ -n "$IS_VM" ] || vltag="${IP_VLTAGS[$ip_i]}"
elif [ -n "$secad" ]; then
addr="${IP_ADDRS[$ip_i]}"
fi
local -i nic_phys=${NIC_I_BY_PHYS_IP_I[$ip_i]}
[ -n "$secad" ] || [ $nic_phys -lt 0 ] || nic_i=$nic_phys # don't show if sec addr
if [ -z "$secad" ]; then
printf "$fmt" "$config" "$addr" "$sprefix" "$sbits" "$virtrt" "$ns" "$nic_i" "$iface" "$vltag" "$vlan" "$state" "$mac" "$vnic"
else
printf "$fmt" "$config" "$addr" "$NA" "$NA" "$NA" "$NA" "$NA" "$NA" "$NA" "$NA" "$NA" "$NA" "$NA"
fi
ip_i+=1
done
else
printf "$fmt" "$config" "$addr" "$sprefix" "$sbits" "$virtrt" "$ns" "$nic_i" "$iface" "$vltag" "$vlan" "$state" "$mac" "$vnic"
fi
done
}
oci_vcn_help() {
cat <<EOF
NAME
$THIS -- display and configure Oracle OCI Virtual Cloud Networks on instance
SYNOPSIS
$THIS [-s] [-e <IP address> <VNIC OCID>]
$THIS -c [-q] [-s] [-n [<format>] [-r]] [-e <IP address> <VNIC OCID> [-e ...]]
$THIS -d [-q] [-s] [-e <IP address> <VNIC OCID>]
DESCRIPTION
This shows the current OCI Virtual interface Cards provisioned in the cloud
and configured on this instance. When a secondary VNIC is provisioned in OCI it must
be explicitly configured on the instance using this script or similar commands.
The first version of this command displays the currently provisioned VNICs and the
current IP configuration for this instance. VNICs that are not yet configured are
marked with '$ADD' and IP configurations that no longer have an associated VNIC
are marked with '$DELETE'.
The second version, with -c, configures VNICs that do not have an IP configuration
and deletes the IP configurations of VNICs that are not currently provisioned.
This puts the instance IP configuration in sync with current OCI provisioning.
If one or more optional -e options are present the secondary IP addresses are
configured on the same interfaces as the corresponding VNIC.
The configuring interfaces can optionally be placed inside separate network
namespaces. This is necessary when VNICs are in subnets (different VCNs) with
overlapping address blocks and the network applications are not bound directly
to interfaces. Network namespaces require applications to be launched in them
explicitly (via 'ip netns exec <ns>') in order to establish association with
the interface. When namespaces are not used, policy-based routing is configured
to provide a default route to the secondary VNIC\'s virtual router (default
gateway) when the VNIC\'s address is the source address.
Bare Metal secondary VNICs are configured using VLANs (where there is no
corresponding physical interface). These will look like 2 addition interfaces
when showing IP links, with names like '$MACVLAN_FORMAT' for the MAC VLAN
and '$VLAN_FORMAT' for the IP VLAN.
The third version, -d, deletes all IP configuration for provisioned secondary VNICs
as long as there is no -e option. If one or more optional -e options are present
only the given secondary IP addresses are deconfigured and the remaining configuration
is left as is.
This script is made to be run periodically to pick up changes in VNIC provisioning
(whether adding or deleting). Note that these IP configuration changes are not
persistent, the script must, at a minimum, be run on each startup.
-c Add IP configuration for VNICs that are not configured and delete
for VNICs that are no longer provisioned.
-d Deconfigure all VNICs (except the primary). If a -e option is also
present only the secondary IP address(es) are deconfigured.
-e <IP address> <VNIC OCID>
Secondary private IP address to configure or deconfigure.
-h Print help.
-n [<format>]
When configuring, place interfaces in namespace identified by the given
format. Format can include \$nic and \$vltag variables. The name
defaults to '$DEF_NS_FORMAT_BM' for BMs and '$DEF_NS_FORMAT_VM' for VMs.
When configuring multiple VNICs ensure the namespaces are unique.
-q Suppress information messages.
-r Start sshd in namespace (if -n is present)
-s Show information on all provisioning and interface configuration.
This is the default action if no options are given.
Columns:
CONFIG '$ADD' indicates missing IP config, '$DELETE' missing VNIC
ADDR IP address
SPREFIX subnet CIDR prefix
SBITS subnet mask bits
VIRTRT virutal router IP address
NS namespace (if any)
IND interface index (if BM)
IFACE interface (underlying physical if VLAN is also set)
VLTAG VLAN tag (if BM)
VLAN IP virtual LAN (if any)
STATE state of interface
MAC MAC address
VNIC VNIC object identifier
-v Verbose information messages.
EXAMPLES
$THIS
Show all provisioned VNICs and configured IP addresses.
$THIS -c
Set configuration without a namespace.
$THIS -c -n ''
Set configuration using a namespace with the default format.
$THIS -c -n 'myns\$vltag'
Set configuration using a namespace with format 'myns\$vltag'.
SEE ALSO:
OCI networking overview including route tables and security lists:
https://docs.us-phoenix-1.oraclecloud.com/Content/Network/Concepts/overview.htm
Note: secondary VNIC in different VCN not handled, requires manually adding default route:
ip route add <VCN2 CIDR> via <VCN2 sec VNIC subnet virt router ip> dev <VCN2 sec VNIC ifname>
EOF
}
# TODO secondary private IPs in metadata
declare show=''
declare config=''
declare deconfig=''
declare os_ver="$OS_ID-$OS_VERSION"
declare os_maj_ver="$OS_ID-$OS_MAJ_VERSION"
while [ $# -ge 1 ]; do
declare opt="$1"
shift
case $opt in
-c) config='t';;
-d) deconfig='t';;
-e) if [ $# -lt 2 ]; then oci_vcn_err "secondary private IP address option requires <IP address> <VNIC OCID>"; fi
SEC_ADDRS+=($1); shift
SEC_VNICS+=($1); shift
;;
-n) if [ "$os_maj_ver" = "ol-6" ] || [ "$os_maj_ver" = "centos-6" ]; then
oci_vcn_err "namespaces not supported on this os version ($os_ver)"
fi
if [ $# -ge 1 ] && ! [[ "$1" =~ ^\- ]]; then
[ -z "$1" ] || NS_FORMAT="$1"
shift
fi
USE_NS='t';;
-r) START_SSHD='t'
[ -n "$SSHD" ] || oci_vcn_err "missing sshd command";;
-s) show='t';;
-h) oci_vcn_help; exit 0;;
-q) if [ -n "$DEBUG" ]; then oci_vcn_err "cannot specify quiet with verbose"; fi
QUIET='t';;
-v) if [ -n "$QUIET" ]; then oci_vcn_err "cannot specify verbose with quiet"; fi
DEBUG='t';;
-*) oci_vcn_err "unknown option $opt";;
esac
done
[ -z "$START_SSHD" ] || [ -n "$USE_NS" ] || oci_vcn_err "cannot start sshd if namespace is not created"
[ $EUID -eq 0 ] || oci_vcn_err "must be run as root"
# read all metadata and ip config
oci_vcn_md_read
oci_vcn_read
# process options
if [ -n "$config" ]; then
[ -z "$deconfig" ] || oci_vcn_err "conflicting options"
oci_vcn_config
[ -z "$show" ] || oci_vcn_read # reread if show
elif [ -n "$deconfig" ]; then
if [ ${#SEC_ADDRS[@]} -gt 0 ]; then # just deconfig addrs
oci_vcn_config_or_deconfig_sec_addrs
else # deconfig all
oci_vcn_deconfig_all
fi
[ -z "$show" ] || oci_vcn_read # reread if show
else
show='t'
fi
[ -z "$show" ] || oci_vcn_show
