#!/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. # switch libraries # . "${LIB}/vm-switch-netgraph" . "${LIB}/vm-switch-manual" . "${LIB}/vm-switch-standard" . "${LIB}/vm-switch-vale" . "${LIB}/vm-switch-vxlan" # create switches from rc list on init # this should run once per boot to make sure switches from the # configuration file have bridge interfaces. If any new switches are # created, the create function takes care of setting them up # switch::init(){ local _switchlist _switch _type config::core::get "_switchlist" "switch_list" if [ -n "${_switchlist}" ]; then for _switch in ${_switchlist}; do # get the switch type config::core::get "_type" "type_${_switch}" case "${_type}" in vxlan) switch::vxlan::init "${_switch}" ;; manual) switch::manual::init "${_switch}" ;; vale) ;; netgraph) ;; *) switch::standard::init "${_switch}" ;; esac done fi } # list switches configured # switch::list(){ local _switchlist _switch _type local _id _format="%s^%s^%s^%s^%s^%s^%s^%s\n" config::core::get "_switchlist" "switch_list" { printf "${_format}" "NAME" "TYPE" "IFACE" "ADDRESS" "PRIVATE" "MTU" "VLAN" "PORTS" if [ -n "${_switchlist}" ]; then for _switch in ${_switchlist}; do # get the switch type config::core::get "_type" "type_${_switch}" case "${_type}" in netgraph) switch::netgraph::show "${_switch}" "${_format}" ;; vale) switch::vale::show "${_switch}" "${_format}" ;; vxlan) switch::vxlan::show "${_switch}" "${_format}" ;; manual) switch::manual::show "${_switch}" "${_format}" ;; *) switch::standard::show "${_switch}" "${_format}" ;; esac done fi } | column -ts^ } # create a new virtual switch # # @param string _switch name of the switch to create # switch::create(){ local _switch local _type="standard" local _list _curr _vlan _if _bridge _addr _mtu _priv # process options while getopts t:i:n:b:a:m:p _opt; do case ${_opt} in t) _type="${OPTARG}" ;; i) _if="${OPTARG}" ;; n) _vlan="${OPTARG}" ;; b) _bridge="${OPTARG}" ;; a) _addr="${OPTARG}" ;; m) _mtu="${OPTARG}" ;; p) _priv="yes" ;; *) util::usage ;; esac done shift $((OPTIND - 1)) _switch="$1" # check for a valid switch name util::check_name "${_switch}" || util::err "invalid switch name - '${_switch}'" # make sure it's not an existing name config::core::get "_list" "switch_list" for _curr in ${_list}; do [ "${_switch}" = "${_curr}" ] && util::err "switch ${_switch} already exists" done # check vlan number if [ -n "${_vlan}" ]; then echo "${_vlan}" | egrep -qs '^[0-9]{1,4}$' [ $? -eq 0 ] || util::err "invalid vlan number" [ ${_vlan} -ge 4095 ] && util::err "invalid vlan number" fi # check address if [ -n "${_addr}" ]; then echo "${_addr}" | egrep -qs '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$' [ $? -eq 0 ] || util::err "address must be supplied in CIDR notation (a.b.c.d/prefix-len)" fi # check mtu if [ -n "${_mtu}" ]; then echo "${_mtu}" | egrep -qs '^[0-9]{3,4}$' [ $? -eq 0 ] || util::err "invalid mtu" [ ${_mtu} -gt 9000 ] && util::err "invalid mtu" fi # check switch type case "${_type}" in standard) switch::standard::create ;; manual) switch::manual::create ;; netgraph) switch::netgraph::create ;; vale) switch::vale::create ;; vxlan) switch::vxlan::create ;; *) util::err "invalid switch type - '${_type}'" ;; esac } # destroy a switch # remove from configuration and unload any interfaces we created # # @param string _switch name of the switch to remove # switch::remove(){ local _switch="$1" local _type [ -z "${_switch}" ] && util::usage # get the type of switch config::core::get "_type" "type_${_switch}" case "${_type}" in standard) switch::standard::remove "${_switch}" ;; manual) switch::manual::remove "${_switch}" ;; netgraph) switch::netgraph::remove "${_switch}" ;; vale) switch::vale::remove "${_switch}" ;; vxlan) switch::vxlan::remove "${_switch}" ;; *) util::err "unable to remove switch of unknown type" ;; esac # remove all configuration if there's no error if [ $? -eq 0 ]; then config::core::remove "switch_list" "${_switch}" config::core::remove "ports_${_switch} vlan_${_switch} nat_${_switch} type_${_switch}" config::core::remove "addr_${_switch} private_${_switch} mtu_${_switch}" else util::err "failed to remove virtual switch" fi # make sure the exit status indicates success, # even if config::core::remove did not return 0 } # add a new interface to a switch # # @param string _switch name of the switch # @param string _if the interface to add # switch::add_member(){ local _switch="$1" local _if="$2" local _type [ -z "${_switch}" -o -z "${_if}" ] && util::usage # get the type of switch config::core::get "_type" "type_${_switch}" case "${_type}" in standard) switch::standard::add_member "${_switch}" "${_if}" ;; manual) switch::manual::add_member "${_switch}" "${_if}" ;; netgraph) switch::netgraph::add_member "${_switch}" "${_if}" ;; vale) switch::vale::add_member "${_switch}" "${_if}" ;; vxlan) switch::vxlan::add_member "${_switch}" "${_if}" ;; *) util::err "unable to configure switch of unknown type" ;; esac } # remove a member interface from a virtual switch # # @param string _switch name of the switch # @param string _if the interface to remove # switch::remove_member(){ local _switch="$1" local _if="$2" local _type [ -z "${_switch}" -o -z "${_if}" ] && util::usage # get the type of switch config::core::get "_type" "type_${_switch}" case "${_type}" in standard) switch::standard::remove_member "${_switch}" "${_if}" ;; manual) switch::manual::remove_member "${_switch}" "${_if}" ;; netgraph) switch::netgraph::remove_member "${_switch}" "${_if}" ;; vale) switch::vale::remove_member "${_switch}" "${_if}" ;; vxlan) switch::vxlan::remove_member "${_switch}" "${_if}" ;; *) util::err "unable to configure switch of unknown type" ;; esac } # change the vlan number on a virtual switch # # @param string _switch name of the switch # @param int _vlan the vlan number (0 to turn vlan off) # switch::vlan(){ local _switch="$1" local _vlan="$2" local _id _type [ -z "${_switch}" -o -z "${_vlan}" ] && util::usage switch::id "_id" "${_switch}" switch::type "_type" "${_switch}" [ -z "${_id}" ] && util::err "unable to locate specified virtual switch" echo "${_vlan}" | egrep -qs '^[0-9]{1,4}$' [ $? -eq 0 ] || util::err "invalid vlan number" [ ${_vlan} -ge 4095 ] && util::err "invalid vlan number" case "${_type}" in standard) switch::standard::vlan "${_switch}" "${_vlan}" ;; manual) switch::manual::vlan "${_switch}" "${_vlan}" ;; netgraph) switch::netgraph::vlan "${_switch}" "${_vlan}" ;; vale) switch::vale::vlan "${_switch}" "${_vlan}" ;; vxlan) switch::vxlan::vlan "${_switch}" "${_vlan}" ;; *) util::err "unable to configure switch of unknown type" ;; esac } # enable or diable private flag on a switch # note that we don't update existing interfaces; this # makes things easy for us and any guests booted after # will get the new setting # # @param string _switch the switch to update # @param string _priv on,yes|off,no # switch::private(){ local _switch="$1" local _priv="$2" local _type # try to get switch type [ -z "${_switch}" -o -z "${_priv}" ] && util::usage switch::type "_type" "${_switch}" || util::err "specified switch does not appear to be valid" case "${_type}" in standard|manual|vxlan) if util::yesno "${_priv}"; then config::core::set "private_${_switch}" "yes" else config::core::set "private_${_switch}" "no" fi ;; netgraph) util::err "unable to configure private mode on netgraph switches" ;; vale) util::err "unable to configure private mode on vale switches" ;; *) util::err "unable to configure switch of unknown type" ;; esac } # enable or disable nat functionality on a virtual switch # # @param string _switch name of the switch # @param string _nat on|off # switch::nat(){ util::warn "internal nat support is currently disabled" util::warn "please add an address to the virtual switch and configure your firewall for NAT manually" } # set or remove ip address from a virtual switch # # @param string _switch name of the switch # @param string _addr the ip address to add (or "none" to remove # switch::address(){ local _switch="$1" local _addr="$2" local _id _type [ -z "${_switch}" ] && util::usage switch::id "_id" "${_switch}" switch::type "_type" "${_switch}" [ -z "${_id}" ] && util::err "unable to locate specified virtual switch" # check address if [ -n "${_addr}" ] && [ "${_addr}" != "none" ]; then echo "${_addr}" | egrep -qs '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$' [ $? -eq 0 ] || util::err "address must be supplied in CIDR notation (a.b.c.d/prefix-len)" fi case "${_type}" in standard) switch::standard::address "${_switch}" "${_addr}" ;; manual) ;& netgraph) ;& vale) ;& vxlan) util::err "feature not currently supported on switches of this type" ;; *) util::err "unable to configure switch of unknown type" ;; esac } # return the type for a switch # # @param string _var variable to put type into # @param string _switch the switch name # switch::type(){ local _var="$1" local _switch="$2" [ -z "${_switch}" ] && return 1 config::core::get "${_var}" "type_${_switch}" "standard" } # check if a switch is configured for private members # # @param string _switch switch name # @return succes (0) if it's private # switch::is_private(){ local _switch="$1" local _priv config::core::get "_priv" "private_${_switch}" util::yesno "${_priv}" } # get the bridge id for a virtual switch # # @param string _var variable to put name into # @param string _switch the name of the switch # switch::id(){ local _var="$1" local _switch="$2" local _type [ -z "${_switch}" ] && return 1 # get switch type config::core::get "_type" "type_${_switch}" case "${_type}" in vale) switch::vale::id "${_var}" "${_switch}" ;; netgraph) switch::netgraph::id "${_var}" "${_switch}" ;; manual) switch::manual::id "${_var}" "${_switch}" ;; *) switch::standard::id "${_var}" "${_switch}" ;; esac } # get a virt interface id for a port/switch # # @param string _var variable name to put result into # @param string _switch switch name to get id for # switch::__viid(){ local _hash=$(md5 -qs "${2}" | cut -c1-5) setvar "$1" "viid-${_hash}@" } # retrieve interface name, given a switch name # we convert to viid then look for the matching group # # @param string _var variable to put interface name into # @param string _switch the switch name # switch::find(){ local _var="$1" local _switch="$2" local _viid _name switch::__viid "_viid" "${_switch}" _name=$(ifconfig -g "${_viid}" 2>/dev/null) [ -n "${_name}" ] && setvar "${_var}" "${_name}" } # mark an interface with a unique viid # i say unique, its 5 chars from an md5 hash which # should be enough for half a dozen switches # # @parem string _switch switch name # @param string _iface interface to mark # switch::set_viid(){ local _switch="$1" local _iface="$2" local _viid switch::__viid "_viid" "${_switch}" ifconfig "${_iface}" group "${_viid}" >/dev/null 2>&1 } # create a network interface for a guest # relies heavily on variables set in the main vm::run function # # @modifies _func _devices # switch::provision(){ local _switch _mac _type config::get "_switch" "network${_num}_switch" config::get "_mac" "network${_num}_mac" # set a static mac if we don't have one [ -z "${_mac}" ] && vm::generate_static_mac switch::type "_type" "${_switch}" case "${_type}" in vale) switch::vale::provision ;; netgraph) switch::netgraph::provision ;; standard) ;& manual) ;& vxlan) switch::standard::provision ;; *) util::warn "unable to configure interface ${_num}" ;; esac }