git: 19b0b1f648d8 - main - examples/jails: Encode ifnames used as derive_mac counters

From: Devin Teske <dteske_at_FreeBSD.org>
Date: Thu, 03 Sep 2026 06:20:18 UTC
The branch main has been updated by dteske:

URL: https://cgit.FreeBSD.org/src/commit/?id=19b0b1f648d8f114f77e82495618587b1729c4cc

commit 19b0b1f648d8f114f77e82495618587b1729c4cc
Author:     Devin Teske <dteske@FreeBSD.org>
AuthorDate: 2026-09-03 06:17:52 +0000
Commit:     Devin Teske <dteske@FreeBSD.org>
CommitDate: 2026-09-03 06:17:52 +0000

    examples/jails: Encode ifnames used as derive_mac counters
    
    derive_mac keeps a per-parent branch index in a global named from the
    parent interface so the N nibble can increment when the same PHY is
    presented more than once. That name must be a POSIX identifier; a
    vlan-style parent (em0.20) is not.
    
    Encode the ifname first (alnum unchanged, every other byte as _HH) so
    the lookup stays a symbol-table hit and em0.20 does not collide with
    em0_20. Same change in jib (9.2) and jng (9.4).
    
    In jng, also address netgraph by node name. ngctl(8) treats `.' and
    `:' as control characters, so ng_ether(4) names its node after the
    sanitized ifname (vtnet0.20 becomes vtnet0_20). Sanitize the parent
    ifname where it enters and use that for every ngctl call; ifconfig(8)
    and derive_mac keep the real name. Previously jng failed outright on
    such parents where jib did not.
    
    PR:             291143
    Reported by:    Victor <tschetter.victor@gmail.com>
    MFC after:      1 week
    Reviewed by:    jlduran
    Differential Revision:  https://reviews.freebsd.org/D59326
---
 share/examples/jails/jib | 32 +++++++++++++---
 share/examples/jails/jng | 99 +++++++++++++++++++++++++++++++++---------------
 2 files changed, 96 insertions(+), 35 deletions(-)

diff --git a/share/examples/jails/jib b/share/examples/jails/jib
index 44d53c44bf11..b18c60da15ec 100755
--- a/share/examples/jails/jib
+++ b/share/examples/jails/jib
@@ -7,7 +7,7 @@
 ############################################################ IDENT(1)
 #
 # $Title: if_bridge(4) management script for vnet jails $
-# $Version: 9.1 $
+# $Version: 9.2 $
 #
 ############################################################ INFORMATION
 #
@@ -111,7 +111,7 @@
 #
 ############################################################ GLOBALS
 
-VERSION='$Version: 9.1 $'
+VERSION='$Version: 9.2 $'
 
 pgm="${0##*/}" # Program basename
 
@@ -157,6 +157,25 @@ action_usage()
 	exit $FAILURE
 }
 
+iface_encode()
+{
+	LC_ALL=C iface="$1" awk 'BEGIN {
+		for (n = 0; n < 256; n++)
+			pack[sprintf("%c", n)] = sprintf("_%02x", n)
+		numbers = "0123456789"
+		uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+		lowercase = "abcdefghijklmnopqrstuvwxyz"
+		valid = "[" numbers uppercase lowercase "]"
+		iface = ENVIRON["iface"]
+		len = length(iface)
+		for (n = 1; n <= len; n++) {
+			let = substr(iface, n, 1)
+			_iface = _iface (let ~ valid ? let : pack[let])
+		}
+		print _iface
+	}'
+}
+
 derive_mac()
 {
 	local OPTIND=1 OPTARG __flag
@@ -169,13 +188,16 @@ derive_mac()
 	done
 	shift $(( $OPTIND - 1 ))
 
+	local __iface="$1"
 	if [ ! "$__mac_num" ]; then
-		eval __mac_num=\${_${iface}_num:--1}
+		local __iface_encoded
+		__iface_encoded=$( iface_encode "$__iface" )
+		eval __mac_num=\${_${__iface_encoded}_num:--1}
 		__mac_num=$(( $__mac_num + 1 ))
-		eval _${iface}_num=\$__mac_num
+		eval _${__iface_encoded}_num=\$__mac_num
 	fi
 
-	local __iface="$1" __name="$2" __var_to_set="$3" __var_to_set_b="$4"
+	local __name="$2" __var_to_set="$3" __var_to_set_b="$4"
 	local __iface_devid __new_devid __num __new_devid_b
 	#
 	# Calculate MAC address derived from given iface.
diff --git a/share/examples/jails/jng b/share/examples/jails/jng
index 48dc3eb67967..db7f16334884 100755
--- a/share/examples/jails/jng
+++ b/share/examples/jails/jng
@@ -7,7 +7,7 @@
 ############################################################ IDENT(1)
 #
 # $Title: netgraph(4) management script for vnet jails $
-# $Version: 9.3 $
+# $Version: 9.4 $
 #
 ############################################################ INFORMATION
 #
@@ -138,7 +138,7 @@ NG_BRIDGE_MAX_STALENESS=4294967295
 
 ############################################################ GLOBALS
 
-VERSION='$Version: 9.3 $'
+VERSION='$Version: 9.4 $'
 
 pgm="${0##*/}" # Program basename
 
@@ -214,6 +214,25 @@ action_usage()
 	die
 }
 
