vm-bhyve-laylo/lib/vm-switch-standard
2023-05-28 10:49:27 +02:00

406 lines
13 KiB
Bash

#!/bin/sh
#-------------------------------------------------------------------------+
# Copyright (C) 2016 Matt Churchyard (churchers@gmail.com)
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# creaate bridge interface for a standard switch
#
# @param string _name name of the switch
#
switch::standard::init(){
local _name="$1"
local _id _addr _mtu _len _ifconf
# see if it already exists
switch::standard::id "_id" "${_name}" && return 0
# get the length of the switch name
# it's useful for other utilities to use switch name as interface name
# as it's static. can't do that if it's > 12 chars
_len=$(echo -n "${_name}" | wc -m)
if [ ${_len} -le 12 ]; then
_ifconf="name vm-${_name}"
else
_ifconf="descr vm/${_name}"
fi
# create a bridge for this switch
_id=$(ifconfig bridge create ${_ifconf} group vm-switch up 2>/dev/null)
[ $? -eq 0 ] || util::err "failed to create bridge interface for switch ${_name}"
switch::set_viid "${_name}" "${_id}"
# randomise mac if feature is available
[ ${VERSION_BSD} -ge 1102000 ] && ifconfig "${_id}" link random
# try to set ip address
config::core::get "_addr" "addr_${_name}"
[ -n "${_addr}" ] && ifconfig "${_id}" inet ${_addr} 2>/dev/null
config::core::get "_addr" "addr6_${_name}"
[ -n "${_addr}" ] && ifconfig "${_id}" inet6 ${_addr} 2>/dev/null
# custom mtu?
config::core::get "_mtu" "mtu_${_name}"
[ -n "${_mtu}" ] && ifconfig "${_id}" mtu ${_mtu}
# add member interfaces
switch::standard::__add_members "${_name}" "${_id}"
}
# show the configuration details for a switch
#
# @param string _name the switch name
# @param string _format output format
#
switch::standard::show(){
local _name="$1"
local _format="$2"
local _id _vlan _ports _addr _mtu _priv
switch::find "_id" "${_name}"
config::core::get "_ports" "ports_${_name}"
config::core::get "_vlan" "vlan_${_name}"
config::core::get "_addr" "addr_${_name}"
config::core::get "_mtu" "mtu_${_name}"
config::core::get "_priv" "private_${_name}" "no"
printf "${_format}" "${_name}" "standard" "${_id:--}" "${_addr:--}" "${_priv}" "${_mtu:--}" \
"${_vlan:--}" "${_ports:--}"
}
# create a standard virtual switch
#
switch::standard::create(){
# store configuration
config::core::set "switch_list" "${_switch}" "1"
config::core::set "type_${_switch}" "standard"
[ -n "${_if}" ] && config::core::set "ports_${_switch}" "${_if}"
[ -n "${_vlan}" ] && config::core::set "vlan_${_switch}" "${_vlan}"
[ -n "${_addr}" ] && config::core::set "addr_${_switch}" "${_addr}"
[ -n "${_priv}" ] && config::core::set "private_${_switch}" "${_priv}"
[ -n "${_mtu}" ] && config::core::set "mtu_${_switch}" "${_mtu}"
config::core::load
switch::standard::init "${_switch}"
}
# destroy a standard switch
#
# @param string _switch name of the switch to destroy
#
switch::standard::remove(){
local _switch="$1"
local _id
# get the bridge id
switch::standard::id "_id" "${_switch}"
[ $? -eq 0 ] || return 1
# remove all member interfaces
switch::standard::__remove_members "${_switch}" "${_id}"
# destroy the bridge
ifconfig "${_id}" destroy >/dev/null 2>&1
}
# add a new interface to this switch
#
# @param string _switch name of the switch
# @param string _if the interface to add
#
switch::standard::add_member(){
local _switch="$1"
local _if="$2"
local _id _vlan _mtu
switch::standard::id "_id" "${_switch}" || util::err "unable to locate switch id"
config::core::get "_vlan" "vlan_${_switch}"
config::core::get "_mtu" "mtu_${_switch}"
switch::standard::__configure_port "${_switch}" "${_id}" "${_if}" "${_vlan}" "${_mtu}"
config::core::set "ports_${_switch}" "${_if}" "1"
}
# remove a member interface from this switch
#
# @param string _switch name of the switch
# @param string _if the interface to remove
#
switch::standard::remove_member(){
local _switch="$1"
local _if="$2"
local _id _vlan
switch::standard::id "_id" "${_switch}" || util::err "unable to locate switch id"
config::core::remove "ports_${_switch}" "${_if}"
config::core::get "_vlan" "vlan_${_switch}"
switch::standard::__unconfigure_port "${_switch}" "${_id}" "${_if}" "${_vlan}"
}
# set vlan id
#
# @param string _switch name of switch
# @param int _vlan vlan id to set
#
switch::standard::vlan(){
local _switch="$1"
local _vlan="$2"
local _id
switch::standard::id "_id" "${_switch}" || util::err "unable to locate switch id"
switch::standard::__remove_members "${_switch}" "${_id}"
# update configuration
if [ "${_vlan}" = "0" ]; then
config::core::remove "vlan_${_switch}"
else
config::core::set "vlan_${_switch}" "${_vlan}"
fi
config::core::load
switch::standard::__add_members "${_switch}" "${_id}"
}
# set or remove ip address
#
# @param string _swtich name of the switch
# @param string _addr address or "none"
# @scope _id switch if from parent switch::address
#
switch::standard::address(){
local _switch="$1"
local _addr="$2"
local _curr
if [ "${_addr}" = "none" ]; then
config::core::get "_curr" "addr_${_switch}"
[ $? -eq 0 ] || util::err "unable to locate an existing address for this switch"
config::core::remove "addr_${_switch}"
ifconfig "${_id}" "${_curr}" delete
else
config::core::set "addr_${_switch}" "${_addr}"
ifconfig "${_id}" "${_addr}"
fi
}
# add all member interfaces to a switch
#
# @param string _switch the name of the switch
# @param string _id interface id for the switch
#
switch::standard::__add_members(){
local _switch="$1"
local _id="$2"
local _ports _vlan _port _mtu
# get the id if not provided
if [ -z "${_id}" ]; then
switch::standard::id "_id" "${_switch}" || util:err "failed to get switch id while adding members"
fi
config::core::get "_ports" "ports_${_switch}"
config::core::get "_vlan" "vlan_${_switch}"
config::core::get "_mtu" "mtu_${_switch}"
if [ -n "${_ports}" ]; then
for _port in ${_ports}; do
switch::standard::__configure_port "${_switch}" "${_id}" "${_port}" "${_vlan}" "${_mtu}"
done
fi
}
# remove member interfaces from a switch
#
# @param string _switch the name of the switch
# @param string _id bridge id if already known
#
switch::standard::__remove_members(){
local _switch="$1"
local _id="$2"
local _ports _port _vlan
# get id if not given to us
if [ -z "${_id}" ]; then
switch::standard::id "_id" "${_switch}"
[ $? -eq 0 ] || util::err "failed to get switch id while removing members"
fi
# get full port list
config::core::get "_ports" "ports_${_switch}"
config::core::get "_vlan" "vlan_${_switch}"
if [ -n "${_ports}" ]; then
for _port in ${_ports}; do
switch::standard::__unconfigure_port "${_switch}" "${_id}" "${_port}" "${_vlan}"
done
fi
}
# configure a local port for our bridge
#
# @param string _switch the switch to add port to
# @param string _id the bridge id of the switch
# @param string _port the interface to add
# @param int _vlan vlan number if assigned to this switch
# @param int _mtu custom mtu to use for this port
#
switch::standard::__configure_port(){
local _switch="$1"
local _id="$2"
local _port="$3"
local _vlan="$4"
local _mtu="$5"
local _viface _vname
# try to set mtu of port?
[ -n "${_mtu}" ] && ifconfig "${_port}" mtu ${_mtu} >/dev/null 2>&1
# vlan enabled?
if [ -n "${_vlan}" ]; then
# see if vlan interface already exists
_vname="${_port}.${_vlan}"
switch::standard::id "_viface" "${_vname}"
# create if needed
if [ $? -ne 0 ]; then
# use our id as the interface name here.
# it should always be a valid name and interface.vlan-id is much easier to understand in ifconfig
# than a bunch of vlanX interfaces
_viface=$(ifconfig vlan create vlandev "${_port}" vlan "${_vlan}" descr "vm-vlan/${_switch}/${_vname}" name "${_vname}" group vm-vlan up 2>/dev/null)
[ $? -eq 0 ] || util::err "failed to create vlan interface for port ${_port} on switch ${_switch}"
fi
switch::set_viid "${_vname}" "${_viface}"
ifconfig ${_id} addm ${_viface} >/dev/null 2>&1
else
# add to bridge, nice and simple :)
ifconfig ${_id} addm ${_port} >/dev/null 2>&1
fi
[ $? -eq 0 ] || util::err "failed to add member ${_port} to the virtual switch ${_switch}"
}
# unconfigure a local port
#
# @param string _switch the switch to remove port from
# @param string _id the bridge id of the switch
# @param string _port the interface to remove
# @param string _vlan vlan number if assigned to this switch
#
switch::standard::__unconfigure_port(){
local _switch="$1"
local _id="$2"
local _port="$3"
local _vlan="$4"
local _vid
if [ -n "${_vlan}" ]; then
# get vlan interface
switch::standard::id "_vid" "${_port}.${_vlan}"
# remove the vlan interface, it will be removed from bridge automatically
[ $? -eq 0 ] && ifconfig ${_vid} destroy >/dev/null 2>&1
else
ifconfig ${_id} deletem ${_port} >/dev/null 2>&1
fi
}
# get the id for a standard switch
# this returns the associated bridge name
#
# @param string _var variable to put id into
# @param string _switch the switch to look for
# @return 0 on success
#
switch::standard::id(){
switch::find "$1" "$2"
}
# creates a standard tap interface for a guest
# relies heavily on variables set in the main vm::run function
#
# @modifies _func _devices
# @return 1 if we don't get a tap device
#
switch::standard::provision(){
local _tap _custom_tap _sid _mtu _member_type _iname
config::get "_custom_tap" "network${_num}_device"
config::get "_iname" "network${_num}_name"
# create interface
if [ -n "${_custom_tap}" ]; then
_tap="${_custom_tap}"
elif [ -n "${_iname}" ]; then
_tap=$(ifconfig tap create name "${_iname}")
else
_tap=$(ifconfig tap create)
fi
[ -z "${_tap}" ] && return 1;
util::log "guest" "${_name}" "initialising network device ${_tap}"
ifconfig "${_tap}" descr "vmnet/${_name}/${_num}/${_switch:-custom}" group vm-port >/dev/null 2>&1
if [ -n "${_switch}" ]; then
switch::id "_sid" "${_switch}"
# should this be a span member?
_member_type="addm"
config::yesno "network${_num}_span" && _member_type="span"
if [ -n "${_sid}" ]; then
_mtu=$(ifconfig "${_sid}" | head -n1 | awk '{print $NF}')
if [ "${_mtu}" != "1500" ]; then
util::log "guest" "${_name}" "setting mtu of ${_tap} to ${_mtu}"
ifconfig "${_tap}" mtu "${_mtu}" >/dev/null 2>&1
fi
util::log "guest" "${_name}" "adding ${_tap} -> ${_sid} (${_switch} ${_member_type})"
ifconfig "${_sid}" "${_member_type}" "${_tap}" >/dev/null 2>&1 || util::log "guest" "${_name}" "failed to add ${_tap} to ${_sid}"
util::log "guest" "${_name}" "bring up ${_tap} -> ${_sid} (${_switch} ${_member_type})"
ifconfig "${_tap}" up >/dev/null 2>&1 || util::log "guest" "${_name}" "failed to bring up ${_tap} in ${_sid}"
# set private if configured
switch::is_private "${_switch}" && ifconfig "${_sid}" "private" "${_tap}" >/dev/null 2>&1
else
util::log "guest" "${_name}" "failed to find virtual switch '${_switch}'"
fi
fi
_devices="${_devices} -s ${_slot}:${_func},${_emulation},${_tap}"
[ -n "${_mac}" ] && _devices="${_devices},mac=${_mac}"
_func=$((_func + 1))
[ -z "${_custom_tap}" ] && _taplist="${_taplist}${_taplist:+ }${_tap}"
}