meta-facebook: fix ipv6-ll address

The correct MAC address of NCSI managerd ethernet interface is obtained
during NCSI config process. It will need run extra workaround to
regenerated IPv6-LL address based on new MAC address from NCSI.

Add service for checking the MAC address is correctly get from NIC card
via NCSI, then regenerate the IPv6-LL address.

Tested on Harma:
```
root@harma:~# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 10:70:FD:E4:3F:63
          inet addr:10.10.12.110  Bcast:10.10.255.255  Mask:255.255.0.0
          inet6 addr: fe80::1270:fdff:fee4:3f63/64 Scope:Link
          inet6 addr: 2018:5::e52e/128 Scope:Global
          inet6 addr: 2019:7::1270:fdff:fee4:3f63/64 Scope:Global
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:97642 errors:0 dropped:539 overruns:0 frame:0
          TX packets:1625 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:15804387 (15.0 MiB)  TX bytes:140035 (136.7 KiB)
          Interrupt:42
root@harma:~# systemctl status network-wait-ipv6-ll@eth0.service
* network-wait-ipv6-ll@eth0.service - Wait IPv6 link-local initialization
     Loaded: loaded (/usr/lib/systemd/system/network-wait-ipv6-ll@.service; static)
     Active: active (exited) since Tue 2024-01-09 03:33:43 PST; 38s ago
    Process: 271 ExecStart=/usr/libexec/check-ipv6-ll eth0 (code=exited, status=0/SUCCESS)
   Main PID: 271 (code=exited, status=0/SUCCESS)
        CPU: 148ms

Jan 09 03:33:41 harma systemd[1]: Starting Wait IPv6 link-local initialization...
Jan 09 03:33:41 harma check-ipv6-ll[271]: non-global mac address, start fix eth0 ipv6-ll
Jan 09 03:33:43 harma check-ipv6-ll[271]: eth0 mac: 10:70:fd:e4:3f:63
Jan 09 03:33:43 harma check-ipv6-ll[271]: expected ipv6-ll: fe80::1270:fdff:fee4:3f63
Jan 09 03:33:43 harma systemd[1]: Finished Wait IPv6 link-local initialization.
```

Signed-off-by: Potin Lai <potin.lai@quantatw.com>
Change-Id: Id3c40beda9f290d36565f0e8e146be772c9a9983
diff --git a/meta-facebook/conf/machine/include/facebook-ncsi-nic.inc b/meta-facebook/conf/machine/include/facebook-ncsi-nic.inc
new file mode 100644
index 0000000..2e93a14
--- /dev/null
+++ b/meta-facebook/conf/machine/include/facebook-ncsi-nic.inc
@@ -0,0 +1,2 @@
+MACHINE_FEATURES:append = " fb-ncsi-nic"
+MACHINEOVERRIDES:append = ":mf-fb-ncsi-nic"
diff --git a/meta-facebook/meta-harma/conf/machine/harma.conf b/meta-facebook/meta-harma/conf/machine/harma.conf
index 1599d0c..83f22a1 100644
--- a/meta-facebook/meta-harma/conf/machine/harma.conf
+++ b/meta-facebook/meta-harma/conf/machine/harma.conf
@@ -17,6 +17,7 @@
 require conf/machine/include/ast2600.inc
 require conf/machine/include/obmc-bsp-common.inc
 require conf/machine/include/facebook-tpm2.inc
+require conf/machine/include/facebook-ncsi-nic.inc
 
 FLASH_SIZE = "131072"
 
