#!/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. # create a vxlan switch # we create a bridge and then add the vxlan interface to it # # @param string _name name of the switch # switch::vxlan::init(){ local _name="$1" local _id _vlan _if _maddr _addr _ifconf # see if the bridge already exists switch::standard::id "_id" "${_name}" && return 0 # need a vlan id and interface config::core::get "_vlan" "vlan_${_name}" config::core::get "_if" "ports_${_name}" [ -z "${_vlan}" -o -z "${_if}" ] && return 1 # get local address for this interface _local=$(ifconfig ${_if} | grep "inet " | cut -d" " -f 2) [ -z "${_local}" ] && return 1 # come up with an ip address for multicast data switch::vxlan::__multicast "_maddr" "${_name}" # create the vxlan interface ifconfig "vxlan${_vlan}" create vxlanid "${_vlan}" vxlanlocal "${_local}" vxlangroup "${_maddr}" \ vxlandev "${_if}" descr "vm-vxlan/${_switch}" group vm-vlan up >/dev/null 2>&1 [ $? -eq 0 ] || return 1 # 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 # bridge vxlan to our guest switch # static route traffic for this multicast address via our specified interface ifconfig "${_id}" addm "vxlan${_vlan}" route add -net ${_maddr}/32 -iface ${_if} >/dev/null 2>&1 # custom address for bridge? config::core::get "_addr" "addr_${_name}" [ -n "${_addr}" ] && ifconfig "${_id}" inet ${_addr} } # show the configuration details for a vxlan switch # # @param string _name the switch name # @param string _format output format # switch::vxlan::show(){ local _name="$1" local _format="$2" local _id _vlan _port _addr _priv switch::standard::id "_id" "${_name}" config::core::get "_vlan" "vlan_${_name}" config::core::get "_port" "ports_${_name}" config::core::get "_addr" "addr_${_name}" config::core::get "_priv" "private_${_name}" "no" printf "${_format}" "${_name}" "vxlan" "${_id:--}" "${_addr:--}" "${_priv}" "n/a" "${_vlan}" "${_port}" } # create a vxlan switch # switch::vxlan::create(){ # we must have an interface and vlan to use [ -z "${_if}" -o -z "${_vlan}" ] && util::err "vxlan switches must be created with an interface and vlan id specified" # store configuration config::core::set "switch_list" "${_switch}" "1" config::core::set "type_${_switch}" "vxlan" config::core::set "vlan_${_switch}" "${_vlan}" config::core::set "ports_${_switch}" "${_if}" [ -n "${_addr}" ] && config::core::set "addr_${_switch}" "${_addr}" [ -n "${_priv}" ] && config::core::set "private_${_switch}" "${_priv}" config::core::load switch::vxlan::init "${_switch}" } # destroy a vxlan switch # # @param string _switch the switch to remove # switch::vxlan::remove(){ local _switch="$1" local _id _vlan _maddr # try to get guest bridge and vxlan id switch::standard::id "_id" "${_switch}" [ $? -eq 0 ] || return 1 config::core::get "_vlan" "vlan_${_switch}" [ -z "${_vlan}" ] && return 1 # get the multicast address we used for this switch # and try to remove any route we may have added switch::vxlan::__multicast "_maddr" "${_switch}" route del -net "${_maddr}/32" >/dev/null 2>&1 # destroy the guest bridge ifconfig ${_id} destroy >/dev/null 2>&1 [ $? -eq 0 ] || return 1 # destroy the vxlan ifconfig "vxlan${_vlan}" destroy >/dev/null 2>&1 } # add a new interface to this switch # we only allow a single physical interface for # vxlan switches. this must be set at creation time # so this just reports an error # # @param string _switch name of the vxlan switch # @param string _if the interface to add # switch::vxlan::add_member(){ util::err "vxlan interface must be configured at creation time" } # remove an interface from a switch # we don't support this here # # @param string _switch name of the switch # @param string _if the interface to remove # switch::vxlan::remove_member(){ util::err "vxlan interface must be configured at creation time" } # set vlan id # # @param string _switch name of switch # @param int _vlan vlan id to set # switch::vxlan::vlan(){ util::err "vxlan id can only be set at creation time" } # get the multicast address for a vxlan switch # # @param string _var variable to put address into # @param string _switch the switch name # switch::vxlan::__multicast(){ local _var="$1" local _switch="$2" local _hash _l_addr _octet _pos # come up with an ip address for multicast data _hash=$(md5 -qs "${_switch}") _l_addr="239" for _pos in 1-2 3-4 5-6; do _octet=$(printf ".%d" "0x`echo "${_hash}"| cut -c ${_pos}`") _l_addr="${_l_addr}${_octet}" done setvar "${_var}" "${_l_addr}" }