+iface_encode()
+{
+	LC_ALL=C iface="$1" awk 'BEGIN {
+		for (n = 0; n < 256; n++)
+			pack[sprintf("%c", n)] = sprintf("_%02x", n)
+		numbers = "0123456789"
+		uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+		lowercase = "abcdefghijklmnopqrstuvwxyz"
+		valid = "[" numbers uppercase lowercase "]"
+		iface = ENVIRON["iface"]
+		len = length(iface)
+		for (n = 1; n <= len; n++) {
+			let = substr(iface, n, 1)
+			_iface = _iface (let ~ valid ? let : pack[let])
+		}
+		print _iface
+	}'
+}
+
 derive_mac()
 {
 	local OPTIND=1 OPTARG __flag
@@ -226,13 +245,16 @@ derive_mac()
 	done
 	shift $(( $OPTIND - 1 ))
 
+	local __iface="$1"
 	if [ ! "$__mac_num" ]; then
-		eval __mac_num=\${_${iface}_num:--1}
+		local __iface_encoded
+		__iface_encoded=$( iface_encode "$__iface" )
+		eval __mac_num=\${_${__iface_encoded}_num:--1}
 		__mac_num=$(( $__mac_num + 1 ))
-		eval _${iface}_num=\$__mac_num
+		eval _${__iface_encoded}_num=\$__mac_num
 	fi
 
-	local __iface="$1" __name="$2" __var_to_set="$3" __var_to_set_b="$4"
+	local __name="$2" __var_to_set="$3" __var_to_set_b="$4"
 	local __iface_devid __new_devid __num __new_devid_b
 	#
 	# Calculate MAC address derived from given iface.
@@ -391,6 +413,16 @@ jng_pin_jiface()
 	jng_bridge_persist_hosts "$pbridge" || : persist optional
 }
 
+ng_ether_sanitize_ifname()
+{
+	# NB: Emulates function of same name in sys/netgraph/ng_ether.c
+	ifname="$1" awk 'BEGIN {
+		_ifname = ENVIRON["ifname"]
+		gsub(/[.:]/, "_", _ifname)
+		print _ifname
+	}'
+}
+
 jng_bridge_usage="bridge [-h] [-b BRIDGE_NAME] NAME [!|=]iface0 [[!|=]iface1 ...]"
 jng_bridge_descr="Create ng0_NAME [ng1_NAME ...]"
 jng_bridge()
@@ -417,7 +449,7 @@ jng_bridge()
 
 	mustberoot_to_continue
 
-	local iface parent jiface jiface_devid
+	local iface node parent jiface jiface_devid
 	local new clone_mac no_derive num quad mtu i=0
 	for iface in $*; do
 
