Initial commit

This commit is contained in:
Your Name
2026-04-23 17:07:55 +08:00
commit b7e39e063b
16725 changed files with 1625565 additions and 0 deletions
@@ -0,0 +1,19 @@
SUMMARY = "ACPI Power/Sleep state daemon to allow host state events"
DESCRIPTION = "ACPI Power/Sleep state daemon to allow host state events"
GOOGLE_MISC_PROJ = "acpi-power-state-daemon"
require ../google-misc/google-misc.inc
inherit pkgconfig systemd
DEPENDS += " \
phosphor-dbus-interfaces \
sdbusplus \
systemd \
"
SYSTEMD_SERVICE:${PN} = " \
acpi-power-state.service \
host-s0-state.target \
host-s5-state.target \
"
@@ -0,0 +1,39 @@
SUMMARY = "Glome Config"
DESCRIPTION = "Glome config file provides a glome config file"
PR = "r1"
# This is required to replace the glome/config that is removed in glome_git.bb
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
# Example Privkey: A0F1D0A0CB254839D04637F567325B850B5174850B129E811F5E203A42CC3B6C
GLOME_PUBLIC_KEY ?= "AC11D4582261F2D05CDDE1BD94383393D26C5C269642EE26D7EABD1EADC03C14"
GLOME_KEY_VERSION ?= "4"
GLOME_URL_PREFIX ?= "http://example-glome-service/"
SRC_URI = "file://config.in"
do_install:append() {
if [ -z '${GLOME_PUBLIC_KEY}' ]; then
echo 'Missing GLOME_PUBLIC_KEY' >&2
exit 1
fi
if [ -z '${GLOME_KEY_VERSION}' ]; then
echo 'Missing GLOME_KEY_VERSION' >&2
exit 1
fi
if [ -z '${GLOME_URL_PREFIX}' ]; then
echo 'Missing GLOME_URL_PREFIX' >&2
exit 1
fi
sed ${WORKDIR}/config.in \
-e 's#@PUBLIC_KEY@#${GLOME_PUBLIC_KEY}#' \
-e 's#@KEY_VERSION@#${GLOME_KEY_VERSION}#' \
-e 's#@URL_PREFIX@#${GLOME_URL_PREFIX}#' \
> ${WORKDIR}/config
install -d ${D}${sysconfdir}/glome
install -m 0644 ${WORKDIR}/config ${D}${sysconfdir}/glome
}
@@ -0,0 +1,8 @@
# This is the configuration file for serial console authentication with glome.
# /usr/sbin/glome-login tries to read this file on startup at its canonical
# location /etc/glome/config.
[service]
key = @PUBLIC_KEY@
key-version = @KEY_VERSION@
url-prefix = @URL_PREFIX@
@@ -0,0 +1,59 @@
SUMMARY = "Glome Login Scripts"
DESCRIPTION = "Glome Login Scripts"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
GLOME_FALLBACK_SERV ?= ""
GLOME_FALLBACK_OBJ ?= ""
GLOME_HOSTNAME_SUFFIX ?= ""
GLOME_BOARDSN_KEY ?= "bmc-boardsn"
RDEPENDS:${PN} += "bash"
RDEPENDS:${PN} += "glome"
RDEPENDS:${PN} += "jq"
RDEPENDS:${PN} += "obmc-console"
SRC_URI += "file://glome-login.sh.in"
do_install:append() {
if [ -z '${GLOME_FALLBACK_SERV}' ]; then
echo 'Missing GLOME_FALLBACK_SERV' >&2
exit 1
fi
if [ -z '${GLOME_FALLBACK_OBJ}' ]; then
echo 'Missing GLOME_FALLBACK_OBJ' >&2
exit 1
fi
if [ -z '${GLOME_HOSTNAME_SUFFIX}' ]; then
echo 'Missing GLOME_HOSTNAME_SUFFIX' >&2
exit 1
fi
sed ${WORKDIR}/glome-login.sh.in \
-e 's#@INV_SERV@#${GLOME_FALLBACK_SERV}#' \
-e 's#@INV_OBJ@#${GLOME_FALLBACK_OBJ}#' \
-e 's#@HOSTNAME_SUFFIX@#${GLOME_HOSTNAME_SUFFIX}#' \
-e 's#@BOARDSN_KEY@#${GLOME_BOARDSN_KEY}#' \
> ${WORKDIR}/glome-login.sh
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/glome-login.sh ${D}${bindir}
}
# This is an example to override the glome login service in the bbappend for 'prod'
#
#FILES:${PN}:append:prod = " \
# ${systemd_system_unitdir}/serial-to-bmc@.service.d/bmc-login-glome-override.conf \
# ${systemd_system_unitdir}/serial-getty@.service.d/bmc-login-glome-override.conf \
# "
#
#do_install:append:prod() {
# install -D -m 0644 ${WORKDIR}/bmc-login-glome-override.conf \
# ${D}${systemd_system_unitdir}/serial-to-bmc@.service.d/bmc-login-glome-override.conf
# install -D -m 0644 ${WORKDIR}/bmc-login-glome-override.conf \
# ${D}${systemd_system_unitdir}/serial-getty@.service.d/bmc-login-glome-override.conf
#}
@@ -0,0 +1,42 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -eo pipefail
HOSTNAME="$(hostname)"
USER="${1?Missing first param: USER (Usually passed by agetty via \\u)}"
if [[ "$HOSTNAME" =~ ^([^-.]+)[^.]*(.*[.]corp[.]google[.]com)$ ]]; then
# for google corp address the suffix must be removed from the name
HOSTNAME="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
fi
if [[ "${HOSTNAME}" == *"@HOSTNAME_SUFFIX@" ]]; then
# Valid hostname is already set, invoke normal glome
exec /usr/sbin/glome-login -M "${HOSTNAME}" "${USER}"
fi
# Get the board serial number from the FRU EEPROM
# Service passed in as a parameter would be either inventory-manager or
# entity-manager depending on platforms
# Path to the FRU EEPROM object has to be passed in as a parameter
# If the target platform has neither of them, the fallback mechanism is useless
INT="xyz.openbmc_project.Inventory.Decorator.Asset"
PART="SerialNumber"
BOARDSN="$(busctl get-property -j "@INV_SERV@" "@INV_OBJ@" "${INT}" "${PART}" | jq -r '.data')"
WARN_MSG="WARNING: Hostname is not set, using Board Serial Number"
echo "${WARN_MSG}"
echo "${WARN_MSG}" | systemd-cat -t gbmc-glome -p warning
exec /usr/sbin/glome-login -M "@BOARDSN_KEY@:${BOARDSN}" "${USER}"
@@ -0,0 +1,29 @@
SUMMARY = "GLOME Login Client"
DESCRIPTION = "GLOME login is first application of the GLOME protocol. It is used to authorize serial console access to Linux machines"
PR = "r1"
PV = "0.1+git${SRCPV}"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"
inherit meson pkgconfig
DEPENDS += " \
openssl \
glome-config \
"
S = "${WORKDIR}/git"
SRC_URI = "git://github.com/google/glome.git;branch=master;protocol=https"
SRCREV = "978ad9fb165f1e382c875f2ce08a1fc4f2ddcf1b"
PACKAGECONFIG ??= ""
PACKAGECONFIG[glome-cli] = "-Dglome-cli=true,-Dglome-cli=false"
PACKAGECONFIG[pam-glome] = "-Dpam-glome=true,-Dpam-glome=false,libpam"
EXTRA_OEMESON = "-Dtests=false"
# remove the default glome config so it can be overridden by `glome-config`
do_install:append() {
rm -f ${D}${sysconfdir}/glome/config
}
@@ -0,0 +1,14 @@
SUMMARY = "Add dev default CA"
DESCRIPTION = "Add dev default CA"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
SRC_URI += "file://certs/authority/"
FILES:${PN} = "${sysconfdir}/ssl/certs/authority/*"
do_install(){
install -d ${D}${sysconfdir}/ssl/certs/authority
install -m 0644 -D ${WORKDIR}/certs/authority/* \
${D}${sysconfdir}/ssl/certs/authority
}
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDdDCCAlygAwIBAgIBBjANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJUVzEP
MA0GA1UEChMGUXVhbnRhMQswCQYDVQQLEwJDQTEQMA4GA1UEAxMHT3BlbkJNQzAe
Fw03MDAxMDEwMDAwMDBaFw0zNTEyMzExMjAwMDBaMD0xCzAJBgNVBAYTAlRXMQ8w
DQYDVQQKEwZRdWFudGExCzAJBgNVBAsTAkNBMRAwDgYDVQQDEwdPcGVuQk1DMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3IEc5ydpW3grWW2axOYXMgF/
Wwj8dXtdbpUwgI44RJS/++szEI07OGKXgn4JakWPCSTzMYrq2IteYx7f+hrqqoc3
QZd3tejIGfddGuv8vaAqKlSxaViTjdarRNfa6ARwJao6vQ6XhNy4Phn6mzCSAfGo
m1t+JZ3WxMqlsK+NueU30QqKvnYNFmJ7SbwA0htOn8o9WZj5QBTtXP9Clh6aiWyv
C6nNYlvPfiMLauc3DwcUEcgzI0slWhxSQJVjLmRrR1GT4A8LdVZfgLrUaZR3hlBm
zgtrLJ9BwrdNv4+Q0J/0DEETx4h3SUem5+rZSGaXUvCt+H/VM5sLCRa5+C/oewID
AQABo38wfTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTpEhTEnCIZo7dCDFtq
UjMRcOI9SDAfBgNVHSMEGDAWgBTpEhTEnCIZo7dCDFtqUjMRcOI9SDALBgNVHQ8E
BAMCAb4wHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEB
CwUAA4IBAQAT+DvCSEZq+nHmvFP05zFS4IN6DikccDZFyG/O06htFLo3NFYxugGI
OSDXyR3QzhVsDApIwFsNfYzK7asQMqIBdCTHLQNz8YtW4+lwr/WOijgNMK6H0MqI
sisuESpHJgg6XcvfqPkFUBT+sV9ae55QpbvPLalVDZ+JQEyxwfJYnO0AKoepWv/3
H5cXTIpgoxD225MeLOu1F5yg3M3DQd2vPmtykgixMDR+dShxR0AFIwfy94XWTZ7K
JSp5JysdLcekJ5g2c53CYQCqKIYPhKd/VCt/pERjyAcxHnm/PnmTFRA7ft2aAY82
QzkGV2F6gz+GGWjG8cVp3h0T/FF72UFe
-----END CERTIFICATE-----
@@ -0,0 +1,23 @@
SUMMARY = "Add default Users"
DESCRIPTION = "Add Users"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
EXCLUDE_FROM_WORLD = "1"
DEPENDS = "bmcweb"
DEPENDS += "phosphor-ipmi-host"
DEPENDS += "phosphor-user-manager"
RDEPENDS:${PN} = "bmcweb"
RDEPENDS:${PN} += "phosphor-ipmi-host"
RDEPENDS:${PN} += "phosphor-user-manager"
inherit useradd
USERADD_PACKAGES = "${PN}"
USERADD_PARAM:${PN} = "-m -N -u 1000 -g 100 -s /bin/nologin \
-G 'web,redfish,priv-admin' Megapede; "
GROUPMEMS_PARAM:${PN} = "-g priv-admin -a root; "
GROUPMEMS_PARAM:${PN} += "-g ipmi -a root; "
ALLOW_EMPTY:${PN} = "1"
@@ -0,0 +1,26 @@
SUMMARY = "Google BIOS Public Keys"
DESCRIPTION = "Google BIOS Public Keys"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
FILESEXTRAPATHS:prepend:gbmc := "${THISDIR}/${PN}:"
SRC_URI:append:gbmc = " \
file://platforms_secure.pem \
file://platforms_bringup.pem \
"
FILES:${PN} += "${datadir}/google-bios-key/platforms_secure.pem"
FILES:${PN} += "${datadir}/google-bios-key/platforms_bringup.pem"
FILES:${PN} += "${datadir}/platforms_secure.pem"
FILES:${PN} += "${datadir}/platforms_bringup.pem"
do_install() {
install -d ${D}${datadir}/google-bios-key
install -m 0644 ${WORKDIR}/platforms_secure.pem ${D}${datadir}/google-bios-key
install -m 0644 ${WORKDIR}/platforms_bringup.pem ${D}${datadir}/google-bios-key
ln -s -r ${D}${datadir}/google-bios-key/platforms_secure.pem ${D}${datadir}/platforms_secure.pem
ln -s -r ${D}${datadir}/google-bios-key/platforms_bringup.pem ${D}${datadir}/platforms_bringup.pem
}
@@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq1licj5L+p5orm2eQsjW
7z1L5As2q0ZbyFtE/xeH/8NAbKfTA1LSKiqElcPTM47oFDixGtCm7bubcClWT8Uq
Ol6t4CITlgRLFPP06z63mnE83EKj1ZBTATgtO+PN1P/RrQXp0pCPy0AFYtWZPT4J
GzxACBwVI/XEuUnCOG4ErXLfgfPZadOMUmP9X5pXeyBgN/al0vThIVpQ7DUix6jd
awYd2AwM1TG9qRX1Fmlsut9zUGDoqEOCAdIgx5z9GOTfwzbIg1NWk12iLQyufJ4P
FAa4a5QVZYTKcDUNYadx7Qwg/gNspiAIdtB/1ORz1ew1d1csCHqUZcgkCwtaqMYg
ZiQ6+7tpJY0vnWyaNXcylOvmZjOlovV3i5NUJ/r74bDC8U+5XKH3ZSmhA6AsnA54
QnNHLgsnG89JvfBP2c9UJqb49sw6VkEE3Y8lc134QBaLU3N3LmRVmM4zzPRGbbEK
+nmZtBztbKiSHTqMoYGCY5aN1peQdzgNYk5P8mnVWzAIh5y1MzxkEVeu82/zCj+/
T54D3jS+a/jxAGnUUmTMYsOYXNqJsPKz36qvrFLstSjrjHVEZjniaetjAuWtVGsO
DziPDyywN14iIeR5HdNpqeJ6mCZnY2rLtBzIZRK/JK0LpCvqdEmqg2u/J2Vx2ClE
DnNnPSr8XSpsFkaxGYyFjEUCAwEAAQ==
-----END PUBLIC KEY-----
@@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzCYi0R3IiNenOlVNlpOC
mIZ/0nq6OGS5Dt1Yex5nr7/bIr+Phk7mct+V7gMJzfdyCOI3n2S2sGDWADUNfvDJ
LQgV4xOfoUwhzQ292cp2UFUrPblfZaz0C5x7BRVtj7yfBp8FRFVSpZTNI6QjEQ6m
JxVh5C8CYjdQQJLz+wayVZTk72z0iaSBdua+eZYrAc3+ckTvTWJ9V7yO40iyJOXQ
ixxHo/p49oHoNp4+q0a6DYYmjka6MESeFbbkND5PkyNV4+vAbgtzkoGnnutk+zQ3
K374d9+aOqXlbLBXAUaNkfnF1XjUYQFLrNkFbvOqYura1mBxWuz/OIAfbWxQltzo
6cYl0YNw7zbHzSwiTPvt31wxg9EqqIV7QD4hXLAgoclJIK5vhUqIbn3kjSwOv2Rv
MQ17wLeCkYaxL4Ovscx6lDnOw+Y76KGia5zky0GvidjaDgzUZTt46/uOtjxxXy5k
KhDwqs1QoU0LKBvJL8O7bYj6t0LLQTsI2UW9szVGGsiKB3l0SfIzfvz6eNNng7Uf
T9zLPW9jq0BGG61KiYK+Dy/0JwauPub3WzcZwSXnl1OCMLD90HIA2/YsFtfE+mm/
Wlta+cR3REHd//DiRlkT59K0H5H8JBrJKhkAVi/gzqoAYhLAOj9irWzsZ2Assm74
oa3/lWlmVsKQTAGOCQFeAHsCAwEAAQ==
-----END PUBLIC KEY-----
@@ -0,0 +1,14 @@
HOMEPAGE = "http://github.com/openbmc/google-misc"
PR = "r1"
PV = "1.0+git${SRCPV}"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://../../LICENSE;md5=34400b68072d710fecd0a2940a0d1658"
SRC_URI += "git://github.com/openbmc/google-misc;branch=master;protocol=https"
SRCREV = "f8bb4779279d8f4e054d9601fc508598b2ba3007"
S = "${WORKDIR}/git/subprojects/${GOOGLE_MISC_PROJ}"
inherit meson pkgconfig
EXTRA_OEMESON += "-Dwerror=false"
@@ -0,0 +1,249 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This is intended to be used as a library for managing gpio line values.
# Executing this directly will do nothing.
[ -n "${gpio_ctrl_init-}" ] && return
# Map names of GPIOs to GPIO number
# This maps a schematic name to a gpiochip and line at a specific offset
declare -A GPIO_NAMES_TO_SCM=(
# Examples
#['SYS_RESET_N']='label=/pinctrl@f0800000/gpio@f0012000 21'
#['PWRBTN_N']='label=/pinctrl@f0800000/gpio@f0012000 23'
#['PCH_SLP_S5_R_N']='of_node=/ahb/apb/i2c@8e000/cpu_seq@6b 9'
#['PCH_PWRGOOD_R']='of_node=/ahb/apb/i2c@8e000/cpu_seq@6b 6'
)
# Load configurations from a known location in the filesystem to populate
# named GPIOs
shopt -s nullglob
for conf in /usr/share/gpio-ctrl/conf.d/*.sh; do
# shellcheck source=/dev/null
source "$conf"
done
declare -A gpio_sysfs_lookup_cache=()
declare -A gpio_lookup_cache=()
declare -A gpio_init=()
##################################################
# Looks up the sysfs GPIO number
# Arguments:
# $1: GPIO name
# Return:
# 0 if success, non-zero if error
# stdout: The GPIO number
##################################################
gpio_name_to_num() {
local name="$1"
if [ -n "${gpio_lookup_cache["$name"]+1}" ]; then
echo "${gpio_lookup_cache["$name"]}"
return 0
fi
local scm="${GPIO_NAMES_TO_SCM["$name"]-}"
if [ -z "$scm" ]; then
echo "Missing gpio definition: $name" >&2
return 1
fi
local id="${scm% *}"
local type="${id%=*}"
local val="${id#*=}"
local offset="${scm#* }"
local sysfs
if [ -n "${gpio_sysfs_lookup_cache["$id"]+1}" ]; then
sysfs="${gpio_sysfs_lookup_cache["$id"]}"
else
case "$type" in
label)
if ! sysfs="$(grep -xl "$val" /sys/class/gpio/gpiochip*/label)"; then
echo "Failed to find gpiochip: $val" >&2
return 1
fi
sysfs="${sysfs%/label}"
;;
of_node)
for sysfs in $(echo /sys/class/gpio/gpiochip*); do
local link
# Ignore errors because not all devices have of_nodes
link="$(readlink -f "$sysfs/device/of_node" 2>/dev/null)" || continue
[ "${link#/sys/firmware/devicetree/base}" = "$val" ] && break
sysfs=
done
if [ -z "$sysfs" ]; then
echo "Failed to find gpiochip: $val" >&2
return 1
fi
;;
*)
echo "Invalid GPIO type $type" >&2
return 1
;;
esac
gpio_sysfs_lookup_cache["$id"]="$sysfs"
fi
local ngpio
ngpio=$(<"$sysfs"/ngpio)
if (( ngpio <= offset )); then
echo "$name with gpiochip $sysfs only has $ngpio but wants $offset" >&2
return 1
fi
gpio_lookup_cache["$name"]=$(( $(<"$sysfs"/base) + offset ))
echo "${gpio_lookup_cache["$name"]}"
}
##################################################
# Populates the GPIO lookup cache
# Most calls to gpio_name_to_num that would
# normally cache the sysfs lookups for gpios run
# inside subshells. This prevents them from
# populating a global cache and greatly speeding
# up future lookups. This call allows scripts to
# populate the cache prior to looking up gpios.
##################################################
gpio_build_cache() {
local timeout="${1-0}"
shift
local gpios=("$@")
if (( ${#gpios[@]} == 0 )); then
gpios=("${!GPIO_NAMES_TO_SCM[@]}")
fi
local deadline=$(( timeout + SECONDS ))
local name
for name in "${gpios[@]}"; do
while true; do
gpio_name_to_num "$name" >/dev/null && break
if (( deadline <= SECONDS )); then
echo "Timed out building gpio cache" >&2
return 1
fi
done
done
}
##################################################
# Initializes the gpio state
# This operation is idempotent and can be applied
# repeatedly to the same gpio. It will make sure the
# gpio ends up in the initialized state even if it
# was.
# Arguments:
# $1: GPIO name
# Return:
# 0 if success, non-zero if error
##################################################
gpio_init_() {
local name="$1"
local num="$2"
if [ -n "${gpio_init["$name"]+1}" ]; then
return 0
fi
if [ ! -e "/sys/class/gpio/gpio$num" ]; then
if ! echo "$num" >'/sys/class/gpio/export'; then
echo "Failed to export $name gpio$num" >&2
return 1
fi
fi
local active_low=0
if [[ "${name%_N}" != "$name" ]]; then
active_low=1
fi
if ! echo "$active_low" >"/sys/class/gpio/gpio$num/active_low"; then
echo "Failed to set active_low for $name gpio$num" >&2
return 1
fi
gpio_init["$name"]=1
}
gpio_init() {
local name="$1"
# Ensure the cache is updated by not running in a subshell
gpio_name_to_num "$name" >/dev/null || return
gpio_init_ "$name" "$(gpio_name_to_num "$name")"
}
##################################################
# Sets the output GPIO value.
# Arguments:
# $1: GPIO name
# $2: GPIO value, "1" or "0"
# Return:
# 0 if success, non-zero if error
##################################################
gpio_set_value_() {
local name="$1"
local num="$2"
local val="$3"
gpio_init_ "$name" "$num" || return
if ! echo out >"/sys/class/gpio/gpio$num/direction"; then
echo "Failed to set output for $name gpio$num" >&2
return 1
fi
if ! echo "$val" >"/sys/class/gpio/gpio$num/value"; then
echo "Failed to set $name gpio$num = $val" >&2
return 1
fi
}
gpio_set_value() {
local name="$1"
local val="$2"
# Ensure the cache is updated by not running in a subshell
gpio_name_to_num "$name" >/dev/null || return
gpio_set_value_ "$name" "$(gpio_name_to_num "$name")" "$val"
}
##################################################
# Get GPIO value
# Arguments:
# $1: GPIO name
# Return:
# 0 if success, non-zero if error
# stdout: The value of the gpio
##################################################
gpio_get_value_() {
local name="$1"
local num="$2"
gpio_init_ "$name" "$num" || return
if ! cat "/sys/class/gpio/gpio$num/value"; then
echo "Failed to get $name gpio$num value" >&2
return 1
fi
}
gpio_get_value() {
local name="$1"
# Ensure the cache is updated by not running in a subshell
gpio_name_to_num "$name" >/dev/null || return
gpio_get_value_ "$name" "$(gpio_name_to_num "$name")"
}
gpio_ctrl_init=1
@@ -0,0 +1,14 @@
SUMMARY = "GPIO control library for bash scripts"
DESCRIPTION = "GPIO control library for bash scripts."
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
PR = "r1"
SRC_URI += "file://lib.sh"
RDEPENDS:${PN} = "bash"
do_install() {
install -d ${D}${datadir}/gpio-ctrl
install -m 0755 ${WORKDIR}/lib.sh ${D}${datadir}/gpio-ctrl/
}
@@ -0,0 +1,11 @@
[Unit]
Description=Ensure host is off on clean AC cycle
Before=host-poweron.service
Before=firmware-updates-pre.target
[Service]
Type=oneshot
ExecStart=/usr/bin/host_ensure_off.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,7 @@
[Unit]
Description=Powercycle host
[Service]
Type=oneshot
Environment=DONT_STOP_WATCHDOG=1
ExecStart=/usr/bin/host_powercycle.sh
@@ -0,0 +1,6 @@
[Unit]
Description=Powercycle host
[Service]
Type=oneshot
ExecStart=/usr/bin/host_powercycle.sh
@@ -0,0 +1,7 @@
[Unit]
Description=Power off host
[Service]
Type=oneshot
Environment=DONT_STOP_WATCHDOG=1
ExecStart=/usr/bin/host_poweroff.sh
@@ -0,0 +1,6 @@
[Unit]
Description=Power off host
[Service]
Type=oneshot
ExecStart=/usr/bin/host_poweroff.sh
@@ -0,0 +1,10 @@
[Unit]
Description=Power on host
After=firmware-updates.target
[Service]
Type=oneshot
ExecStart=/usr/bin/host_poweron.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,7 @@
[Unit]
Description=Cold Reset Host
[Service]
Type=oneshot
Environment=DONT_STOP_WATCHDOG=1
ExecStart=/usr/bin/host_reset.sh cold
@@ -0,0 +1,6 @@
[Unit]
Description=Cold Reset Host
[Service]
Type=oneshot
ExecStart=/usr/bin/host_reset.sh cold
@@ -0,0 +1,7 @@
[Unit]
Description=Warm Reset Host
[Service]
Type=oneshot
Environment=DONT_STOP_WATCHDOG=1
ExecStart=/usr/bin/host_reset.sh warm
@@ -0,0 +1,6 @@
[Unit]
Description=Warm Reset Host
[Service]
Type=oneshot
ExecStart=/usr/bin/host_reset.sh warm
@@ -0,0 +1,22 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if ! was_ac_reset.sh; then
echo "Not a tray reset, ignoring" >&2
exit 0
fi
echo "Powering off the host" >&2
host_poweroff.sh
@@ -0,0 +1,22 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/gpio/gpio-ctrl/lib.sh
source /usr/share/gpio-host-pwr/lib.sh || exit
gpio_build_cache 10 "$HOST_GPIO_PGOOD" || exit 255
pgood="$(gpio_get_value "$HOST_GPIO_PGOOD")" || exit 255
echo "HOST_PGOOD=$pgood" >&2
(( pgood == 0 ))
@@ -0,0 +1,25 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/gpio/gpio-ctrl/lib.sh
source /usr/share/gpio-host-pwr/lib.sh || exit
gpio_build_cache 10 "$HOST_GPIO_PGOOD" "$HOST_GPIO_PWR_BTN" || exit
rc=0
host_poweroff.sh || rc=$?
# We want to ensure we aren't too quickly bouncing the power button
sleep 0.5
host_poweron.sh || rc=$?
exit $rc
@@ -0,0 +1,66 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/gpio/gpio-ctrl/lib.sh
source /usr/share/gpio-host-pwr/lib.sh || exit
gpio_build_cache 10 "$HOST_GPIO_PGOOD" "$HOST_GPIO_PWR_BTN" || exit
gpio_init "$HOST_GPIO_PGOOD" || exit
# Set the power status LED
if [ -n "$HOST_LED_PWR" ]; then
echo 'none' > /sys/class/leds/"$HOST_LED_PWR"/trigger || true
echo '0' > /sys/class/leds/"$HOST_LED_PWR"/brightness || true
fi
# Ensure the watchdog is no longer going to run
host_pwr_stop_watchdog || true
# We don't want to do anything if the machine is already off
pgood="$(gpio_get_value "$HOST_GPIO_PGOOD")" || exit
if (( pgood == 0 )); then
echo 'Host is already off, doing nothing' >&2
exit 0
fi
# Do a long push of the button if PGOOD
echo 'Stopping host power' >&2
rc=0
gpio_set_value "$HOST_GPIO_PWR_BTN" 1 || rc=$?
# Loop until we realize that host power is off
# Limit the loop count to 10 seconds as we should never
# have to wait this long for poweroff
s=$SECONDS
while true; do
if ! pgood="$(gpio_get_value "$HOST_GPIO_PGOOD")"; then
rc=1
break
fi
if (( pgood == 0 )); then
echo 'Host is now off' >&2
break
fi
if (( SECONDS - s > 10 )); then
echo 'Poweroff timed out, terminating' >&2
rc=2
break
fi
sleep 0.1
done
gpio_set_value "$HOST_GPIO_PWR_BTN" 0 || rc=$?
exit $rc
@@ -0,0 +1,66 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/gpio/gpio-ctrl/lib.sh
source /usr/share/gpio-host-pwr/lib.sh || exit
gpio_build_cache 10 "$HOST_GPIO_PGOOD" "$HOST_GPIO_PWR_BTN" || exit
gpio_init "$HOST_GPIO_PGOOD" || exit
# Set the power status LED
if [ -n "$HOST_LED_PWR" ]; then
echo 'none' > /sys/class/leds/"$HOST_LED_PWR"/trigger || true
echo '255' > /sys/class/leds/"$HOST_LED_PWR"/brightness || true
fi
# Ensure the watchdog is available before the host starts
host_pwr_start_watchdog || true
# We don't want to do anything if the machine is already on
pgood="$(gpio_get_value "$HOST_GPIO_PGOOD")" || exit
if (( pgood == 1 )); then
echo 'Host is already running, doing nothing' >&2
exit 0
fi
# Do a quick push of the button if PGOOD
echo "Starting host power" >&2
rc=0
gpio_set_value "$HOST_GPIO_PWR_BTN" 1 || rc=$?
sleep 0.1
gpio_set_value "$HOST_GPIO_PWR_BTN" 0 || rc=$?
# Loop until we realize that host power is on
# Limit the loop count to 10 seconds as we should never
# have to wait this long for poweroff
s=$SECONDS
while true; do
if ! pgood="$(gpio_get_value "$HOST_GPIO_PGOOD")"; then
rc=1
break
fi
if (( pgood == 1 )); then
echo 'Host is now on' >&2
break
fi
if (( SECONDS - s > 10 )); then
echo 'Poweron timed out, terminating' >&2
rc=2
break
fi
sleep 0.1
done
exit $rc
@@ -0,0 +1,40 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/gpio/gpio-ctrl/lib.sh
source /usr/share/gpio-host-pwr/lib.sh || exit
if [[ "${1-}" == "warm" ]]; then
rst_txt='WARM' >&2
rst_gpio="$HOST_GPIO_WARM_RESET"
else
rst_txt='COLD' >&2
rst_gpio="$HOST_GPIO_COLD_RESET"
fi
gpio_build_cache 10 "$rst_gpio" || exit
# Do a quick push of the button if PGOOD
echo "Issuing $rst_txt reset" >&2
rc=0
gpio_set_value "$rst_gpio" 1 || rc=$?
sleep 0.1
gpio_set_value "$rst_gpio" 0 || rc=$?
# Make sure the watchdog is stopped while the host is in reset
# and can't possibly restart it.
host_pwr_stop_watchdog || true
exit $rc
@@ -0,0 +1,73 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This is intended to be used as a library for managing gpio line values.
# Executing this directly will do nothing.
[ -n "${host_pwr_init-}" ] && return
# shellcheck source=meta-google/recipes-google/gpio/gpio-ctrl/lib.sh
source /usr/share/gpio-ctrl/lib.sh || exit
# Read by the tooling to determine if the machine is powered on or off
# shellcheck disable=SC2034
HOST_GPIO_PGOOD='unset'
# Set according to whether the host is powered on or off
# shellcheck disable=SC2034
HOST_LED_PWR=''
# Written by the tooling to assert the power button
# shellcheck disable=SC2034
HOST_GPIO_PWR_BTN='unset'
# Written by the tooling to assert a cold reset
# shellcheck disable=SC2034
HOST_GPIO_COLD_RESET='unset'
# Written by the tooling to assert a warm reset
# shellcheck disable=SC2034
HOST_GPIO_WARM_RESET='unset'
# Load configurations from a known location in the filesystem to populate
# named GPIOs
shopt -s nullglob
for conf in /usr/share/gpio-host-pwr/conf.d/*.sh; do
# shellcheck source=/dev/null
source "$conf"
done
##################################################
# Stop the host watchdog
# Return:
# 0 if success, non-zero if error
##################################################
host_pwr_stop_watchdog() {
# Check to see if we can stop the watchdog safely
# We don't want to stop if we are called from the watchdog itself
if [ -n "${DONT_STOP_WATCHDOG-}" ]; then
return 0
fi
echo 'Stopping the host watchdog' >&2
systemctl stop phosphor-watchdog@host0
}
##################################################
# Start the host watchdog
# Return:
# 0 if success, non-zero if error
##################################################
host_pwr_start_watchdog() {
echo 'Starting the host watchdog' >&2
systemctl start phosphor-watchdog@host0
}
host_pwr_init=1
@@ -0,0 +1,57 @@
SUMMARY = "GPIO based powercontrol for a host system"
DESCRIPTION = "GPIO based powercontrol for a host system."
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
inherit systemd
RDEPENDS:${PN} += " \
bash \
gpio-ctrl \
"
SRC_URI += " \
file://host-ensure-off.service \
file://host-powercycle-watchdog.service \
file://host-powercycle.service \
file://host-poweroff-watchdog.service \
file://host-poweroff.service \
file://host-poweron.service \
file://host-reset-cold-watchdog.service \
file://host-reset-cold.service \
file://host-reset-warm-watchdog.service \
file://host-reset-warm.service \
file://host_ensure_off.sh \
file://host_isoff.sh \
file://host_powercycle.sh \
file://host_poweroff.sh \
file://host_poweron.sh \
file://host_reset.sh \
file://lib.sh \
"
SYSTEMD_PACKAGES = "${PN}"
SYSTEMD_SERVICE:${PN} += " \
host-reset-cold.service \
host-reset-cold-watchdog.service \
host-reset-warm.service \
host-reset-warm-watchdog.service \
host-ensure-off.service \
host-powercycle.service \
host-powercycle-watchdog.service \
host-poweroff.service \
host-poweroff-watchdog.service \
host-poweron.service \
"
do_install() {
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/host_*.sh ${D}${bindir}/
install -d ${D}${datadir}/gpio-host-pwr
install -m 0755 ${WORKDIR}/lib.sh ${D}${datadir}/gpio-host-pwr/
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/*.service ${D}${systemd_system_unitdir}/
}
@@ -0,0 +1,33 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##################################################
# Stop the host watchdog
# Return:
# 0 if success, non-zero if error
##################################################
stop_host_watchdog() {
if (( $# != 0 )); then
echo 'Usage: stop_host_watchdog' >&2
return 1
fi
local srv='xyz.openbmc_project.Watchdog'
local obj='/xyz/openbmc_project/watchdog/host0'
local intf='xyz.openbmc_project.State.Watchdog'
local method='Enabled'
local args=('b' 'false')
busctl set-property "${srv}" "${obj}" "${intf}" "${method}" "${args[@]}"
}
@@ -0,0 +1,16 @@
SUMMARY = "Watchdog Shell Library"
DESCRIPTION = "Watchdog Shell Library"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
SRC_URI += " \
file://libwatchdog.sh \
"
RDEPENDS:${PN} = "bash"
do_install() {
install -d ${D}${libexecdir}
install -m 0755 ${WORKDIR}/libwatchdog.sh ${D}${libexecdir}/
}
@@ -0,0 +1,21 @@
SUMMARY = "Google i2c OEM commands"
DESCRIPTION = "Google i2c OEM commands"
HOMEPAGE = "https://github.com/openbmc/google-ipmi-i2c"
PR = "r1"
PV = "0.1+git${SRCPV}"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327"
inherit meson pkgconfig
DEPENDS += "phosphor-ipmi-host"
S = "${WORKDIR}/git"
SRC_URI = "git://github.com/openbmc/google-ipmi-i2c;branch=master;protocol=https"
SRCREV = "6c84db51ea55f247e1f8054b62fff3b66b8017b7"
FILES:${PN}:append = " ${libdir}/ipmid-providers"
FILES:${PN}:append = " ${libdir}/host-ipmid"
FILES:${PN}:append = " ${libdir}/net-ipmid"
HOSTIPMI_PROVIDER_LIBRARY += "libi2ccmds.so"
@@ -0,0 +1,38 @@
SUMMARY = "Google Sys OEM commands"
DESCRIPTION = "Google Sys OEM commands"
HOMEPAGE = "https://github.com/openbmc/google-ipmi-sys"
PR = "r1"
PV = "0.1+git${SRCPV}"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327"
inherit meson pkgconfig systemd
DEPENDS += " \
nlohmann-json \
phosphor-dbus-interfaces \
phosphor-logging \
phosphor-ipmi-host \
sdbusplus \
systemd \
"
S = "${WORKDIR}/git"
SRC_URI = "git://github.com/openbmc/google-ipmi-sys;branch=master;protocol=https"
SRCREV = "15d4d21c091cd57a0e25888a8d360ba3a32965ff"
FILES:${PN} += "${libdir}/ipmid-providers"
SYSTEMD_PACKAGES = "${PN}"
SYSTEMD_SERVICE:${PN} += " \
gbmc-bare-metal-active.target \
gbmc-host-poweroff.target \
gbmc-psu-hardreset.target \
gbmc-psu-hardreset-pre.target \
gbmc-psu-hardreset-time.service \
"
EXTRA_OEMESON += "-Dtests=disabled"
CXXFLAGS:append:gbmc = '${@"" if not d.getVar("GBMC_NCSI_IF_NAME") else \
" -DNCSI_IPMI_CHANNEL=1 -DNCSI_IF_NAME=" + d.getVar("GBMC_NCSI_IF_NAME")}'
@@ -0,0 +1,16 @@
SUMMARY = "Shell functions for manipulating IPMI formatted EEPROMs"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI += "file://lib.sh"
S = "${WORKDIR}"
DATA = "${datadir}/ipmi-fru"
FILES:${PN} += "${DATA}"
do_install:append() {
install -d -m0755 ${D}${DATA}
install -m0644 lib.sh ${D}${DATA}/
}
@@ -0,0 +1,247 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${ipmi_fru_init-}" ] && return
# shellcheck disable=SC2034
IPMI_FRU_COMMON_HEADER_INTERNAL_OFFSET_IDX=1
# shellcheck disable=SC2034
IPMI_FRU_COMMON_HEADER_CHASSIS_OFFSET_IDX=2
# shellcheck disable=SC2034
IPMI_FRU_COMMON_HEADER_BOARD_OFFSET_IDX=3
# shellcheck disable=SC2034
IPMI_FRU_COMMON_HEADER_PRODUCT_OFFSET_IDX=4
# shellcheck disable=SC2034
IPMI_FRU_COMMON_HEADER_MULTI_RECORD_OFFSET_IDX=5
# shellcheck disable=SC2034
IPMI_FRU_AREA_HEADER_SIZE_IDX=1
# shellcheck disable=SC2034
IPMI_FRU_CHECKSUM_IDX=-1
offset_1bw() {
local addr="$1"
local off="$2"
local extra="${3-0}"
echo "w$((1+extra))@$addr $(( off & 0xff ))"
}
offset_2bw() {
local addr="$1"
local off="$2"
local extra="${3-0}"
echo "w$((2+extra))@$addr $(( (off >> 8) & 0xff )) $(( off & 0xff ))"
}
of_name_to_eeproms() {
local names
if ! names="$(grep -xl "$1" /sys/bus/i2c/devices/*/of_node/name)"; then
echo "Failed to find eeproms with of_name '$1'" >&2
return 1
fi
echo "${names//of_node\/name/eeprom}"
}
of_name_to_eeprom() {
local eeproms
eeproms="$(of_name_to_eeproms "$1")" || return
if (( "$(echo "$eeproms" | wc -l)" != 1 )); then
echo "Got more than one eeprom for '$1': $eeproms" >&2
return 1
fi
echo "$eeproms"
}
# Each element is a `filename`
declare -A IPMI_FRU_EEPROM_FILE=()
# Each element is a `bus` + `addr` + `offset_bytes_func`
declare -A IPMI_FRU_EEPROM_BUSADDR=()
ipmi_fru_device_alloc() {
local fdn="$1"
local idx="$2"
local json
json="$(busctl -j call xyz.openbmc_project.FruDevice \
/xyz/openbmc_project/FruDevice/"$fdn" org.freedesktop.DBus.Properties \
GetAll s xyz.openbmc_project.FruDevice)" || return 80
local jqq='.data[0] | (.BUS.data | tostring) + " " + (.ADDRESS.data | tostring)'
local busaddr
# shellcheck disable=SC2207
busaddr=($(echo "$json" | jq -r "$jqq")) || return
# FRU 0 is hardcoded and FruDevice does not report the correct bus for it
# Hardcode a workaround for this specifically known bus
if (( busaddr[0] == 0 && busaddr[1] == 0 )); then
IPMI_FRU_EEPROM_FILE["$idx"]=/etc/fru/baseboard.fru.bin
else
local offset_bw=offset_2bw
local last=0
local rsp=0x100
local start=$SECONDS
# Query the FRU multiple times to ensure the return value is stabilized
while (( last != rsp )); do
# It shouldn't take > 0.1s to stabilize, limit instability
if (( SECONDS - start >= 10 )); then
echo "Timed out determining offset for ${busaddr[0]}@${busaddr[1]}" >&2
return 1
fi
last=$rsp
# shellcheck disable=SC2046
rsp=$(i2ctransfer -f -y "${busaddr[0]}" $(offset_1bw "${busaddr[1]}" 0) r1) || return
done
# FRUs never start with 0xff bytes, so we can figure out addressing mode
if (( rsp != 0xff )); then
offset_bw=offset_1bw
fi
IPMI_FRU_EEPROM_BUSADDR["$idx"]="${busaddr[*]} $offset_bw"
fi
}
ipmi_fru_alloc() {
local name="$1"
local -n ret="$2"
# Pick the first free index to return as the allocated entry
for (( ret = 0; ret < "${#IPMI_FRU_EEPROM_FILE[@]}"; ++ret )); do
[ -n "${IPMI_FRU_EEPROM_FILE[*]+1}" ] || \
[ -n "${IPMI_FRU_EEPROM_BUSADDR[*]+1}" ]|| break
done
if [[ "$name" =~ ^of-name:(.*)$ || "$name" =~ ^([^:]*)$ ]]; then
local ofn="${BASH_REMATCH[1]}"
local file
file="$(of_name_to_eeprom "$ofn")" || return
IPMI_FRU_EEPROM_FILE["$ret"]="$file"
elif [[ "$name" =~ ^frudev-name:(.*)$ ]]; then
local fdn="${BASH_REMATCH[1]}"
local start=$SECONDS
local file
while (( SECONDS - start < 300 )); do
local rc=0
ipmi_fru_device_alloc "$fdn" "$ret" || rc=$?
(( rc == 0 )) && break
# Immediately return any errors, 80 is special to signify retry
(( rc != 80 )) && return $rc
sleep 1
done
else
echo "Invalid IPMI FRU eeprom specification: $name" >&2
return 1
fi
}
ipmi_fru_free() {
unset 'IPMI_FRU_EEPROM_FILE[$1]'
unset 'IPMI_FRU_EEPROM_BUSADDR[$1]'
}
checksum() {
local -n checksum_arr="$1"
local checksum=0
for byte in "${checksum_arr[@]}"; do
checksum=$((checksum + byte))
done
echo $((checksum & 0xff))
}
fix_checksum() {
local -n fix_checksum_arr="$1"
old_cksum=${fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]}
((fix_checksum_arr[IPMI_FRU_CHECKSUM_IDX]-=$(checksum fix_checksum_arr)))
((fix_checksum_arr[IPMI_FRU_CHECKSUM_IDX]&=0xff))
printf 'Corrected %s checksum from 0x%02X -> 0x%02X\n' \
"$1" "${old_cksum}" "${fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]}" >&2
}
read_bytes() {
# shellcheck disable=SC2206
local busaddr=(${IPMI_FRU_EEPROM_BUSADDR["$1"]-})
local file="${IPMI_FRU_EEPROM_FILE["$1"]-$1}"
local offset="$2"
local size="$3"
if (( "${#busaddr[@]}" > 0)); then
echo "Reading ${busaddr[*]} at $offset for $size" >&2
# shellcheck disable=SC2046
i2ctransfer -f -y "${busaddr[0]}" $("${busaddr[2]}" "${busaddr[1]}" "$offset") "r$size"
else
echo "Reading $file at $offset for $size" >&2
dd if="$file" bs=1 count="$size" skip="$offset" 2>/dev/null | \
hexdump -v -e '1/1 "%d "'
fi
}
write_bytes() {
# shellcheck disable=SC2206
local busaddr=(${IPMI_FRU_EEPROM_BUSADDR["$1"]-})
local file="${IPMI_FRU_EEPROM_FILE["$1"]-$1}"
local offset="$2"
local -n bytes_arr="$3"
if (( "${#busaddr[@]}" > 0)); then
echo "Writing ${busaddr[*]} at $offset for ${#bytes_arr[@]}" >&2
# shellcheck disable=SC2046
i2ctransfer -f -y "${busaddr[0]}" $("${busaddr[2]}" "${busaddr[1]}" "$offset" "${#bytes_arr[@]}") "${bytes_arr[@]}"
else
local hexstr
hexstr="$(printf '\\x%x' "${bytes_arr[@]}")" || return
echo "Writing $file at $offset for ${#bytes_arr[@]}" >&2
# shellcheck disable=SC2059
printf "$hexstr" | dd of="$file" bs=1 seek="$offset" 2>/dev/null
fi
}
read_header() {
local eeprom="$1"
local -n header_arr="$2"
# shellcheck disable=SC2207
header_arr=($(read_bytes "$eeprom" 0 8)) || return
echo "Checking $eeprom FRU Header version" >&2
# FRU header is always version 1
(( header_arr[0] == 1 )) || return
echo "Checking $eeprom FRU Header checksum" >&2
local sum
sum="$(checksum header_arr)" || return
# Checksums should be valid
(( sum == 0 )) || return 10
}
read_area() {
local eeprom="$1"
local offset="$2"
local -n area_arr="$3"
local area_size="${4-0}"
offset=$((offset*8))
# shellcheck disable=SC2207
area_arr=($(read_bytes "$eeprom" "$offset" 8)) || return
echo "Checking $eeprom $offset FRU Area version" >&2
# FRU Area is always version 1
(( area_arr[0] == 1 )) || return
if (( area_size == 0 )); then
area_size=${area_arr[$IPMI_FRU_AREA_HEADER_SIZE_IDX]}
fi
# shellcheck disable=SC2207
area_arr=($(read_bytes "$eeprom" "$offset" $((area_size*8)))) || return
echo "Checking $eeprom $offset FRU Area checksum" >&2
local sum
sum="$(checksum area_arr)" || return
# Checksums should be valid
(( sum == 0 )) || return 10
}
ipmi_fru_init=1
@@ -0,0 +1,18 @@
SUMMARY = "gBMC Health Metrics Blob"
DESCRIPTION = "BMC health metrics IPMI blob handler."
GOOGLE_MISC_PROJ = "metrics-ipmi-blobs"
require ../google-misc/google-misc.inc
inherit pkgconfig
DEPENDS += " \
phosphor-ipmi-blobs \
phosphor-logging \
protobuf-native \
protobuf \
"
FILES:${PN} += "${libdir}/blob-ipmid"
EXTRA_OEMESON += "-Dtests=disabled"
@@ -0,0 +1,16 @@
SUMMARY = "Google libcr51sign"
DESCRIPTION = "Google libcr51sign"
GOOGLE_MISC_PROJ = "libcr51sign"
require ../google-misc/google-misc.inc
inherit pkgconfig
# We need to suppress these warnings in OpenSSL 3.0+ until a fix is available
CFLAGS += " \
-Wno-deprecated-declarations \
"
DEPENDS += " \
openssl \
"
@@ -0,0 +1,24 @@
SUMMARY = "Nanopb library"
DESCRIPTION = "Nanopb - Protocol Buffers for Embedded Systems"
HOMEPAGE = "https://github.com/nanopb/nanopb"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=9db4b73a55a3994384112efcdb37c01f"
inherit cmake python3native
SRC_URI = "git://github.com/nanopb/nanopb;branch=master;protocol=https"
SRCREV = "f7e4140a27d9e63517b5d596bc117bd6d5248888"
S = "${WORKDIR}/git"
DEPENDS = "protobuf-native python3-protobuf"
RDEPENDS:${PN}-generator += "python3 python3-protobuf"
PACKAGES:prepend = "${PN}-generator ${PN}-runtime "
FILES:${PN}-generator = "${libdir}/python* ${bindir}"
FILES:${PN}-runtime = "${libdir}/*.so.*"
BBCLASSEXTEND = "native"
@@ -0,0 +1,5 @@
[NetDev]
Name=gbmcbrncsidhcp
Kind=veth
[Peer]
Name=gbmcncsidhcp
@@ -0,0 +1,4 @@
[Match]
Name=gbmcbrncsidhcp
[Network]
Bridge=gbmcbr
@@ -0,0 +1,5 @@
[NetDev]
Name=gbmcncsidhcp
Kind=veth
[Peer]
Name=gbmcbrncsidhcp
@@ -0,0 +1,10 @@
[Match]
Name=gbmcncsidhcp
[Network]
DHCP=false
IPv6AcceptRA=false
LLMNR=false
MulticastDNS=false
LinkLocalAddressing=ipv6
# TODO: Change address back to fdb5:0481:10ce::1/64
Address=fdb5:0481:10ce::2/64
@@ -0,0 +1,33 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${gbmc_ncsi_clear_ip-}" ] && return
source /usr/libexec/ncsid_lib.sh || exit
gbmc_ncsi_clear_ip_hook() {
local ip="${1-}"
# We only want to clear our IPs if we are assigning a new IP
[ -z "$ip" ] && return
echo "Removing Persistent NCSI IPs" >&2
SetStatic xyz.openbmc_project.Network '@NCSI_IF@' 2>/dev/null || true
UpdateIP xyz.openbmc_project.Network '@NCSI_IF@' '0.0.0.0' '0' 2>/dev/null || true
UpdateIP xyz.openbmc_project.Network '@NCSI_IF@' '::' '0' 2>/dev/null || true
}
GBMC_BR_LIB_SET_IP_HOOKS+=(gbmc_ncsi_clear_ip_hook)
gbmc_ncsi_clear_ip=1
@@ -0,0 +1,45 @@
table inet filter {
chain ncsi_input {
type filter hook input priority 0; policy drop;
iifname != @NCSI_IF@ accept
ct state established accept
ip6 daddr ff00::/8 goto ncsi_brd_input
ip6 daddr fe80::/64 goto ncsi_legacy_input
}
chain ncsi_gbmc_br_pub_input {
jump gbmc_br_pub_input
jump ncsi_legacy_input
reject
}
chain gbmc_br_pub_input {
ip6 nexthdr icmpv6 accept
}
chain ncsi_legacy_input {
jump ncsi_any_input
tcp dport 3959 accept
udp dport 3959 accept
tcp dport 3967 accept
udp dport 3967 accept
}
chain ncsi_brd_input {
jump ncsi_any_input
}
chain ncsi_any_input {
icmpv6 type nd-neighbor-advert accept
icmpv6 type nd-neighbor-solicit accept
icmpv6 type nd-router-advert accept
}
chain ncsi_forward {
type filter hook forward priority 0; policy drop;
iifname != @NCSI_IF@ accept
oifname != gbmcbr drop
ip6 daddr fdb5:0481:10ce::/64 drop
ip6 saddr fdb5:0481:10ce::/64 drop
}
chain ncsi_dhcp_input {
type filter hook input priority 0; policy drop;
iifname != ncsigbmc accept
ip6 nexthdr icmpv6 accept
udp dport 547 accept
}
}
@@ -0,0 +1,12 @@
[Unit]
BindsTo=ncsid@@NCSI_IF@.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ip link set @NCSI_IF@ alias ncsi
ExecStop=/sbin/ip link set @NCSI_IF@ alias ''
[Install]
WantedBy=nic-hostful@@NCSI_IF@.target
WantedBy=nic-hostless@@NCSI_IF@.target
@@ -0,0 +1,152 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${gbmc_ncsi_br_deprecated_ips_lib-}" ] && return
source /usr/share/network/lib.sh || exit
gbmc_ncsi_br_deprecated_ips_init=
gbmc_ncsi_br_deprecated_ips_confip=
gbmc_ncsi_br_deprecated_ips_lastip=
gbmc_ncsi_br_deprecated_ips_lastncsi=
gbmc_ncsi_br_deprecated_ips_confncsi=
gbmc_ncsi_br_deprecated_ips_update() {
[ -n "$gbmc_ncsi_br_deprecated_ips_init" ] || return
[ "$gbmc_ncsi_br_deprecated_ips_confip" != "$gbmc_ncsi_br_deprecated_ips_lastip" ] || \
[ "$gbmc_ncsi_br_deprecated_ips_confncsi" != "$gbmc_ncsi_br_deprecated_ips_lastncsi" ] || return
gbmc_ncsi_br_deprecated_ips_confip="$gbmc_ncsi_br_deprecated_ips_lastip"
gbmc_ncsi_br_deprecated_ips_confncsi="$gbmc_ncsi_br_deprecated_ips_lastncsi"
printf 'gBMC NCSI Deprecated Addrs: %s\n' \
"${gbmc_ncsi_br_deprecated_ips_lastip:-(deleted)}" >&2
local contents=
local nfcontents=
if [ -n "$gbmc_ncsi_br_deprecated_ips_lastip" ]; then
local pfx_bytes=()
ip_to_bytes pfx_bytes "$gbmc_ncsi_br_deprecated_ips_lastip"
local pfx="$(ip_bytes_to_str pfx_bytes)"
(( pfx_bytes[9] &= 0xf0 ))
local stateless_pfx="$(ip_bytes_to_str pfx_bytes)"
local stateless_ip=
if [ -e /sys/class/net/gbmcbr ]; then
local gbmcbr_mac="$(ip link show gbmcbr | tail -n 1 | awk '{print $2}')"
local gbmcbr_eui48="$(mac_to_eui48 "$gbmcbr_mac")"
stateless_ip="$(ip_pfx_concat "$stateless_pfx/80" "$gbmcbr_eui48")"
stateless_ip="${stateless_ip%/*}"
fi
pfx_bytes[8]=0
pfx_bytes[9]=0
local host_pfx=
if [ -n "${gbmc_ncsi_br_deprecated_ips_confncsi}" ]; then
# Only impersonate the host if we have an NCSI state machine
host_pfx="$(ip_bytes_to_str pfx_bytes)"
fi
read -r -d '' contents <<EOF
[Network]
IPv6ProxyNDP=yes
IPv6ProxyNDPAddress=$pfx
IPv6ProxyNDPAddress=$stateless_pfx
${host_pfx:+IPv6ProxyNDPAddress=}$host_pfx
${stateless_ip:+IPv6ProxyNDPAddress=}$stateless_ip
EOF
read -r -d '' nfcontents <<EOF
table inet filter {
chain ncsi_input {
ip6 saddr != $pfx/76 ip6 daddr $pfx/76 goto ncsi_gbmc_br_pub_input
${host_pfx:+ip6 daddr $host_pfx/64 goto ncsi_legacy_input}
}
chain ncsi_forward {
ip6 saddr != $pfx/76 ip6 daddr $pfx/76 accept
}
}
EOF
fi
local file
for file in /run/systemd/network/{00,}-bmc-@NCSI_IF@.network.d/50-deprecated.conf; do
mkdir -p -m 755 "$(dirname "$file")"
if [ -z "$contents" ]; then
rm -f "$file"
else
printf '%s' "$contents" >"$file"
fi
done
# Ensure that systemd-networkd performs a reconfiguration as it doesn't
# currently check the mtime of drop-in files.
touch -c /etc/systemd/network/*-bmc-@NCSI_IF@.network
if [ "$(systemctl is-active systemd-networkd)" != 'inactive' ]; then
networkctl reload && networkctl reconfigure @NCSI_IF@
fi
local rfile=/run/nftables/40-gbmc-ncsi-br.rules
mkdir -p -m 755 "$(dirname "$rfile")"
if [ -z "$nfcontents" ]; then
rm -f "$rfile"
else
printf '%s' "$nfcontents" >"$rfile"
fi
systemctl reset-failed nftables && systemctl --no-block reload-or-restart nftables || true
}
gbmc_ncsi_br_deprecated_ips_hook() {
if [ "$change" = 'init' ]; then
gbmc_ncsi_br_deprecated_ips_init=1
gbmc_ip_monitor_defer
elif [ "$change" = 'defer' ]; then
gbmc_ncsi_br_deprecated_ips_update
elif [ "$change" = 'addr' -a "$scope" = 'global' -a "$fam" = 'inet6' ] &&
[ "$intf" = 'gbmcbr' -o "$intf" = '@NCSI_IF@' ] &&
[[ "$flags" != *deprecated* ]]; then
local pfx_bytes=()
ip_to_bytes pfx_bytes "$ip" || return
# No ULA Addresses
if (( pfx_bytes[0] & 0xfe == 0xfc )); then
return
fi
# We only want to allow a <pfx>::fd0x address, where x>0
if (( pfx_bytes[8] != 0xfd || pfx_bytes[9] & 0xf == 0 )); then
return
fi
for (( i = 10; i < 16; ++i )); do
if (( pfx_bytes[i] != 0 )); then
return
fi
done
if [ "$action" = 'add' -a "$ip" != "$gbmc_ncsi_br_deprecated_ips_lastip" ]; then
gbmc_ncsi_br_deprecated_ips_lastip="$ip"
gbmc_ip_monitor_defer
fi
if [ "$action" = 'del' -a "$ip" = "$gbmc_ncsi_br_deprecated_ips_lastip" ]; then
gbmc_ncsi_br_deprecated_ips_lastip=
gbmc_ip_monitor_defer
fi
elif [ "$change" = 'link' -a "$action" = 'add' -a "$intf" = '@NCSI_IF@' ]; then
if ip link show '@NCSI_IF@' | grep -q '^ *alias ncsi$'; then
gbmc_ncsi_br_deprecated_ips_lastncsi=1
gbmc_ip_monitor_defer
else
gbmc_ncsi_br_deprecated_ips_lastncsi=
gbmc_ip_monitor_defer
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_ncsi_br_deprecated_ips_hook)
gbmc_ncsi_br_deprecated_ips_lib=1
@@ -0,0 +1,87 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${gbmc_ncsi_br_pub_addr_lib-}" ] && return
[ ! -e /usr/share/gbmc-br-lib.sh ] && return
source /usr/share/network/lib.sh || exit
source /usr/share/gbmc-br-lib.sh || exit
gbmc_ncsi_br_pub_addr_init=
gbmc_ncsi_br_pub_addr_lastip=
gbmc_ncsi_br_pub_addr_confip=
gbmc_ncsi_br_pub_addr_update() {
[ -n "$gbmc_ncsi_br_pub_addr_init" ] || return
[ "$gbmc_ncsi_br_pub_addr_confip" != "$gbmc_ncsi_br_pub_addr_lastip" ] || return
gbmc_ncsi_br_pub_addr_confip="$gbmc_ncsi_br_pub_addr_lastip"
printf 'gBMC Bridge Pub Addr from NCSI: %s\n' \
"${gbmc_ncsi_br_pub_addr_lastip:-(deleted)}" >&2
local pfx_bytes=()
if [ -n "$gbmc_ncsi_br_pub_addr_lastip" ]; then
ip_to_bytes pfx_bytes "$gbmc_ncsi_br_pub_addr_lastip"
# Ensure we have a /64 or an fdxx address
if (( pfx_bytes[8] != 0xfd || pfx_bytes[9] == 0 )); then
local i
for (( i = 8; i < 16; ++i )); do
if (( pfx_bytes[$i] != 0 )); then
pfx_bytes=()
break
fi
done
fi
fi
local contents=
if (( ${#pfx_bytes[@]} != 0 )); then
pfx_bytes[8]=0xfd
# We never want to use the stateless pfx
if (( pfx_bytes[9] == 0 )); then
pfx_bytes[9]=0x01
fi
# Remove any existing persisted IP
gbmc_br_set_ip
# Load the IP to the bridge non-persistently
gbmc_br_reload_ip "$(ip_bytes_to_str pfx_bytes)"
else
gbmc_br_reload_ip
fi
}
gbmc_ncsi_br_pub_addr_hook() {
if [ "$change" = 'init' ]; then
gbmc_ncsi_br_pub_addr_init=1
gbmc_ip_monitor_defer
elif [ "$change" = 'defer' ]; then
gbmc_ncsi_br_pub_addr_update
elif [ "$change" = 'addr' -a "$intf" = '@NCSI_IF@' ] &&
[ "$scope" = 'global' -a "$fam" = 'inet6' ] &&
[[ "$flags" != *deprecated* ]]; then
if [ "$action" = 'add' -a "$ip" != "$gbmc_ncsi_br_pub_addr_lastip" ]; then
gbmc_ncsi_br_pub_addr_lastip="$ip"
gbmc_ip_monitor_defer
fi
if [ "$action" = 'del' -a "$ip" = "$gbmc_ncsi_br_pub_addr_lastip" ]; then
gbmc_ncsi_br_pub_addr_lastip=
gbmc_ip_monitor_defer
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_ncsi_br_pub_addr_hook)
gbmc_ncsi_br_pub_addr_lib=1
@@ -0,0 +1,13 @@
[Unit]
Description=gBMC DHCP Relay Agent Daemon
After=network.target
StartLimitIntervalSec=10
StartLimitBurst=3
[Service]
Restart=always
RestartSec=5
ExecStart=/usr/sbin/dhcrelay -d --no-pid -rp 3967 -l gbmcncsidhcp -u ff02::1:2%%@NCSI_IF@
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,15 @@
[Unit]
Description=gBMC NCSI RA Discovery
After=network.target
StartLimitIntervalSec=10
StartLimitBurst=3
Conflicts=nic-hostless@@NCSI_IF@.target
Conflicts=nic-hostful@@NCSI_IF@.target
[Service]
Restart=always
RestartSec=5
ExecStart=/usr/libexec/gbmc-ncsi-ip-from-ra.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,95 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ ! -e /usr/share/gbmc-br-lib.sh ] && exit
# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
source /usr/share/network/lib.sh || exit
# shellcheck source=meta-google/recipes-google/networking/gbmc-bridge/gbmc-br-lib.sh
source /usr/share/gbmc-br-lib.sh || exit
NCSI_IF='@NCSI_IF@'
old_pfx=
old_fqdn=
old_rtr=
set_host() {
[[ -n "$host" && -n "$domain" && -n "$hextet" ]] || return
local fqdn="$host-n$hextet.$domain"
[ "$fqdn" != "$old_fqdn" ] || return
old_fqdn="$fqdn"
echo "Found hostname $fqdn" >&2
hostnamectl set-hostname "$fqdn" || true
}
set_net() {
[[ -n "$pfx" && -n "$rtr" ]] || return
[[ "$pfx" != "$old_pfx" || "$rtr" != "$old_rtr" ]] || return
old_pfx="$pfx"
old_rtr="$rtr"
echo "Found prefix $pfx from $rtr" >&2
# We no longer need NCSId if we are in this configuration
systemctl stop --no-block ncsid@"$NCSI_IF" || true
# Save the IP address for the interface
gbmc_br_set_ip "$pfx" || true
# DHCP Relay workaround until alternate source port is supported
# TODO: Remove this once internal relaying cleanups land
gbmc-ncsi-smartnic-wa.sh || true
}
w=60
while true; do
start=$SECONDS
while read -r line; do
if [ -z "$line" ]; then
hextet=
pfx=
host=
domain=
elif [[ "$line" =~ ^Prefix' '*:' '*(.*)/([0-9]+)$ ]]; then
t_pfx="${BASH_REMATCH[1]}"
t_pfx_len="${BASH_REMATCH[2]}"
ip_to_bytes t_pfx_b "$t_pfx" || continue
(( t_pfx_len == 76 && t_pfx_b[8] & 0xfd == 0xfd )) || continue
(( t_pfx_b[9] |= 1 ))
hextet="fd$(printf '%02x' "${t_pfx_b[9]}")"
pfx="$(ip_bytes_to_str t_pfx_b)"
elif [[ "$line" =~ ^'DNS search list'' '*:' '*([^.]+)(.*[.]google[.]com)$ ]]; then
# Ideally, we use PCRE and with lookahead and can do this in a single regex
# ^([a-zA-Z0-9-]+(?=-n[a-fA-F0-9]{1,4})|[a-zA-Z0-9-]+(?!-n[a-fA-F0-9]{1,4}))[^.]*[.]((?:[a-zA-Z0-9]*[.])*google[.]com)$
# Instead we do multiple steps to extract the needed info
host="${BASH_REMATCH[1]}"
domain="${BASH_REMATCH[2]#.}"
if [[ "$host" =~ (-n[a-fA-F0-9]{1,4})$ ]]; then
host="${host%"${BASH_REMATCH[1]}"}"
fi
elif [[ "$line" =~ ^from' '(.*)$ ]]; then
rtr="${BASH_REMATCH[1]}"
set_net || true
set_host || true
fi
done < <(rdisc6 -d -m "$NCSI_IF" -w $(( w * 1000 )) 2>/dev/null)
# If rdisc6 exits early we still want to wait the full `w` time before
# starting again.
(( timeout = start + w - SECONDS ))
sleep $(( timeout < 0 ? 0 : timeout ))
done
@@ -0,0 +1,85 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${gbmc_ncsi_nft_lib-}" ] && return
source /usr/share/network/lib.sh || exit
gbmc_ncsi_nft_init=
gbmc_ncsi_nft_lastip4=
gbmc_ncsi_nft_lastip6=
gbmc_ncsi_nft_update() {
[ -n "$gbmc_ncsi_nft_init" ] || return
printf 'NCSI firewall for IPv4(%s) IPv6(%s)\n' \
"${gbmc_ncsi_nft_lastip4:-(deleted)}" \
"${gbmc_ncsi_nft_lastip6:-(deleted)}" >&2
local contents=
contents+='table inet filter {'$'\n'
contents+=' chain ncsi_input {'$'\n'
local ip4="$gbmc_ncsi_nft_lastip4"
if [ -n "$ip4" ]; then
contents+=" ip daddr $ip4 goto ncsi_legacy_input"$'\n'
fi
local ip6="$gbmc_ncsi_nft_lastip6"
if [ -n "$ip6" ]; then
contents+=" ip6 daddr $ip6 goto ncsi_legacy_input"$'\n'
fi
contents+=' }'$'\n'
contents+='}'$'\n'
local rfile=/run/nftables/30-gbmc-ncsi-in.rules
mkdir -p -m 755 "$(dirname "$rfile")"
printf '%s' "$contents" >"$rfile"
systemctl reset-failed nftables && systemctl --no-block reload-or-restart nftables || true
}
gbmc_ncsi_nft_hook() {
if [ "$change" = 'init' ]; then
gbmc_ncsi_nft_init=1
gbmc_ncsi_nft_update
elif [ "$change" = 'addr' -a "$intf" = '@NCSI_IF@' -a "$scope" = 'global' ]; then
if [ "$fam" = 'inet6' ]; then
local -n lastip='gbmc_ncsi_nft_lastip6'
local pfx_bytes=()
ip_to_bytes pfx_bytes "$ip" || return
# We only want to allow a <pfx>:: address
for (( i = 8; i < 16; ++i )); do
if (( pfx_bytes[i] != 0 )); then
return
fi
done
else
local -n lastip='gbmc_ncsi_nft_lastip4'
fi
if [ "$action" = 'add' -a "$ip" != "$lastip" ]; then
lastip="$ip"
gbmc_ncsi_nft_update
fi
if [ "$action" = 'del' -a "$ip" = "$lastip" ]; then
lastip=
gbmc_ncsi_nft_update
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_ncsi_nft_hook)
gbmc_ncsi_nft_lib=1
@@ -0,0 +1,17 @@
[Unit]
Description=Set NICEnabled property to true
Wants=xyz.openbmc_project.Network.service
After=xyz.openbmc_project.Network.service
Wants=mapper-wait@-xyz-openbmc_project-network-@NCSI_IF@.service
After=mapper-wait@-xyz-openbmc_project-network-@NCSI_IF@.service
StartLimitIntervalSec=10
StartLimitBurst=3
[Service]
Type=oneshot
ExecStart=busctl set-property xyz.openbmc_project.Network /xyz/openbmc_project/network/@NCSI_IF@ xyz.openbmc_project.Network.EthernetInterface NICEnabled b true
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,52 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
override=/run/systemd/system/gbmc-ncsi-dhcrelay.service.d/10-nosp.conf
mkdir -p "$(dirname "$override")"
echo '[Service]' >"$override"
echo 'ExecStart=' >>"$override"
# Remove the source relay port from the relay, bringing it back to run on
# the default port 547. Our relays don't support the source port option needed
# to run on 3967 for our legacy NICs.
grep '^ExecStart=' /lib/systemd/system/gbmc-ncsi-dhcrelay.service | \
sed 's, -rp 3967,,' >>"$override"
override=/run/systemd/system/gbmc-br-dhcp.service.d/10-direct.conf
mkdir -p "$(dirname "$override")"
echo '[Service]' >"$override"
echo 'ExecStart=' >>"$override"
# Switch the gbmcbr interface for the NCSI one to avoid passing the SOLICIT
# message through the BMC relay.
grep '^ExecStart=' /lib/systemd/system/gbmc-br-dhcp.service | \
sed 's, -i gbmcbr, -i @NCSI_IF@,' >>"$override"
systemctl daemon-reload
systemctl reset-failed gbmc-ncsi-dhcrelay
systemctl restart --no-block gbmc-ncsi-dhcrelay
systemctl reset-failed gbmc-br-dhcp
systemctl restart --no-block gbmc-br-dhcp
read -r -d '' contents <<EOF
table inet filter {
chain ncsi_legacy_input {
udp dport {546,547} accept
}
}
EOF
rfile=/run/nftables/60-gbmc-ncsi-ra.rules
mkdir -p "$(dirname "$rfile")"
printf '%s' "$contents" >"$rfile"
systemctl reset-failed nftables
systemctl --no-block reload-or-restart nftables
@@ -0,0 +1,21 @@
[Unit]
Description=SSL/SSH multiplexer
Requires=gbmc-ncsi-sslh.socket
After=gbmc-ncsi-sslh.socket
[Service]
ExecStart=/usr/sbin/sslh -n -f --ssh [::1]:22 --http [::1]:80 --tls [::1]:443
KillMode=process
#Hardening
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
MountFlags=private
NoNewPrivileges=true
PrivateDevices=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
MemoryDenyWriteExecute=true
DynamicUser=true
@@ -0,0 +1,6 @@
[Socket]
BindToDevice=@NCSI_IF@
ListenStream=3967
[Install]
WantedBy=sockets.target
@@ -0,0 +1,149 @@
SUMMARY = "Configures ncsi for a gBMC system"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
inherit systemd
SRC_URI += " \
file://-bmc-gbmcbrncsidhcp.netdev \
file://-bmc-gbmcbrncsidhcp.network \
file://-bmc-gbmcncsidhcp.netdev \
file://-bmc-gbmcncsidhcp.network \
file://50-gbmc-ncsi.rules.in \
file://gbmc-ncsi-dhcrelay.service.in \
file://gbmc-ncsi-ip-from-ra.service.in \
file://gbmc-ncsi-ip-from-ra.sh.in \
file://gbmc-ncsi-smartnic-wa.sh.in \
file://gbmc-ncsi-sslh.socket.in \
file://gbmc-ncsi-sslh.service \
file://gbmc-ncsi-nft.sh.in \
file://gbmc-ncsi-br-pub-addr.sh.in \
file://gbmc-ncsi-br-deprecated-ips.sh.in \
file://gbmc-ncsi-set-nicenabled.service.in \
file://gbmc-ncsi-alias.service.in \
file://50-gbmc-ncsi-clear-ip.sh.in \
"
S = "${WORKDIR}"
RDEPENDS:${PN} += " \
bash \
dhcp-relay \
gbmc-ip-monitor \
ncsid \
network-sh \
nftables-systemd \
sslh \
ndisc6-rdisc6 \
"
FILES:${PN} += " \
${datadir}/gbmc-br-lib \
${datadir}/gbmc-ip-monitor \
${systemd_unitdir} \
"
SYSTEMD_SERVICE:${PN} += " \
gbmc-ncsi-dhcrelay.service \
gbmc-ncsi-sslh.service \
gbmc-ncsi-sslh.socket \
gbmc-ncsi-set-nicenabled.service \
gbmc-ncsi-ip-from-ra.service \
"
do_install:append() {
if_name='${GBMC_NCSI_IF_NAME}'
if [ -z "$if_name" ]; then
echo "Missing if_name" >&2
exit 1
fi
install -d -m0755 ${D}${sysconfdir}/sysctl.d
echo "net.ipv6.conf.$if_name.accept_dad=0" \
>>${D}${sysconfdir}/sysctl.d/25-gbmc-ncsi.conf
echo "net.ipv6.conf.$if_name.dad_transmits=0" \
>>${D}${sysconfdir}/sysctl.d/25-gbmc-ncsi.conf
install -d -m0755 ${D}${systemd_unitdir}/network
install -m0644 ${WORKDIR}/-bmc-gbmcbrncsidhcp.netdev \
${D}${systemd_unitdir}/network/
install -m0644 ${WORKDIR}/-bmc-gbmcbrncsidhcp.network \
${D}${systemd_unitdir}/network/
install -m0644 ${WORKDIR}/-bmc-gbmcncsidhcp.netdev \
${D}${systemd_unitdir}/network/
install -m0644 ${WORKDIR}/-bmc-gbmcncsidhcp.network \
${D}${systemd_unitdir}/network/
netdir=${D}${systemd_unitdir}/network/00-bmc-$if_name.network.d
install -d -m0755 "$netdir"
echo '[Network]' >>"$netdir"/gbmc-ncsi.conf
echo 'DHCP=false' >>"$netdir"/gbmc-ncsi.conf
echo 'IPv6AcceptRA=false' >>"$netdir"/gbmc-ncsi.conf
echo 'LLMNR=false' >>"$netdir"/gbmc-ncsi.conf
echo 'MulticastDNS=false' >>"$netdir"/gbmc-ncsi.conf
echo 'LinkLocalAddressing=ipv6' >>"$netdir"/gbmc-ncsi.conf
nftdir=${D}${sysconfdir}/nftables
install -d -m0755 "$nftdir"
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/50-gbmc-ncsi.rules.in \
>"$nftdir"/50-gbmc-ncsi.rules
wantdir=${D}${systemd_system_unitdir}/multi-user.target.wants
install -d -m0755 "$wantdir"
ln -sv ../ncsid@.service "$wantdir"/ncsid@$if_name.service
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-alias.service.in \
>${D}${systemd_system_unitdir}/gbmc-ncsi-alias.service
install -d -m0755 "${D}${systemd_system_unitdir}/nic-hostless@$if_name.target.wants"
ln -sv ../gbmc-ncsi-alias.service "${D}${systemd_system_unitdir}/nic-hostless@$if_name.target.wants"/
install -d -m0755 "${D}${systemd_system_unitdir}/nic-hostful@$if_name.target.wants"
ln -sv ../gbmc-ncsi-alias.service "${D}${systemd_system_unitdir}/nic-hostful@$if_name.target.wants"/
install -m 0644 ${WORKDIR}/gbmc-ncsi-sslh.service ${D}${systemd_system_unitdir}
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-sslh.socket.in \
>${D}${systemd_system_unitdir}/gbmc-ncsi-sslh.socket
mondir=${D}${datadir}/gbmc-ip-monitor/
install -d -m0755 $mondir
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-nft.sh.in \
>${WORKDIR}/gbmc-ncsi-nft.sh
install -m644 ${WORKDIR}/gbmc-ncsi-nft.sh $mondir
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-br-pub-addr.sh.in \
>${WORKDIR}/gbmc-ncsi-br-pub-addr.sh
install -m644 ${WORKDIR}/gbmc-ncsi-br-pub-addr.sh $mondir
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-br-deprecated-ips.sh.in \
>${WORKDIR}/gbmc-ncsi-br-deprecated-ips.sh
install -m644 ${WORKDIR}/gbmc-ncsi-br-deprecated-ips.sh $mondir
brlibdir=${D}${datadir}/gbmc-br-lib/
install -d -m0755 $brlibdir
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/50-gbmc-ncsi-clear-ip.sh.in \
>${WORKDIR}/50-gbmc-ncsi-clear-ip.sh
install -m644 ${WORKDIR}/50-gbmc-ncsi-clear-ip.sh $brlibdir
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-set-nicenabled.service.in \
>${D}${systemd_system_unitdir}/gbmc-ncsi-set-nicenabled.service
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-dhcrelay.service.in \
>${D}${systemd_system_unitdir}/gbmc-ncsi-dhcrelay.service
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-ip-from-ra.service.in \
>${WORKDIR}/gbmc-ncsi-ip-from-ra.service
install -m0644 ${WORKDIR}/gbmc-ncsi-ip-from-ra.service ${D}${systemd_system_unitdir}
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-ip-from-ra.sh.in \
>${WORKDIR}/gbmc-ncsi-ip-from-ra.sh
install -d -m0755 ${D}${libexecdir}
install -m0755 ${WORKDIR}/gbmc-ncsi-ip-from-ra.sh ${D}${libexecdir}/
sed "s,@NCSI_IF@,$if_name,g" ${WORKDIR}/gbmc-ncsi-smartnic-wa.sh.in \
>${WORKDIR}/gbmc-ncsi-smartnic-wa.sh
install -d -m0755 ${D}${bindir}
install -m0755 ${WORKDIR}/gbmc-ncsi-smartnic-wa.sh ${D}${bindir}/
}
do_rm_work:prepend() {
# HACK: Work around broken do_rm_work not properly calling rm with `--`
# It doesn't like filenames that start with `-`
rm -rf -- ${WORKDIR}/-*
}
@@ -0,0 +1,40 @@
SUMMARY = "Google NCSI daemon"
DESCRIPTION = "Google NCSI daemon."
GOOGLE_MISC_PROJ = "ncsid"
require ../google-misc/google-misc.inc
inherit systemd
EXTRA_OEMESON = " \
-Dtests=disabled \
"
SYSTEMD_SERVICE:${PN} += " \
dhcp4@.service \
dhcp6@.service \
ncsid@.service \
nic-hostful@.target \
nic-hostless@.target \
update-ra-gw@.service \
update-ra-neighbor@.service \
update-ra-neighbor@.timer \
update-static-neighbors@.service \
update-static-neighbors@.timer \
"
DEPENDS += " \
fmt \
sdbusplus \
stdplus \
"
RDEPENDS:${PN} += " \
bash \
busybox \
iputils-arping \
jq \
ndisc6-ndisc6 \
ndisc6-rdisc6 \
systemd \
"
@@ -0,0 +1,5 @@
table inet filter {
chain gbmc_br_pub_input {
tcp dport 23 accept
}
}
@@ -0,0 +1,22 @@
SUMMARY = "Google DHCP completion daemon"
DESCRIPTION = "Google DHCP completion daemon"
GOOGLE_MISC_PROJ = "dhcp-done"
require ../google-misc/google-misc.inc
inherit systemd
SYSTEMD_SERVICE:${PN} += "dhcp-done@.service"
DEPENDS += " \
sdeventplus \
stdplus \
"
SRC_URI += "file://50-dhcp-done.rules"
FILES:${PN} += "${sysconfdir}/nftables"
do_install:append() {
nftables_dir=${D}${sysconfdir}/nftables
install -d -m0755 "$nftables_dir"
install -m0644 ${WORKDIR}/50-dhcp-done.rules $nftables_dir/
}
@@ -0,0 +1,185 @@
#!/bin/bash
# shellcheck disable=SC2317
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cd "$(dirname "$0")" || exit
if [ -e ../gbmc-ip-monitor.bb ]; then
# shellcheck source=meta-google/recipes-google/test/test-sh/lib.sh
source '../../test/test-sh/lib.sh'
else
# shellcheck source=meta-google/recipes-google/test/test-sh/lib.sh
source "$SYSROOT/usr/share/test/lib.sh"
fi
# shellcheck source=meta-google/recipes-google/networking/files/gbmc-ip-monitor.sh
source gbmc-ip-monitor.sh
test_init_empty() {
ip() {
return 0
}
str="$(gbmc_ip_monitor_generate_init)" || fail
expect_streq "$str" '[INIT]'
}
test_init_link_populated() {
ip() {
if [ "$1" = 'link' ]; then
cat <<EOF
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether aa:aa:aa:aa:aa:aa brd ff:ff:ff:ff:ff:ff
altname enp0s31f6
EOF
fi
return 0
}
str="$(gbmc_ip_monitor_generate_init)" || fail
expect_streq "$str" <<EOF
[LINK]1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[LINK]2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether aa:aa:aa:aa:aa:aa brd ff:ff:ff:ff:ff:ff
altname enp0s31f6
[INIT]
EOF
}
test_init_addr_populated() {
ip() {
if [ "$1" = 'addr' ]; then
cat <<EOF
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether aa:aa:aa:aa:aa:aa brd ff:ff:ff:ff:ff:ff
altname enp0s31f6
inet 192.168.242.57/23 brd 192.168.243.255 scope global dynamic noprefixroute eno2
valid_lft 83967sec preferred_lft 83967sec
inet6 fd01:ff2:5687:4:cf03:45f3:983a:96eb/64 scope global temporary dynamic
valid_lft 518788sec preferred_lft 183sec
EOF
fi
return 0
}
str="$(gbmc_ip_monitor_generate_init)" || fail
expect_streq "$str" <<EOF
[ADDR]1: lo inet 127.0.0.1/8 scope host lo
[ADDR]1: lo inet6 ::1/128 scope host
[ADDR]2: eno2 inet 192.168.242.57/23 brd 192.168.243.255 scope global dynamic noprefixroute eno2
[ADDR]2: eno2 inet6 fd01:ff2:5687:4:cf03:45f3:983a:96eb/64 scope global temporary dynamic
[INIT]
EOF
}
test_init_route_populated() {
ip() {
if [[ "$1" == "-4" && "${2-}" == 'route' ]]; then
cat <<EOF
default via 192.168.243.254 dev eno2 proto dhcp metric 100
192.168.242.0/23 dev eno2 proto kernel scope link src 192.168.242.57 metric 100
EOF
elif [[ "$1" == "-6" && "${2-}" == 'route' ]]; then
cat <<EOF
::1 dev lo proto kernel metric 256 pref medium
fd01:ff2:5687:4::/64 dev eno2 proto ra metric 100 pref medium
fe80::/64 dev eno2 proto kernel metric 100 pref medium
EOF
fi
return 0
}
str="$(gbmc_ip_monitor_generate_init)" || fail
expect_streq "$str" <<EOF
[ROUTE]default via 192.168.243.254 dev eno2 proto dhcp metric 100
[ROUTE]192.168.242.0/23 dev eno2 proto kernel scope link src 192.168.242.57 metric 100
[ROUTE]::1 dev lo proto kernel metric 256 pref medium
[ROUTE]fd01:ff2:5687:4::/64 dev eno2 proto ra metric 100 pref medium
[ROUTE]fe80::/64 dev eno2 proto kernel metric 100 pref medium
[INIT]
EOF
}
testParseNonTag() {
expect_err 2 gbmc_ip_monitor_parse_line ''
expect_err 2 gbmc_ip_monitor_parse_line ' '
expect_err 2 gbmc_ip_monitor_parse_line ' Data'
expect_err 2 gbmc_ip_monitor_parse_line ' [LINK]'
expect_err 2 gbmc_ip_monitor_parse_line ' [ROUTE]'
}
testParseInit() {
expect_err 0 gbmc_ip_monitor_parse_line '[INIT]'
expect_streq "$change" 'init'
}
testParseAddrAdd() {
expect_err 0 gbmc_ip_monitor_parse_line '[ADDR]2: eno2 inet6 fd01:ff2:5687:4:cf03:45f3:983a:96eb/128 metric 1024 scope global temporary dynamic'
expect_streq "$change" 'addr'
expect_streq "$action" 'add'
expect_streq "$intf" 'eno2'
expect_streq "$fam" 'inet6'
expect_streq "$ip" 'fd01:ff2:5687:4:cf03:45f3:983a:96eb'
expect_streq "$scope" 'global'
expect_streq "$flags" 'temporary dynamic'
}
testParseAddrDel() {
expect_err 0 gbmc_ip_monitor_parse_line '[ADDR]Deleted 2: eno2 inet6 fe80::aaaa:aaff:feaa:aaaa/64 scope link'
expect_streq "$change" 'addr'
expect_streq "$action" 'del'
expect_streq "$intf" 'eno2'
expect_streq "$fam" 'inet6'
expect_streq "$ip" 'fe80::aaaa:aaff:feaa:aaaa'
expect_streq "$scope" 'link'
expect_streq "$flags" ''
}
testParseRouteAdd() {
expect_err 0 gbmc_ip_monitor_parse_line "[ROUTE]fd01:ff2:5687:4::/64 dev eno2 proto ra metric 100 pref medium"
expect_streq "$change" 'route'
expect_streq "$action" 'add'
expect_streq "$route" 'fd01:ff2:5687:4::/64 dev eno2 proto ra metric 100 pref medium'
}
testParseRouteDel() {
expect_err 0 gbmc_ip_monitor_parse_line "[ROUTE]Deleted fd01:ff2:5687:4::/64 dev eno2 proto ra metric 100 pref medium"
expect_streq "$change" 'route'
expect_streq "$action" 'del'
expect_streq "$route" 'fd01:ff2:5687:4::/64 dev eno2 proto ra metric 100 pref medium'
}
testParseLinkAdd() {
expect_err 0 gbmc_ip_monitor_parse_line "[LINK]2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000" \
< <(echo 'link/ether aa:aa:aa:aa:aa:aa brd ff:ff:ff:ff:ff:ff')
expect_streq "$change" 'link'
expect_streq "$action" 'add'
expect_streq "$intf" 'eno2'
expect_streq "$mac" 'aa:aa:aa:aa:aa:aa'
}
testParseLinkDel() {
expect_err 0 gbmc_ip_monitor_parse_line "[LINK]Deleted 2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000" \
< <(echo 'link/ether aa:aa:aa:aa:aa:aa brd ff:ff:ff:ff:ff:ff')
expect_streq "$change" 'link'
expect_streq "$action" 'del'
expect_streq "$intf" 'eno2'
expect_streq "$mac" 'aa:aa:aa:aa:aa:aa'
}
main
@@ -0,0 +1,10 @@
[Unit]
Before=systemd-networkd.service
[Service]
Type=notify
ExecStart=/usr/libexec/gbmc-ip-monitor.sh
TimeoutStartSec=5min
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,145 @@
#!/bin/bash
# shellcheck disable=SC2034
# shellcheck disable=SC2317
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# A list of functions which get executed for each netlink event received.
# These are configured by the files included below.
GBMC_IP_MONITOR_HOOKS=()
# Load configurations from a known location in the filesystem to populate
# hooks that are executed after each event.
shopt -s nullglob
for conf in /usr/share/gbmc-ip-monitor/*.sh; do
# shellcheck source=/dev/null
source "$conf"
done
gbmc_ip_monitor_run_hooks() {
local hook
for hook in "${GBMC_IP_MONITOR_HOOKS[@]}"; do
"$hook" || continue
done
}
gbmc_ip_monitor_generate_init() {
ip link | sed 's,^[^ ],[LINK]\0,'
local intf=
local line
while read -r line; do
[[ "$line" =~ ^([0-9]+:[[:space:]][^:]+) ]] && intf="${BASH_REMATCH[1]}"
[[ "$line" =~ ^[[:space:]]*inet ]] && echo "[ADDR]$intf $line"
done < <(ip addr)
ip -4 route | sed 's,^,[ROUTE],'
ip -6 route | sed 's,^,[ROUTE],'
echo '[INIT]'
}
GBMC_IP_MONITOR_DEFER_OUTSTANDING=
gbmc_ip_monitor_defer_() {
sleep 1
printf '[DEFER]\n' >&"$GBMC_IP_MONITOR_DEFER"
}
gbmc_ip_monitor_defer() {
[ -z "$GBMC_IP_MONITOR_DEFER_OUTSTANDING" ] || return 0
gbmc_ip_monitor_defer_ &
GBMC_IP_MONITOR_DEFER_OUTSTANDING=1
}
gbmc_ip_monitor_parse_line() {
local line="$1"
if [[ "$line" == '[INIT]'* ]]; then
change=init
echo "Initialized" >&2
elif [[ "$line" == '[ADDR]'* ]]; then
change=addr
action=add
pfx_re='^\[ADDR\](Deleted )?[0-9]+:[[:space:]]*'
intf_re='([^ ]+)[[:space:]]+'
fam_re='([^ ]+)[[:space:]]+'
addr_re='([^/]+)/[0-9]+[[:space:]]+'
metric_re='(metric[[:space:]]+[^ ]+[[:space:]]+)?'
brd_re='(brd[[:space:]]+[^ ]+[[:space:]]+)?'
scope_re='scope[[:space:]]+([^ ]+)[[:space:]]*(.*)'
combined_re="${pfx_re}${intf_re}${fam_re}${addr_re}${metric_re}${brd_re}${scope_re}"
if ! [[ "$line" =~ ${combined_re} ]]; then
echo "Failed to parse addr: $line" >&2
return 1
fi
if [ -n "${BASH_REMATCH[1]}" ]; then
action=del
fi
intf="${BASH_REMATCH[2]}"
fam="${BASH_REMATCH[3]}"
ip="${BASH_REMATCH[4]}"
scope="${BASH_REMATCH[7]}"
flags="${BASH_REMATCH[8]}"
elif [[ "$line" == '[ROUTE]'* ]]; then
line="${line#[ROUTE]}"
change=route
action=add
if ! [[ "$line" =~ ^\[ROUTE\](Deleted )?(.*)$ ]]; then
echo "Failed to parse link: $line" >&2
return 1
fi
if [ -n "${BASH_REMATCH[1]}" ]; then
action=del
fi
route="${BASH_REMATCH[2]}"
elif [[ "$line" == '[LINK]'* ]]; then
change='link'
action=add
pfx_re='^\[LINK\](Deleted )?[0-9]+:[[:space:]]*'
intf_re='([^:]+):[[:space:]]+'
if ! [[ "$line" =~ ${pfx_re}${intf_re} ]]; then
echo "Failed to parse link: $line" >&2
return 1
fi
if [ -n "${BASH_REMATCH[1]}" ]; then
action=del
fi
intf="${BASH_REMATCH[2]}"
read -ra data || return
mac="${data[1]}"
elif [[ "$line" == '[DEFER]'* ]]; then
GBMC_IP_MONITOR_DEFER_OUTSTANDING=
change=defer
else
return 2
fi
}
return 0 2>/dev/null
cleanup() {
local st="$?"
trap - HUP INT QUIT ABRT TERM EXIT
jobs -l -p | xargs -r kill || true
exit "$st"
}
trap cleanup HUP INT QUIT ABRT TERM EXIT
FIFODIR="$(mktemp -d)"
mkfifo "$FIFODIR"/fifo
exec {GBMC_IP_MONITOR_DEFER}<>"$FIFODIR"/fifo
rm -rf "$FIFODIR"
while read -r line; do
gbmc_ip_monitor_parse_line "$line" || continue
gbmc_ip_monitor_run_hooks || continue
if [ "$change" = 'init' ]; then
systemd-notify --ready
fi
done < <(gbmc_ip_monitor_generate_init; ip monitor link addr route label & cat <&"$GBMC_IP_MONITOR_DEFER")
@@ -0,0 +1,10 @@
[Unit]
Before=systemd-networkd.service
[Service]
Restart=on-failure
Type=oneshot
ExecStart=/usr/libexec/gbmc-mac-config.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,91 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/ipmi/ipmi-fru-sh/lib.sh
source /usr/share/ipmi-fru/lib.sh || exit
ipmi_fru_alloc '@EEPROM@' eeprom || exit
header=()
read_header "$eeprom" header || exit
internal_offset=${header[$IPMI_FRU_COMMON_HEADER_INTERNAL_OFFSET_IDX]}
if (( internal_offset == 0 )); then
echo "Internal offset invalid for eeprom" >&2
exit 1
fi
# Our MAC Address configuration lives in the internal area with a format
# Offset Data
# 0 Version (Always 1)
# 1 Type (Always 1 for MAC Address)
# 2 Area Length in bytes (Always 32 bytes or 4 IPMI FRU sectors)
# 3-8 MAC Address Base Octets
# 9 Num Allocate MACs from Base
# 10-30 Padding (Always 0xFF)
# 31 IPMI FRU Checksum
internal=()
read_area "$eeprom" "$internal_offset" internal 4 || exit
if (( internal[1] != 1 || internal[2] != 32 )); then
echo "Not a MAC internal region" >&2
exit 1
fi
mac=("${internal[@]:3:6}")
num="${internal[9]}"
macstr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' "${mac[@]}")
echo "Base MAC $macstr num $num" >&2
rc=0
# Pre-Determine if we will miss an allocation due to the number of
# addresses the FRU actually supports.
# shellcheck disable=SC2190
declare -A num_to_intfs=(@NUM_TO_INTFS@)
for key in "${!num_to_intfs[@]}"; do
if (( key >= num )); then
echo "${num_to_intfs[$key]} at $key is out of range" >&2
rc=1
fi
done
# Write out each MAC override to the runtime networkd configuration
for (( i=0; i<num; i++ )); do
if (( mac[5] > 0xff )); then
echo "MAC assignment too large: ${mac[*]}" >&2
rc=2
break
fi
for intf in ${num_to_intfs[$i]}; do
macstr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' "${mac[@]}")
echo "Setting $intf to $macstr" >&2
for override in /run/systemd/network/{00,}-bmc-$intf.network.d; do
mkdir -p "$override"
printf '[Link]\nMACAddress=%s\n' "$macstr" >"$override"/50-mac.conf
done
for override in /run/systemd/network/{00,}-bmc-$intf.netdev.d; do
mkdir -p "$override"
printf '[NetDev]\nMACAddress=%s\n' "$macstr" >"$override"/50-mac.conf
done
# In case we don't have any interface configs, set the MAC directly
# This is safe to apply, as systemd-networkd will always override this
# value based on written configs.
if ip link show "$intf" >/dev/null 2>&1 && \
! ip link set dev "$intf" address "$macstr"; then
echo "Setting MAC($macstr) on $intf failed" >&2
fi
done
(( ++mac[5] ))
done
exit $rc
@@ -0,0 +1,21 @@
[Unit]
Description=IPERF3 Server
[Service]
ExecStart=/usr/bin/iperf3 -s
#Hardening
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
MountFlags=private
NoNewPrivileges=true
PrivateDevices=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
MemoryDenyWriteExecute=true
DynamicUser=true
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,155 @@
SUMMARY = "Configures the gbmc bridge and filter rules"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
inherit systemd
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI += " \
file://-bmc-gbmcbr.netdev \
file://-bmc-gbmcbr.network.in \
file://-bmc-gbmcbrdummy.netdev \
file://-bmc-gbmcbrdummy.network \
file://+-bmc-gbmcbrusb.network \
file://ipmi.service.in \
file://50-gbmc-br.rules \
file://gbmc-br-ula.sh \
file://gbmc-br-from-ra.sh \
file://gbmc-br-ensure-ra.sh \
file://gbmc-br-ensure-ra.service \
file://gbmc-br-gw-src.sh \
file://gbmc-br-nft.sh \
file://gbmc-br-dhcp.sh \
file://50-gbmc-psu-hardreset.sh \
file://gbmc-br-dhcp.service \
file://gbmc-br-dhcp-term.sh \
file://gbmc-br-dhcp-term.service \
file://gbmc-br-lib.sh \
file://gbmc-br-load-ip.service \
file://gbmc-start-dhcp.sh \
file://50-gbmc-br-cn-redirect.rules \
"
FILES:${PN}:append = " \
${datadir}/gbmc-ip-monitor \
${datadir}/gbmc-br-dhcp \
${datadir}/gbmc-br-lib.sh \
${systemd_unitdir}/network \
${sysconfdir}/nftables \
${sysconfdir}/avahi/services \
"
RDEPENDS:${PN}:append = " \
bash \
dhcp-done \
gbmc-ip-monitor \
mstpd-mstpd \
network-sh \
ndisc6-rdisc6 \
nftables-systemd \
"
SYSTEMD_SERVICE:${PN} += " \
gbmc-br-ensure-ra.service \
gbmc-br-dhcp.service \
gbmc-br-dhcp-term.service \
gbmc-br-load-ip.service \
"
GBMC_BR_MAC_ADDR ?= ""
# Generated via https://cd34.com/rfc4193/ based on a MAC from a machine I own
# and we allocated it downstream. Intended to only be used within a complete
# system of multiple network endpoints.
GBMC_ULA_PREFIX = "fdb5:0481:10ce:0"
def mac_to_eui64(mac):
if not mac:
return ''
b = [int(c, 16) for c in mac.split(':')]
b[0] ^= 2
b.insert(3, 0xfe)
b.insert(3, 0xff)
idx = range(0, len(b)-1, 2)
return ':'.join([format((b[i] << 8) + b[i+1], '04x') for i in idx])
GBMC_BRIDGE_INTFS ?= ""
ethernet_bridge_install() {
# install udev rules if any
if [ -z "${GBMC_BRIDGE_INTFS}"]; then
return
fi
cat /dev/null > ${WORKDIR}/-ether-bridge.network
echo "[Match]" >> ${WORKDIR}/-ether-bridge.network
echo "Name=${GBMC_BRIDGE_INTFS}" >> ${WORKDIR}/-ether-bridge.network
echo "[Network]" >> ${WORKDIR}/-ether-bridge.network
echo "Bridge=gbmcbr" >> ${WORKDIR}/-ether-bridge.network
install -d ${D}/${sysconfdir}/systemd/network
install -m 0644 ${WORKDIR}/-ether-bridge.network ${D}/${sysconfdir}/systemd/network/
}
do_install() {
netdir=${D}${systemd_unitdir}/network
install -d -m0755 $netdir
if [ ! -z "${GBMC_BR_MAC_ADDR}" ]; then
sfx='${@mac_to_eui64(GBMC_BR_MAC_ADDR)}'
addr="Address=${GBMC_ULA_PREFIX}:$sfx/64\nAddress=fe80::$sfx/64"
sed -i "s,@ADDR@,$addr," ${WORKDIR}/-bmc-gbmcbr.network.in
else
sed -i '/@ADDR@/d' ${WORKDIR}/-bmc-gbmcbr.network.in
fi
ethernet_bridge_install
install -m0644 ${WORKDIR}/-bmc-gbmcbr.netdev $netdir/
install -m0644 ${WORKDIR}/-bmc-gbmcbr.network.in $netdir/-bmc-gbmcbr.network
install -m0644 ${WORKDIR}/-bmc-gbmcbrdummy.netdev $netdir/
install -m0644 ${WORKDIR}/-bmc-gbmcbrdummy.network $netdir/
install -m0644 ${WORKDIR}/+-bmc-gbmcbrusb.network $netdir/
nftables_dir=${D}${sysconfdir}/nftables
install -d -m0755 "$nftables_dir"
install -m0644 ${WORKDIR}/50-gbmc-br.rules $nftables_dir/
install -m0644 ${WORKDIR}/50-gbmc-br-cn-redirect.rules $nftables_dir/
avahi_dir=${D}${sysconfdir}/avahi/services
install -d -m 0755 "$avahi_dir"
sed -i 's,@MACHINE@,${MACHINE},g' ${WORKDIR}/ipmi.service.in
sed -i 's,@EXTRA_ATTRS@,,g' ${WORKDIR}/ipmi.service.in
sed 's,@NAME@,bmc,g' ${WORKDIR}/ipmi.service.in >${avahi_dir}/bmc.ipmi.service
sed 's,@NAME@,${MACHINE}-bmc,g' ${WORKDIR}/ipmi.service.in >${avahi_dir}/${MACHINE}-bmc.ipmi.service
mondir=${D}${datadir}/gbmc-ip-monitor
install -d -m0755 "$mondir"
install -m0644 ${WORKDIR}/gbmc-br-ula.sh "$mondir"/
install -m0644 ${WORKDIR}/gbmc-br-from-ra.sh "$mondir"/
install -m0644 ${WORKDIR}/gbmc-br-gw-src.sh "$mondir"/
install -m0644 ${WORKDIR}/gbmc-br-nft.sh "$mondir"/
install -d -m0755 ${D}${libexecdir}
install -m0755 ${WORKDIR}/gbmc-br-ensure-ra.sh ${D}${libexecdir}/
install -m0755 ${WORKDIR}/gbmc-br-dhcp.sh ${D}${libexecdir}/
install -m0755 ${WORKDIR}/gbmc-br-dhcp-term.sh ${D}${libexecdir}/
install -d -m0755 ${D}${systemd_system_unitdir}
install -m0644 ${WORKDIR}/gbmc-br-ensure-ra.service ${D}${systemd_system_unitdir}/
install -m0644 ${WORKDIR}/gbmc-br-dhcp.service ${D}${systemd_system_unitdir}/
install -m0644 ${WORKDIR}/gbmc-br-dhcp-term.service ${D}${systemd_system_unitdir}/
install -m0644 ${WORKDIR}/gbmc-br-load-ip.service ${D}${systemd_system_unitdir}/
install -d -m0755 ${D}${datadir}/gbmc-br-dhcp
install -m0644 ${WORKDIR}/50-gbmc-psu-hardreset.sh ${D}${datadir}/gbmc-br-dhcp/
install -m0644 ${WORKDIR}/gbmc-br-lib.sh ${D}${datadir}/
install -d ${D}/${bindir}
install -m0755 ${WORKDIR}/gbmc-start-dhcp.sh ${D}${bindir}/
}
do_rm_work:prepend() {
# HACK: Work around broken do_rm_work not properly calling rm with `--`
# It doesn't like filenames that start with `-`
rm -rf -- ${WORKDIR}/-*
}
@@ -0,0 +1,8 @@
[Match]
Property=ID_BUS=usb ID_VENDOR_ID=18d1 ID_MODEL_ID=022b
[Network]
Bridge=gbmcbr
[Bridge]
# USB speeds tend to be better than 100mbit (100 cost) but worse
# than 1gbit (10 cost). Generally around 200mbit.
Cost=85
@@ -0,0 +1,5 @@
[NetDev]
Name=gbmcbr
Kind=bridge
[Bridge]
STP=true
@@ -0,0 +1,15 @@
[Match]
Name=gbmcbr
[Network]
@ADDR@
DHCP=false
IPv6AcceptRA=true
LLMNR=true
MulticastDNS=true
LinkLocalAddressing=ipv6
IPv6PrefixDelegation=yes
[IPv6AcceptRA]
DHCPv6Client=false
RouteMetric=1056
[IPv6PrefixDelegation]
RouterLifetimeSec=0
@@ -0,0 +1,3 @@
[NetDev]
Name=gbmcbrdummy
Kind=dummy
@@ -0,0 +1,4 @@
[Match]
Name=gbmcbrdummy
[Network]
Bridge=gbmcbr
@@ -0,0 +1,24 @@
table bridge filter {
chain gbmcbr_mark {
type filter hook prerouting priority -300;
iifname == "cn0" mark set 1 return
iifname == "cn1" mark set 2 return
}
}
table inet raw {
chain gbmcbr_nat_input {
type filter hook prerouting priority -300;
# client should only use 10166 for this purpose and
# it should NOT use service port directly
# otherwise drop later if the packets goes into input
tcp dport 10167-10168 mark set 0xff
mark 1 tcp dport 10166 tcp dport set 10167 notrack
mark 2 tcp dport 10166 tcp dport set 10168 notrack
}
chain gbmcbr_nat_output {
type filter hook output priority -300;
tcp sport 10167 tcp sport set 10166 notrack
tcp sport 10168 tcp sport set 10166 notrack
}
}
@@ -0,0 +1,38 @@
table bridge filter {
chain gbmc_br_prerouting {
type filter hook prerouting priority 0;
iifname != gbmcbr accept
# Sometimes our links are over NCSI and we don't want to broadcast
# those packets over the entire bridge. They are only relevant P2P.
ether type 0x88F8 drop
}
}
table inet filter {
chain gbmc_br_input {
type filter hook input priority 0; policy drop;
iifname != gbmcbr accept
mark 0xff drop
ct state established accept
jump gbmc_br_int_input
jump gbmc_br_pub_input
reject
}
set gbmc_br_int_addrs {
type ipv6_addr;
flags interval
elements = {
ff00::/8,
fe80::/64,
fdb5:0481:10ce::/64,
}
}
chain gbmc_br_int_input {
ip6 daddr @gbmc_br_int_addrs accept
ip6 saddr @gbmc_br_int_addrs accept
}
chain gbmc_br_pub_input {
ip6 nexthdr icmpv6 accept
}
}
@@ -0,0 +1,37 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${gbmc_psu_hardreset-}" ] && return
gbmc_psu_hardreset_needed=
gbmc_psu_hardreset_hook() {
# We don't always need a powercycle, allow skipping
if [ -z "${gbmc_psu_hardreset_needed-}" ]; then
echo "Skipping tray powercycle" >&2
return 0
fi
echo "Powercycling" >&2
systemctl start gbmc-psu-hardreset.target || return
# Ensure that we don't continue the DHCP process while waiting for the
# powercycle.
exit 0
}
GBMC_BR_DHCP_HOOKS+=(gbmc_psu_hardreset_hook)
gbmc_psu_hardreset=1
@@ -0,0 +1,13 @@
[Unit]
Description=gBMC DHCP Client terminator
After=network.target
StartLimitIntervalSec=10
StartLimitBurst=3
[Service]
Restart=on-failure
RestartSec=5
ExecStart=/usr/libexec/gbmc-br-dhcp-term.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,106 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
source /usr/share/network/lib.sh || exit
# Wait until a well known service is network available
echo 'Waiting for network reachability' >&2
while true; do
before=$SECONDS
addrs="$(ip addr show gbmcbr | grep '^ *inet6' | awk '{print $2}')"
for addr in $addrs; do
# Remove the prefix length
ip="${addr%/*}"
ip_to_bytes ip_bytes "$ip" || continue
# Ignore ULAs and non-gBMC addresses
(( ip_bytes[0] & 0xfc == 0xfc || ip_bytes[8] != 0xfd )) && continue
# Only allow for the short, well known addresses <pfx>:fd01:: and not
# <pfx>:fd00:83c1:292d:feef. Otherwise, powercycle may be unavailable.
(( ip_bytes[9] == 0 )) && continue
for i in {10..15}; do
(( ip_bytes[i] != 0 )) && continue 2
done
echo "Trying reachability from $ip" >&2
for i in {0..5}; do
ping -I "$ip" -c 1 -W 1 2001:4860:4860::8888 >/dev/null 2>&1 && break 3
sleep 1
done
done
# Ensure we only complete the addr lookup loop every 10s
tosleep=$((before + 10 - SECONDS))
if (( tosleep > 0 )); then
sleep "$tosleep"
fi
done
# We need to guarantee we wait at least 5 minutes from reachable in
# case networking just came up
wait_min=5
echo "Network is reachable, waiting $wait_min min" >&2
sleep $((60 * wait_min))
get_dhcp_unit_json() {
busctl -j call \
org.freedesktop.systemd1 \
/org/freedesktop/systemd1/unit/gbmc_2dbr_2ddhcp_2eservice \
org.freedesktop.DBus.Properties \
GetAll s org.freedesktop.systemd1.Unit
}
# Follow the process and make sure it idles for at least 5 minutes before
# shutting down. This allows for failures and retries to happen.
while true; do
json="$(get_dhcp_unit_json)" || exit
last_ms="$(echo "$json" | jq -r '.data[0].StateChangeTimestampMonotonic.data')"
if pid="$(cat /run/gbmc-br-dhcp.pid 2>/dev/null)"; then
# If the DHCP configuration process is running, wait for it to finish
echo "DHCP still running ($pid), waiting" >&2
while [[ -e /proc/$pid ]]; do
sleep 1
done
# Wait for systemd to detect the process state change
while true; do
json="$(get_dhcp_unit_json)" || exit
ms="$(echo "$json" | jq -r '.data[0].StateChangeTimestampMonotonic.data')"
(( ms != last_ms )) && break
sleep 1
done
fi
echo 'Checking DHCP Active State' >&2
json="$(get_dhcp_unit_json)" || exit
activestr="$(echo "$json" | jq -r '.data[0].ActiveState.data')"
# The process is already stopped, we are done
[[ "$activestr" == 'inactive' ]] && exit
# If the process is running, give it at least 5 minutes from when it started
cur_s="$(cut -d' ' -f1 /proc/uptime)"
# Remove floating point if applied since bash can't perform float arithmetic
cur_s="${cur_s%.*}"
if [[ "$activestr" == 'active' ]]; then
active_ms="$(echo "$json" | jq -r '.data[0].ActiveEnterTimestampMonotonic.data')"
else
active_ms=$((cur_s*1000*1000))
fi
w=$((active_ms/1000/1000 + (wait_min*60) - cur_s))
[ "$w" -lt 0 ] && break
echo "Waiting ${w}s for DHCP process" >&2
sleep "$w"
done
echo "Stopping DHCP processing" >&2
systemctl stop --no-block gbmc-br-dhcp
@@ -0,0 +1,14 @@
[Unit]
Description=gBMC DHCP Client
After=network.target
StartLimitIntervalSec=10
StartLimitBurst=3
[Service]
Restart=on-failure
RestartSec=5
ExecCondition=/bin/bash -c "! /bin/systemctl is-active -q dhcp-done@*"
ExecStart=/usr/bin/udhcpc6 -f -q -O fqdn -O bootfile_url -O bootfile_param -i gbmcbr -s /usr/libexec/gbmc-br-dhcp.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,80 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# A list of functions which get executed for each bound DHCP lease.
# These are configured by the files included below.
# Shellcheck does not understand how this gets referenced
# shellcheck disable=SC2034
GBMC_BR_DHCP_HOOKS=()
# A dict of outstanding items that should prevent DHCP completion
declare -A GBMC_BR_DHCP_OUTSTANDING=()
# SC can't find this path during repotest
# shellcheck disable=SC1091
source /usr/share/network/lib.sh || exit
# SC can't find this path during repotest
# shellcheck disable=SC1091
source /usr/share/gbmc-br-lib.sh || exit
# Load configurations from a known location in the filesystem to populate
# hooks that are executed after each event.
gbmc_br_source_dir /usr/share/gbmc-br-dhcp || exit
# Write out the current PID and cleanup when complete
trap 'rm -f /run/gbmc-br-dhcp.pid' EXIT
echo "$$" >/run/gbmc-br-dhcp.pid
if [ "$1" = bound ]; then
# Variable is from the environment via udhcpc6
# shellcheck disable=SC2154
echo "DHCPv6(gbmcbr): $ipv6/128" >&2
pfx_bytes=()
ip_to_bytes pfx_bytes "$ipv6"
# Ensure we are a BMC and have a suffix nibble, the 0th index is reserved
if (( pfx_bytes[8] != 0xfd || pfx_bytes[9] & 0xf == 0 )); then
echo "Invalid address" >&2
exit 1
fi
# Ensure we don't have more than a /80 address
for (( i = 10; i < 16; ++i )); do
if (( pfx_bytes[i] != 0 )); then
echo "Invalid address" >&2
exit 1
fi
done
pfx="$(ip_bytes_to_str pfx_bytes)"
gbmc_br_set_ip "$pfx" || exit
if [ -n "${fqdn-}" ]; then
echo "Using hostname $fqdn" >&2
hostnamectl set-hostname "$fqdn" || true
fi
gbmc_br_run_hooks GBMC_BR_DHCP_HOOKS || exit
# If any of our hooks had expectations we should fail here
if [ "${#GBMC_BR_DHCP_OUTSTANDING[@]}" -gt 0 ]; then
echo "Not done with DHCP process: ${!GBMC_BR_DHCP_OUTSTANDING[*]}" >&2
exit 1
fi
# Ensure that the installer knows we have completed processing DHCP by
# running a service that reports completion
echo 'Start DHCP Done' >&2
systemctl start dhcp-done@DONE --no-block
fi
@@ -0,0 +1,5 @@
[Service]
ExecStart=/usr/libexec/gbmc-br-ensure-ra.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,27 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Every 30 seconds, send out an RA so that the kernel will receive a response.
# This ensures that all BMCs (even ones that think they are routers) get updated
# information from the other systems on the network.
w=30
while true; do
start=$SECONDS
rdisc6 -m gbmcbr -r 1 -w $(( w * 1000 )) >/dev/null 2>/dev/null
# If rdisc6 exits early we still want to wait the full `w` time before
# starting again.
(( timeout = start + w - SECONDS ))
sleep $(( timeout < 0 ? 0 : timeout ))
done
@@ -0,0 +1,102 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[[ -n ${gbmc_br_from_ra_lib-} ]] && return
# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
source /usr/share/network/lib.sh || exit
gbmc_br_from_ra_init=
gbmc_br_from_ra_mac=
declare -A gbmc_br_from_ra_pfxs=()
declare -A gbmc_br_from_ra_prev_addrs=()
gbmc_br_from_ra_update() {
[[ -n $gbmc_br_from_ra_init && -n $gbmc_br_from_ra_mac ]] || return
local pfx
for pfx in "${!gbmc_br_from_ra_pfxs[@]}"; do
local cidr
if ! cidr="$(ip_pfx_to_cidr "$pfx")"; then
unset 'gbmc_br_from_ra_pfxs[$pfx]'
continue
fi
if (( cidr == 80 )); then
local sfx
if ! sfx="$(mac_to_eui48 "$gbmc_br_from_ra_mac")"; then
unset 'gbmc_br_from_ra_pfxs[$pfx]'
continue
fi
local addr
if ! addr="$(ip_pfx_concat "$pfx" "$sfx")"; then
unset 'gbmc_br_from_ra_pfxs[$pfx]'
continue
fi
else
unset 'gbmc_br_from_ra_pfxs[$pfx]'
continue
fi
local valid="${gbmc_br_from_ra_pfxs["$pfx"]}"
if (( valid > 0 )); then
if [[ -z ${gbmc_br_from_ra_prev_addrs["$addr"]-} ]]; then
echo "gBMC Bridge RA Addr Add: $addr" >&2
gbmc_br_from_ra_prev_addrs["$addr"]=1
fi
ip addr replace "$addr" dev gbmcbr noprefixroute
else
if [[ -n ${gbmc_br_from_ra_prev_addrs["$addr"]-} ]]; then
echo "gBMC Bridge RA Addr Del: $addr" >&2
unset 'gbmc_br_from_ra_prev_addrs[$addr]'
fi
ip addr del "$addr" dev gbmcbr
unset 'gbmc_br_from_ra_pfxs[$pfx]'
fi
done
}
gbmc_br_from_ra_hook() {
# shellcheck disable=SC2154
if [[ $change == init ]]; then
gbmc_br_from_ra_init=1
gbmc_ip_monitor_defer
elif [[ $change == defer ]]; then
gbmc_br_from_ra_update
elif [[ $change == route && $route != *' via '* ]] &&
[[ $route =~ ^(.* dev gbmcbr proto ra .*)( +expires +([^ ]+)sec).*$ ]]; then
pfx="${route%% *}"
# shellcheck disable=SC2154
if [[ $action == add ]]; then
gbmc_br_from_ra_pfxs["$pfx"]="${BASH_REMATCH[3]}"
gbmc_ip_monitor_defer
elif [[ $action == del ]]; then
gbmc_br_from_ra_pfxs["$pfx"]=0
gbmc_ip_monitor_defer
fi
elif [[ $change == link && $intf == gbmcbr ]]; then
rdisc6 -m gbmcbr -r 1 -w 100 >/dev/null 2>&1
if [[ $action == add && $mac != "$gbmc_br_from_ra_mac" ]]; then
gbmc_br_from_ra_mac="$mac"
gbmc_ip_monitor_defer
fi
if [[ $action == del && $mac == "$gbmc_br_from_ra_mac" ]]; then
gbmc_br_from_ra_mac=
gbmc_ip_monitor_defer
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_br_from_ra_hook)
gbmc_br_from_ra_lib=1
@@ -0,0 +1,117 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[[ -n ${gbmc_br_gw_src_lib-} ]] && return
# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
source /usr/share/network/lib.sh || exit
gbmc_br_gw_src_ip_stateful=
gbmc_br_gw_src_ip_stateless=
declare -A gbmc_br_gw_src_routes=()
gbmc_br_gw_defgw=
gbmc_br_set_router() {
local defgw=
local route
for route in "${!gbmc_br_gw_src_routes[@]}"; do
if [[ $route != *' dev gbmcbr '* ]]; then
defgw=1
break
fi
done
[[ $defgw == "$gbmc_br_gw_defgw" ]] && return
gbmc_br_gw_defgw="$defgw"
local files=(/run/systemd/network/{00,}-bmc-gbmcbr.network.d/50-defgw.conf)
if [[ -n $defgw ]]; then
local file
for file in "${files[@]}"; do
mkdir -p "$(dirname "$file")"
printf '[IPv6PrefixDelegation]\nRouterLifetimeSec=30\n' >"$file"
done
else
rm -f "${files[@]}"
fi
if [[ $(systemctl is-active systemd-networkd) != inactive ]]; then
networkctl reload && networkctl reconfigure gbmcbr
fi
}
gbmc_br_gw_src_update() {
local gbmc_br_gw_src_ip="${gbmc_br_gw_src_ip_stateful:-$gbmc_br_gw_src_ip_stateless}"
[[ -n $gbmc_br_gw_src_ip ]] || return
local route
for route in "${!gbmc_br_gw_src_routes[@]}"; do
[[ $route != *" src $gbmc_br_gw_src_ip "* ]] || continue
echo "gBMC Bridge Updating GW source [$gbmc_br_gw_src_ip]: $route" >&2
# shellcheck disable=SC2086
ip route change $route src "$gbmc_br_gw_src_ip" && \
unset 'gbmc_br_gw_src_routes[$route]'
done
}
gbmc_br_gw_src_hook() {
# We only want to match default gateway routes that are dynamic
# (have an expiration time). These will be updated with our preferred
# source.
# shellcheck disable=SC2154
if [[ $change == route && $route == 'default '*':'* ]]; then
if [[ $route =~ ^(.*)( +expires +[^ ]+)(.*)$ ]]; then
route="${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
fi
if [[ $action == add && -z ${gbmc_br_gw_src_routes["$route"]} ]]; then
gbmc_br_gw_src_routes["$route"]=1
gbmc_br_gw_src_update
gbmc_br_set_router
elif [[ $action == del && -n "${gbmc_br_gw_src_routes["$route"]}" ]]; then
unset 'gbmc_br_gw_src_routes[$route]'
gbmc_br_gw_src_update
gbmc_br_set_router
fi
# Match only global IP addresses on the bridge that match the BMC stateless
# prefix (<mpfx>:fd00:). So 2002:af4:3480:2248:fd00:6345:3069:9186 would be
# matched as the preferred source IP for outoging traffic.
elif [[ $change == addr && $intf == gbmcbr && $scope == global ]] &&
[[ $fam == inet6 && $flags != *tentative* ]]; then
local ip_bytes=()
if ! ip_to_bytes ip_bytes "$ip"; then
echo "gBMC Bridge Ensure RA Invalid IP: $ip" >&2
return 1
fi
# Ignore ULAs and non-gBMC addresses
if (( ip_bytes[0] & 0xfe == 0xfc || ip_bytes[8] != 0xfd )); then
return 0
fi
if (( ip_bytes[9] & 0x0f != 0 )); then
local -n gbmc_br_gw_src_ip=gbmc_br_gw_src_ip_stateful
else
local -n gbmc_br_gw_src_ip=gbmc_br_gw_src_ip_stateless
fi
if [[ $action == add && $ip != "$gbmc_br_gw_src_ip" ]]; then
gbmc_br_gw_src_ip="$ip"
gbmc_br_gw_src_update
fi
if [[ $action == del && $ip == "$gbmc_br_gw_src_ip" ]]; then
gbmc_br_gw_src_ip=
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_br_gw_src_hook)
gbmc_br_gw_src_lib=1
@@ -0,0 +1,133 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${gbmc_br_lib_init-}" ] && return
# SC can't find this path during repotest
# shellcheck disable=SC1091
source /usr/share/network/lib.sh || exit
# A list of functions which get executed for each configured IP.
# These are configured by the files included below.
# Shellcheck does not understand how this gets referenced
# shellcheck disable=SC2034
GBMC_BR_LIB_SET_IP_HOOKS=()
gbmc_br_source_dir() {
local dir="$1"
local file
while read -r -d $'\0' file; do
# SC doesn't like dynamic source loading
# shellcheck disable=SC1090
source "$file" || return
done < <(shopt -s nullglob; for f in "$dir"/*.sh; do printf '%s\0' "$f"; done)
}
# Load configurations from a known location in the filesystem to populate
# hooks that are executed after each event.
gbmc_br_source_dir /usr/share/gbmc-br-lib || exit
gbmc_br_run_hooks() {
local -n hookvar="$1"
shift
local hook
for hook in "${hookvar[@]}"; do
"$hook" "$@" || return
done
}
gbmc_br_reload() {
if [ "$(systemctl is-active systemd-networkd)" != 'inactive' ]; then
networkctl reload && networkctl reconfigure gbmcbr
fi
}
gbmc_br_no_ip() {
echo "Runtime removing gbmcbr IP" >&2
rm -f /run/systemd/network/{00,}-bmc-gbmcbr.network.d/50-public.conf
gbmc_br_reload
}
gbmc_br_reload_ip() {
local ip="${1-}"
if [ -z "$ip" ] && ! ip="$(cat /var/google/gbmc-br-ip 2>/dev/null)"; then
echo "Ignoring unconfigured IP" >&2
gbmc_br_no_ip
return 0
fi
# Remove legacy network configuration
rm -rf /etc/systemd/network/{00,}-bmc-gbmcbr.network.d
local pfx_bytes=()
if ! ip_to_bytes pfx_bytes "$ip"; then
echo "Ignoring Invalid IPv6: $ip" >&2
gbmc_br_no_ip
return 0
fi
local pfx
pfx="$(ip_bytes_to_str pfx_bytes)"
(( pfx_bytes[9] &= 0xf0 ))
local stateless_pfx
stateless_pfx="$(ip_bytes_to_str pfx_bytes)"
local contents
read -r -d '' contents <<EOF
[Network]
Address=$pfx/128
[IPv6Prefix]
Prefix=$stateless_pfx/80
PreferredLifetimeSec=60
ValidLifetimeSec=60
[IPv6RoutePrefix]
Route=$pfx/80
LifetimeSec=60
[Route]
Destination=$stateless_pfx/76
Type=unreachable
Metric=1024
EOF
echo "Runtime setting gbmcbr IP: $pfx" >&2
local file
for file in /run/systemd/network/{00,}-bmc-gbmcbr.network.d/50-public.conf; do
mkdir -p "$(dirname "$file")"
printf '%s' "$contents" >"$file"
done
gbmc_br_reload
}
gbmc_br_set_ip() {
local ip="${1-}"
local old_ip=
if [ -n "$ip" ]; then
old_ip="$(cat /var/google/gbmc-br-ip 2>/dev/null)"
[ "$old_ip" == "$ip" ] && return
mkdir -p /var/google || return
echo "$ip" >/var/google/gbmc-br-ip || return
else
[ ! -f "/var/google/gbmc-br-ip" ] && return
rm -rf /var/google/gbmc-br-ip
fi
gbmc_br_run_hooks GBMC_BR_LIB_SET_IP_HOOKS "$ip" || return
gbmc_br_reload_ip "$ip"
}
gbmc_br_lib_init=1
@@ -0,0 +1,9 @@
[Unit]
Before=gbmc-ip-monitor.service
Before=systemd-networkd.service
[Service]
ExecStart=/bin/bash -c 'source /usr/share/gbmc-br-lib.sh && gbmc_br_reload_ip'
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,74 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[[ -n ${gbmc_br_nft_lib-} ]] && return
# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
source /usr/share/network/lib.sh || exit
gbmc_br_nft_pfx=
gbmc_br_nft_update() {
printf 'gBMC Bridge input firewall for %s\n' \
"${gbmc_br_nft_pfx:-(deleted)}" >&2
local contents=
contents+='table inet filter {'$'\n'
contents+=' chain gbmc_br_int_input {'$'\n'
if [[ -n ${gbmc_br_nft_pfx-} ]]; then
contents+=" ip6 saddr $gbmc_br_nft_pfx"
contents+=" ip6 daddr $gbmc_br_nft_pfx accept"$'\n'
fi
contents+=' }'$'\n'
contents+='}'$'\n'
local rfile=/run/nftables/40-gbmc-br-int.rules
mkdir -p "$(dirname "$rfile")"
printf '%s' "$contents" >"$rfile"
# shellcheck disable=SC2015
systemctl reset-failed nftables && systemctl --no-block reload-or-restart nftables || true
}
gbmc_br_nft_hook() {
# Match only global IP addresses on the bridge that match the BMC prefix
# (<mpfx>:fdxx:). So 2002:af4:3480:2248:fd02:6345:3069:9186 would become
# a 2002:af4:3480:2248:fd00/76 rule.
# shellcheck disable=SC2154
if [[ $change == addr && $intf == gbmcbr && $scope == global ]] &&
[[ $fam == inet6 && $flags != *tentative* ]]; then
local ip_bytes=()
if ! ip_to_bytes ip_bytes "$ip"; then
echo "gBMC Bridge NFT Invalid IP: $ip" >&2
return 1
fi
if (( ip_bytes[8] != 0xfd )); then
return 0
fi
local i
for (( i=9; i<16; i++ )); do
ip_bytes["$i"]=0
done
pfx="$(ip_bytes_to_str ip_bytes)/76"
if [[ $action == add && $pfx != "$gbmc_br_nft_pfx" ]]; then
gbmc_br_nft_pfx="$pfx"
gbmc_br_nft_update
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_br_nft_hook)
gbmc_br_nft_lib=1
@@ -0,0 +1,74 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[[ -n ${gbmc_br_ula_lib-} ]] && return
# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
source /usr/share/network/lib.sh || exit
gbmc_br_ula_init=
gbmc_br_ula_mac=
gbmc_br_ula_update() {
[[ -n $gbmc_br_ula_init ]] || return
echo "gBMC Bridge ULA MAC: ${gbmc_br_ula_mac:-(deleted)}" >&2
local addr=
contents='[Network]'$'\n'
if [[ -n $gbmc_br_ula_mac ]]; then
local sfx
if sfx="$(mac_to_eui64 "$gbmc_br_ula_mac")" &&
addr="$(ip_pfx_concat "fdb5:0481:10ce::/64" "$sfx")"; then
contents+="Address=$addr"$'\n'
fi
fi
local netfile
for netfile in /run/systemd/network/{00,}-bmc-gbmcbr.network.d/60-ula.conf; do
mkdir -p "$(dirname "$netfile")"
printf '%s' "$contents" >"$netfile"
done
# Ensure that systemd-networkd performs a reconfiguration as it doesn't
# currently check the mtime of drop-in files.
touch -c /lib/systemd/network/*-bmc-gbmcbr.network
if [[ $(systemctl is-active systemd-networkd) != inactive ]]; then
networkctl reload
networkctl reconfigure gbmcbr
fi
}
gbmc_br_ula_hook() {
# shellcheck disable=SC2154
if [[ $change == init ]]; then
gbmc_br_ula_init=1
gbmc_br_ula_update
elif [[ $change == link && $intf == gbmcbr ]]; then
if [[ $action == add && $mac != "$gbmc_br_ula_mac" ]]; then
gbmc_br_ula_mac="$mac"
gbmc_br_ula_update
fi
if [[ $action == del && $mac == "$gbmc_br_ula_mac" ]]; then
gbmc_br_ula_mac=
gbmc_br_ula_update
fi
fi
}
GBMC_IP_MONITOR_HOOKS+=(gbmc_br_ula_hook)
gbmc_br_ula_lib=1
@@ -0,0 +1,20 @@
#!/bin/bash
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# stop dhcp term service to prevent race condition
systemctl is-active --quiet gbmc-br-dhcp-term && systemctl stop gbmc-br-dhcp-term
# start the dhcp service
systemctl start gbmc-br-dhcp
@@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name>@NAME@</name>
<service>
<type>_ipmi._udp</type>
<port>623</port>
<txt-record>Machine=@MACHINE@</txt-record>
@EXTRA_ATTRS@
</service>
</service-group>
@@ -0,0 +1,35 @@
SUMMARY = "Allows hooking netlink events to perform network actions"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
inherit systemd
SRC_URI += " \
file://gbmc-ip-monitor.service \
file://gbmc-ip-monitor.sh \
file://gbmc-ip-monitor-test.sh \
"
S = "${WORKDIR}"
DEPENDS += "test-sh"
RDEPENDS:${PN} += " \
bash \
iproute2 \
"
SYSTEMD_SERVICE:${PN} += "gbmc-ip-monitor.service"
do_compile() {
SYSROOT="$PKG_CONFIG_SYSROOT_DIR" bash gbmc-ip-monitor-test.sh || exit
}
do_install:append() {
install -d -m0755 ${D}${libexecdir}
install -m0755 gbmc-ip-monitor.sh ${D}${libexecdir}/
install -d -m0755 ${D}${systemd_system_unitdir}
install -m0644 gbmc-ip-monitor.service ${D}${systemd_system_unitdir}/
}
@@ -0,0 +1,30 @@
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
inherit systemd
RDEPENDS:${PN} += "iperf3"
SRC_URI += "file://iperf3.service"
SYSTEMD_SERVICE:${PN} += "iperf3.service"
do_install() {
# Install service definitions
install -d -m 0755 ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/iperf3.service ${D}${systemd_system_unitdir}
}
# Allow IPERF3 to run on the gbmcbr node on DEV builds
do_install:append:dev() {
nftables_dir=${D}${sysconfdir}/nftables
rules=$nftables_dir/50-gbmc-iperf3-dev.rules
install -d -m0755 $nftables_dir
echo 'table inet filter {' >"$rules"
echo ' chain gbmc_br_pub_input {' >>"$rules"
echo ' tcp dport 5201 accept' >>"$rules"
echo ' }' >>"$rules"
echo '}' >>"$rules"
}
@@ -0,0 +1,50 @@
SUMMARY = "Configures MAC addresses on a gBMC system"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
inherit systemd
SRC_URI += " \
file://gbmc-mac-config.service \
file://gbmc-mac-config.sh.in \
"
S = "${WORKDIR}"
RDEPENDS:${PN} += " \
bash \
ipmi-fru-sh \
"
FILES:${PN} += "${systemd_unitdir}"
SYSTEMD_SERVICE:${PN} += "gbmc-mac-config.service"
GBMC_MAC_EEPROM_OF_NAME ?= ""
# Maps the MAC address offset from the base address to an interface name
# in bash associative array syntax.
# Ex. "[0]=eth0 [2]=eth2"
GBMC_MAC_IF_MAP ?= ""
do_install:append() {
if [ -z '${GBMC_MAC_EEPROM_OF_NAME}' ]; then
echo 'Missing GBMC_MAC_EEPROM_OF_NAME' >&2
exit 1
fi
# Build time dictionary sanity check
bash -c "declare -A dict=(${GBMC_MAC_IF_MAP})"
sed gbmc-mac-config.sh.in \
-e 's#@EEPROM@#${GBMC_MAC_EEPROM_OF_NAME}#' \
-e "s#@NUM_TO_INTFS@#${GBMC_MAC_IF_MAP}#" \
>gbmc-mac-config.sh
install -d -m0755 ${D}${libexecdir}
install -m0755 gbmc-mac-config.sh ${D}${libexecdir}/
install -d -m0755 ${D}${systemd_system_unitdir}
install -m0644 gbmc-mac-config.service ${D}${systemd_system_unitdir}/
}
@@ -0,0 +1,34 @@
SUMMARY = "Rename the network device name"
PR = "r1"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
GBMC_ETHER_MAP ?= ""
inherit systemd
S = "${WORKDIR}"
FILES:${PN} += "${systemd_unitdir}"
do_install() {
netdir=${D}${systemd_unitdir}/network
install -d -m0755 $netdir
# install dev renaming files if any
if [ -z "${GBMC_ETHER_MAP}"]; then
return
fi
devmap="${GBMC_ETHER_MAP}"
for str in $devmap
do
devaddr="$(echo "${str}" | cut -d'|' -f1)"
devname="$(echo "${str}" | cut -d'|' -f2)"
echo "[Match]" > ${WORKDIR}/30-netdev-${devname}.link
echo "Path=*-${devaddr}" >> ${WORKDIR}/30-netdev-${devname}.link
echo "[Link]" >> ${WORKDIR}/30-netdev-${devname}.link
echo "Name=${devname}" >> ${WORKDIR}/30-netdev-${devname}.link
install -m0644 ${WORKDIR}/30-netdev-${devname}.link ${netdir}
done
}
@@ -0,0 +1,31 @@
divert(-1)
define(`HOST_MAC_ARG', `ifelse($1, `invalid', `',
ifelse($1, `', `',
` --host-mac "$1"'))')
define(`DEV_MAC_ARG', `ifelse($1, `invalid', `',
ifelse($1, `', `',
` --dev-mac "$1"'))')
divert(0)dnl
dnl
[Unit]
Description=USB Gadget
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=M_SCRIPT_INSTALL_DIR/usb_network.sh \
--product-id "M_BMC_USB_PRODUCT_ID" \
--product-name "M_BMC_USB_PRODUCT_NAME" \
--dev-type "M_BMC_USB_TYPE" \
HOST_MAC_ARG(M_BMC_USB_HOST_MAC) \
DEV_MAC_ARG(M_BMC_USB_DEV_MAC) \
--iface-name "M_BMC_USB_IFACE" \
--bind-device "M_BMC_USB_BIND_DEV"
ExecStop=M_SCRIPT_INSTALL_DIR/usb_network.sh stop \
--dev-type "M_BMC_USB_TYPE" \
--iface-name "M_BMC_USB_IFACE"
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,231 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# List of options the script accepts. Trailing column means that the option
# requires an argument.
ARGUMENT_LIST=(
"help"
"product-id:"
"product-name:"
"host-mac:"
"bind-device:"
"dev-mac:"
"dev-type:"
"gadget-dir-name:"
"iface-name:"
)
print_usage() {
cat <<HELP
$0 [OPTIONS] [stop|start]
Create USB Gadget Configuration
--product-id USB Product Id for the gadget.
--product-name Product name string (en) for the gadget.
--host-mac MAC address of the host part of the connection. Optional.
--dev-mac MAC address of the device (gadget) part of the connection. Optional.
--dev-type Type of gadget to instantiate. Default: "eem"
--bind-device Name of the device to bind, as listed in /sys/class/udc/
--gadget-dir-name Optional base name for gadget directory. Default: iface-name
--iface-name name of the network interface.
--help Print this help and exit.
HELP
}
gadget_start() {
# Always provide a basic network configuration
mkdir -p /run/systemd/network || return
cat >/run/systemd/network/+-bmc-"${IFACE_NAME}".network <<EOF
[Match]
Name=${IFACE_NAME}
EOF
# Add the gbmcbr configuration if this is a relevant device
if (( ID_VENDOR == 0x18d1 && ID_PRODUCT == 0x22b )); then
cat >>/run/systemd/network/+-bmc-"${IFACE_NAME}".network <<EOF
[Network]
Bridge=gbmcbr
[Bridge]
Cost=85
EOF
fi
# Ignore any failures due to systemd being unavailable at boot
networkctl reload || true
local gadget_dir="${CONFIGFS_HOME}/usb_gadget/${GADGET_DIR_NAME}"
mkdir -p "${gadget_dir}" || return
echo "${ID_VENDOR}" >"${gadget_dir}/idVendor" || return
echo "${ID_PRODUCT}" >"${gadget_dir}/idProduct" || return
local str_en_dir="${gadget_dir}/strings/0x409"
mkdir -p "${str_en_dir}" || return
echo "${STR_EN_VENDOR}" >"${str_en_dir}/manufacturer" || return
echo "${STR_EN_PRODUCT}" >"${str_en_dir}/product" || return
local config_dir="${gadget_dir}/configs/c.1"
mkdir -p "${config_dir}" || return
echo 100 > "${config_dir}/MaxPower" || return
mkdir -p "${config_dir}/strings/0x409" || return
echo "${DEV_TYPE^^}" > "${config_dir}/strings/0x409/configuration" || return
local func_dir="${gadget_dir}/functions/${DEV_TYPE}.${IFACE_NAME}"
mkdir -p "${func_dir}" || return
if [[ -n $HOST_MAC_ADDR ]]; then
echo "${HOST_MAC_ADDR}" >"${func_dir}"/host_addr || return
fi
if [[ -n $DEV_MAC_ADDR ]]; then
echo "${DEV_MAC_ADDR}" >"${func_dir}"/dev_addr || return
fi
ln -s "${func_dir}" "${config_dir}" || return
# This only works on kernel 5.12+, we have to ignore failures for now
echo "$IFACE_NAME" >"${func_dir}"/ifname || true
echo "${BIND_DEVICE}" >"${gadget_dir}"/UDC || return
# Try to reconfigure a few times in case we race with systemd-networkd
local start=$SECONDS
while (( SECONDS - start < 5 )); do
local ifname
ifname="$(<"${func_dir}"/ifname)" || return
[ "${IFACE_NAME}" = "$ifname" ] && break
ip link set dev "$ifname" down && \
ip link set dev "$ifname" name "${IFACE_NAME}" && break
sleep 1
done
ip link set dev "$IFACE_NAME" up || return
}
gadget_stop() {
local gadget_dir="${CONFIGFS_HOME}/usb_gadget/${GADGET_DIR_NAME}"
rm -f "${gadget_dir}/configs/c.1/${DEV_TYPE}.${IFACE_NAME}"
rmdir "${gadget_dir}/functions/${DEV_TYPE}.${IFACE_NAME}" \
"${gadget_dir}/configs/c.1/strings/0x409" \
"${gadget_dir}/configs/c.1" \
"${gadget_dir}/strings/0x409" \
"${gadget_dir}" || true
rm -f /run/systemd/network/+-bmc-"${IFACE_NAME}".network
networkctl reload || true
}
opts="$(getopt \
--longoptions "$(printf "%s," "${ARGUMENT_LIST[@]}")" \
--name "$(basename "$0")" \
--options "" \
-- "$@"
)"
eval set -- "$opts"
CONFIGFS_HOME=${CONFIGFS_HOME:-/sys/kernel/config}
ID_VENDOR="0x18d1" # Google
ID_PRODUCT=""
STR_EN_VENDOR="Google"
STR_EN_PRODUCT=""
DEV_MAC_ADDR=""
DEV_TYPE="eem"
HOST_MAC_ADDR=""
BIND_DEVICE=""
ACTION="start"
GADGET_DIR_NAME=""
IFACE_NAME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--product-id)
ID_PRODUCT=$2
shift 2
;;
--product-name)
STR_EN_PRODUCT=$2
shift 2
;;
--host-mac)
HOST_MAC_ADDR=$2
shift 2
;;
--dev-mac)
DEV_MAC_ADDR=$2
shift 2
;;
--dev-type)
DEV_TYPE=$2
shift 2
;;
--bind-device)
BIND_DEVICE=$2
shift 2
;;
--gadget-dir-name)
GADGET_DIR_NAME=$2
shift 2
;;
--iface-name)
IFACE_NAME=$2
shift 2
;;
--help)
print_usage
exit 0
;;
start)
ACTION="start"
shift 1
break
;;
stop)
ACTION="stop"
shift 1
break
;;
--)
shift 1
;;
*)
break
;;
esac
done
if [ -z "$GADGET_DIR_NAME" ]; then
GADGET_DIR_NAME="$IFACE_NAME"
fi
if [[ $ACTION == "stop" ]]; then
gadget_stop
else
if [ -z "$ID_PRODUCT" ]; then
echo "Product ID is missing" >&2
exit 1
fi
if [ -z "$IFACE_NAME" ]; then
echo "Interface name is missing" >&2
exit 1
fi
if [ -z "$BIND_DEVICE" ]; then
echo "Bind device is missing" >&2
exit 1
fi
rc=0
gadget_start || rc=$?
(( rc == 0 )) || gadget_stop || true
exit $rc
fi
@@ -0,0 +1,290 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TEMPDIRS=()
# Script under test
SUT=$PWD/usb_network.sh
TEST_STATUS="OK"
test_setup() {
echo -n "Testing $1 ..."
FAKE_CONFIGFS="$(mktemp -d)"
TEMPDIRS+=("${FAKE_CONFIGFS}")
FAKE_GADGETFS="$FAKE_CONFIGFS"/usb_gadget
mkdir -p "$FAKE_GADGETFS"
}
test_teardown() {
echo ${TEST_STATUS}
rm -rf -- "${TEMPDIRS[@]}"
TEMPDIRS=()
}
test_fail() {
echo -n " $* " >&2
TEST_STATUS=FAIL
test_teardown
exit 1
}
check_file_content() {
local filename="$1"
local expected_content="$2"
if [[ ! -f ${filename} ]]; then
test_fail "File ${filename} does not exist!"
fi
local actual_content
actual_content=$(<"${filename}")
if [[ $expected_content != "$actual_content" ]]; then
test_fail "Expected ${expected_content}, got ${actual_content}"
fi
}
test_gadget_creation_with_defaults() {
local extra_args=()
local gadget_dir="$1"
if [[ $gadget_dir == "" ]]; then
gadget_dir="g1";
else
extra_args+=(--gadget-dir-name "${gadget_dir}")
fi
local product_name="Souvenier BMC"
local product_id="0xcafe"
local host_mac="ab:cd:ef:10:11:12"
local dev_mac="12:11:10:ef:cd:ab"
local bind_device="f80002000.udc"
if ! CONFIGFS_HOME="${FAKE_CONFIGFS}" "${SUT}" --product-id "${product_id}" \
--product-name "${product_name}" \
--host-mac "${host_mac}" \
--dev-mac "${dev_mac}" \
--bind-device "${bind_device}" \
"${extra_args[@]}"; then
test_fail "${SUT} failed"
fi
if [[ ! -d ${FAKE_GADGETFS}/${gadget_dir} ]]; then
test_fail "Gadget was not created!"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idVendor" "0x18d1"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idProduct" "${product_id}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/manufacturer" "Google"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/product" "${product_name}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/MaxPower" "100"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/strings/0x409/configuration" "EEM"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/dev_addr" "${dev_mac}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/host_addr" "${host_mac}"
if [[ ! -d ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0 ]]; then
test_fail "Function directory was not created"
fi
local func_link="${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/ee.usb0"
if [[ ! -L ${func_link} ]]; then
test_fail "Symlink to the function was not created in the config"
fi
local link_dest
link_dest="$(realpath "${func_link}")"
if [[ $link_dest != "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0" ]]; then
test_fail "Symlink points to the wrong file/dir"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/UDC" "${bind_device}"
}
test_gadget_creation_with_override() {
mkdir -p "${FAKE_GADGETFS}"/g1/{strings,configs,functions}
touch "${FAKE_GADGETFS}"/g1/{idVendor,idProduct}
test_gadget_creation_with_defaults
}
test_gadget_stopping() {
local extra_args=()
local gadget_dir="$1"
local iface_name="$2"
if [[ $gadget_dir == "" ]]; then
gadget_dir="g1";
else
extra_args+=(--gadget-dir-name "${gadget_dir}")
fi
if [[ $iface_name == "" ]]; then
iface_name="usb0";
else
extra_args+=(--iface-name "${iface_name}")
fi
CONFIGFS_HOME=${FAKE_CONFIGFS} ${SUT} "${extra_args[@]}" stop
if test -d "${FAKE_GADGETFS}/${gadget_dir}"; then
test_fail "Gadget was not removed!"
fi
}
test_gadget_creation_no_macs() {
local gadget_dir="g1";
local product_name="Souvenier BMC"
local product_id="0xcafe"
local bind_device="f80002000.udc"
CONFIGFS_HOME=${FAKE_CONFIGFS} ${SUT} --product-id "${product_id}" \
--product-name "${product_name}" \
--bind-device "${bind_device}"
if test $? -ne 0; then
test_fail "${SUT} failed"
fi
if ! test -d "${FAKE_GADGETFS}/${gadget_dir}"; then
test_fail "Gadget was not created!"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idVendor" "0x18d1"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idProduct" "${product_id}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/manufacturer" "Google"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/product" "${product_name}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/MaxPower" "100"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/strings/0x409/configuration" "EEM"
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/dev_addr ]]; then
test_fail "dev_addr should not be set"
fi
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/host_addr ]]; then
test_fail "host_addr should not be set"
fi
local func_link="${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/eem.usb0"
if [[ ! -L ${func_link} ]]; then
test_fail "Symlink to the function was not created in the config"
fi
local link_dest
link_dest="$(realpath "${func_link}")"
if [[ $link_dest != ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0 ]]; then
test_fail "Symlink points to the wrong file/dir"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/UDC" "${bind_device}"
}
test_gadget_creation_alt_iface() {
local gadget_dir="g1";
local product_name="Souvenier BMC"
local product_id="0xcafe"
local bind_device="f80002000.udc"
local iface_name="iface0"
if ! CONFIGFS_HOME=${FAKE_CONFIGFS} ${SUT} --product-id "${product_id}" \
--product-name "${product_name}" \
--bind-device "${bind_device}" \
--iface-name "${iface_name}"; then
test_fail "${SUT} failed"
fi
if [[ ! -d "${FAKE_GADGETFS}/${gadget_dir}" ]]; then
test_fail "Gadget was not created!"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idVendor" "0x18d1"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idProduct" "${product_id}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/manufacturer" "Google"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/product" "${product_name}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/MaxPower" "100"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/strings/0x409/configuration" "EEM"
if [[ ! -d ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name} ]]; then
test_fail "Function directory was not created"
fi
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name}/dev_addr ]]; then
test_fail "dev_addr should not be set"
fi
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name}/host_addr ]]; then
test_fail "host_addr should not be set"
fi
local func_link="${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/eem.${iface_name}"
if [[ ! -L ${func_link} ]]; then
test_fail "Symlink to the function was not created in the config"
fi
local link_dest
link_dest="$(realpath "${func_link}")"
if [[ $link_dest != "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name}" ]]; then
test_fail "Symlink points to the wrong file/dir"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/UDC" "${bind_device}"
}
# -------------------------------------------------------------------
test_setup "Device Creation"
test_gadget_creation_with_defaults
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation With Override"
test_gadget_creation_with_override
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Test Device Stop"
test_gadget_creation_with_defaults
test_gadget_stopping
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation/Stopping, Alternative Name"
test_gadget_creation_with_defaults "gAlt"
test_gadget_stopping "gAlt"
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation without MAC Addrs"
test_gadget_creation_no_macs
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation/Stopping, Alternative Interface"
test_gadget_creation_alt_iface
test_teardown
# -------------------------------------------------------------------
echo "SUCCESS!"
@@ -0,0 +1,57 @@
SUMMARY = "Google USB EEM Gadget Configuration Script"
DESCRIPTION = "Google USB EEM Gadget Configuration Script"
PR = "r1"
PV = "0.2"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
FILESEXTRAPATHS:prepend = "${THISDIR}/${PN}:"
inherit systemd
DEPENDS += "m4-native"
DEPENDS += "systemd"
RDEPENDS:${PN} += "bash"
BMC_USB_ECM_PRODUCT_ID ??= ""
BMC_USB_ECM_PRODUCT_NAME ??= "${MACHINE} BMC"
BMC_USB_ECM_HOST_MAC ??= "invalid"
BMC_USB_ECM_DEV_MAC ??= "invalid"
BMC_USB_ECM_BIND_DEV ??= ""
BMC_USB_TYPE ??= "eem"
BMC_USB_IFACE ??= "gusb0"
SRC_URI += "file://usb_network.service.m4"
SRC_URI += "file://usb_network.sh"
SYSTEMD_PACKAGES = "${PN}"
SYSTEMD_SERVICE:${PN} = "${@'usb_network.service' if d.getVar('BMC_USB_ECM_PRODUCT_ID') else ''}"
do_compile:append() {
if [ -n "${BMC_USB_ECM_PRODUCT_ID}" ]; then
test "X${BMC_USB_ECM_PRODUCT_NAME}" != "X" || bberror "Please define BMC_USB_ECM_PRODUCT_NAME"
test "X${BMC_USB_ECM_BIND_DEV}" != "X" || bberror "Please define BMC_USB_ECM_BIND_DEV"
m4 \
-DM_BMC_USB_PRODUCT_ID="${BMC_USB_ECM_PRODUCT_ID}" \
-DM_BMC_USB_PRODUCT_NAME="${BMC_USB_ECM_PRODUCT_NAME}" \
-DM_BMC_USB_TYPE="${BMC_USB_TYPE}" \
-DM_BMC_USB_HOST_MAC="${BMC_USB_ECM_HOST_MAC}" \
-DM_BMC_USB_DEV_MAC="${BMC_USB_ECM_DEV_MAC}" \
-DM_BMC_USB_IFACE="${BMC_USB_IFACE}" \
-DM_BMC_USB_BIND_DEV="${BMC_USB_ECM_BIND_DEV}" \
-DM_SCRIPT_INSTALL_DIR="${bindir}" \
${WORKDIR}/usb_network.service.m4 > ${WORKDIR}/usb_network.service
fi
}
do_install:append() {
install -d ${D}/${bindir}
install -m 0755 ${WORKDIR}/usb_network.sh ${D}/${bindir}
if [ -n "${BMC_USB_ECM_PRODUCT_ID}" ]; then
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/usb_network.service ${D}${systemd_system_unitdir}
fi
}

Some files were not shown because too many files have changed in this diff Show More