jib revision 295443
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: head/share/examples/jails/jib 295443 2016-02-09 18:11:18Z 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{ 167295373Sdteske local usage action="$1" 168295373Sdteske eval usage=\"\$jib_${action}_usage\" 169295373Sdteske echo "Usage: $pgm $usage" >&2 170295373Sdteske exit $FAILURE 171295373Sdteske} 172295373Sdteske 173295373Sdteskemustberoot_to_continue() 174295373Sdteske{ 175295373Sdteske if [ "$( id -u )" -ne 0 ]; then 176295373Sdteske echo "Must run as root!" >&2 177295373Sdteske exit $FAILURE 178295373Sdteske fi 179295373Sdteske} 180295373Sdteske 181295373Sdteskejib_addm_usage="addm [-b BRIDGE_NAME] NAME interface0 [interface1 ...]" 182295373Sdteskejib_addm_descr="Creates e0b_NAME [e1b_NAME ...]" 183295373Sdteskejib_addm() 184295373Sdteske{ 185295373Sdteske local OPTIND=1 OPTARG flag bridge=bridge 186295373Sdteske while getopts b: flag; do 187295373Sdteske case "$flag" in 188295373Sdteske b) bridge="${OPTARG:-bridge}" ;; 189295373Sdteske *) action_usage addm # NOTREACHED 190295373Sdteske esac 191295373Sdteske done 192295373Sdteske shift $(( $OPTIND - 1 )) 193295373Sdteske 194295373Sdteske local name="$1" 195295373Sdteske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || 196295373Sdteske action_usage addm # NOTREACHED 197295373Sdteske shift 1 # name 198295373Sdteske 199295373Sdteske mustberoot_to_continue 200295373Sdteske 201295442Sdteske local iface iface_devid eiface_devid 202295442Sdteske local eiface_devid_a eiface_devid_b 203295373Sdteske local new num quad i=0 204295373Sdteske for iface in $*; do 205295373Sdteske 206295373Sdteske # 1. Make sure the interface doesn't exist already 207295373Sdteske ifconfig "e${i}a_$name" > /dev/null 2>&1 && continue 208295373Sdteske 209295373Sdteske # 2. Bring the interface up 210295373Sdteske ifconfig $iface up || return 211295373Sdteske 212295373Sdteske # 3. Make sure the interface has been bridged 213295373Sdteske if ! ifconfig "$iface$bridge" > /dev/null 2>&1; then 214295373Sdteske new=$( ifconfig bridge create ) || return 215295373Sdteske ifconfig $new addm $iface || return 216295373Sdteske ifconfig $new name "$iface$bridge" || return 217295443Sdteske ifconfig "$iface$bridge" up || return 218295373Sdteske fi 219295373Sdteske 220295373Sdteske # 4. Create a new interface to the bridge 221295373Sdteske new=$( ifconfig epair create ) || return 222295373Sdteske ifconfig "$iface$bridge" addm $new || return 223295373Sdteske 224295373Sdteske # 5. Rename the new interface 225295373Sdteske ifconfig $new name "e${i}a_$name" || return 226295373Sdteske ifconfig ${new%a}b name "e${i}b_$name" || return 227295443Sdteske ifconfig "e${i}a_$name" up || return 228295443Sdteske ifconfig "e${i}b_$name" up || return 229295373Sdteske 230295373Sdteske # 231295373Sdteske # 6. Set the MAC address of the new interface using a sensible 232295373Sdteske # algorithm to prevent conflicts on the network. 233295373Sdteske # 234295422Sdteske # The formula I'm using is ``NP:SS:SS:II:II:II'' where: 235295422Sdteske # + N denotes 4 bits used as a counter to support branching 236295422Sdteske # each parent interface up to 15 times under the same jail 237295422Sdteske # name (see S below). 238295373Sdteske # + P denotes the special nibble whose value, if one of 239295373Sdteske # 2, 6, A, or E (but usually 2) denotes a privately 240295373Sdteske # administered MAC address (while remaining routable). 241295422Sdteske # + S denotes 16 bits, the sum(1) value of the jail name. 242295373Sdteske # + I denotes bits that are inherited from parent interface. 243295373Sdteske # 244295373Sdteske # The S bits are a CRC-16 checksum of NAME, allowing the jail 245295373Sdteske # to change the epair(4) generation order without affecting the 246295422Sdteske # MAC address. Meanwhile, if... 247295422Sdteske # + the jail NAME changes (e.g., it was duplicated and given 248295422Sdteske # a new name with no other changes) 249295422Sdteske # + the underlying network interface changes 250295422Sdteske # + the jail is moved to another host 251295422Sdteske # the MAC address will be recalculated to a new, similarly 252295422Sdteske # unique value preventing conflict. 253295373Sdteske # 254295373Sdteske iface_devid=$( ifconfig $iface ether | awk '/ether/,$0=$2' ) 255295442Sdteske eiface_devid=${iface_devid#??:??:??} 256295373Sdteske num=$( set -- `echo -n $name | sum` && echo $1 ) 257295373Sdteske quad=$(( $num & 15 )) 258295373Sdteske case "$quad" in 259295373Sdteske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 260295373Sdteske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 261295373Sdteske esac 262295442Sdteske eiface_devid=$quad$eiface_devid 263295373Sdteske num=$(( $num >> 4 )) 264295373Sdteske quad=$(( $num & 15 )) 265295373Sdteske case "$quad" in 266295373Sdteske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 267295373Sdteske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 268295373Sdteske esac 269295442Sdteske eiface_devid=$quad$eiface_devid 270295373Sdteske num=$(( $num >> 4 )) 271295373Sdteske quad=$(( $num & 15 )) 272295373Sdteske case "$quad" in 273295373Sdteske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 274295373Sdteske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 275295373Sdteske esac 276295442Sdteske eiface_devid=$quad:$eiface_devid 277295422Sdteske num=$(( $num >> 4 )) 278295422Sdteske quad=$(( $num & 15 )) 279295422Sdteske case "$quad" in 280295422Sdteske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 281295422Sdteske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 282295422Sdteske esac 283295401Sdteske case "$iface_devid" in 284295442Sdteske ?[Ee]:*) 285295442Sdteske eiface_devid_a=2:$quad$eiface_devid 286295442Sdteske eiface_devid_b=6:$quad$eiface_devid 287295401Sdteske ;; 288295401Sdteske *) 289295442Sdteske eiface_devid_a=2:$quad$eiface_devid 290295442Sdteske eiface_devid_b=e:$quad$eiface_devid 291295401Sdteske esac 292295422Sdteske eval num=\$_${iface}_num 293295422Sdteske if [ "$num" ]; then 294295422Sdteske num=$(( $num + 1 )) 295295422Sdteske eval _${iface}_num=$num 296295422Sdteske else 297295422Sdteske num=0 298295422Sdteske local _${iface}_num=$num 299295422Sdteske fi 300295373Sdteske quad=$(( $num & 15 )) 301295373Sdteske case "$quad" in 302295373Sdteske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 303295373Sdteske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 304295373Sdteske esac 305295373Sdteske eiface_devid_a=$quad$eiface_devid_a 306295373Sdteske eiface_devid_b=$quad$eiface_devid_b 307295373Sdteske ifconfig "e${i}a_$name" ether $eiface_devid_a > /dev/null 2>&1 308295373Sdteske ifconfig "e${i}b_$name" ether $eiface_devid_b > /dev/null 2>&1 309295373Sdteske 310295399Sdteske i=$(( $i + 1 )) # on to next e{i}b_name 311295373Sdteske done # for iface 312295373Sdteske} 313295373Sdteske 314295373Sdteskejib_show_usage="show" 315295373Sdteskejib_show_descr="List possible NAME values for \`show NAME'" 316295373Sdteskejib_show1_usage="show NAME" 317295399Sdteskejib_show1_descr="Lists e0b_NAME [e1b_NAME ...]" 318295373Sdteskejib_show2_usage="show [NAME]" 319295373Sdteskejib_show() 320295373Sdteske{ 321295373Sdteske local OPTIND=1 OPTARG flag 322295373Sdteske while getopts "" flag; do 323295373Sdteske case "$flag" in 324295373Sdteske *) action_usage show2 # NOTREACHED 325295373Sdteske esac 326295373Sdteske done 327295373Sdteske shift $(( $OPTIND - 1 )) 328295373Sdteske if [ $# -eq 0 ]; then 329295373Sdteske ifconfig | awk ' 330295373Sdteske /^[^:[:space:]]+:/ { 331295373Sdteske iface = $1 332295373Sdteske sub(/:.*/, "", iface) 333295373Sdteske next 334295373Sdteske } 335295373Sdteske $1 == "groups:" { 336295373Sdteske for (n = split($0, group); n > 1; n--) { 337295373Sdteske if (group[n] != "bridge") continue 338295373Sdteske print iface 339295373Sdteske next 340295373Sdteske } 341295373Sdteske }' | 342295373Sdteske xargs -rn1 ifconfig | 343295373Sdteske awk '$1 == "member:" && 344295373Sdteske sub(/^e[[:digit:]]+a_/, "", $2), $0 = $2' | 345295373Sdteske sort -u 346295373Sdteske return 347295373Sdteske fi 348295373Sdteske ifconfig | awk -v name="$1" ' 349295373Sdteske match($0, /^e[[:digit:]]+a_/) && sub(/:.*/, "") && 350295373Sdteske substr($1, RSTART + RLENGTH) == name 351295373Sdteske ' | sort 352295373Sdteske} 353295373Sdteske 354295373Sdteskejib_destroy_usage="destroy NAME" 355295373Sdteskejib_destroy_descr="Destroy e0b_NAME [e1b_NAME ...]" 356295373Sdteskejib_destroy() 357295373Sdteske{ 358295373Sdteske local OPTIND=1 OPTARG flag 359295373Sdteske while getopts "" flag; do 360295373Sdteske case "$flag" in 361295373Sdteske *) action_usage destroy # NOTREACHED 362295373Sdteske esac 363295373Sdteske done 364295373Sdteske shift $(( $OPTIND -1 )) 365295373Sdteske local name="$1" 366295373Sdteske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || 367295373Sdteske action_usage destroy # NOTREACHED 368295373Sdteske mustberoot_to_continue 369295373Sdteske jib_show "$name" | xargs -rn1 -I eiface ifconfig eiface destroy 370295373Sdteske} 371295373Sdteske 372295373Sdteske############################################################ MAIN 373295373Sdteske 374295373Sdteske# 375295373Sdteske# Command-line arguments 376295373Sdteske# 377295373Sdteskeaction="$1" 378295373Sdteske[ "$action" ] || usage # NOTREACHED 379295373Sdteske 380295373Sdteske# 381295373Sdteske# Validate action argument 382295373Sdteske# 383295373Sdteskeif [ "$BASH_VERSION" ]; then 384295373Sdteske type="$( type -t "jib_$action" )" || usage # NOTREACHED 385295373Sdteskeelse 386295373Sdteske type="$( type "jib_$action" 2> /dev/null )" || usage # NOTREACHED 387295373Sdteskefi 388295373Sdteskecase "$type" in 389295373Sdteske*function) 390295373Sdteske shift 1 # action 391295373Sdteske eval "jib_$action" \"\$@\" 392295373Sdteske ;; 393295373Sdteske*) usage # NOTREACHED 394295373Sdteskeesac 395295373Sdteske 396295373Sdteske################################################################################ 397295373Sdteske# END 398295373Sdteske################################################################################ 399