17879db76SDevin Teske#!/bin/sh 27879db76SDevin Teske#- 37879db76SDevin Teske# Copyright (c) 2016 Devin Teske 47879db76SDevin Teske# All rights reserved. 57879db76SDevin Teske# 67879db76SDevin Teske# Redistribution and use in source and binary forms, with or without 77879db76SDevin Teske# modification, are permitted provided that the following conditions 87879db76SDevin Teske# are met: 97879db76SDevin Teske# 1. Redistributions of source code must retain the above copyright 107879db76SDevin Teske# notice, this list of conditions and the following disclaimer. 117879db76SDevin Teske# 2. Redistributions in binary form must reproduce the above copyright 127879db76SDevin Teske# notice, this list of conditions and the following disclaimer in the 137879db76SDevin Teske# documentation and/or other materials provided with the distribution. 147879db76SDevin Teske# 157879db76SDevin Teske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 167879db76SDevin Teske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177879db76SDevin Teske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187879db76SDevin Teske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197879db76SDevin Teske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207879db76SDevin Teske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217879db76SDevin Teske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227879db76SDevin Teske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237879db76SDevin Teske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247879db76SDevin Teske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257879db76SDevin Teske# SUCH DAMAGE. 267879db76SDevin Teske# 277879db76SDevin Teske# $FreeBSD$ 287879db76SDevin Teske# 297879db76SDevin Teske############################################################ IDENT(1) 307879db76SDevin Teske# 317879db76SDevin Teske# $Title: if_bridge(4) management script for vnet jails $ 327879db76SDevin Teske# 337879db76SDevin Teske############################################################ INFORMATION 347879db76SDevin Teske# 357879db76SDevin Teske# Use this tool with jail.conf(5) (or rc.conf(5) ``legacy'' configuration) to 36*626ddc51SDevin Teske# manage `vnet' interfaces for jails. Designed to automate the creation of vnet 37*626ddc51SDevin Teske# interface(s) during jail `prestart' and destroy said interface(s) during jail 38*626ddc51SDevin Teske# `poststop'. 39*626ddc51SDevin Teske# 40*626ddc51SDevin Teske# In jail.conf(5) format: 417879db76SDevin Teske# 427879db76SDevin Teske# ### BEGIN EXCERPT ### 437879db76SDevin Teske# 447879db76SDevin Teske# xxx { 457879db76SDevin Teske# host.hostname = "xxx.yyy"; 467879db76SDevin Teske# path = "/vm/xxx"; 477879db76SDevin Teske# 487879db76SDevin Teske# # 497879db76SDevin Teske# # NB: Below 2-lines required 507879db76SDevin Teske# # NB: The number of eNb_xxx interfaces should match the number of 517879db76SDevin Teske# # arguments given to `jib addm xxx' in exec.prestart value. 527879db76SDevin Teske# # 537879db76SDevin Teske# vnet; 547879db76SDevin Teske# vnet.interface = "e0b_xxx e1b_xxx ..."; 557879db76SDevin Teske# 567879db76SDevin Teske# exec.clean; 577879db76SDevin Teske# exec.system_user = "root"; 587879db76SDevin Teske# exec.jail_user = "root"; 597879db76SDevin Teske# 607879db76SDevin Teske# # 617879db76SDevin Teske# # NB: Below 2-lines required 627879db76SDevin Teske# # NB: The number of arguments after `jib addm xxx' should match 637879db76SDevin Teske# # the number of eNb_xxx arguments in vnet.interface value. 647879db76SDevin Teske# # 657879db76SDevin Teske# exec.prestart += "jib addm xxx em0 em1 ..."; 667879db76SDevin Teske# exec.poststop += "jib destroy xxx"; 677879db76SDevin Teske# 687879db76SDevin Teske# # Standard recipe 697879db76SDevin Teske# exec.start += "/bin/sh /etc/rc"; 707879db76SDevin Teske# exec.stop = "/bin/sh /etc/rc.shutdown"; 717879db76SDevin Teske# exec.consolelog = "/var/log/jail_xxx_console.log"; 727879db76SDevin Teske# mount.devfs; 737879db76SDevin Teske# 747879db76SDevin Teske# # Optional (default off) 757879db76SDevin Teske# #allow.mount; 767879db76SDevin Teske# #allow.set_hostname = 1; 777879db76SDevin Teske# #allow.sysvipc = 1; 787879db76SDevin Teske# #devfs_ruleset = "11"; # rule to unhide bpf for DHCP 797879db76SDevin Teske# } 807879db76SDevin Teske# 817879db76SDevin Teske# ### END EXCERPT ### 827879db76SDevin Teske# 837879db76SDevin Teske# In rc.conf(5) ``legacy'' format (used when /etc/jail.conf does not exist): 847879db76SDevin Teske# 857879db76SDevin Teske# ### BEGIN EXCERPT ### 867879db76SDevin Teske# 877879db76SDevin Teske# jail_enable="YES" 887879db76SDevin Teske# jail_list="xxx" 897879db76SDevin Teske# 907879db76SDevin Teske# # 917879db76SDevin Teske# # Global presets for all jails 927879db76SDevin Teske# # 937879db76SDevin Teske# jail_devfs_enable="YES" # mount devfs 947879db76SDevin Teske# 957879db76SDevin Teske# # 967879db76SDevin Teske# # Global options (default off) 977879db76SDevin Teske# # 987879db76SDevin Teske# #jail_mount_enable="YES" # mount /etc/fstab.{name} 997879db76SDevin Teske# #jail_set_hostname_allow="YES" # Allow hostname to change 1007879db76SDevin Teske# #jail_sysvipc_allow="YES" # Allow SysV Interprocess Comm. 1017879db76SDevin Teske# 1027879db76SDevin Teske# # xxx 1037879db76SDevin Teske# jail_xxx_hostname="xxx.shxd.cx" # hostname 1047879db76SDevin Teske# jail_xxx_rootdir="/vm/xxx" # root directory 1057879db76SDevin Teske# jail_xxx_vnet_interfaces="e0b_xxx e1bxxx ..." # vnet interface(s) 1067879db76SDevin Teske# jail_xxx_exec_prestart0="jib addm xxx em0 em1 ..." # bridge interface(s) 1077879db76SDevin Teske# jail_xxx_exec_poststop0="jib destroy xxx" # destroy interface(s) 1087879db76SDevin Teske# #jail_xxx_mount_enable="YES" # mount /etc/fstab.xxx 1097879db76SDevin Teske# #jail_xxx_devfs_ruleset="11" # rule to unhide bpf for DHCP 1107879db76SDevin Teske# 1117879db76SDevin Teske# ### END EXCERPT ### 1127879db76SDevin Teske# 1137879db76SDevin Teske# Note that the legacy rc.conf(5) format is converted to 1147879db76SDevin Teske# /var/run/jail.{name}.conf by /etc/rc.d/jail if jail.conf(5) is missing. 1157879db76SDevin Teske# 1167879db76SDevin Teske# ASIDE: dhclient(8) inside a vnet jail... 1177879db76SDevin Teske# 1187879db76SDevin Teske# To allow dhclient(8) to work inside a vnet jail, make sure the following 1197879db76SDevin Teske# appears in /etc/devfs.rules (which should be created if it doesn't exist): 1207879db76SDevin Teske# 1217879db76SDevin Teske# [devfsrules_jail=11] 1227879db76SDevin Teske# add include $devfsrules_hide_all 1237879db76SDevin Teske# add include $devfsrules_unhide_basic 1247879db76SDevin Teske# add include $devfsrules_unhide_login 1257879db76SDevin Teske# add include $devfsrules_unhide_bpf 1267879db76SDevin Teske# 1277879db76SDevin Teske# And set ether devfs.ruleset="11" (jail.conf(5)) or 1287879db76SDevin Teske# jail_{name}_devfs_ruleset="11" (rc.conf(5)). 1297879db76SDevin Teske# 1307879db76SDevin Teske# NB: While this tool can't create every type of desirable topology, it should 1317879db76SDevin Teske# handle most setups, minus some which considered exotic or purpose-built. 1327879db76SDevin Teske# 1337879db76SDevin Teske############################################################ GLOBALS 1347879db76SDevin Teske 1357879db76SDevin Teskepgm="${0##*/}" # Program basename 1367879db76SDevin Teske 1377879db76SDevin Teske# 1387879db76SDevin Teske# Global exit status 1397879db76SDevin Teske# 1407879db76SDevin TeskeSUCCESS=0 1417879db76SDevin TeskeFAILURE=1 1427879db76SDevin Teske 1437879db76SDevin Teske############################################################ FUNCTIONS 1447879db76SDevin Teske 1457879db76SDevin Teskeusage() 1467879db76SDevin Teske{ 1477879db76SDevin Teske local action usage descr 1487879db76SDevin Teske exec >&2 1497879db76SDevin Teske echo "Usage: $pgm action [arguments]" 1507879db76SDevin Teske echo "Actions:" 1517879db76SDevin Teske for action in \ 1527879db76SDevin Teske addm \ 1537879db76SDevin Teske show \ 1547879db76SDevin Teske show1 \ 1557879db76SDevin Teske destroy \ 1567879db76SDevin Teske ; do 1577879db76SDevin Teske eval usage=\"\$jib_${action}_usage\" 1587879db76SDevin Teske [ "$usage" ] || continue 1597879db76SDevin Teske eval descr=\"\$jib_${action}_descr\" 1607879db76SDevin Teske printf "\t%s\n\t\t%s\n" "$usage" "$descr" 1617879db76SDevin Teske done 1627879db76SDevin Teske exit $FAILURE 1637879db76SDevin Teske} 1647879db76SDevin Teske 1657879db76SDevin Teskeaction_usage() 1667879db76SDevin Teske{ 1677879db76SDevin Teske local usage action="$1" 1687879db76SDevin Teske eval usage=\"\$jib_${action}_usage\" 1697879db76SDevin Teske echo "Usage: $pgm $usage" >&2 1707879db76SDevin Teske exit $FAILURE 1717879db76SDevin Teske} 1727879db76SDevin Teske 1737879db76SDevin Teskemustberoot_to_continue() 1747879db76SDevin Teske{ 1757879db76SDevin Teske if [ "$( id -u )" -ne 0 ]; then 1767879db76SDevin Teske echo "Must run as root!" >&2 1777879db76SDevin Teske exit $FAILURE 1787879db76SDevin Teske fi 1797879db76SDevin Teske} 1807879db76SDevin Teske 1817879db76SDevin Teskejib_addm_usage="addm [-b BRIDGE_NAME] NAME interface0 [interface1 ...]" 1827879db76SDevin Teskejib_addm_descr="Creates e0b_NAME [e1b_NAME ...]" 1837879db76SDevin Teskejib_addm() 1847879db76SDevin Teske{ 1857879db76SDevin Teske local OPTIND=1 OPTARG flag bridge=bridge 1867879db76SDevin Teske while getopts b: flag; do 1877879db76SDevin Teske case "$flag" in 1887879db76SDevin Teske b) bridge="${OPTARG:-bridge}" ;; 1897879db76SDevin Teske *) action_usage addm # NOTREACHED 1907879db76SDevin Teske esac 1917879db76SDevin Teske done 1927879db76SDevin Teske shift $(( $OPTIND - 1 )) 1937879db76SDevin Teske 1947879db76SDevin Teske local name="$1" 1957879db76SDevin Teske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || 1967879db76SDevin Teske action_usage addm # NOTREACHED 1977879db76SDevin Teske shift 1 # name 1987879db76SDevin Teske 1997879db76SDevin Teske mustberoot_to_continue 2007879db76SDevin Teske 2017879db76SDevin Teske local iface iface_devid eiface_devid_a eiface_devid_b 2027879db76SDevin Teske local new num quad i=0 2037879db76SDevin Teske for iface in $*; do 2047879db76SDevin Teske 2057879db76SDevin Teske # 1. Make sure the interface doesn't exist already 2067879db76SDevin Teske ifconfig "e${i}a_$name" > /dev/null 2>&1 && continue 2077879db76SDevin Teske 2087879db76SDevin Teske # 2. Bring the interface up 2097879db76SDevin Teske ifconfig $iface up || return 2107879db76SDevin Teske 2117879db76SDevin Teske # 3. Make sure the interface has been bridged 2127879db76SDevin Teske if ! ifconfig "$iface$bridge" > /dev/null 2>&1; then 2137879db76SDevin Teske new=$( ifconfig bridge create ) || return 2147879db76SDevin Teske ifconfig $new addm $iface || return 2157879db76SDevin Teske ifconfig $new name "$iface$bridge" || return 2167879db76SDevin Teske fi 2177879db76SDevin Teske 2187879db76SDevin Teske # 4. Create a new interface to the bridge 2197879db76SDevin Teske new=$( ifconfig epair create ) || return 2207879db76SDevin Teske ifconfig "$iface$bridge" addm $new || return 2217879db76SDevin Teske 2227879db76SDevin Teske # 5. Rename the new interface 2237879db76SDevin Teske ifconfig $new name "e${i}a_$name" || return 2247879db76SDevin Teske ifconfig ${new%a}b name "e${i}b_$name" || return 2257879db76SDevin Teske 2267879db76SDevin Teske # 2277879db76SDevin Teske # 6. Set the MAC address of the new interface using a sensible 2287879db76SDevin Teske # algorithm to prevent conflicts on the network. 2297879db76SDevin Teske # 2307879db76SDevin Teske # The formula I'm using is ``SP:SS:SI:II:II:II'' where: 2317879db76SDevin Teske # + S denotes 16 bits of sum(1) data, split because P (below). 2327879db76SDevin Teske # + P denotes the special nibble whose value, if one of 2337879db76SDevin Teske # 2, 6, A, or E (but usually 2) denotes a privately 2347879db76SDevin Teske # administered MAC address (while remaining routable). 2357879db76SDevin Teske # + I denotes bits that are inherited from parent interface. 2367879db76SDevin Teske # 2377879db76SDevin Teske # The S bits are a CRC-16 checksum of NAME, allowing the jail 2387879db76SDevin Teske # to change the epair(4) generation order without affecting the 2397879db76SDevin Teske # MAC address. Meanwhile, if the jail NAME changes (e.g., it 2407879db76SDevin Teske # was duplicated and given a new name with no other changes), 2417879db76SDevin Teske # the underlying network interface changes, or the jail is 2427879db76SDevin Teske # moved to another host, the MAC address will be recalculated 2437879db76SDevin Teske # to a new, similarly unique value preventing conflict. 2447879db76SDevin Teske # 2457879db76SDevin Teske iface_devid=$( ifconfig $iface ether | awk '/ether/,$0=$2' ) 2467879db76SDevin Teske eiface_devid_a=${iface_devid#??:??:?} 2477879db76SDevin Teske eiface_devid_b=${iface_devid#??:??:?} 2487879db76SDevin Teske num=$( set -- `echo -n $name | sum` && echo $1 ) 2497879db76SDevin Teske quad=$(( $num & 15 )) 2507879db76SDevin Teske case "$quad" in 2517879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2527879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2537879db76SDevin Teske esac 2547879db76SDevin Teske eiface_devid_a=:$quad$eiface_devid_a 2557879db76SDevin Teske eiface_devid_b=:$quad$eiface_devid_b 2567879db76SDevin Teske num=$(( $num >> 4 )) 2577879db76SDevin Teske quad=$(( $num & 15 )) 2587879db76SDevin Teske case "$quad" in 2597879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2607879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2617879db76SDevin Teske esac 2627879db76SDevin Teske eiface_devid_a=$quad$eiface_devid_a 2637879db76SDevin Teske eiface_devid_b=$quad$eiface_devid_b 2647879db76SDevin Teske num=$(( $num >> 4 )) 2657879db76SDevin Teske quad=$(( $num & 15 )) 2667879db76SDevin Teske case "$quad" in 2677879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2687879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2697879db76SDevin Teske esac 2707879db76SDevin Teske eiface_devid_a=2:$quad$eiface_devid_a 2717879db76SDevin Teske eiface_devid_b=6:$quad$eiface_devid_b 2727879db76SDevin Teske num=$(( $num >> 4 )) 2737879db76SDevin Teske quad=$(( $num & 15 )) 2747879db76SDevin Teske case "$quad" in 2757879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2767879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2777879db76SDevin Teske esac 2787879db76SDevin Teske eiface_devid_a=$quad$eiface_devid_a 2797879db76SDevin Teske eiface_devid_b=$quad$eiface_devid_b 2807879db76SDevin Teske ifconfig "e${i}a_$name" ether $eiface_devid_a > /dev/null 2>&1 2817879db76SDevin Teske ifconfig "e${i}b_$name" ether $eiface_devid_b > /dev/null 2>&1 2827879db76SDevin Teske 2830df549cbSDevin Teske i=$(( $i + 1 )) # on to next e{i}b_name 2847879db76SDevin Teske done # for iface 2857879db76SDevin Teske} 2867879db76SDevin Teske 2877879db76SDevin Teskejib_show_usage="show" 2887879db76SDevin Teskejib_show_descr="List possible NAME values for \`show NAME'" 2897879db76SDevin Teskejib_show1_usage="show NAME" 2900df549cbSDevin Teskejib_show1_descr="Lists e0b_NAME [e1b_NAME ...]" 2917879db76SDevin Teskejib_show2_usage="show [NAME]" 2927879db76SDevin Teskejib_show() 2937879db76SDevin Teske{ 2947879db76SDevin Teske local OPTIND=1 OPTARG flag 2957879db76SDevin Teske while getopts "" flag; do 2967879db76SDevin Teske case "$flag" in 2977879db76SDevin Teske *) action_usage show2 # NOTREACHED 2987879db76SDevin Teske esac 2997879db76SDevin Teske done 3007879db76SDevin Teske shift $(( $OPTIND - 1 )) 3017879db76SDevin Teske if [ $# -eq 0 ]; then 3027879db76SDevin Teske ifconfig | awk ' 3037879db76SDevin Teske /^[^:[:space:]]+:/ { 3047879db76SDevin Teske iface = $1 3057879db76SDevin Teske sub(/:.*/, "", iface) 3067879db76SDevin Teske next 3077879db76SDevin Teske } 3087879db76SDevin Teske $1 == "groups:" { 3097879db76SDevin Teske for (n = split($0, group); n > 1; n--) { 3107879db76SDevin Teske if (group[n] != "bridge") continue 3117879db76SDevin Teske print iface 3127879db76SDevin Teske next 3137879db76SDevin Teske } 3147879db76SDevin Teske }' | 3157879db76SDevin Teske xargs -rn1 ifconfig | 3167879db76SDevin Teske awk '$1 == "member:" && 3177879db76SDevin Teske sub(/^e[[:digit:]]+a_/, "", $2), $0 = $2' | 3187879db76SDevin Teske sort -u 3197879db76SDevin Teske return 3207879db76SDevin Teske fi 3217879db76SDevin Teske ifconfig | awk -v name="$1" ' 3227879db76SDevin Teske match($0, /^e[[:digit:]]+a_/) && sub(/:.*/, "") && 3237879db76SDevin Teske substr($1, RSTART + RLENGTH) == name 3247879db76SDevin Teske ' | sort 3257879db76SDevin Teske} 3267879db76SDevin Teske 3277879db76SDevin Teskejib_destroy_usage="destroy NAME" 3287879db76SDevin Teskejib_destroy_descr="Destroy e0b_NAME [e1b_NAME ...]" 3297879db76SDevin Teskejib_destroy() 3307879db76SDevin Teske{ 3317879db76SDevin Teske local OPTIND=1 OPTARG flag 3327879db76SDevin Teske while getopts "" flag; do 3337879db76SDevin Teske case "$flag" in 3347879db76SDevin Teske *) action_usage destroy # NOTREACHED 3357879db76SDevin Teske esac 3367879db76SDevin Teske done 3377879db76SDevin Teske shift $(( $OPTIND -1 )) 3387879db76SDevin Teske local name="$1" 3397879db76SDevin Teske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || 3407879db76SDevin Teske action_usage destroy # NOTREACHED 3417879db76SDevin Teske mustberoot_to_continue 3427879db76SDevin Teske jib_show "$name" | xargs -rn1 -I eiface ifconfig eiface destroy 3437879db76SDevin Teske} 3447879db76SDevin Teske 3457879db76SDevin Teske############################################################ MAIN 3467879db76SDevin Teske 3477879db76SDevin Teske# 3487879db76SDevin Teske# Command-line arguments 3497879db76SDevin Teske# 3507879db76SDevin Teskeaction="$1" 3517879db76SDevin Teske[ "$action" ] || usage # NOTREACHED 3527879db76SDevin Teske 3537879db76SDevin Teske# 3547879db76SDevin Teske# Validate action argument 3557879db76SDevin Teske# 3567879db76SDevin Teskeif [ "$BASH_VERSION" ]; then 3577879db76SDevin Teske type="$( type -t "jib_$action" )" || usage # NOTREACHED 3587879db76SDevin Teskeelse 3597879db76SDevin Teske type="$( type "jib_$action" 2> /dev/null )" || usage # NOTREACHED 3607879db76SDevin Teskefi 3617879db76SDevin Teskecase "$type" in 3627879db76SDevin Teske*function) 3637879db76SDevin Teske shift 1 # action 3647879db76SDevin Teske eval "jib_$action" \"\$@\" 3657879db76SDevin Teske ;; 3667879db76SDevin Teske*) usage # NOTREACHED 3677879db76SDevin Teskeesac 3687879db76SDevin Teske 3697879db76SDevin Teske################################################################################ 3707879db76SDevin Teske# END 3717879db76SDevin Teske################################################################################ 372