diff --git a/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll/check-ipv6-ll b/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll/check-ipv6-ll
new file mode 100644
index 0000000..154943e
--- /dev/null
+++ b/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll/check-ipv6-ll
@@ -0,0 +1,71 @@
+#!/bin/bash -e
+
+eth_intf="$1"
+exp_ll=""
+ll=""
+
+is_local_mac() {
+    if [ "$((0x${1:0:2} & 0x2))" -gt 0 ]; then
+        return 0
+    fi
+    return 1
+}
+
+mac_to_ll() {
+    IFS=':' read -r -a oct <<< "$1"
+    echo "fe80::$(printf %x $((0x${oct[0]} ^ 2)))${oct[1]}:${oct[2]}ff:fe${oct[3]}:${oct[4]}${oct[5]}" | \
+        sed -E 's/:0+/:/g; s/:{3,}/::/; s/:$/:0/'
+}
+
+get_ll() {
+    ip -6 addr show dev "$eth_intf" scope link | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d'
+}
+
+if ! is_local_mac "$(cat /sys/class/net/"$eth_intf"/address)"; then
+    echo "global mac address found"
+    exit 0;
+else
+    echo "non-global mac address, start fix $eth_intf ipv6-ll"
+fi
+
+# bring up ethernet interface to start NCSI init process
+ifconfig "$eth_intf" up
+
+for _ in {1..30}; do
+    # wait getting address from NC-SI
+    mac=$(cat /sys/class/net/"$eth_intf"/address)
+    if ! is_local_mac "$mac"; then
+        exp_ll=$(mac_to_ll "$mac")
+        echo "$eth_intf mac: $mac"
+        echo "expected ipv6-ll: $exp_ll"
+        ll=$(get_ll)
+        if [ "$ll" == "$exp_ll" ]; then
+            break
+        fi
+        # re-generate link-local address
+        ip -6 addr flush dev "$eth_intf" scope link
+        echo 1 > /proc/sys/net/ipv6/conf/"$eth_intf"/addr_gen_mode
+        echo 0 > /proc/sys/net/ipv6/conf/"$eth_intf"/addr_gen_mode
+        break;
+    fi
+    sleep 1
+done
+
+# exp_ll is not assigned if global mac address not found
+if [ -z "$exp_ll" ]; then
+    echo "no global mac address found"
+    exit 1
+fi
+
+# wait for ipv6-ll to be generated
+if [ "$ll" != "$exp_ll" ]; then
+    for _ in {1..100}; do
+        usleep 10000
+        ll=$(get_ll)
+        if [ "$ll" == "$exp_ll" ]; then
+            break;
+        fi
+    done
+fi
+
+exit 0;
diff --git a/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll/network-wait-ipv6-ll@.service b/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll/network-wait-ipv6-ll@.service
new file mode 100644
index 0000000..3ebcd21
--- /dev/null
+++ b/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll/network-wait-ipv6-ll@.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Wait IPv6 link-local initialization
+Before=systemd-networkd.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/libexec/check-ipv6-ll %i
+
+[Install]
+WantedBy=multi-user.target
diff --git a/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll_0.1.bb b/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll_0.1.bb
new file mode 100644
index 0000000..28e016e
--- /dev/null
+++ b/meta-facebook/recipes-fb/network-wait-ipv6-ll/network-wait-ipv6-ll_0.1.bb
@@ -0,0 +1,24 @@
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
+
+inherit obmc-phosphor-systemd
+
+RDEPENDS:${PN} += " bash"
+
+SRC_URI += " \
+    file://check-ipv6-ll \
+    file://network-wait-ipv6-ll@.service \
+    "
+
+do_install() {
+    install -d ${D}${libexecdir}
+    install -m 0755 ${WORKDIR}/check-ipv6-ll ${D}${libexecdir}
+}
+
+NCSI_ETH_INTF ?= "eth0"
+
+TGT = "${SYSTEMD_DEFAULT_TARGET}"
+NCSI_WAIT_IPV6_LL_INSTFMT="../network-wait-ipv6-ll@.service:${TGT}.wants/network-wait-ipv6-ll@{0}.service"
+
+SYSTEMD_SERVICE:${PN} += "network-wait-ipv6-ll@.service"
+SYSTEMD_LINK:${PN} += "${@compose_list(d, 'NCSI_WAIT_IPV6_LL_INSTFMT', 'NCSI_ETH_INTF')}"
diff --git a/meta-facebook/recipes-phosphor/images/fb-phosphor-image.inc b/meta-facebook/recipes-phosphor/images/fb-phosphor-image.inc
index 1c2281e..7172028 100644
--- a/meta-facebook/recipes-phosphor/images/fb-phosphor-image.inc
+++ b/meta-facebook/recipes-phosphor/images/fb-phosphor-image.inc
@@ -25,6 +25,9 @@
     mmc-utils \
     util-linux-blkdiscard \
     "
+OBMC_IMAGE_EXTRA_INSTALL:append:mf-fb-ncsi-nic = " \
+    network-wait-ipv6-ll \
+    "
 
 IMAGE_LINGUAS="en-us"