jib revision 295554
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 295554 2016-02-12 01:41:40Z 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 173295546Sdteskederive_mac() 174295546Sdteske{ 175295546Sdteske local OPTIND=1 OPTARG __flag 176295546Sdteske local __mac_num= __make_pair= 177295546Sdteske while getopts 2n: __flag; do 178295546Sdteske case "$__flag" in 179295546Sdteske 2) __make_pair=1 ;; 180295546Sdteske n) __mac_num=${OPTARG%%[^0-9]*} ;; 181295546Sdteske esac 182295546Sdteske done 183295546Sdteske shift $(( $OPTIND - 1 )) 184295546Sdteske 185295546Sdteske if [ ! "$__mac_num" ]; then 186295546Sdteske eval __mac_num=\${_${iface}_num:--1} 187295546Sdteske __mac_num=$(( $__mac_num + 1 )) 188295546Sdteske eval _${iface}_num=\$__mac_num 189295546Sdteske fi 190295546Sdteske 191295546Sdteske local __iface="$1" __name="$2" __var_to_set="$3" __var_to_set_b="$4" 192295546Sdteske local __iface_devid __new_devid __num __new_devid_b 193295546Sdteske # 194295546Sdteske # Calculate MAC address derived from given iface. 195295546Sdteske # 196295546Sdteske # The formula I'm using is ``NP:SS:SS:II:II:II'' where: 197295546Sdteske # + N denotes 4 bits used as a counter to support branching 198295546Sdteske # each parent interface up to 15 times under the same jail 199295546Sdteske # name (see S below). 200295546Sdteske # + P denotes the special nibble whose value, if one of 201295546Sdteske # 2, 6, A, or E (but usually 2) denotes a privately 202295546Sdteske # administered MAC address (while remaining routable). 203295546Sdteske # + S denotes 16 bits, the sum(1) value of the jail name. 204295546Sdteske # + I denotes bits that are inherited from parent interface. 205295546Sdteske # 206295546Sdteske # The S bits are a CRC-16 checksum of NAME, allowing the jail 207295546Sdteske # to change link numbers in ng_bridge(4) without affecting the 208295546Sdteske # MAC address. Meanwhile, if... 209295546Sdteske # + the jail NAME changes (e.g., it was duplicated and given 210295546Sdteske # a new name with no other changes) 211295546Sdteske # + the underlying network interface changes 212295546Sdteske # + the jail is moved to another host 213295546Sdteske # the MAC address will be recalculated to a new, similarly 214295546Sdteske # unique value preventing conflict. 215295546Sdteske # 216295546Sdteske __iface_devid=$( ifconfig $__iface ether | awk '/ether/,$0=$2' ) 217295553Sdteske # ??:??:??:II:II:II 218295553Sdteske __new_devid=${__iface_devid#??:??:??} # => :II:II:II 219295553Sdteske # => :SS:SS:II:II:II 220295546Sdteske __num=$( set -- `echo -n "$__name" | sum` && echo $1 ) 221295553Sdteske __new_devid=$( printf :%02x:%02x \ 222295553Sdteske $(( $__num >> 8 & 255 )) $(( $__num & 255 )) )$__new_devid 223295553Sdteske # => P:SS:SS:II:II:II 224295546Sdteske case "$__iface_devid" in 225295546Sdteske ?2:*) __new_devid=a$__new_devid __new_devid_b=e$__new_devid ;; 226295546Sdteske ?[Ee]:*) __new_devid=2$__new_devid __new_devid_b=6$__new_devid ;; 227295546Sdteske *) __new_devid=2$__new_devid __new_devid_b=e$__new_devid 228295546Sdteske esac 229295553Sdteske # => NP:SS:SS:II:II:II 230295546Sdteske __new_devid=$( printf %x $(( $__mac_num & 15 )) )$__new_devid 231295546Sdteske __new_devid_b=$( printf %x $(( $__mac_num & 15 )) )$__new_devid_b 232295546Sdteske 233295546Sdteske # 234295546Sdteske # Return derivative MAC address(es) 235295546Sdteske # 236295546Sdteske if [ "$__make_pair" ]; then 237295546Sdteske if [ "$__var_to_set" -a "$__var_to_set_b" ]; then 238295546Sdteske eval $__var_to_set=\$__new_devid 239295546Sdteske eval $__var_to_set_b=\$__new_devid_b 240295546Sdteske else 241295546Sdteske echo $__new_devid $__new_devid_b 242295546Sdteske fi 243295546Sdteske else 244295546Sdteske if [ "$__var_to_set" ]; then 245295546Sdteske eval $__var_to_set=\$__new_devid 246295546Sdteske else 247295546Sdteske echo $__new_devid 248295546Sdteske fi 249295546Sdteske fi 250295546Sdteske} 251295546Sdteske 252295373Sdteskemustberoot_to_continue() 253295373Sdteske{ 254295373Sdteske if [ "$( id -u )" -ne 0 ]; then 255295373Sdteske echo "Must run as root!" >&2 256295373Sdteske exit $FAILURE 257295373Sdteske fi 258295373Sdteske} 259295373Sdteske 260295554Sdteskejib_addm_usage="addm [-b BRIDGE_NAME] NAME [!]iface0 [[!]iface1 ...]" 261295373Sdteskejib_addm_descr="Creates e0b_NAME [e1b_NAME ...]" 262295373Sdteskejib_addm() 263295373Sdteske{ 264295373Sdteske local OPTIND=1 OPTARG flag bridge=bridge 265295373Sdteske while getopts b: flag; do 266295373Sdteske case "$flag" in 267295373Sdteske b) bridge="${OPTARG:-bridge}" ;; 268295373Sdteske *) action_usage addm # NOTREACHED 269295373Sdteske esac 270295373Sdteske done 271295373Sdteske shift $(( $OPTIND - 1 )) 272295373Sdteske 273295373Sdteske local name="$1" 274295373Sdteske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || 275295373Sdteske action_usage addm # NOTREACHED 276295373Sdteske shift 1 # name 277295373Sdteske 278295373Sdteske mustberoot_to_continue 279295373Sdteske 280295546Sdteske local iface eiface_devid_a eiface_devid_b 281295554Sdteske local new no_derive num quad i=0 282295373Sdteske for iface in $*; do 283295373Sdteske 284295554Sdteske no_derive= 285295554Sdteske case "$iface" in 286295554Sdteske !*) iface=${iface#!} no_derive=1 ;; 287295554Sdteske esac 288295554Sdteske 289295373Sdteske # 1. Make sure the interface doesn't exist already 290295373Sdteske ifconfig "e${i}a_$name" > /dev/null 2>&1 && continue 291295373Sdteske 292295373Sdteske # 2. Bring the interface up 293295373Sdteske ifconfig $iface up || return 294295373Sdteske 295295373Sdteske # 3. Make sure the interface has been bridged 296295373Sdteske if ! ifconfig "$iface$bridge" > /dev/null 2>&1; then 297295373Sdteske new=$( ifconfig bridge create ) || return 298295373Sdteske ifconfig $new addm $iface || return 299295373Sdteske ifconfig $new name "$iface$bridge" || return 300295443Sdteske ifconfig "$iface$bridge" up || return 301295373Sdteske fi 302295373Sdteske 303295373Sdteske # 4. Create a new interface to the bridge 304295373Sdteske new=$( ifconfig epair create ) || return 305295373Sdteske ifconfig "$iface$bridge" addm $new || return 306295373Sdteske 307295373Sdteske # 5. Rename the new interface 308295373Sdteske ifconfig $new name "e${i}a_$name" || return 309295373Sdteske ifconfig ${new%a}b name "e${i}b_$name" || return 310295443Sdteske ifconfig "e${i}a_$name" up || return 311295443Sdteske ifconfig "e${i}b_$name" up || return 312295373Sdteske 313295373Sdteske # 314295373Sdteske # 6. Set the MAC address of the new interface using a sensible 315295373Sdteske # algorithm to prevent conflicts on the network. 316295373Sdteske # 317295554Sdteske eiface_devid_a= eiface_devid_b= 318295554Sdteske [ "$no_derive" ] || derive_mac -2 $iface "$name" \ 319295554Sdteske eiface_devid_a eiface_devid_b 320295554Sdteske if [ "$eiface_devid_a" -a "$eiface_devid_b" ]; then 321295554Sdteske ifconfig "e${i}a_$name" ether $eiface_devid_a 322295554Sdteske ifconfig "e${i}b_$name" ether $eiface_devid_b 323295554Sdteske fi > /dev/null 2>&1 324295373Sdteske 325295399Sdteske i=$(( $i + 1 )) # on to next e{i}b_name 326295373Sdteske done # for iface 327295373Sdteske} 328295373Sdteske 329295373Sdteskejib_show_usage="show" 330295373Sdteskejib_show_descr="List possible NAME values for \`show NAME'" 331295373Sdteskejib_show1_usage="show NAME" 332295399Sdteskejib_show1_descr="Lists e0b_NAME [e1b_NAME ...]" 333295373Sdteskejib_show2_usage="show [NAME]" 334295373Sdteskejib_show() 335295373Sdteske{ 336295373Sdteske local OPTIND=1 OPTARG flag 337295373Sdteske while getopts "" flag; do 338295373Sdteske case "$flag" in 339295373Sdteske *) action_usage show2 # NOTREACHED 340295373Sdteske esac 341295373Sdteske done 342295373Sdteske shift $(( $OPTIND - 1 )) 343295373Sdteske if [ $# -eq 0 ]; then 344295373Sdteske ifconfig | awk ' 345295373Sdteske /^[^:[:space:]]+:/ { 346295373Sdteske iface = $1 347295373Sdteske sub(/:.*/, "", iface) 348295373Sdteske next 349295373Sdteske } 350295373Sdteske $1 == "groups:" { 351295373Sdteske for (n = split($0, group); n > 1; n--) { 352295373Sdteske if (group[n] != "bridge") continue 353295373Sdteske print iface 354295373Sdteske next 355295373Sdteske } 356295373Sdteske }' | 357295373Sdteske xargs -rn1 ifconfig | 358295373Sdteske awk '$1 == "member:" && 359295373Sdteske sub(/^e[[:digit:]]+a_/, "", $2), $0 = $2' | 360295373Sdteske sort -u 361295373Sdteske return 362295373Sdteske fi 363295373Sdteske ifconfig | awk -v name="$1" ' 364295373Sdteske match($0, /^e[[:digit:]]+a_/) && sub(/:.*/, "") && 365295373Sdteske substr($1, RSTART + RLENGTH) == name 366295373Sdteske ' | sort 367295373Sdteske} 368295373Sdteske 369295373Sdteskejib_destroy_usage="destroy NAME" 370295373Sdteskejib_destroy_descr="Destroy e0b_NAME [e1b_NAME ...]" 371295373Sdteskejib_destroy() 372295373Sdteske{ 373295373Sdteske local OPTIND=1 OPTARG flag 374295373Sdteske while getopts "" flag; do 375295373Sdteske case "$flag" in 376295373Sdteske *) action_usage destroy # NOTREACHED 377295373Sdteske esac 378295373Sdteske done 379295373Sdteske shift $(( $OPTIND -1 )) 380295373Sdteske local name="$1" 381295373Sdteske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || 382295373Sdteske action_usage destroy # NOTREACHED 383295373Sdteske mustberoot_to_continue 384295373Sdteske jib_show "$name" | xargs -rn1 -I eiface ifconfig eiface destroy 385295373Sdteske} 386295373Sdteske 387295373Sdteske############################################################ MAIN 388295373Sdteske 389295373Sdteske# 390295373Sdteske# Command-line arguments 391295373Sdteske# 392295373Sdteskeaction="$1" 393295373Sdteske[ "$action" ] || usage # NOTREACHED 394295373Sdteske 395295373Sdteske# 396295373Sdteske# Validate action argument 397295373Sdteske# 398295373Sdteskeif [ "$BASH_VERSION" ]; then 399295373Sdteske type="$( type -t "jib_$action" )" || usage # NOTREACHED 400295373Sdteskeelse 401295373Sdteske type="$( type "jib_$action" 2> /dev/null )" || usage # NOTREACHED 402295373Sdteskefi 403295373Sdteskecase "$type" in 404295373Sdteske*function) 405295373Sdteske shift 1 # action 406295373Sdteske eval "jib_$action" \"\$@\" 407295373Sdteske ;; 408295373Sdteske*) usage # NOTREACHED 409295373Sdteskeesac 410295373Sdteske 411295373Sdteske################################################################################ 412295373Sdteske# END 413295373Sdteske################################################################################ 414