summaryrefslogtreecommitdiff
path: root/meta-google
diff options
context:
space:
mode:
Diffstat (limited to 'meta-google')
-rw-r--r--meta-google/conf/layer.conf2
-rw-r--r--meta-google/recipes-core/dropbear/dropbear/dropbear.default1
-rw-r--r--meta-google/recipes-core/dropbear/dropbear_%.bbappend7
-rw-r--r--meta-google/recipes-core/systemd/systemd_%.bbappend3
-rw-r--r--meta-google/recipes-extended/networking/sslh_git.bb2
-rw-r--r--meta-google/recipes-google/acpi-power-state-daemon/acpi-power-state-daemon_git.bb31
-rw-r--r--meta-google/recipes-google/google-misc/google-misc.inc12
-rw-r--r--meta-google/recipes-google/ipmi/metrics-ipmi-blobs_git.bb27
-rw-r--r--meta-google/recipes-google/kcs/gbmc-kcs-config.bb46
-rw-r--r--meta-google/recipes-google/kcs/google-kcsbridge_git.bb16
-rw-r--r--meta-google/recipes-google/nanopb/nanopb_0.4.5.bb24
-rw-r--r--meta-google/recipes-google/ncsi/ncsid_git.bb19
-rwxr-xr-xmeta-google/recipes-google/networking/files/gbmc-ip-monitor-test.sh181
-rw-r--r--meta-google/recipes-google/networking/files/gbmc-ip-monitor.service9
-rwxr-xr-xmeta-google/recipes-google/networking/files/gbmc-ip-monitor.sh122
-rw-r--r--meta-google/recipes-google/networking/gbmc-ip-monitor.bb35
-rw-r--r--meta-google/recipes-google/networking/network-sh.bb23
-rw-r--r--meta-google/recipes-google/networking/network-sh/lib.sh106
-rwxr-xr-xmeta-google/recipes-google/networking/network-sh/test.sh84
-rw-r--r--meta-google/recipes-google/nftables/files/nft-configure.sh1
-rw-r--r--meta-google/recipes-google/nftables/files/nftables.service2
-rw-r--r--meta-google/recipes-google/ssh/authorized-keys-comp.bb34
-rw-r--r--meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.service6
-rw-r--r--meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.sh51
-rw-r--r--meta-google/recipes-google/ssh/gbmc-dev-ssh-key.bb15
-rw-r--r--meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.priv7
-rw-r--r--meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.pub1
-rw-r--r--meta-google/recipes-google/test/test-sh.bb21
-rwxr-xr-xmeta-google/recipes-google/test/test-sh/lib.sh57
-rwxr-xr-xmeta-google/recipes-google/test/test-sh/test.sh145
-rw-r--r--meta-google/recipes-kernel/linux/files/gbmc.cfg16
-rw-r--r--meta-google/recipes-phosphor/dbus/phosphor-dbus-interfaces-mapper-config-native.bbappend2
-rw-r--r--meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend4
33 files changed, 1076 insertions, 36 deletions
diff --git a/meta-google/conf/layer.conf b/meta-google/conf/layer.conf
index 24a84800c..ed605d1e6 100644
--- a/meta-google/conf/layer.conf
+++ b/meta-google/conf/layer.conf
@@ -14,4 +14,4 @@ BBFILES_DYNAMIC += " \
BBFILE_COLLECTIONS += "google-layer"
BBFILE_PATTERN_google-layer := "^${LAYERDIR}/"
LAYERVERSION_google-layer = "1"
-LAYERSERIES_COMPAT_google-layer = "dunfell gatesgarth"
+LAYERSERIES_COMPAT_google-layer = "gatesgarth hardknott"
diff --git a/meta-google/recipes-core/dropbear/dropbear/dropbear.default b/meta-google/recipes-core/dropbear/dropbear/dropbear.default
new file mode 100644
index 000000000..e5f778145
--- /dev/null
+++ b/meta-google/recipes-core/dropbear/dropbear/dropbear.default
@@ -0,0 +1 @@
+DROPBEAR_EXTRA_ARGS=""
diff --git a/meta-google/recipes-core/dropbear/dropbear_%.bbappend b/meta-google/recipes-core/dropbear/dropbear_%.bbappend
index 20c53fecb..e93eba8b6 100644
--- a/meta-google/recipes-core/dropbear/dropbear_%.bbappend
+++ b/meta-google/recipes-core/dropbear/dropbear_%.bbappend
@@ -1,3 +1,10 @@
+FILESEXTRAPATHS_prepend_gbmc := "${THISDIR}/${PN}:"
+SRC_URI_append_gbmc = " file://dropbear.default"
+SYSTEMD_AUTO_ENABLE_${PN}_prod = "disable"
+
+FILESEXTRAPATHS_remove_gbmc_bandaid := "${THISDIR}/${PN}:"
+SYSTEMD_AUTO_ENABLE_${PN}_bandaid_prod = "enable"
+
# Allow SSH to the mgmt node on DEV builds
do_install_append_gbmc_dev() {
nftables_dir=${D}${sysconfdir}/nftables
diff --git a/meta-google/recipes-core/systemd/systemd_%.bbappend b/meta-google/recipes-core/systemd/systemd_%.bbappend
index 88065e2e0..d40168e45 100644
--- a/meta-google/recipes-core/systemd/systemd_%.bbappend
+++ b/meta-google/recipes-core/systemd/systemd_%.bbappend
@@ -3,3 +3,6 @@ PACKAGECONFIG_append_gbmc = " coredump"
# Disable timesync as we don't use it and it makes rebooting much slower
PACKAGECONFIG_remove_gbmc = "timesyncd"
+
+# We don't need any legacy sysv rc compatability
+PACKAGECONFIG_remove_gbmc = "sysvinit"
diff --git a/meta-google/recipes-extended/networking/sslh_git.bb b/meta-google/recipes-extended/networking/sslh_git.bb
index c874f4420..749ee1d41 100644
--- a/meta-google/recipes-extended/networking/sslh_git.bb
+++ b/meta-google/recipes-extended/networking/sslh_git.bb
@@ -4,7 +4,7 @@ LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263"
SRC_URI = "git://github.com/yrutschle/sslh"
-SRCREV = "de8e5725c27ba6941f47254e6fcb485e94f2de35"
+SRCREV = "b72baa0622ae2d29152dc251d7c21fe80a3052da"
S = "${WORKDIR}/git"
inherit perlnative
diff --git a/meta-google/recipes-google/acpi-power-state-daemon/acpi-power-state-daemon_git.bb b/meta-google/recipes-google/acpi-power-state-daemon/acpi-power-state-daemon_git.bb
index bf26a8c07..0755d4eab 100644
--- a/meta-google/recipes-google/acpi-power-state-daemon/acpi-power-state-daemon_git.bb
+++ b/meta-google/recipes-google/acpi-power-state-daemon/acpi-power-state-daemon_git.bb
@@ -1,28 +1,19 @@
SUMMARY = "ACPI Power/Sleep state daemon to allow host state events"
DESCRIPTION = "ACPI Power/Sleep state daemon to allow host state events"
-HOMEPAGE = "http://github.com/openbmc/google-misc"
-PR = "r1"
-PV = "1.0+git${SRCPV}"
+GOOGLE_MISC_PROJ = "acpi-power-state-daemon"
-LICENSE = "Apache-2.0"
-LIC_FILES_CHKSUM = "file://../LICENSE;md5=34400b68072d710fecd0a2940a0d1658"
+require ../google-misc/google-misc.inc
-SRC_URI += "git://github.com/openbmc/google-misc"
-SRCREV = "15fe169d1fbdd510bb9cfc9bb725baf0613350ff"
-S = "${WORKDIR}/git/acpi-power-state-daemon"
-
-inherit meson
-inherit pkgconfig
-inherit systemd
+inherit pkgconfig systemd
DEPENDS += " \
- phosphor-dbus-interfaces \
- sdbusplus \
- systemd \
- "
+ phosphor-dbus-interfaces \
+ sdbusplus \
+ systemd \
+ "
SYSTEMD_SERVICE_${PN} = " \
- acpi-power-state.service \
- host-s0-state.target \
- host-s5-state.target \
- "
+ acpi-power-state.service \
+ host-s0-state.target \
+ host-s5-state.target \
+ "
diff --git a/meta-google/recipes-google/google-misc/google-misc.inc b/meta-google/recipes-google/google-misc/google-misc.inc
new file mode 100644
index 000000000..95f52613a
--- /dev/null
+++ b/meta-google/recipes-google/google-misc/google-misc.inc
@@ -0,0 +1,12 @@
+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"
+SRCREV = "4c68ffb8b08fa4484824586ef4a981bcfabd38bb"
+
+S = "${WORKDIR}/git/subprojects/${GOOGLE_MISC_PROJ}"
+inherit meson
diff --git a/meta-google/recipes-google/ipmi/metrics-ipmi-blobs_git.bb b/meta-google/recipes-google/ipmi/metrics-ipmi-blobs_git.bb
new file mode 100644
index 000000000..4518f49b5
--- /dev/null
+++ b/meta-google/recipes-google/ipmi/metrics-ipmi-blobs_git.bb
@@ -0,0 +1,27 @@
+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}/ipmid-providers/libmetricsblob.so* \
+ ${libdir}/blob-ipmid/libmetricsblob.so* \
+ "
+BLOBIPMI_PROVIDER_LIBRARY += "libmetricsblob.so"
+
+INSANE_SKIP_${PN} += "dev-so"
+
+do_install_append() {
+ install -d ${D}/${libdir}/blob-ipmid
+ ln -s ../ipmid-providers/libmetricsblob.so ${D}/${libdir}/blob-ipmid/libmetricsblob.so.0
+}
diff --git a/meta-google/recipes-google/kcs/gbmc-kcs-config.bb b/meta-google/recipes-google/kcs/gbmc-kcs-config.bb
new file mode 100644
index 000000000..ca7110660
--- /dev/null
+++ b/meta-google/recipes-google/kcs/gbmc-kcs-config.bb
@@ -0,0 +1,46 @@
+SUMMARY = "Configures KCS 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
+
+S = "${WORKDIR}"
+
+PROVIDES += "virtual-obmc-host-ipmi-hw"
+RPROVIDES_${PN} += "virtual-obmc-host-ipmi-hw"
+
+FILES_${PN} += "${systemd_system_unitdir}"
+RDEPENDS_${PN} += "google-kcsbridge"
+
+GBMC_KCS_DEV ?= ""
+
+def systemd_escape_char(c):
+ return '\\x{:x}'.format(ord(c))
+
+def systemd_escape(unit):
+ import string
+ ret = ''
+ if len(unit) > 0 and unit[0] == '.':
+ ret += systemd_escape_char(unit[0])
+ unit = unit[1:]
+ for c in unit:
+ if c == '/':
+ ret += '-'
+ elif c not in {*string.ascii_letters, *string.digits, ':', '_', '.'}:
+ ret += systemd_escape_char(c)
+ else:
+ ret += c
+ return ret
+
+do_install_append() {
+ if [ -z '${GBMC_KCS_DEV}' ]; then
+ echo "Missing GBMC_KCS_DEV" >&2
+ exit 1
+ fi
+
+ wantdir=${D}${systemd_system_unitdir}/multi-user.target.wants
+ install -d -m0755 $wantdir
+ inst="${@systemd_escape(GBMC_KCS_DEV)}"
+ ln -sv ../kcsbridge@.service $wantdir/kcsbridge@$inst.service
+}
diff --git a/meta-google/recipes-google/kcs/google-kcsbridge_git.bb b/meta-google/recipes-google/kcs/google-kcsbridge_git.bb
new file mode 100644
index 000000000..4b3138eee
--- /dev/null
+++ b/meta-google/recipes-google/kcs/google-kcsbridge_git.bb
@@ -0,0 +1,16 @@
+SUMMARY = "Google NCSI daemon"
+DESCRIPTION = "Google NCSI daemon."
+GOOGLE_MISC_PROJ = "kcsbridge"
+
+require ../google-misc/google-misc.inc
+
+inherit systemd
+
+DEPENDS += " \
+ fmt \
+ sdbusplus \
+ sdeventplus \
+ stdplus \
+"
+
+SYSTEMD_SERVICE_${PN} += "kcsbridge@.service"
diff --git a/meta-google/recipes-google/nanopb/nanopb_0.4.5.bb b/meta-google/recipes-google/nanopb/nanopb_0.4.5.bb
new file mode 100644
index 000000000..232872e75
--- /dev/null
+++ b/meta-google/recipes-google/nanopb/nanopb_0.4.5.bb
@@ -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"
+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"
diff --git a/meta-google/recipes-google/ncsi/ncsid_git.bb b/meta-google/recipes-google/ncsi/ncsid_git.bb
index de5eb6e5b..4d74c992a 100644
--- a/meta-google/recipes-google/ncsi/ncsid_git.bb
+++ b/meta-google/recipes-google/ncsi/ncsid_git.bb
@@ -1,17 +1,10 @@
SUMMARY = "Google NCSI daemon"
DESCRIPTION = "Google NCSI daemon."
-HOMEPAGE = "http://github.com/openbmc/google-misc"
-PR = "r1"
-PV = "1.0+git${SRCPV}"
+GOOGLE_MISC_PROJ = "ncsid"
-LICENSE = "Apache-2.0"
-LIC_FILES_CHKSUM = "file://../LICENSE;md5=34400b68072d710fecd0a2940a0d1658"
+require ../google-misc/google-misc.inc
-SRC_URI += "git://github.com/openbmc/google-misc"
-SRCREV = "15fe169d1fbdd510bb9cfc9bb725baf0613350ff"
-S = "${WORKDIR}/git/ncsid"
-
-inherit meson systemd
+inherit systemd
SYSTEMD_SERVICE_${PN} += " \
dhcp4@.service \
@@ -21,13 +14,13 @@ SYSTEMD_SERVICE_${PN} += " \
nic-hostless@.target \
update-static-neighbors@.service \
update-static-neighbors@.timer \
-"
+ "
DEPENDS += " \
fmt \
sdbusplus \
stdplus \
-"
+ "
RDEPENDS_${PN} += " \
bash \
@@ -37,4 +30,4 @@ RDEPENDS_${PN} += " \
ndisc6-ndisc6 \
ndisc6-rdisc6 \
systemd \
-"
+ "
diff --git a/meta-google/recipes-google/networking/files/gbmc-ip-monitor-test.sh b/meta-google/recipes-google/networking/files/gbmc-ip-monitor-test.sh
new file mode 100755
index 000000000..8b5f3492f
--- /dev/null
+++ b/meta-google/recipes-google/networking/files/gbmc-ip-monitor-test.sh
@@ -0,0 +1,181 @@
+#!/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.
+
+cd "$(dirname "$0")"
+source gbmc-ip-monitor.sh
+if [ -e ../gbmc-ip-monitor.bb ]; then
+ source '../../test/test-sh/lib.sh'
+else
+ source "$SYSROOT/usr/share/test/lib.sh"
+fi
+
+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" -a "${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" -a "${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 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
diff --git a/meta-google/recipes-google/networking/files/gbmc-ip-monitor.service b/meta-google/recipes-google/networking/files/gbmc-ip-monitor.service
new file mode 100644
index 000000000..435eac91d
--- /dev/null
+++ b/meta-google/recipes-google/networking/files/gbmc-ip-monitor.service
@@ -0,0 +1,9 @@
+[Unit]
+Before=systemd-networkd.service
+
+[Service]
+Type=notify
+ExecStart=/usr/libexec/gbmc-ip-monitor.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/meta-google/recipes-google/networking/files/gbmc-ip-monitor.sh b/meta-google/recipes-google/networking/files/gbmc-ip-monitor.sh
new file mode 100755
index 000000000..baeff9a85
--- /dev/null
+++ b/meta-google/recipes-google/networking/files/gbmc-ip-monitor.sh
@@ -0,0 +1,122 @@
+#!/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 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
+ 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 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_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:]]+(brd[[:space:]]+[^ ]+[[:space:]]+)?'
+ scope_re='scope[[:space:]]+([^ ]+)[[:space:]]*(.*)'
+ combined_re="${pfx_re}${intf_re}${fam_re}${addr_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[6]}"
+ flags="${BASH_REMATCH[7]}"
+ 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 line || break
+ data=($line)
+ mac="${data[1]}"
+ else
+ return 2
+ fi
+}
+
+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
+
+return 0 2>/dev/null
+
+while read line; do
+ gbmc_ip_monitor_parse_line || continue
+ gbmc_ip_monitor_run_hooks || continue
+ if [ "$change" = 'init' ]; then
+ systemd-notify --ready
+ fi
+done < <(gbmc_ip_monitor_generate_init; exec ip monitor link addr route label)
diff --git a/meta-google/recipes-google/networking/gbmc-ip-monitor.bb b/meta-google/recipes-google/networking/gbmc-ip-monitor.bb
new file mode 100644
index 000000000..32804302b
--- /dev/null
+++ b/meta-google/recipes-google/networking/gbmc-ip-monitor.bb
@@ -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}/
+}
diff --git a/meta-google/recipes-google/networking/network-sh.bb b/meta-google/recipes-google/networking/network-sh.bb
new file mode 100644
index 000000000..a377b9e2a
--- /dev/null
+++ b/meta-google/recipes-google/networking/network-sh.bb
@@ -0,0 +1,23 @@
+SUMMARY = "Shell functions for manipulating network addresses"
+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"
+SRC_URI += "file://test.sh"
+S = "${WORKDIR}"
+
+DATA = "${datadir}/network"
+FILES_${PN} += "${DATA}"
+
+DEPENDS += "test-sh"
+
+do_compile() {
+ SYSROOT="$PKG_CONFIG_SYSROOT_DIR" bash test.sh || exit
+}
+
+do_install_append() {
+ install -d -m0755 ${D}${DATA}
+ install -m0644 lib.sh ${D}${DATA}/
+}
diff --git a/meta-google/recipes-google/networking/network-sh/lib.sh b/meta-google/recipes-google/networking/network-sh/lib.sh
new file mode 100644
index 000000000..f37f7196d
--- /dev/null
+++ b/meta-google/recipes-google/networking/network-sh/lib.sh
@@ -0,0 +1,106 @@
+#!/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 "${network_init-}" ] && return
+
+mac_to_bytes() {
+ local -n bytes="$1"
+ local str="$2"
+
+ # Verify that the MAC is Valid
+ [[ "$str" =~ ^[[:xdigit:]]{1,2}(:[[:xdigit:]]{1,2}){5}$ ]] || return
+
+ # Split the mac into hex bytes
+ local oldifs="$IFS"
+ IFS=:
+ local byte
+ for byte in $str; do
+ bytes+=(0x$byte)
+ done
+ IFS="$oldifs"
+}
+
+mac_to_eui48() {
+ local mac_bytes=()
+ mac_to_bytes mac_bytes "$1" || return
+
+ # Return the EUI-64 bytes in the IPv6 format
+ printf '%02x%02x:%02x%02x:%02x%02x\n' "${mac_bytes[@]}"
+}
+
+mac_to_eui64() {
+ local mac_bytes=()
+ mac_to_bytes mac_bytes "$1" || return
+
+ # Using EUI-64 conversion rules, create the suffix bytes from MAC bytes
+ # Invert bit-0 of the first byte, and insert 0xfffe in the middle.
+ local suffix_bytes=(
+ $((mac_bytes[0] ^ 1))
+ ${mac_bytes[@]:1:2}
+ $((0xff)) $((0xfe))
+ ${mac_bytes[@]:3:3}
+ )
+
+ # Return the EUI-64 bytes in the IPv6 format
+ printf '%02x%02x:%02x%02x:%02x%02x:%02x%02x\n' "${suffix_bytes[@]}"
+}
+
+ipv6_pfx_concat() {
+ local pfx="$1"
+ local sfx="$2"
+
+ # Validate the prefix
+ if ! [[ "$pfx" =~ ^(([0-9a-fA-F]{1,4}:)+):/([0-9]+)$ ]]; then
+ echo "Invalid IPv6 prefix: $pfx" >&2
+ return 1
+ fi
+ local addr="${BASH_REMATCH[1]}"
+ local cidr="${BASH_REMATCH[3]}"
+ # Ensure prefix doesn't have too many bytes
+ local nos="${addr//:/}"
+ if (( ${#addr} - ${#nos} > (cidr+7)/16 )); then
+ echo "Too many prefix bytes: $pfx" >&2
+ return 1
+ fi
+
+ # Validate the suffix
+ if ! [[ "$sfx" =~ ^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4})*$ ]]; then
+ echo "Invalid IPv6 suffix: $sfx" >&2
+ return 1
+ fi
+ # Ensure suffix doesn't have too many bytes
+ local nos="${sfx//:/}"
+ if (( ${#sfx} - ${#nos} >= (128-cidr)/16 )); then
+ echo "Too many suffix bytes: $sfx" >&2
+ return 1
+ fi
+
+ local comb="$addr:$sfx"
+ local nos="${comb//:/}"
+ if (( ${#comb} - ${#nos} == 8 )); then
+ comb="$addr$sfx"
+ fi
+ echo "$comb/$cidr"
+}
+
+ipv6_pfx_to_cidr() {
+ [[ "$1" =~ ^[0-9a-fA-F:]+/([0-9]+)$ ]] || return
+ echo "${BASH_REMATCH[1]}"
+}
+
+network_init=1
+return 0 2>/dev/null
+echo "network is a library, not executed directly" >&2
+exit 1
diff --git a/meta-google/recipes-google/networking/network-sh/test.sh b/meta-google/recipes-google/networking/network-sh/test.sh
new file mode 100755
index 000000000..57387c47c
--- /dev/null
+++ b/meta-google/recipes-google/networking/network-sh/test.sh
@@ -0,0 +1,84 @@
+#!/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.
+
+cd "$(dirname "$0")"
+if [ -e ../network-sh.bb ]; then
+ source '../../test/test-sh/lib.sh'
+else
+ source "$SYSROOT/usr/share/test/lib.sh"
+fi
+source lib.sh
+
+test_mac_to_bytes() {
+ out=()
+ expect_err 1 mac_to_bytes out ''
+ expect_err 1 mac_to_bytes out '00'
+ expect_err 1 mac_to_bytes out '12:34:56:78:90:'
+ expect_err 1 mac_to_bytes out ':12:34:56:78:90'
+ expect_err 1 mac_to_bytes out '12:34:56:78:90:0:'
+ expect_err 1 mac_to_bytes out '12:34:56:78:90:0:2'
+
+ expect_err 0 mac_to_bytes out 'a2:0:f:de:0:29'
+ expected=(0xa2 0 0xf 0xde 0 0x29)
+ for (( i=0; i < ${#expected[@]}; ++i )); do
+ expect_numeq "${out[$i]}" "${expected[$i]}"
+ done
+}
+
+test_mac_to_eui_48() {
+ str="$(mac_to_eui48 '12:34:56:78:90:af')" || fail
+ expect_streq "$str" '1234:5678:90af'
+}
+
+test_eui_64() {
+ str="$(mac_to_eui64 '12:34:56:78:90:af')" || fail
+ expect_streq "$str" '1334:56ff:fe78:90af'
+}
+
+test_ipv6_pfx_concat() {
+ # Invalid inputs
+ expect_err 1 ipv6_pfx_concat 'fd/64' '1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::' '1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01:' '1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::/a0' '1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::/64' ':1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::/64' '::'
+
+ # Too many address bits
+ expect_err 1 ipv6_pfx_concat 'fd01:1:1:1:1::/64' '1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::/64' '1:0:1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::/65' '1:1234:5678:90af'
+ expect_err 1 ipv6_pfx_concat 'fd01::/72' '1:1234:5678:90af'
+
+ str="$(ipv6_pfx_concat 'fd01::/64' '1')" || fail
+ expect_streq "$str" 'fd01::1/64'
+ str="$(ipv6_pfx_concat 'fd01::/72' '1234:5678:90af')" || fail
+ expect_streq "$str" 'fd01::1234:5678:90af/72'
+ str="$(ipv6_pfx_concat 'fd01:eeee:aaaa:cccc::/64' 'a:1234:5678:90af')" || fail
+ expect_streq "$str" 'fd01:eeee:aaaa:cccc:a:1234:5678:90af/64'
+}
+
+test_ipv6_pfx_to_cidr() {
+ expect_err 1 ipv6_pfx_to_cidr 'z/64'
+ expect_err 1 ipv6_pfx_to_cidr '64'
+
+ cidr="$(ipv6_pfx_to_cidr 'fd01::/64')" || fail
+ expect_numeq "$cidr" 64
+ cidr="$(ipv6_pfx_to_cidr 'fd01:eeee:aaaa:cccc:a:1234:5678:90af/128')" || fail
+ expect_numeq "$cidr" 128
+}
+
+return 0 2>/dev/null
+main
diff --git a/meta-google/recipes-google/nftables/files/nft-configure.sh b/meta-google/recipes-google/nftables/files/nft-configure.sh
index a82c2826f..05bb23d8b 100644
--- a/meta-google/recipes-google/nftables/files/nft-configure.sh
+++ b/meta-google/recipes-google/nftables/files/nft-configure.sh
@@ -9,6 +9,7 @@ for dir in /run/nftables /etc/nftables /usr/share/nftables; do
let i+=1
done
rc=0
+nft flush ruleset || rc=$?
for key in $(printf "%s\n" "${!basemap[@]}" | sort -r); do
echo "Executing ${basemap[$key]}" >&2
nft -f "${basemap[$key]}" || rc=$?
diff --git a/meta-google/recipes-google/nftables/files/nftables.service b/meta-google/recipes-google/nftables/files/nftables.service
index 79f0bb5b0..770a3d3ac 100644
--- a/meta-google/recipes-google/nftables/files/nftables.service
+++ b/meta-google/recipes-google/nftables/files/nftables.service
@@ -5,7 +5,7 @@ Before=network-pre.target
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/libexec/nft-configure.sh
-ExecStop=/bin/bash -c 'nft flush ruleset'
+ExecStop=/usr/sbin/nft flush ruleset
[Install]
WantedBy=multi-user.target
diff --git a/meta-google/recipes-google/ssh/authorized-keys-comp.bb b/meta-google/recipes-google/ssh/authorized-keys-comp.bb
new file mode 100644
index 000000000..81ee868a8
--- /dev/null
+++ b/meta-google/recipes-google/ssh/authorized-keys-comp.bb
@@ -0,0 +1,34 @@
+SUMMARY = "Compiles a set of authorized_keys files into a single file"
+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://authorized-keys-comp.service \
+ file://authorized-keys-comp.sh \
+ "
+
+S = "${WORKDIR}"
+
+RDEPENDS_${PN} += "bash"
+
+SYSTEMD_SERVICE_${PN} += "authorized-keys-comp.service"
+
+FILES_${PN} += "/home"
+
+AUTHORIZED_KEYS_COMP_USERS ?= "root"
+
+do_install_append() {
+ install -d -m0755 ${D}${libexecdir}
+ install -m0755 authorized-keys-comp.sh ${D}${libexecdir}/
+
+ install -d -m0755 ${D}${systemd_system_unitdir}
+ install -m0644 authorized-keys-comp.service ${D}${systemd_system_unitdir}/
+
+ for user in ${AUTHORIZED_KEYS_COMP_USERS}; do
+ install -d -m0755 ${D}/home/$user/.ssh
+ ln -sv /run/authorized_keys/$user ${D}/home/$user/.ssh/authorized_keys
+ done
+}
diff --git a/meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.service b/meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.service
new file mode 100644
index 000000000..92f9b2699
--- /dev/null
+++ b/meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.service
@@ -0,0 +1,6 @@
+[Service]
+Type=oneshot
+ExecStart=/usr/libexec/authorized-keys-comp.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.sh b/meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.sh
new file mode 100644
index 000000000..caff0a7a4
--- /dev/null
+++ b/meta-google/recipes-google/ssh/authorized-keys-comp/authorized-keys-comp.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+shopt -s nullglob
+
+# We want to iterate over all system users, check if they are opted-in to ssh
+# authorized_keys building, and then construct their keyfile
+for user in $(cut -d':' -f1 /etc/passwd); do
+ home="$(eval echo ~$user)" || continue
+ link="$(readlink $home/.ssh/authorized_keys 2>/dev/null)" || continue
+ # Users are only opted-in if they symlink to our well-known directory where
+ # the final output of this script lives.
+ if [ "$link" != "/run/authorized_keys/$user" ]; then
+ echo "Ignoring $user $home/.ssh/authorized_keys" >&2
+ continue
+ fi
+
+ echo "Updating $link" >&2
+ declare -A basemap=()
+ declare -a dirs=(
+ "/usr/share/authorized_keys.d/$user"
+ "$home/.ssh/authorized_keys.d"
+ "/run/authorized_keys.d/$user"
+ )
+ # Build a map that can be used for sorting directories by their priority
+ # and prioritizing the last listed directories over the later ones. We
+ # append a counter to ensure that there is a stable sorting mechanism for
+ # duplicate filenames. Duplicate filenames will be overridden by higher
+ # priority directories.
+ # Ex.
+ # /usr/share/authorized_keys.d/root/10-key
+ # /usr/share/authorized_keys.d/root/15-key
+ # /run/authorized_keys.d/root/10-key
+ # /run/authorized_keys.d/root/20-key
+ # Becomes
+ # ["10-key"]="/run/authorized_keys.d/root/10-key"
+ # ["15-key"]="/usr/share/authorized_keys.d/root/15-key"
+ # ["20-key"]="/run/authorized_keys.d/root/20-key"
+ for dir in "${dirs[@]}"; do
+ for file in "$dir"/*; do
+ basemap["${file##*/}"]="$file"
+ done
+ done
+ rm -f /run/authorized_keys.tmp
+ touch /run/authorized_keys.tmp
+ for key in $(printf "%s\n" "${!basemap[@]}" | sort -r); do
+ echo " Including ${basemap[$key]}" >&2
+ cat "${basemap[$key]}" >>/run/authorized_keys.tmp
+ done
+ mkdir -p /run/authorized_keys
+ mv /run/authorized_keys.tmp /run/authorized_keys/$user
+ chown $user /run/authorized_keys/$user
+done
diff --git a/meta-google/recipes-google/ssh/gbmc-dev-ssh-key.bb b/meta-google/recipes-google/ssh/gbmc-dev-ssh-key.bb
new file mode 100644
index 000000000..a9d371e70
--- /dev/null
+++ b/meta-google/recipes-google/ssh/gbmc-dev-ssh-key.bb
@@ -0,0 +1,15 @@
+SUMMARY = "Publicly exposed development SSH key"
+PR = "r1"
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
+
+SRC_URI += "file://gbmc-dev.pub"
+
+S = "${WORKDIR}"
+
+FILES_${PN} += " ${datadir}/authorized_keys.d"
+
+do_install() {
+ install -d ${D}${datadir}/authorized_keys.d/root
+ install -m 0755 ${S}/gbmc-dev.pub ${D}${datadir}/authorized_keys.d/root/50-gbmc-dev
+}
diff --git a/meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.priv b/meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.priv
new file mode 100644
index 000000000..5fe3fb720
--- /dev/null
+++ b/meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.priv
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAC+PxTCx2WyYG5iaq+GO0RArtziSb4kH/uzMZKyh5LKgAAAJAQPe9NED3v
+TQAAAAtzc2gtZWQyNTUxOQAAACAC+PxTCx2WyYG5iaq+GO0RArtziSb4kH/uzMZKyh5LKg
+AAAECx890MYHciMhMENgtQURn4zw10U3v5jAMymfFI9FaqwAL4/FMLHZbJgbmJqr4Y7REC
+u3OJJviQf+7MxkrKHksqAAAACGdibWMtZGV2AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.pub b/meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.pub
new file mode 100644
index 000000000..82eb5f63d
--- /dev/null
+++ b/meta-google/recipes-google/ssh/gbmc-dev-ssh-key/gbmc-dev.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAL4/FMLHZbJgbmJqr4Y7RECu3OJJviQf+7MxkrKHksq gbmc-dev
diff --git a/meta-google/recipes-google/test/test-sh.bb b/meta-google/recipes-google/test/test-sh.bb
new file mode 100644
index 000000000..7edbb9189
--- /dev/null
+++ b/meta-google/recipes-google/test/test-sh.bb
@@ -0,0 +1,21 @@
+SUMMARY = "Shell functions for testing shell scripts"
+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"
+SRC_URI += "file://test.sh"
+S = "${WORKDIR}"
+
+DATA = "${datadir}/test"
+FILES_${PN} += "${DATA}"
+
+do_compile() {
+ SYSROOT="$PKG_CONFIG_SYSROOT_DIR" bash test.sh || exit
+}
+
+do_install_append() {
+ install -d -m0755 ${D}${DATA}
+ install -m0644 lib.sh ${D}${DATA}/
+}
diff --git a/meta-google/recipes-google/test/test-sh/lib.sh b/meta-google/recipes-google/test/test-sh/lib.sh
new file mode 100755
index 000000000..cc04c5dbf
--- /dev/null
+++ b/meta-google/recipes-google/test/test-sh/lib.sh
@@ -0,0 +1,57 @@
+#!/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.
+
+expect_streq() {
+ local r="${2-$(cat)}"
+ [ "$1" = "$r" ] && return
+ echo " Line ${BASH_LINENO[0]} '$1' != '$r'" >&2
+ test_err=1
+}
+
+expect_numeq() {
+ (( "$1" == "$2" )) && return
+ echo " Line ${BASH_LINENO[0]} '$1' != '$2'" >&2
+ test_err=1
+}
+
+expect_err() {
+ local expected=$1
+ shift
+ local rc=0
+ "$@" || rc="$?"
+ (( rc == expected )) && return
+ echo " Line ${BASH_LINENO[0]} Status '$rc' != '$expected'" >&2
+ test_err=1
+}
+
+fail() {
+ echo " Line ${BASH_LINENO[0]} Fail" >&2
+ test_err=1
+}
+
+main() {
+ local agg_err=0
+ for f in $(declare -F | grep 'declare -f test[A-Z_]' | awk '{print $3}'); do
+ echo "[$f] Running..." >&2
+ local test_err=0
+ if "$f" && (( test_err == 0 )); then
+ echo "[$f] Success" >&2
+ else
+ echo "[$f] Failed ($?)" >&2
+ agg_err=1
+ fi
+ done
+ return $agg_err
+}
diff --git a/meta-google/recipes-google/test/test-sh/test.sh b/meta-google/recipes-google/test/test-sh/test.sh
new file mode 100755
index 000000000..c74029d17
--- /dev/null
+++ b/meta-google/recipes-google/test/test-sh/test.sh
@@ -0,0 +1,145 @@
+#!/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.
+
+source "$(dirname "$0")/lib.sh" || exit
+
+(
+ echo '## Test Pass' >&2
+ test_pass() {
+ return 0
+ }
+ main || exit
+) || exit
+
+(
+ echo '## Test Fail' >&2
+ i=0
+ test_pass1() {
+ (( i++ ))
+ return 0
+ }
+ test_fail() {
+ return 1
+ (( i++ ))
+ return 0
+ }
+ test_pass2() {
+ (( i++ ))
+ return 0
+ }
+ ! main || exit
+ (( i == 2 )) || exit
+) || exit
+
+(
+ echo '## Test Deferred Fail' >&2
+ i=0
+ test_expect_fail() {
+ test_err=1 || return
+ (( i++ ))
+ return 0
+ }
+ test_pass() {
+ (( i++ ))
+ return 0
+ }
+ ! main || exit
+ (( i == 2 )) || exit
+) || exit
+
+(
+ echo '## Test Fail' >&2
+ i=0
+ test_fail() {
+ fail 'Failed' || return
+ (( i++ ))
+ return 0
+ }
+ ! main || exit
+ (( i == 1 )) || exit
+) || exit
+
+(
+ echo '## Test Expect Err' >&2
+ i=0
+ test_expect_err() {
+ expect_err 1 false || return
+ (( i++ ))
+ return 0
+ }
+ main || exit
+ (( i == 1 )) || exit
+) || exit
+
+(
+ echo '## Test Expect Err Error' >&2
+ i=0
+ test_expect_err() {
+ expect_err 0 false || return
+ (( i++ ))
+ return 0
+ }
+ ! main || exit
+ (( i == 1 )) || exit
+) || exit
+
+(
+ echo '## Test Num EQ' >&2
+ i=0
+ test_num_eq() {
+ expect_numeq 15 0xf || return
+ expect_numeq 1 1 || return
+ (( i++ ))
+ return 0
+ }
+ main || exit
+ (( i == 1 )) || exit
+) || exit
+
+(
+ echo '## Test Num EQ Error' >&2
+ i=0
+ test_num_eq() {
+ expect_numeq 15 10 || return
+ (( i++ ))
+ return 0
+ }
+ ! main || exit
+ (( i == 1 )) || exit
+) || exit
+
+(
+ echo '## Test Str EQ' >&2
+ i=0
+ test_str_eq() {
+ expect_streq abz abz || return
+ (( i++ ))
+ return 0
+ }
+ main || exit
+ (( i == 1 )) || exit
+) || exit
+
+(
+ echo '## Test Str EQ Error' >&2
+ i=0
+ test_str_eq() {
+ expect_streq 15 0xf || return
+ (( i++ ))
+ return 0
+ }
+ ! main || exit
+ (( i == 1 )) || exit
+) || exit
diff --git a/meta-google/recipes-kernel/linux/files/gbmc.cfg b/meta-google/recipes-kernel/linux/files/gbmc.cfg
index 4385cc9eb..89e1ef9a7 100644
--- a/meta-google/recipes-kernel/linux/files/gbmc.cfg
+++ b/meta-google/recipes-kernel/linux/files/gbmc.cfg
@@ -36,3 +36,19 @@ CONFIG_DUMMY=y
CONFIG_BRIDGE_NETFILTER=y
CONFIG_NF_TABLES_BRIDGE=y
CONFIG_BRIDGE_NF_EBTABLES=y
+
+# Remove features we won't use
+CONFIG_WLAN=n
+CONFIG_SUSPEND=n
+CONFIG_USB_CHIPIDEA=n
+CONFIG_NETWORK_FILESYSTEMS=n
+CONFIG_RD_GZIP=n
+CONFIG_RD_BZIP2=n
+CONFIG_RD_LZMA=n
+CONFIG_RD_LZO=n
+CONFIG_RD_LZ4=n
+CONFIG_CRYPTO_LZO=n
+CONFIG_INPUT_LEDS=n
+CONFIG_INPUT_MOUSE=n
+CONFIG_KEYBOARD_ATKBD=n
+CONFIG_SERIO_LIBPS2=n
diff --git a/meta-google/recipes-phosphor/dbus/phosphor-dbus-interfaces-mapper-config-native.bbappend b/meta-google/recipes-phosphor/dbus/phosphor-dbus-interfaces-mapper-config-native.bbappend
new file mode 100644
index 000000000..8ba6e7c33
--- /dev/null
+++ b/meta-google/recipes-phosphor/dbus/phosphor-dbus-interfaces-mapper-config-native.bbappend
@@ -0,0 +1,2 @@
+PHOSPHOR_MAPPER_SERVICE_append = " com.google.gbmc"
+PHOSPHOR_MAPPER_INTERFACE_append = " com.google.gbmc"
diff --git a/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend b/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend
index acccde532..0c74fc8bb 100644
--- a/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend
+++ b/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend
@@ -7,7 +7,11 @@ OBMC_IMAGE_EXTRA_INSTALL_append = " phosphor-ipmi-flash"
OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = " iproute2 iproute2-ss"
OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = " gbmc-systemd-config"
OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = " gbmc-iperf3"
+OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = " authorized-keys-comp"
+OBMC_IMAGE_EXTRA_INSTALL_append_gbmc_dev = " gbmc-dev-ssh-key"
OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = \
'${@"" if not d.getVar("GBMC_NCSI_IF_NAME") else " gbmc-ncsi-config"}'
OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = \
'${@"" if not d.getVar("GBMC_MAC_EEPROM_OF_NAME") else " gbmc-mac-config"}'
+OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = \
+ '${@"" if not d.getVar("GBMC_KCS_DEV") else " gbmc-kcs-config"}'