@@ -428,6 +460,10 @@ jng_bridge()
 		!*) iface=${iface#!} no_derive=1 ;;
 		esac
 
+		# ngctl(8) treats `.' and `:' as control characters, so
+		# ng_ether(4) names its node after the sanitized ifname
+		node=$( ng_ether_sanitize_ifname "$iface" )
+
 		# Make sure the interface doesn't exist already
 		jiface=ng${i}_$name
 		if quietly ngctl msg "$jiface:" getifname; then
@@ -439,18 +475,18 @@ jng_bridge()
 		ifconfig $iface up || return
 
 		# Set promiscuous mode and don't overwrite src addr
-		ngctl msg $iface: setpromisc 1 || return
-		ngctl msg $iface: setautosrc 0 || return
+		ngctl msg $node: setpromisc 1 || return
+		ngctl msg $node: setautosrc 0 || return
 
 		# Make sure the interface has been bridged
 		# NB: You must connect uplinkX before linkX
 		# NB: see ng_bridge(4) for policy on first connected hook
-		if ! quietly ngctl info ${iface}bridge:; then
-			ngctl mkpeer $iface: bridge lower uplink1 || return
-			ngctl connect $iface: $iface:lower upper link0 ||
+		if ! quietly ngctl info ${node}bridge:; then
+			ngctl mkpeer $node: bridge lower uplink1 || return
+			ngctl connect $node: $node:lower upper link0 ||
 				return
-			ngctl name $iface:lower ${iface}bridge || return
-			jng_bridge_persist_hosts ${iface}bridge ||
+			ngctl name $node:lower ${node}bridge || return
+			jng_bridge_persist_hosts ${node}bridge ||
 				: persist optional
 		fi
 
@@ -459,33 +495,33 @@ jng_bridge()
 		# Optionally create a secondary bridge
 		# NB: This time, you want to only connect linkX (no uplinkX)
 		if [ "$bridge" != "bridge" ] &&
-		   ! quietly ngctl info "$iface$bridge:"
+		   ! quietly ngctl info "$node$bridge:"
 		then
 			num=1
-			while quietly ngctl msg ${iface}bridge: getstats $num
+			while quietly ngctl msg ${node}bridge: getstats $num
 			do
 				num=$(( $num + 1 ))
 			done
-			ngctl mkpeer $iface:lower bridge link$num link0 ||
+			ngctl mkpeer $node:lower bridge link$num link0 ||
 				return
-			ngctl name ${iface}bridge:link$num "$iface$bridge" ||
+			ngctl name ${node}bridge:link$num "$node$bridge" ||
 				return
 		fi
 
 		# Create a new interface to the bridge
 		num=1
-		while quietly ngctl msg "$iface$bridge:" getstats $num; do
+		while quietly ngctl msg "$node$bridge:" getstats $num; do
 			num=$(( $num + 1 ))
 		done
-		ngctl mkpeer "$iface$bridge:" eiface link$num ether || return
+		ngctl mkpeer "$node$bridge:" eiface link$num ether || return
 
 		# Rename the new interface
 		while [ ${#jiface} -gt 15 ]; do # OS limitation
 			jiface=${jiface%?}
 		done
-		new=$( ngctl show -n "$iface$bridge:link$num" ) || return
+		new=$( ngctl show -n "$node$bridge:link$num" ) || return
 		new=$( set -- $new; echo $2 )
-		ngctl name "$iface$bridge:link$num" $jiface || return
+		ngctl name "$node$bridge:link$num" $jiface || return
 		ifconfig $new name $jiface || return
 		ifconfig $jiface mtu $mtu || return
 		ifconfig $jiface up || return
@@ -515,7 +551,7 @@ jng_pin()
 {
 	local OPTIND=1 OPTARG flag
 	local show_all= err=$SUCCESS
-	local name iface jiface
+	local name iface node jiface
 
 	while getopts ah flag; do
 		case "$flag" in
@@ -528,8 +564,9 @@ jng_pin()
 		[ $# -eq 0 ] ||
 			action_usage pin "too many arguments" # NOTREACHED
 		for iface in $( ifconfig -l ); do
-			quietly ngctl info ${iface}bridge: || continue
-			jng_bridge_persist_hosts ${iface}bridge ||
+			node=$( ng_ether_sanitize_ifname "$iface" )
+			quietly ngctl info ${node}bridge: || continue
+			jng_bridge_persist_hosts ${node}bridge ||
 				: persist optional
 		done
 		set -- $( jls -q name 2> /dev/null )
@@ -663,7 +700,7 @@ jng_stats()
 {
 	local OPTIND=1 OPTARG flag
 	local show_all=
-	local name iface ether=
+	local name iface node ether=
 	while getopts ahj flag; do
 		case "$flag" in
 		a) show_all=1 ;;
@@ -682,7 +719,8 @@ jng_stats()
 
 		# Get a list of bridged ng_ether(4) devices
 		for iface in $( ifconfig -l ); do
-			quietly ngctl info ${iface}bridge: || continue
+			node=$( ng_ether_sanitize_ifname "$iface" )
+			quietly ngctl info ${node}bridge: || continue
 			ether="$ether $iface"
 		done
 		set -- $ether $( "$0" show )
@@ -702,14 +740,15 @@ jng_stats()
 			# NOTREACHED
 		if ifconfig -l | xargs -n1 2> /dev/null | fgrep -qw "$name"
 		then
+			node=$( ng_ether_sanitize_ifname "$name" )
 			[ "$STATS_FMT" != "text" ] ||
-				echo "${name}bridge:uplink1 [lower]"
-			ngctl msg ${name}bridge: getstats -1 |
+				echo "${node}bridge:uplink1 [lower]"
+			ngctl msg ${node}bridge: getstats -1 |
 				fmt_stats -n "${name}.lower" -t "$now"
 
 			[ "$STATS_FMT" != "text" ] ||
-				echo "${name}bridge:link0 [upper]"
-			ngctl msg ${name}bridge: getstats 0 |
+				echo "${node}bridge:link0 [upper]"
+			ngctl msg ${node}bridge: getstats 0 |
 				fmt_stats -n "${name}.upper" -t "$now"
 		fi
 		local jiface