From f31c33e77ae337bde43c711a617cefbe98df4d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Fri, 7 Aug 2026 01:09:50 +0800 Subject: [PATCH] feat(native): add JD read-only anchor HTTP service --- ...1-jd-native-service-equivalence-staging.md | 59 +++++ ...-ANCHOR-SERVICE-QEMU-RECEIPT-20260807.hdlp | 35 +++ .../guanghu-os/native/x86_64-bios/boot.asm | 2 + .../native/x86_64-bios/ghal-virtio.asm | 240 ++++++++++++++++++ .../build-native-final-resident-candidate.sh | 1 + .../scripts/qemu-native-net-peer.py | 122 ++++++++- .../test-native-final-resident-candidate.sh | 21 +- 7 files changed, 476 insertions(+), 4 deletions(-) create mode 100644 product-source/hololake-platform/docs/adr/0171-jd-native-service-equivalence-staging.md create mode 100644 product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/NATIVE-ANCHOR-SERVICE-QEMU-RECEIPT-20260807.hdlp diff --git a/product-source/hololake-platform/docs/adr/0171-jd-native-service-equivalence-staging.md b/product-source/hololake-platform/docs/adr/0171-jd-native-service-equivalence-staging.md new file mode 100644 index 0000000..1afb899 --- /dev/null +++ b/product-source/hololake-platform/docs/adr/0171-jd-native-service-equivalence-staging.md @@ -0,0 +1,59 @@ +# ADR-0171 · 京东原生服务等价分阶段门禁 + +## 状态 + +Accepted for staged verification. Production native cutover remains `0`. + +## 为什么不能把“原生内核已常驻”当成“服务器部署完成” + +JD-FD-PRIMARY 在原生光湖 OS 常驻时已经证明: + +- GOSK/GHAL 在物理机启动; +- Linux 内核与 Linux 用户态不在场; +- 五域、代码对象、分支回执和人格索引可从原生盘面读写; +- 受保护的人类授权恢复能力有效。 + +但现有公共代码频道和导航服务由 Linux 上的 Forgejo、Node 服务及反向 SSH 前门共同提供。 +原生常驻时这条 Linux 数据面消失,`/code/` 与公共导航锚点都返回 `502`。因此内核常驻和 +生产服务等价是两个独立的二值门。 + +## 分阶段目标 + +1. `NATIVE_ANCHOR_HTTP_QEMU` + - GHAL 直接处理 TCP/IPv4; + - 原生端口 `3922` 回应 `GET /v1/anchor`; + - 返回唯一锚点 `GLW-PUBLIC-NAV-ANCHOR-001`; + - 不引入 Linux 用户态或通用套接字库。 +2. `NATIVE_ANCHOR_HTTP_PHYSICAL` + - 京东物理机原生启动; + - 由服务器外部节点完成 TCP、校验和与正文回读; + - 同一窗口验证受保护恢复。 +3. `PUBLIC_FRONT_DOOR_NATIVE_ANCHOR` + - `https://guanghulab.com/api/ai/v1/anchor` 在京东原生常驻时仍返回 `200`; + - 返回值必须与唯一公共锚点一致; + - 不允许把用户的个人腾讯云服务器静默改成生产承载节点。 +4. `NATIVE_CODE_CHANNEL_READ_ONLY` + - 原生数据面可读取仓库对象、引用和提交; + - 先提供只读发现能力,再设计带授权的写入。 +5. `NATIVE_CODE_CHANNEL_MUTATION` + - HLDP 意图、授权、对象写入、分支移动和回执形成闭环。 +6. `PRODUCTION_NATIVE_CUTOVER` + - 公共导航与代码频道全部通过物理公网门禁; + - 回滚路径与服务器自有回执可读; + - 才允许恢复原生默认启动。 + +## 当前实现边界 + +本阶段实现的是一个有界的只读 TCP 投影,不是通用 TCP/IP 栈,也不是 Forgejo 替代品。 +它从收到的帧派生对端 MAC、IP、端口和确认号,直接构造 SYN-ACK 与带 FIN 的 HTTP 200 +响应。它与现有 ICMP 控制/恢复通道并存。 + +当前正文包含稳定的唯一入口映射。它尚未在原生常驻期间自动拉取 REPO-012 的新版本, +因此“公共导航自动更新”仍需后续的原生锚点快照同步或带认证的增量更新协议。 + +## 安全决定 + +- 京东的无人值守默认启动继续保持 Linux 维护位; +- 原生候选保留为非默认的一次性门禁入口; +- 物理原生门禁失败时立即走已经证明的受保护恢复路径; +- 不用协议文件、QEMU 结果或端口发包冒充公网服务等价。 diff --git a/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/NATIVE-ANCHOR-SERVICE-QEMU-RECEIPT-20260807.hdlp b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/NATIVE-ANCHOR-SERVICE-QEMU-RECEIPT-20260807.hdlp new file mode 100644 index 0000000..72eb7cf --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/NATIVE-ANCHOR-SERVICE-QEMU-RECEIPT-20260807.hdlp @@ -0,0 +1,35 @@ +schema: guanghu.jd-native-anchor-service-qemu-receipt/v1 +receipt_id: GH-OS-JD-FD-PRIMARY-NATIVE-ANCHOR-QEMU-20260807-001 +status: PASS_100 +observed_at: 2026-08-07T01:08:24+08:00 +node_id: JD-FD-PRIMARY +source_branch: codex/jd-native-anchor-service-20260807 +candidate: + lba_start: 105 + sector_count: 29 + sha256: 2911b5f6a067efb7b5b7ec330833cb9f8a6b965b5a05802770dd27032678f32f +native_anchor_service: + transport: TCP_IPV4_OVER_GHAL_VIRTIO_NET + port: 3922 + request: GET /v1/anchor + response_schema: guanghu.native-public-anchor/v1 + anchor_id: GLW-PUBLIC-NAV-ANCHOR-001 + entry_path: LL-CMPN-0001 + world_node_id: SYS-GLW-0001 + repository_id: REPO-012 + branch: main + code_entry: https://guanghulab.com/code/bingshuo/guanghu-ice-heart + runtime: GUANGHU_OS_NATIVE +verification: + tcp_syn_ack_checksum: PASS_100 + http_response_checksum: PASS_100 + response_body_exact_match: PASS_100 + wrong_recovery_capability_kept_native_resident: PASS_100 + protected_recovery_capability_returned_hosted_linux: PASS_100 +boundary: + native_anchor_http_qemu_capability: 100 + native_anchor_http_physical_capability: 0 + public_front_door_native_anchor_equivalence: 0 + code_channel_service_equivalence: 0 + production_native_cutover: 0 +next_action: PHYSICAL_ONE_TIME_NATIVE_ANCHOR_HTTP_GATE_WITH_PROTECTED_RECOVERY diff --git a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm index d21f02a..191fa18 100644 --- a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm +++ b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm @@ -500,6 +500,8 @@ physical_proof_gestational_index_initialized: db 0 physical_proof_gestational_index_present: db 0 physical_proof_gestational_index_read_verified: db 0 physical_proof_last_completed_sequence: db 0 +physical_proof_anchor_tcp_syn_ack_tx: db 0 +physical_proof_anchor_http_response_tx: db 0 times 512 - ($ - physical_proof_sector) db 0 %endif diff --git a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm index 8ea7310..3849245 100644 --- a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm +++ b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm @@ -34,6 +34,8 @@ bits 64 %define ETHERNET_HEADER_SIZE 14 %define IPV4_HEADER_SIZE 20 %define ICMP_HEADER_SIZE 8 +%define TCP_HEADER_SIZE 20 +%define GHOS_NATIVE_ANCHOR_PORT_NETWORK 0x520f %define GHOS_LOGIN_MAGIC_OFFSET 60 %define GHOS_LOGIN_MAGIC_SIZE 16 ; JD Cloud exposes 4096-entry legacy queues. A queue of that size occupies @@ -1116,6 +1118,10 @@ ghal_net_icmp_exchange_once: and eax, ecx shl eax, 3 mov r14d, [r12 + rax + 8] +%ifdef GHOS_NATIVE_ANCHOR_SERVICE + call ghal_net_try_anchor_http + jnc .ignore_packet +%endif cmp r14d, GHOS_LOGIN_MAGIC_OFFSET + GHOS_LOGIN_MAGIC_SIZE * 2 jb .ignore_packet cmp word [VIRTIO_NET_RX_BUFFER + 22], 0x0008 @@ -1279,6 +1285,225 @@ ghal_net_checksum: xchg bl, bh ret +%ifdef GHOS_NATIVE_ANCHOR_SERVICE +; A deliberately small read-only TCP projection for the unique public anchor. +; It is not a general socket stack and it does not claim Forgejo equivalence. +; CF=0 means an anchor packet was handled; CF=1 means the caller should keep +; applying the existing native-control packet parser. +ghal_net_try_anchor_http: + cmp r14d, VIRTIO_NET_HEADER_SIZE + ETHERNET_HEADER_SIZE + IPV4_HEADER_SIZE + TCP_HEADER_SIZE + jb .not_anchor + cmp word [VIRTIO_NET_RX_BUFFER + 22], 0x0008 + jne .not_anchor + cmp byte [VIRTIO_NET_RX_BUFFER + 24], 0x45 + jne .not_anchor + cmp byte [VIRTIO_NET_RX_BUFFER + 33], 6 + jne .not_anchor + cmp dword [VIRTIO_NET_RX_BUFFER + 40], GHOS_NATIVE_IPV4_DWORD + jne .not_anchor + cmp word [VIRTIO_NET_RX_BUFFER + 46], GHOS_NATIVE_ANCHOR_PORT_NETWORK + jne .not_anchor + cmp byte [VIRTIO_NET_RX_BUFFER + 56], 0x50 + jne .not_anchor + test byte [VIRTIO_NET_RX_BUFFER + 57], 0x02 + jnz .handle_syn + cmp dword [VIRTIO_NET_RX_BUFFER + 64], 0x20544547 + jne .handled_without_reply + mov byte [rel ghal_anchor_tcp_flags], 0x19 + mov rsi, ghal_anchor_http_response + mov word [rel ghal_anchor_tcp_payload_size], ghal_anchor_http_response_end - ghal_anchor_http_response + call ghal_net_send_anchor_tcp + jc .not_anchor + mov byte [rel physical_proof_anchor_http_response_tx], 1 + mov rsi, msg_ghal_anchor_http_ready + call serial_write64 + clc + ret + +.handle_syn: + mov byte [rel ghal_anchor_tcp_flags], 0x12 + mov word [rel ghal_anchor_tcp_payload_size], 0 + call ghal_net_send_anchor_tcp + jc .not_anchor + mov byte [rel physical_proof_anchor_tcp_syn_ack_tx], 1 +.handled_without_reply: + clc + ret +.not_anchor: + stc + ret + +; Build a single TCP response from the current RX frame. The peer address, +; ports and acknowledgement are derived from that frame, keeping this native +; projection independent from a Linux network stack. +ghal_net_send_anchor_tcp: + mov rdi, VIRTIO_NET_TX_BUFFER + xor eax, eax + mov ecx, VIRTIO_NET_BUFFER_SIZE / 8 + rep stosq + xor ecx, ecx +.copy_peer_mac: + cmp ecx, 6 + jae .copy_native_mac + mov al, [VIRTIO_NET_RX_BUFFER + 16 + rcx] + mov [VIRTIO_NET_TX_BUFFER + 10 + rcx], al + inc ecx + jmp .copy_peer_mac +.copy_native_mac: + xor ecx, ecx +.copy_native_mac_loop: + cmp ecx, 6 + jae .headers + mov al, [rel physical_proof_mac + rcx] + mov [VIRTIO_NET_TX_BUFFER + 16 + rcx], al + inc ecx + jmp .copy_native_mac_loop +.headers: + mov word [VIRTIO_NET_TX_BUFFER + 22], 0x0008 + mov byte [VIRTIO_NET_TX_BUFFER + 24], 0x45 + mov byte [VIRTIO_NET_TX_BUFFER + 25], 0 + movzx eax, word [rel ghal_anchor_tcp_payload_size] + add eax, IPV4_HEADER_SIZE + TCP_HEADER_SIZE + xchg al, ah + mov [VIRTIO_NET_TX_BUFFER + 26], ax + mov word [VIRTIO_NET_TX_BUFFER + 28], 0x4147 + mov word [VIRTIO_NET_TX_BUFFER + 30], 0 + mov byte [VIRTIO_NET_TX_BUFFER + 32], 39 + mov byte [VIRTIO_NET_TX_BUFFER + 33], 6 + mov word [VIRTIO_NET_TX_BUFFER + 34], 0 + mov dword [VIRTIO_NET_TX_BUFFER + 36], GHOS_NATIVE_IPV4_DWORD + mov eax, [VIRTIO_NET_RX_BUFFER + 36] + mov [VIRTIO_NET_TX_BUFFER + 40], eax + mov word [VIRTIO_NET_TX_BUFFER + 44], GHOS_NATIVE_ANCHOR_PORT_NETWORK + mov ax, [VIRTIO_NET_RX_BUFFER + 44] + mov [VIRTIO_NET_TX_BUFFER + 46], ax + mov eax, 0x534f4847 + mov [VIRTIO_NET_TX_BUFFER + 48], eax + mov eax, [VIRTIO_NET_RX_BUFFER + 48] + bswap eax + movzx ecx, word [VIRTIO_NET_RX_BUFFER + 26] + xchg cl, ch + sub ecx, IPV4_HEADER_SIZE + TCP_HEADER_SIZE + test byte [VIRTIO_NET_RX_BUFFER + 57], 0x02 + jz .ack_payload + inc ecx +.ack_payload: + add eax, ecx + bswap eax + mov [VIRTIO_NET_TX_BUFFER + 52], eax + mov byte [VIRTIO_NET_TX_BUFFER + 56], 0x50 + mov al, [rel ghal_anchor_tcp_flags] + mov [VIRTIO_NET_TX_BUFFER + 57], al + mov word [VIRTIO_NET_TX_BUFFER + 58], 0x0040 + mov word [VIRTIO_NET_TX_BUFFER + 60], 0 + mov word [VIRTIO_NET_TX_BUFFER + 62], 0 + movzx ecx, word [rel ghal_anchor_tcp_payload_size] + test ecx, ecx + jz .checksums + mov rsi, ghal_anchor_http_response + mov rdi, VIRTIO_NET_TX_BUFFER + 64 + rep movsb +.checksums: + mov rsi, VIRTIO_NET_TX_BUFFER + 24 + mov ecx, IPV4_HEADER_SIZE + xor ebx, ebx + call ghal_net_checksum + mov [VIRTIO_NET_TX_BUFFER + 34], bx + movzx r15d, word [rel ghal_anchor_tcp_payload_size] + add r15d, TCP_HEADER_SIZE + call ghal_net_tcp_checksum + mov [VIRTIO_NET_TX_BUFFER + 60], bx + + mov qword [VIRTIO_NET_TX_QUEUE], VIRTIO_NET_TX_BUFFER + mov eax, r15d + add eax, VIRTIO_NET_HEADER_SIZE + ETHERNET_HEADER_SIZE + IPV4_HEADER_SIZE + mov dword [VIRTIO_NET_TX_QUEUE + 8], eax + mov word [VIRTIO_NET_TX_QUEUE + 12], 0 + mov word [VIRTIO_NET_TX_QUEUE + 14], 0 + movzx ecx, word [rel physical_proof_net_tx_queue_size] + test ecx, ecx + jz .tx_fail + mov eax, ecx + shl eax, 4 + mov ebx, VIRTIO_NET_TX_QUEUE + add rbx, rax + movzx r11d, word [rel ghal_net_tx_next_index] + mov eax, r11d + dec eax + mov edx, ecx + dec edx + and eax, edx + mov word [rbx + rax * 2 + 4], 0 + mov word [rbx + 2], r11w + mov eax, ecx + shl eax, 1 + lea r13, [rbx + rax + 4] + add r13, 4095 + and r13, -4096 + sfence + mov dx, [rel ghal_net_io_base] + add dx, VIRTIO_QUEUE_NOTIFY + mov ax, 1 + out dx, ax + mov ecx, 0x20000000 +.wait_tx: + cmp word [r13 + 2], r11w + je .tx_complete + pause + loop .wait_tx +.tx_fail: + stc + ret +.tx_complete: + inc word [rel ghal_net_tx_next_index] + clc + ret + +ghal_net_tcp_checksum: + xor ebx, ebx + mov rsi, VIRTIO_NET_TX_BUFFER + 36 + mov ecx, 8 + call ghal_net_checksum_add + add ebx, 6 + add ebx, r15d + mov rsi, VIRTIO_NET_TX_BUFFER + 44 + mov ecx, r15d + call ghal_net_checksum_add + jmp ghal_net_checksum_fold + +ghal_net_checksum_add: +.words: + cmp ecx, 2 + jb .tail + movzx eax, word [rsi] + xchg al, ah + add ebx, eax + add rsi, 2 + sub ecx, 2 + jmp .words +.tail: + test ecx, ecx + jz .done + movzx eax, byte [rsi] + shl eax, 8 + add ebx, eax +.done: + ret + +ghal_net_checksum_fold: + mov eax, ebx + shr eax, 16 + and ebx, 0xffff + add ebx, eax + mov eax, ebx + shr eax, 16 + and ebx, 0xffff + add ebx, eax + not bx + xchg bl, bh + ret +%endif + ghal_net_io_base: dw 0 ghal_block_io_base: dw 0 ghal_net_pci_device: dd 0 @@ -1294,6 +1519,10 @@ ghal_net_command_kind: db 0 ghal_net_matched_kind: db 0 ghal_net_expected_sequence: db 1 ghal_net_resident_retry_count: db 0 +%ifdef GHOS_NATIVE_ANCHOR_SERVICE +ghal_anchor_tcp_flags: db 0 +ghal_anchor_tcp_payload_size: dw 0 +%endif msg_ghal_net_discovered: db "GHOS_GHAL_VIRTIO_NET=DISCOVERED", 13, 10, 0 msg_ghal_block_discovered: db "GHOS_GHAL_VIRTIO_BLOCK=DISCOVERED", 13, 10, 0 @@ -1301,6 +1530,17 @@ msg_ghal_net_ready: db "GHOS_GHAL_VIRTIO_NET_QUEUE=DRIVER_OK", 13, 10, 0 msg_ghal_block_ready: db "GHOS_GHAL_VIRTIO_BLOCK_QUEUE=DRIVER_OK", 13, 10, 0 msg_ghal_arp_ready: db "GHOS_GHAL_ARP_GATEWAY=VERIFIED", 13, 10, 0 msg_ghal_icmp_login_ready: db "GHOS_GHAL_ICMP_LOGIN=VERIFIED", 13, 10, 0 +%ifdef GHOS_NATIVE_ANCHOR_SERVICE +msg_ghal_anchor_http_ready: db "GHOS_NATIVE_ANCHOR_HTTP=RESPONSE_TX", 13, 10, 0 +ghal_anchor_http_response: + db "HTTP/1.1 200 OK", 13, 10 + db "Content-Type: application/json", 13, 10 + db "Cache-Control: no-store", 13, 10 + db "Connection: close", 13, 10 + db "Content-Length: 286", 13, 10, 13, 10 + db '{"schema":"guanghu.native-public-anchor/v1","anchor_id":"GLW-PUBLIC-NAV-ANCHOR-001","entry_path":"LL-CMPN-0001","world_node_id":"SYS-GLW-0001","repository_id":"REPO-012","branch":"main","code_entry":"https://guanghulab.com/code/bingshuo/guanghu-ice-heart","runtime":"GUANGHU_OS_NATIVE"}' +ghal_anchor_http_response_end: +%endif msg_ghal_world_store_ready: db "GHOS_HLDP_WORLD_STORE=WRITE_READ_VERIFIED", 13, 10, 0 msg_ghal_code_channel_store_ready: db "GHOS_CODE_CHANNEL_STORE=WRITE_READ_VERIFIED", 13, 10, 0 msg_ghal_recovery_ready: db "GHOS_NATIVE_RECOVERY_BEACON=WRITE_READ_VERIFIED", 13, 10, 0 diff --git a/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh index a998837..dfe20a5 100755 --- a/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh +++ b/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh @@ -43,6 +43,7 @@ while IFS= read -r argument; do layout_args+=("${argument}"); done \ -dGHOS_PHYSICAL_CANDIDATE=1 \ -dGHOS_NATIVE_RESIDENT=1 \ -dGHOS_NATIVE_FINAL_RESIDENT=1 \ + -dGHOS_NATIVE_ANCHOR_SERVICE=1 \ "${native_root}/boot.asm" \ -o guanghu-os-x86_64-bios-final-resident.img ) diff --git a/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py b/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py index 04aa690..a9075ab 100644 --- a/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py +++ b/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py @@ -15,6 +15,17 @@ COMMIT_MAGIC = b"HLDP-CODE-COMMIT" BRANCH_MAGIC = b"HLDP-BRANCH-MOVE" RECOVERY_MAGIC = b"HLDP-RECOVER-OS!" NATIVE_ACK_MAGIC = b"HLDP-NATIVE-ACK!" +ANCHOR_PORT = 3922 +ANCHOR_CLIENT_PORT = 40392 +ANCHOR_CLIENT_SEQUENCE = 0x10203040 +ANCHOR_BODY = ( + b'{"schema":"guanghu.native-public-anchor/v1",' + b'"anchor_id":"GLW-PUBLIC-NAV-ANCHOR-001",' + b'"entry_path":"LL-CMPN-0001","world_node_id":"SYS-GLW-0001",' + b'"repository_id":"REPO-012","branch":"main",' + b'"code_entry":"https://guanghulab.com/code/bingshuo/guanghu-ice-heart",' + b'"runtime":"GUANGHU_OS_NATIVE"}' +) def checksum(payload: bytes) -> int: @@ -88,6 +99,62 @@ def authenticated_reply(frame: bytes, response_capability: bytes = NATIVE_ACK_MA return bytes(reply) +def tcp_frame( + flags: int, + sequence: int, + acknowledgement: int, + payload: bytes = b"", +) -> bytes: + tcp = bytearray( + struct.pack( + "!HHIIBBHHH", + ANCHOR_CLIENT_PORT, + ANCHOR_PORT, + sequence, + acknowledgement, + 5 << 4, + flags, + 16384, + 0, + 0, + ) + + payload + ) + pseudo = GATEWAY_IP + GUEST_IP + b"\0\x06" + struct.pack("!H", len(tcp)) + tcp[16:18] = struct.pack("!H", checksum(pseudo + tcp)) + ip = bytearray( + b"\x45\x00" + + struct.pack("!H", 20 + len(tcp)) + + b"\x47\x41\x00\x00\x40\x06\x00\x00" + + GATEWAY_IP + + GUEST_IP + ) + ip[10:12] = struct.pack("!H", checksum(ip)) + return GUEST_MAC + PEER_MAC + b"\x08\x00" + bytes(ip) + bytes(tcp) + + +def verify_anchor_tcp(frame: bytes, expected_flags: int) -> tuple[int, int, bytes]: + assert frame[0:6] == PEER_MAC + assert frame[6:12] == GUEST_MAC + assert frame[12:14] == b"\x08\x00" + assert frame[23] == 6 + assert frame[26:30] == GUEST_IP + assert frame[30:34] == GATEWAY_IP + assert checksum(frame[14:34]) == 0 + total_length = int.from_bytes(frame[16:18], "big") + tcp = frame[34 : 14 + total_length] + assert int.from_bytes(tcp[0:2], "big") == ANCHOR_PORT + assert int.from_bytes(tcp[2:4], "big") == ANCHOR_CLIENT_PORT + assert tcp[13] == expected_flags + pseudo = GUEST_IP + GATEWAY_IP + b"\0\x06" + struct.pack("!H", len(tcp)) + assert checksum(pseudo + tcp) == 0 + return ( + int.from_bytes(tcp[4:8], "big"), + int.from_bytes(tcp[8:12], "big"), + tcp[20:], + ) + + def main() -> None: global GUEST_IP, GATEWAY_IP, RELAY_IP parser = argparse.ArgumentParser() @@ -98,6 +165,7 @@ def main() -> None: parser.add_argument("--final-resident", action="store_true") parser.add_argument("--recovery-token-file") parser.add_argument("--login-only", action="store_true") + parser.add_argument("--anchor-http", action="store_true") parser.add_argument("--guest-ip", default="172.16.0.6") parser.add_argument("--peer-ip", default="172.16.0.1") parser.add_argument("--relay-ip", default="43.153.193.169") @@ -127,6 +195,10 @@ def main() -> None: commit_verified = False branch_verified = False recovery_verified = False + anchor_syn_sent = False + anchor_syn_ack_verified = False + anchor_get_sent = False + anchor_http_verified = False while time.monotonic() < deadline: try: @@ -139,6 +211,36 @@ def main() -> None: continue if frame[12:14] != b"\x08\x00": continue + if frame[23] == 6: + if not anchor_syn_ack_verified: + server_sequence, acknowledgement, payload = verify_anchor_tcp(frame, 0x12) + assert acknowledgement == ANCHOR_CLIENT_SEQUENCE + 1 + assert payload == b"" + anchor_syn_ack_verified = True + request = ( + b"GET /v1/anchor HTTP/1.1\r\n" + b"Host: native.guanghulab.com\r\n" + b"Connection: close\r\n\r\n" + ) + peer.sendto( + tcp_frame( + 0x18, + ANCHOR_CLIENT_SEQUENCE + 1, + server_sequence + 1, + request, + ), + qemu, + ) + anchor_get_sent = True + continue + _, acknowledgement, payload = verify_anchor_tcp(frame, 0x19) + assert acknowledgement > ANCHOR_CLIENT_SEQUENCE + 1 + header, body = payload.split(b"\r\n\r\n", 1) + assert b"HTTP/1.1 200 OK" in header + assert b"Content-Length: 286" in header + assert body == ANCHOR_BODY + anchor_http_verified = True + continue sequence = int.from_bytes(frame[40:42], "big") if sequence <= 3: magic = LOGIN_MAGIC @@ -164,7 +266,11 @@ def main() -> None: peer.sendto(ordinary_reply(frame), qemu) time.sleep(0.05) response_capability = NATIVE_ACK_MAGIC - if args.final_resident and resident_login_count >= 3: + if ( + args.final_resident + and resident_login_count >= 3 + and (not args.anchor_http or anchor_http_verified) + ): response_capability = recovery_capability recovery_verified = True reply = authenticated_reply(frame, response_capability) @@ -172,11 +278,19 @@ def main() -> None: peer.sendto(reply, qemu) if repetition < 3: time.sleep(0.01) + if ( + args.anchor_http + and args.final_resident + and sequence == 6 + and not anchor_syn_sent + ): + peer.sendto(tcp_frame(0x02, ANCHOR_CLIENT_SEQUENCE, 0), qemu) + anchor_syn_sent = True terminal = ( sequence == 3 if args.login_only - else recovery_verified + else recovery_verified and (not args.anchor_http or anchor_http_verified) if args.final_resident else sequence == 16 if args.resident @@ -199,6 +313,10 @@ def main() -> None: f"resident_login_reply_count: {resident_login_count}\n" f"recovery_reply_verified: {str(recovery_verified).lower()}\n" f"final_resident_control: {str(args.final_resident).lower()}\n" + f"anchor_syn_sent: {str(anchor_syn_sent).lower()}\n" + f"anchor_syn_ack_verified: {str(anchor_syn_ack_verified).lower()}\n" + f"anchor_get_sent: {str(anchor_get_sent).lower()}\n" + f"anchor_http_verified: {str(anchor_http_verified).lower()}\n" "recovery_selected_by_native: false\n" "login_magic: HLDP-GHOS-LOGIN!\n" ) diff --git a/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh index 50429cd..241094a 100755 --- a/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh +++ b/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh @@ -50,6 +50,7 @@ python3 "${source_root}/scripts/qemu-native-net-peer.py" \ --qemu-port "${qemu_port}" \ --receipt "${test_root}/peer.hldp" \ --final-resident \ + --anchor-http \ --recovery-token-file "${token_file}" >"${test_root}/peer.log" 2>&1 & peer_pid=$! set +e @@ -71,9 +72,13 @@ wait "${peer_pid}" peer_pid= grep -q '^final_resident_control: true$' "${test_root}/peer.hldp" -grep -q '^resident_login_reply_count: 3$' "${test_root}/peer.hldp" +awk -F': ' '$1 == "resident_login_reply_count" { found = 1; if ($2 < 3) exit 1 } END { if (!found) exit 1 }' \ + "${test_root}/peer.hldp" grep -q '^recovery_reply_verified: true$' "${test_root}/peer.hldp" grep -q '^recovery_selected_by_native: false$' "${test_root}/peer.hldp" +grep -q '^anchor_syn_ack_verified: true$' "${test_root}/peer.hldp" +grep -q '^anchor_http_verified: true$' "${test_root}/peer.hldp" +grep -q '^GHOS_NATIVE_ANCHOR_HTTP=RESPONSE_TX' "${test_root}/serial.log" grep -q '^GHOS_NATIVE_RECOVERY_BEACON=WRITE_READ_VERIFIED' "${test_root}/serial.log" grep -q '^GHOS_DISK_PROOF_OBSERVED_AFTER_RESET=LBA134' "${test_root}/serial.log" @@ -158,6 +163,15 @@ resident_runtime: code_commit_reply_verified: true branch_move_reply_verified: true sustained_login_replies_before_recovery: 2 +native_anchor_service: + tcp_syn_ack: PASS_100 + http_get_v1_anchor: PASS_100 + response_schema: guanghu.native-public-anchor/v1 + anchor_id: GLW-PUBLIC-NAV-ANCHOR-001 + entry_path: LL-CMPN-0001 + world_node_id: SYS-GLW-0001 + repository_id: REPO-012 + runtime: GUANGHU_OS_NATIVE recovery_control: selected_by_native_runtime: false per_deployment_capability_required: true @@ -167,6 +181,9 @@ recovery_control: boundary: qemu_capability: 100 physical_server_capability: 0 + native_anchor_http_qemu_capability: 100 + native_anchor_http_physical_capability: 0 + code_channel_service_equivalence: 0 final_native_residency_proven: false -next_action: PHYSICAL_ONE_TIME_FINAL_RESIDENCY_AND_PROTECTED_RECOVERY_GATE +next_action: PHYSICAL_ONE_TIME_NATIVE_ANCHOR_HTTP_GATE_WITH_PROTECTED_RECOVERY EOF