1295373Sdteske#!/bin/sh 2295373Sdteske#- 3295373Sdteske# Copyright (c) 2016 Devin Teske 4295373Sdteske# All rights reserved. 5295373Sdteske# 6295373Sdteske# Redistribution and use in source and binary forms, with or without 7295373Sdteske# modification, are permitted provided that the following conditions 8295373Sdteske# are met: 9295373Sdteske# 1. Redistributions of source code must retain the above copyright 10295373Sdteske# notice, this list of conditions and the following disclaimer. 11295373Sdteske# 2. Redistributions in binary form must reproduce the above copyright 12295373Sdteske# notice, this list of conditions and the following disclaimer in the 13295373Sdteske# documentation and/or other materials provided with the distribution. 14295373Sdteske# 15295373Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16295373Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17295373Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18295373Sdteske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19295373Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20295373Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21295373Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22295373Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23295373Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24295373Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25295373Sdteske# SUCH DAMAGE. 26295373Sdteske# 27295373Sdteske# $FreeBSD: releng/11.0/share/examples/jails/jib 295587 2016-02-13 00:28:48Z dteske $ 28295373Sdteske# 29295373Sdteske############################################################ IDENT(1) 30295373Sdteske# 31295373Sdteske# $Title: if_bridge(4) management script for vnet jails $ 32295373Sdteske# 33295373Sdteske############################################################ INFORMATION 34295373Sdteske# 35295373Sdteske# Use this tool with jail.conf(5) (or rc.conf(5) ``legacy'' configuration) to 36295400Sdteske# manage `vnet' interfaces for jails. Designed to automate the creation of vnet 37295400Sdteske# interface(s) during jail `prestart' and destroy said interface(s) during jail 38295400Sdteske# `poststop'. 39295373Sdteske# 40295400Sdteske# In jail.conf(5) format: 41295400Sdteske# 42295373Sdteske# ### BEGIN EXCERPT ### 43295373Sdteske# 44295373Sdteske# xxx { 45295373Sdteske# host.hostname = "xxx.yyy"; 46295373Sdteske# path = "/vm/xxx"; 47295373Sdteske# 48295373Sdteske# # 49295373Sdteske# # NB: Below 2-lines required 50295373Sdteske# # NB: The number of eNb_xxx interfaces should match the number of 51295373Sdteske# # arguments given to `jib addm xxx' in exec.prestart value. 52295373Sdteske# # 53295373Sdteske# vnet; 54295373Sdteske# vnet.interface = "e0b_xxx e1b_xxx ..."; 55295373Sdteske# 56295373Sdteske# exec.clean; 57295373Sdteske# exec.system_user = "root"; 58295373Sdteske# exec.jail_user = "root"; 59295373Sdteske# 60295373Sdteske# # 61295373Sdteske# # NB: Below 2-lines required 62295373Sdteske# # NB: The number of arguments after `jib addm xxx' should match 63295373Sdteske# # the number of eNb_xxx arguments in vnet.interface value. 64295373Sdteske# # 65295373Sdteske# exec.prestart += "jib addm xxx em0 em1 ..."; 66295373Sdteske# exec.poststop += "jib destroy xxx"; 67295373Sdteske# 68295373Sdteske# # Standard recipe 69295373Sdteske# exec.start += "/bin/sh /etc/rc"; 70295373Sdteske# exec.stop = "/bin/sh /etc/rc.shutdown"; 71295373Sdteske# exec.consolelog = "/var/log/jail_xxx_console.log"; 72295373Sdteske# mount.devfs; 73295373Sdteske# 74295373Sdteske# # Optional (default off) 75295373Sdteske# #allow.mount; 76295373Sdteske# #allow.set_hostname = 1; 77295373Sdteske# #allow.sysvipc = 1; 78295373Sdteske# #devfs_ruleset = "11"; # rule to unhide bpf for DHCP 79295373Sdteske# } 80295373Sdteske# 81295373Sdteske# ### END EXCERPT ### 82295373Sdteske# 83295373Sdteske# In rc.conf(5) ``legacy'' format (used when /etc/jail.conf does not exist): 84295373Sdteske# 85295373Sdteske# ### BEGIN EXCERPT ### 86295373Sdteske# 87295373Sdteske# jail_enable="YES" 88295373Sdteske# jail_list="xxx" 89295373Sdteske# 90295373Sdteske# # 91295373Sdteske# # Global presets for all jails 92295373Sdteske# # 93295373Sdteske# jail_devfs_enable="YES" # mount devfs 94295373Sdteske# 95295373Sdteske# # 96295373Sdteske# # Global options (default off) 97295373Sdteske# # 98295373Sdteske# #jail_mount_enable="YES" # mount /etc/fstab.{name} 99295373Sdteske# #jail_set_hostname_allow="YES" # Allow hostname to change 100295373Sdteske# #jail_sysvipc_allow="YES" # Allow SysV Interprocess Comm. 101295373Sdteske# 102295373Sdteske# # xxx 103295373Sdteske# jail_xxx_hostname="xxx.shxd.cx" # hostname 104295373Sdteske# jail_xxx_rootdir="/vm/xxx" # root directory 105295373Sdteske# jail_xxx_vnet_interfaces="e0b_xxx e1bxxx ..." # vnet interface(s) 106295373Sdteske# jail_xxx_exec_prestart0="jib addm xxx em0 em1 ..." # bridge interface(s) 107295373Sdteske# jail_xxx_exec_poststop0="jib destroy xxx" # destroy interface(s) 108295373Sdteske# #jail_xxx_mount_enable="YES" # mount /etc/fstab.xxx 109295373Sdteske# #jail_xxx_devfs_ruleset="11" # rule to unhide bpf for DHCP 110295373Sdteske# 111295373Sdteske# ### END EXCERPT ### 112295373Sdteske# 113295373Sdteske# Note that the legacy rc.conf(5) format is converted to 114295373Sdteske# /var/run/jail.{name}.conf by /etc/rc.d/jail if jail.conf(5) is missing. 115295373Sdteske# 116295373Sdteske# ASIDE: dhclient(8) inside a vnet jail... 117295373Sdteske# 118295373Sdteske# To allow dhclient(8) to work inside a vnet jail, make sure the following 119295373Sdteske# appears in /etc/devfs.rules (which should be created if it doesn't exist): 120295373Sdteske# 121295373Sdteske# [devfsrules_jail=11] 122295373Sdteske# add include $devfsrules_hide_all 123295373Sdteske# add include $devfsrules_unhide_basic 124295373Sdteske# add include $devfsrules_unhide_login 125295441Sdteske# add path 'bpf*' unhide 126295373Sdteske# 127295373Sdteske# And set ether devfs.ruleset="11" (jail.conf(5)) or 128295373Sdteske# jail_{name}_devfs_ruleset="11" (rc.conf(5)). 129295373Sdteske# 130295373Sdteske# NB: While this tool can't create every type of desirable topology, it should 131295373Sdteske# handle most setups, minus some which considered exotic or purpose-built. 132295373Sdteske# 133295373Sdteske############################################################ GLOBALS 134295373Sdteske 135295373Sdteskepgm="${0##*/}" # Program basename 136295373Sdteske 137295373Sdteske# 138295373Sdteske# Global exit status 139295373Sdteske# 140295373SdteskeSUCCESS=0 141295373SdteskeFAILURE=1 142295373Sdteske 143295373Sdteske############################################################ FUNCTIONS 144295373Sdteske 145295373Sdteskeusage() 146295373Sdteske{ 147295373Sdteske local action usage descr 148295373Sdteske exec >&2 149295373Sdteske echo "Usage: $pgm action [arguments]" 150295373Sdteske echo "Actions:" 151295373Sdteske for action in \ 152295373Sdteske addm \ 153295373Sdteske show \ 154295373Sdteske show1 \ 155295373Sdteske destroy \ 156295373Sdteske ; do 157295373Sdteske eval usage=\"\$jib_${action}_usage\" 158295373Sdteske [ "$usage" ] || continue 159295373Sdteske eval descr=\"\$jib_${action}_descr\" 160295373Sdteske printf "\t%s\n\t\t%s\n" "$usage" "$descr" 161295373Sdteske done 162295373Sdteske exit $FAILURE 163295373Sdteske} 164295373Sdteske 165295373Sdteskeaction_usage() 166295373Sdteske{ 167295587Sdteske local usage descr action="$1" 168295373Sdteske eval usage=\"\$jib_${action}_usage\" 169295373Sdteske echo "Usage: $pgm $usage" >&2 170295587Sdteske eval descr=\"\$jib_${action}_descr\" 171295587Sdteske printf "\t%s\n" "$descr" 172295373Sdteske exit $FAILURE 173295373Sdteske} 174295373Sdteske 175295546Sdteskederive_mac() 176295546Sdteske{ 177295546Sdteske local OPTIND=1 OPTARG __flag 178295546Sdteske local __mac_num= __make_pair= 179295546Sdteske while getopts 2n: __flag; do 180295546Sdteske case "$__flag" in 181295546Sdteske 2) __make_pair=1 ;; 182295546Sdteske n) __mac_num=${OPTARG%%[^0-9]*} ;; 183295546Sdteske esac 184295546Sdteske done 185295546Sdteske shift $(( $OPTIND - 1 )) 186295546Sdteske 187295546Sdteske if [ ! "$__mac_num" ]; then 188295546Sdteske eval __mac_num=\${_${iface}_num:--1} 189295546Sdteske __mac_num=$(( $__mac_num + 1 )) 190295546Sdteske eval _${iface}_num=\$__mac_num 191295546Sdteske fi 192295546Sdteske 193295546Sdteske local __iface="$1" __name="$2" __var_to_set="$3" __var_to_set_b="$4" 194295546Sdteske local __iface_devid __new_devid __num __new_devid_b 195295546Sdteske # 196295546Sdteske # Calculate MAC address derived from given iface. 197295546Sdteske # 198295546Sdteske # The formula I'm using is ``NP:SS:SS:II:II:II'' where: 199295546Sdteske # + N denotes 4 bits used as a counter to support branching 200295546Sdteske # each parent interface up to 15 times under the same jail 201295546Sdteske # name (see S below). 202295546Sdteske # + P denotes the special nibble whose value, if one of 203295546Sdteske # 2, 6, A, or E (but usually 2) denotes a privately 204295546Sdteske # administered MAC address (while remaining routable). 205295546Sdteske # + S denotes 16 bits, the sum(1) value of the jail name. 206295546Sdteske # + I denotes bits that are inherited from parent interface. 207295546Sdteske # 208295546Sdteske # The S bits are a CRC-16 checksum of NAME, allowing the jail 209295546Sdteske # to change link numbers in ng_bridge(4) without affecting the 210295546Sdteske # MAC address. Meanwhile, if... 211295546Sdteske # + the jail NAME changes (e.g., it was duplicated and given 212295546Sdteske # a new name with no other changes) 213295546Sdteske # + the underlying network interface changes 214295546Sdteske # + the jail is moved to another host 215295546Sdteske # the MAC address will be recalculated to a new, similarly 216295546Sdteske # unique value preventing conflict. 217295546Sdteske # 218295546Sdteske __iface_devid=$( ifconfig $__iface ether | awk '/ether/,$0=$2' ) 219295553Sdteske # ??:??:??:II:II:II 220295553Sdteske __new_devid=${__iface_devid#??:??:??} # => :II:II:II 221295553Sdteske # => :SS:SS:II:II:II 222295546Sdteske __num=$( set -- `echo -n "$__name" | sum` && echo $1 ) 223295553Sdteske __new_devid=$( printf :%02x:%02x \ 224295553Sdteske $(( $__num >> 8 & 255 )) $(( $__num & 255 )) )$__new_devid 225295553Sdteske # => P:SS:SS:II:II:II 226295546Sdteske case "$__iface_devid" in 227295546Sdteske ?2:*) __new_devid=a$__new_devid __new_devid_b=e$__new_devid ;; 228295546Sdteske ?[Ee]:*) __new_devid=2$__new_devid __new_devid_b=6$__new_devid ;; 229295546Sdteske *) __new_devid=2$__new_devid __new_devid_b=e$__new_devid 230295546Sdteske esac 231295553Sdteske # => NP:SS:SS:II:II:II 232295546Sdteske __new_devid=$( printf %x $(( $__mac_num & 15 )) )$__new_devid 233295546Sdteske __new_devid_b=$( printf %x $(( $__mac_num & 15 )) )$__new_devid_b 234295546Sdteske 235295546Sdteske # 236295546Sdteske # Return derivative MAC address(es) 237295546Sdteske # 238295546Sdteske if [ "$__make_pair" ]; then 239295546Sdteske if [ "$__var_to_set" -a "$__var_to_set_b" ]; then 240295546Sdteske eval $__var_to_set=\$__new_devid 241295546Sdteske eval $__var_to_set_b=\$__new_devid_b 242295546Sdteske else 243295546Sdteske echo $__new_devid $__new_devid_b 244295546Sdteske fi 245295546Sdteske else 246295546Sdteske if [ "$__var_to_set" ]; then 247295546Sdteske eval $__var_to_set=\$__new_devid 248295546Sdteske else 249295546Sdteske echo $__new_devid 250295546Sdteske fi 251295546Sdteske fi 252295546Sdteske} 253295546Sdteske 254295373Sdteskemustberoot_to_continue() 255295373Sdteske{ 256295373Sdteske if [ "$( id -u )" -ne 0 ]; then 257295373Sdteske echo "Must run as root!" >&2 258295373Sdteske exit $FAILURE 259295373Sdteske fi 260295373Sdteske} 261295373Sdteske 262295554Sdteskejib_addm_usage="addm [-b BRIDGE_NAME] NAME [!]iface0 [[!]iface1 ...]" 263295373Sdteskejib_addm_descr="Creates e0b_NAME [e1b_NAME ...]" 264295373Sdteskejib_addm() 265295373Sdteske{ 266295373Sdteske local OPTIND=1 OPTARG flag bridge=bridge 267295373Sdteske while getopts b: flag; do 268295373Sdteske case "$flag" in 269295373Sdteske b) bridge="${OPTARG:-bridge}" ;; 270295373Sdteske *) action_usage addm # NOTREACHED 271295373Sdteske esac 272295373Sdteske done 273295373Sdteske shift $(( $OPTIND - 1 )) 274295373Sdteske 275295373Sdteske local name="$1" 276295373Sdteske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || 277295373Sdteske action_usage addm # NOTREACHED 278295373Sdteske shift 1 # name 279295373Sdteske 280295373Sdteske mustberoot_to_continue 281295373Sdteske 282295546Sdteske local iface eiface_devid_a eiface_devid_b 283295554Sdteske local new no_derive num quad i=0 284295373Sdteske for iface in $*; do 285295373Sdteske 286295554Sdteske no_derive= 287295554Sdteske case "$iface" in 288295554Sdteske !*) iface=${iface#!} no_derive=1 ;; 289295554Sdteske esac 290295554Sdteske 291295556Sdteske # Make sure the interface doesn't exist already 292295556Sdteske if ifconfig "e${i}a_$name" > /dev/null 2>&1; then 293295556Sdteske i=$(( $i + 1 )) 294295556Sdteske continue 295295556Sdteske fi 296295373Sdteske 297295556Sdteske # Bring the interface up 298295373Sdteske ifconfig $iface up || return 299295373Sdteske 300295556Sdteske # Make sure the interface has been bridged 301295373Sdteske if ! ifconfig "$iface$bridge" > /dev/null 2>&1; then 302295373Sdteske new=$( ifconfig bridge create ) || return 303295373Sdteske ifconfig $new addm $iface || return 304295373Sdteske ifconfig $new name "$iface$bridge" || return 305295443Sdteske ifconfig "$iface$bridge" up || return 306295373Sdteske fi 307295373Sdteske 308295556Sdteske # Create a new interface to the bridge 309295373Sdteske new=$( ifconfig epair create ) || return 310295373Sdteske ifconfig "$iface$bridge" addm $new || return 311295373Sdteske 312295556Sdteske # Rename the new interface 313295373Sdteske ifconfig $new name "e${i}a_$name" || return 314295373Sdteske ifconfig ${new%a}b name "e${i}b_$name" || return 315295443Sdteske ifconfig "e${i}a_$name" up || return 316295443Sdteske ifconfig "e${i}b_$name" up || return 317295373Sdteske 318295373Sdteske # 319295556Sdteske # Set the MAC address of the new interface using a sensible 320295373Sdteske # algorithm to prevent conflicts on the network. 321295373Sdteske # 322295554Sdteske eiface_devid_a= eiface_devid_b= 323295554Sdteske [ "$no_derive" ] || derive_mac -2 $iface "$name" \ 324295554Sdteske eiface_devid_a eiface_devid_b 325295554Sdteske if [ "$eiface_devid_a" -a "$eiface_devid_b" ]; then 326295554Sdteske ifconfig "e${i}a_$name" ether $eiface_devid_a 327295554Sdteske ifconfig "e${i}b_$name" ether $eiface_devid_b 328295554Sdteske fi > /dev/null 2>&1 329295373Sdteske 330295556Sdteske i=$(( $i + 1 )) 331295373Sdteske done # for iface 332295373Sdteske} 333295373Sdteske 334295373Sdteskejib_show_usage="show" 335295373Sdteskejib_show_descr="List possible NAME values for \`show NAME'" 336295373Sdteskejib_show1_usage="show NAME" 337295399Sdteskejib_show1_descr="Lists e0b_NAME [e1b_NAME ...]" 338295373Sdteskejib_show2_usage="show [NAME]" 339295373Sdteskejib_show() 340295373Sdteske{ 341295373Sdteske local OPTIND=1 OPTARG flag 342295373Sdteske while getopts "" flag; do 343295373Sdteske case "$flag" in 344295373Sdteske *) action_usage show2 # NOTREACHED 345295373Sdteske esac 346295373Sdteske done 347295373Sdteske shift $(( $OPTIND - 1 )) 348295373Sdteske if [ $# -eq 0 ]; then 349295373Sdteske ifconfig | awk ' 350295373Sdteske /^[^:[:space:]]+:/ { 351295373Sdteske iface = $1 352295373Sdteske sub(/:.*/, "", iface) 353295373Sdteske next 354295373Sdteske } 355295373Sdteske $1 == "groups:" { 356295373Sdteske for (n = split($0, group); n > 1; n--) { 357295373Sdteske if (group[n] != "bridge") continue 358295373Sdteske print iface 359295373Sdteske next 360295373Sdteske } 361295373Sdteske }' | 362295373Sdteske xargs -rn1 ifconfig | 363295373Sdteske awk '$1 == "member:" && 364295373Sdteske sub(/^e[[:digit:]]+a_/, "", $2), $0 = $2' | 365295373Sdteske sort -u 366295373Sdteske return 367295373Sdteske fi 368295373Sdteske ifconfig | awk -v name="$1" ' 369295373Sdteske match($0, /^e[[:digit:]]+a_/) && sub(/:.*/, "") && 370295373Sdteske substr($1, RSTART + RLENGTH) == name 371295373Sdteske ' | sort 372295373Sdteske} 373295373Sdteske 374295373Sdteskejib_destroy_usage="destroy NAME" 375295373Sdteskejib_destroy_descr="Destroy e0b_NAME [e1b_NAME ...]" 376295373Sdteskejib_destroy() 377295373Sdteske{ 378295373Sdteske local OPTIND=1 OPTARG flag 379295373Sdteske while getopts "" flag; do 380295373Sdteske case "$flag" in 381295373Sdteske *) action_usage destroy # NOTREACHED 382295373Sdteske esac 383295373Sdteske done 384295373Sdteske shift $(( $OPTIND -1 )) 385295373Sdteske local name="$1" 386295373Sdteske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || 387295373Sdteske action_usage destroy # NOTREACHED 388295373Sdteske mustberoot_to_continue 389295373Sdteske jib_show "$name" | xargs -rn1 -I eiface ifconfig eiface destroy 390295373Sdteske} 391295373Sdteske 392295373Sdteske############################################################ MAIN 393295373Sdteske 394295373Sdteske# 395295373Sdteske# Command-line arguments 396295373Sdteske# 397295373Sdteskeaction="$1" 398295373Sdteske[ "$action" ] || usage # NOTREACHED 399295373Sdteske 400295373Sdteske# 401295373Sdteske# Validate action argument 402295373Sdteske# 403295373Sdteskeif [ "$BASH_VERSION" ]; then 404295373Sdteske type="$( type -t "jib_$action" )" || usage # NOTREACHED 405295373Sdteskeelse 406295373Sdteske type="$( type "jib_$action" 2> /dev/null )" || usage # NOTREACHED 407295373Sdteskefi 408295373Sdteskecase "$type" in 409295373Sdteske*function) 410295373Sdteske shift 1 # action 411295373Sdteske eval "jib_$action" \"\$@\" 412295373Sdteske ;; 413295373Sdteske*) usage # NOTREACHED 414295373Sdteskeesac 415295373Sdteske 416295373Sdteske################################################################################ 417295373Sdteske# END 418295373Sdteske################################################################################ 419