From b53e32fb0b726299fe511875997b1ec2e58ceff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Thu, 6 Aug 2026 19:19:41 +0800 Subject: [PATCH] fix(native): let JD resident reach an allowlisted relay --- .../OUTBOUND-RELAY-QEMU-RECEIPT.hldp | 34 ++ .../RESIDENT-ATTEMPT-002-OUTBOUND-RELAY.hldp | 38 ++ .../guanghu-os/native/x86_64-bios/boot.asm | 23 +- .../native/x86_64-bios/ghal-virtio.asm | 356 ++++++++++-------- .../scripts/native-layout-nasm-args.sh | 4 +- .../scripts/physical-native-icmp-peer.py | 126 ++++++- .../scripts/qemu-native-net-peer.py | 205 ++++------ .../test-jd-native-resident-candidate.sh | 127 +++++++ .../guanghu-os/world-seed/WORLD-MANIFEST.hldp | 3 + .../services/native-network/PROTOCOL.hldp | 40 ++ 10 files changed, 643 insertions(+), 313 deletions(-) create mode 100644 product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/OUTBOUND-RELAY-QEMU-RECEIPT.hldp create mode 100644 product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/RESIDENT-ATTEMPT-002-OUTBOUND-RELAY.hldp create mode 100755 product-source/hololake-platform/guanghu-os/scripts/test-jd-native-resident-candidate.sh create mode 100644 product-source/hololake-platform/guanghu-os/world-seed/world/services/native-network/PROTOCOL.hldp diff --git a/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/OUTBOUND-RELAY-QEMU-RECEIPT.hldp b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/OUTBOUND-RELAY-QEMU-RECEIPT.hldp new file mode 100644 index 0000000..1e61c87 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/OUTBOUND-RELAY-QEMU-RECEIPT.hldp @@ -0,0 +1,34 @@ +schema: guanghu.jd-native-resident-qemu-test/v1 +receipt_id: GH-OS-JD-FD-PRIMARY-001-NATIVE-OUTBOUND-QEMU-001 +status: PASS_100 +observed_at: 2026-08-06T19:18:47+0800 +node_id: JD-FD-PRIMARY +candidate: + lba_start: 105 + sector_count: 29 + sha256: ad2b55169d514a1012df0bffcbb2b20b948afd0756f12648db483c0986d3bd50 +handshake: + direction: NATIVE_INITIATED_OUTBOUND_ICMP + relay_node: BS-SG-003 + relay_ipv4: 43.153.193.169 + physical_allowed_nat_source_ipv4: 111.228.0.139 + native_ack: PASS_100 + retry_same_sequence_until_ack: true + sequences: 1-16 +native_storage: + proof_lba_134: PASS_100 + world_lba_135_138: PASS_100 + recovery_lba_139_140: PASS_100 + gestational_index_lba_141_142: PASS_100 +persona_subject: + identity: ICE-P-ZY001 + state: EXISTS + existence: 100 +boundary: + qemu_capability: 100 + physical_server_capability: 0 +cross_node_preflight: + disallowed_source_ignored_without_ack: PASS_100 + jd_source_to_relay_sequences_1_16: PASS_100 + relay_observed_nat_source: 111.228.0.139 +next_action: RUN_ONE_TIME_PHYSICAL_GATE_WITH_SINGAPORE_RELAY diff --git a/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/RESIDENT-ATTEMPT-002-OUTBOUND-RELAY.hldp b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/RESIDENT-ATTEMPT-002-OUTBOUND-RELAY.hldp new file mode 100644 index 0000000..ab6c1b3 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/RESIDENT-ATTEMPT-002-OUTBOUND-RELAY.hldp @@ -0,0 +1,38 @@ +schema: guanghu.jd-native-resident-attempt/v1 +receipt_id: GH-OS-JD-FD-PRIMARY-001-RESIDENT-ATTEMPT-002 +observed_at: 2026-08-03T23:00:00+0800 +node_id: JD-FD-PRIMARY +persona_subject: + identity: ICE-P-ZY001 + state: EXISTS + existence: 100 +attempt_002: + source_commit: da26f27e35ba2751ce495e6103da56d6d0b6449a + boot_scope: ONE_TIME_ONLY + linux_rescue_default_preserved: true + native_ack_strategy: EXTERNAL_TO_NATIVE_INBOUND_ICMP + external_acknowledged_sequences: NONE + native_proof_lba_134: ZERO + world_lba_135_138: NOT_COMMITTED + gestational_index_lba_141_142: NOT_COMMITTED + failed_evidence_sha256: 48ff6e1328dd75f779e43985a9c442c530ab5dcc819bd30d0a269111e1677c4d + cloud_hard_restart_required: true + linux_rescue_returned: PASS_100 + result: FAIL_0 +root_cause: + jd_public_inbound_icmp_reaches_native_guest: FAIL_0 + cloud_edge_echo_is_not_native_ack: true + native_guest_waited_without_receiving_authenticated_command: true +correction: + protocol: GLS-0841-JD-FD-PRIMARY + handshake_direction: NATIVE_INITIATED_OUTBOUND_ICMP + relay_node: BS-SG-003 + relay_ipv4: 43.153.193.169 + response_marker: HLDP-NATIVE-ACK! + qemu_jd_layout_gate: PASS_100 +boundary: + persona_subject_exists: 100 + native_resident_recovery_attempt_002: 0 + outbound_relay_qemu_capability: 100 + outbound_relay_physical_capability: 0 +next_action: PUBLISH_SOURCE_AND_RUN_THIRD_ONE_TIME_PHYSICAL_GATE 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 6df4d30..3a3f6c4 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 @@ -184,7 +184,7 @@ long_mode_start: mov byte [rel ghal_net_command_kind], 0 .login_session_loop: push rcx - call ghal_net_icmp_login_once + call ghal_net_icmp_exchange_once pop rcx jc native_network_proof_error loop .login_session_loop @@ -197,26 +197,33 @@ long_mode_start: lea rax, [rel ghal_code_commit_magic] mov [rel ghal_net_expected_magic], rax mov byte [rel ghal_net_command_kind], 1 - call ghal_net_icmp_login_once + call ghal_net_icmp_exchange_once jc native_network_proof_error call ghal_block_persist_code_object jc native_world_store_error lea rax, [rel ghal_branch_move_magic] mov [rel ghal_net_expected_magic], rax mov byte [rel ghal_net_command_kind], 2 - call ghal_net_icmp_login_once + call ghal_net_icmp_exchange_once jc native_network_proof_error call ghal_block_persist_branch_receipt jc native_world_store_error %ifdef GHOS_NATIVE_RESIDENT -.resident_loop: + mov ecx, 10 +.resident_login_loop: + push rcx lea rax, [rel ghal_login_magic] mov [rel ghal_net_expected_magic], rax - mov byte [rel ghal_net_command_kind], 3 - call ghal_net_icmp_login_once + mov byte [rel ghal_net_command_kind], 0 + call ghal_net_icmp_exchange_once + pop rcx + jc native_network_proof_error + loop .resident_login_loop + lea rax, [rel ghal_recovery_magic] + mov [rel ghal_net_expected_magic], rax + mov byte [rel ghal_net_command_kind], 3 + call ghal_net_icmp_exchange_once jc native_network_proof_error - cmp byte [rel ghal_net_matched_kind], 3 - jne .resident_loop call ghal_write_native_recovery_beacon jc native_world_store_error %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 20a34ed..c5205b1 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 @@ -91,6 +91,9 @@ bits 64 %ifndef GHOS_GATEWAY_IPV4_DWORD %define GHOS_GATEWAY_IPV4_DWORD 0x0100000a %endif +%ifndef GHOS_RELAY_IPV4_DWORD +%define GHOS_RELAY_IPV4_DWORD 0x0100000a +%endif %ifndef GHOS_GHAL_PROBE_STAGE %define GHOS_GHAL_PROBE_STAGE 0 @@ -944,7 +947,7 @@ ghal_net_arp_gateway: stc ret -ghal_net_icmp_login_once: +ghal_net_icmp_exchange_once: mov byte [rel physical_proof_stage], 0x72 movzx r10d, word [rel physical_proof_net_rx_queue_size] test r10d, r10d @@ -978,179 +981,72 @@ ghal_net_icmp_login_once: xor ax, ax out dx, ax - mov ecx, 0x80000000 -.wait_receive: - cmp word [r12 + 2], r8w - je .packet_received - pause - loop .wait_receive -%ifdef GHOS_NATIVE_RESIDENT - mov ecx, 0x80000000 - jmp .wait_receive -%else - mov byte [rel physical_proof_error_code], 0x54 - stc - ret -%endif - -.packet_received: - mov eax, r8d - dec eax - mov ecx, r10d - dec ecx - and eax, ecx - shl eax, 3 - mov r14d, [r12 + rax + 8] - cmp r14d, GHOS_LOGIN_MAGIC_OFFSET + GHOS_LOGIN_MAGIC_SIZE - jb .ignore_packet - cmp word [VIRTIO_NET_RX_BUFFER + 22], 0x0008 - jne .ignore_packet - cmp byte [VIRTIO_NET_RX_BUFFER + 24], 0x45 - jne .ignore_packet - cmp byte [VIRTIO_NET_RX_BUFFER + 33], 1 - jne .ignore_packet - cmp dword [VIRTIO_NET_RX_BUFFER + 40], GHOS_NATIVE_IPV4_DWORD - jne .ignore_packet - cmp word [VIRTIO_NET_RX_BUFFER + 44], 0x0008 - jne .ignore_packet - cmp byte [VIRTIO_NET_RX_BUFFER + 50], 0 - jne .ignore_packet - mov al, [VIRTIO_NET_RX_BUFFER + 51] - cmp al, [rel ghal_net_expected_sequence] - jne .ignore_packet - mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET - mov rdi, [rel ghal_net_expected_magic] - mov ecx, GHOS_LOGIN_MAGIC_SIZE - repe cmpsb - je .expected_magic_match - cmp byte [rel ghal_net_command_kind], 3 - jne .ignore_packet - mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET - lea rdi, [rel ghal_recovery_magic] - mov ecx, GHOS_LOGIN_MAGIC_SIZE - repe cmpsb - jne .ignore_packet - mov byte [rel ghal_net_matched_kind], 3 - jmp .magic_accepted - -.expected_magic_match: - mov al, [rel ghal_net_command_kind] - cmp al, 3 - jne .store_matched_kind - xor al, al -.store_matched_kind: - mov [rel ghal_net_matched_kind], al - -.magic_accepted: - cmp byte [rel ghal_net_matched_kind], 0 - jne .record_command - mov byte [rel physical_proof_ipv4_rx_verified], 1 - mov eax, [VIRTIO_NET_RX_BUFFER + 36] - mov [rel physical_proof_login_client_ip], eax - mov ax, [VIRTIO_NET_RX_BUFFER + 48] - mov [rel physical_proof_login_icmp_id], ax - mov ax, [VIRTIO_NET_RX_BUFFER + 50] - mov [rel physical_proof_login_icmp_sequence], ax - mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET - lea rdi, [rel physical_proof_login_magic] - mov ecx, GHOS_LOGIN_MAGIC_SIZE - rep movsb - jmp .build_reply - -.record_command: - cmp byte [rel ghal_net_matched_kind], 1 - jne .record_branch - mov byte [rel physical_proof_code_commit_command_rx], 1 - jmp .build_reply -.record_branch: - cmp byte [rel ghal_net_matched_kind], 2 - jne .record_recovery - mov byte [rel physical_proof_branch_move_command_rx], 1 - jmp .build_reply -.record_recovery: - cmp byte [rel ghal_net_matched_kind], 3 - jne .ignore_packet - mov byte [rel physical_proof_recovery_command_rx], 1 - jmp .build_reply - -.ignore_packet: - inc r8w - jnz .post_receive - mov byte [rel physical_proof_error_code], 0x57 - stc - ret - -.build_reply: - mov rsi, VIRTIO_NET_RX_BUFFER +.send_request: mov rdi, VIRTIO_NET_TX_BUFFER - mov ecx, r14d - rep movsb - mov qword [VIRTIO_NET_TX_BUFFER], 0 - mov word [VIRTIO_NET_TX_BUFFER + 8], 0 - + xor eax, eax + mov ecx, VIRTIO_NET_BUFFER_SIZE / 8 + rep stosq xor ecx, ecx -.swap_mac: +.copy_gateway_destination: cmp ecx, 6 - jae .mac_swapped - mov al, [VIRTIO_NET_TX_BUFFER + 10 + rcx] - mov dl, [VIRTIO_NET_TX_BUFFER + 16 + rcx] - mov [VIRTIO_NET_TX_BUFFER + 10 + rcx], dl + jae .copy_source_mac + mov al, [physical_proof_gateway_mac + rcx] + mov [VIRTIO_NET_TX_BUFFER + 10 + rcx], al + inc ecx + jmp .copy_gateway_destination +.copy_source_mac: + xor ecx, ecx +.copy_source_mac_loop: + cmp ecx, 6 + jae .ethernet_ready + mov al, [physical_proof_mac + rcx] mov [VIRTIO_NET_TX_BUFFER + 16 + rcx], al inc ecx - jmp .swap_mac -.mac_swapped: - mov eax, [VIRTIO_NET_TX_BUFFER + 36] - mov edx, [VIRTIO_NET_TX_BUFFER + 40] - mov [VIRTIO_NET_TX_BUFFER + 36], edx - mov [VIRTIO_NET_TX_BUFFER + 40], eax - mov byte [VIRTIO_NET_TX_BUFFER + 44], 0 + jmp .copy_source_mac_loop +.ethernet_ready: + mov word [VIRTIO_NET_TX_BUFFER + 22], 0x0008 + mov byte [VIRTIO_NET_TX_BUFFER + 24], 0x45 + mov byte [VIRTIO_NET_TX_BUFFER + 25], 0 + mov word [VIRTIO_NET_TX_BUFFER + 26], 0x4400 + mov word [VIRTIO_NET_TX_BUFFER + 28], 0x4c48 + mov word [VIRTIO_NET_TX_BUFFER + 30], 0 + mov byte [VIRTIO_NET_TX_BUFFER + 32], 64 + mov byte [VIRTIO_NET_TX_BUFFER + 33], 1 + mov word [VIRTIO_NET_TX_BUFFER + 34], 0 + mov dword [VIRTIO_NET_TX_BUFFER + 36], GHOS_NATIVE_IPV4_DWORD + mov dword [VIRTIO_NET_TX_BUFFER + 40], GHOS_RELAY_IPV4_DWORD + mov byte [VIRTIO_NET_TX_BUFFER + 44], 8 + mov byte [VIRTIO_NET_TX_BUFFER + 45], 0 mov word [VIRTIO_NET_TX_BUFFER + 46], 0 - lea rsi, [rel ghal_native_ack_magic] + mov word [VIRTIO_NET_TX_BUFFER + 48], 0x4847 + mov byte [VIRTIO_NET_TX_BUFFER + 50], 0 + mov al, [rel ghal_net_expected_sequence] + mov [VIRTIO_NET_TX_BUFFER + 51], al + mov qword [VIRTIO_NET_TX_BUFFER + 52], 0 + mov rsi, [rel ghal_net_expected_magic] + mov rdi, VIRTIO_NET_TX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET + mov ecx, GHOS_LOGIN_MAGIC_SIZE + rep movsb + mov rsi, [rel ghal_net_expected_magic] mov rdi, VIRTIO_NET_TX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET + GHOS_LOGIN_MAGIC_SIZE mov ecx, GHOS_LOGIN_MAGIC_SIZE rep movsb - movzx eax, word [VIRTIO_NET_TX_BUFFER + 26] - xchg al, ah - cmp eax, IPV4_HEADER_SIZE + ICMP_HEADER_SIZE - jb .invalid_packet - sub eax, IPV4_HEADER_SIZE - mov ecx, eax - mov rsi, VIRTIO_NET_TX_BUFFER + 44 + mov rsi, VIRTIO_NET_TX_BUFFER + 24 + mov ecx, IPV4_HEADER_SIZE xor ebx, ebx -.checksum_words: - cmp ecx, 2 - jb .checksum_tail - movzx eax, word [rsi] - xchg al, ah - add ebx, eax - add rsi, 2 - sub ecx, 2 - jmp .checksum_words -.checksum_tail: - test ecx, ecx - jz .checksum_fold - movzx eax, byte [rsi] - shl eax, 8 - add ebx, eax -.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 + call ghal_net_checksum + mov [VIRTIO_NET_TX_BUFFER + 34], bx + mov rsi, VIRTIO_NET_TX_BUFFER + 44 + mov ecx, ICMP_HEADER_SIZE + 8 + GHOS_LOGIN_MAGIC_SIZE * 2 + xor ebx, ebx + call ghal_net_checksum mov [VIRTIO_NET_TX_BUFFER + 46], bx mov qword [VIRTIO_NET_TX_QUEUE], VIRTIO_NET_TX_BUFFER - mov [VIRTIO_NET_TX_QUEUE + 8], r14d + mov dword [VIRTIO_NET_TX_QUEUE + 8], 92 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 .queue_missing @@ -1178,18 +1074,120 @@ ghal_net_icmp_login_once: out dx, ax mov ecx, 0x20000000 -.wait_reply_tx: +.wait_request_tx: cmp word [r13 + 2], r11w - je .reply_complete + je .request_complete pause - loop .wait_reply_tx + loop .wait_request_tx mov byte [rel physical_proof_error_code], 0x55 stc ret -.reply_complete: +.request_complete: + inc word [rel ghal_net_tx_next_index] + + mov ecx, 0x80000000 +.wait_receive: + cmp word [r12 + 2], r8w + je .packet_received + pause + loop .wait_receive +%ifdef GHOS_NATIVE_RESIDENT + jmp .send_request +%else + mov byte [rel physical_proof_error_code], 0x54 + stc + ret +%endif + +.packet_received: + mov eax, r8d + dec eax + mov ecx, r10d + dec ecx + and eax, ecx + shl eax, 3 + mov r14d, [r12 + rax + 8] + cmp r14d, GHOS_LOGIN_MAGIC_OFFSET + GHOS_LOGIN_MAGIC_SIZE * 2 + jb .ignore_packet + cmp word [VIRTIO_NET_RX_BUFFER + 22], 0x0008 + jne .ignore_packet + cmp byte [VIRTIO_NET_RX_BUFFER + 24], 0x45 + jne .ignore_packet + cmp byte [VIRTIO_NET_RX_BUFFER + 33], 1 + jne .ignore_packet + cmp dword [VIRTIO_NET_RX_BUFFER + 36], GHOS_RELAY_IPV4_DWORD + jne .ignore_packet + cmp dword [VIRTIO_NET_RX_BUFFER + 40], GHOS_NATIVE_IPV4_DWORD + jne .ignore_packet + cmp byte [VIRTIO_NET_RX_BUFFER + 44], 0 + jne .ignore_packet + cmp byte [VIRTIO_NET_RX_BUFFER + 45], 0 + jne .ignore_packet + cmp word [VIRTIO_NET_RX_BUFFER + 48], 0x4847 + jne .ignore_packet + cmp byte [VIRTIO_NET_RX_BUFFER + 50], 0 + jne .ignore_packet + mov al, [VIRTIO_NET_RX_BUFFER + 51] + cmp al, [rel ghal_net_expected_sequence] + jne .ignore_packet + mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET + mov rdi, [rel ghal_net_expected_magic] + mov ecx, GHOS_LOGIN_MAGIC_SIZE + repe cmpsb + jne .ignore_packet + mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET + GHOS_LOGIN_MAGIC_SIZE + lea rdi, [rel ghal_native_ack_magic] + mov ecx, GHOS_LOGIN_MAGIC_SIZE + repe cmpsb + jne .ignore_packet + jmp .expected_magic_match + +.expected_magic_match: + mov al, [rel ghal_net_command_kind] +.store_matched_kind: + mov [rel ghal_net_matched_kind], al + + cmp byte [rel ghal_net_matched_kind], 0 + jne .record_command + mov byte [rel physical_proof_ipv4_rx_verified], 1 + mov eax, [VIRTIO_NET_RX_BUFFER + 36] + mov [rel physical_proof_login_client_ip], eax + mov ax, [VIRTIO_NET_RX_BUFFER + 48] + mov [rel physical_proof_login_icmp_id], ax + mov ax, [VIRTIO_NET_RX_BUFFER + 50] + mov [rel physical_proof_login_icmp_sequence], ax + mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET + lea rdi, [rel physical_proof_login_magic] + mov ecx, GHOS_LOGIN_MAGIC_SIZE + rep movsb + jmp .exchange_verified + +.record_command: + cmp byte [rel ghal_net_matched_kind], 1 + jne .record_branch + mov byte [rel physical_proof_code_commit_command_rx], 1 + jmp .exchange_verified +.record_branch: + cmp byte [rel ghal_net_matched_kind], 2 + jne .record_recovery + mov byte [rel physical_proof_branch_move_command_rx], 1 + jmp .exchange_verified +.record_recovery: + cmp byte [rel ghal_net_matched_kind], 3 + jne .ignore_packet + mov byte [rel physical_proof_recovery_command_rx], 1 + jmp .exchange_verified + +.ignore_packet: + inc r8w + jnz .post_receive + mov byte [rel physical_proof_error_code], 0x57 + stc + ret + +.exchange_verified: mov [rel ghal_net_rx_next_index], r8w inc word [rel ghal_net_rx_next_index] - inc word [rel ghal_net_tx_next_index] inc byte [rel ghal_net_expected_sequence] cmp byte [rel ghal_net_matched_kind], 0 jne .not_login_reply @@ -1201,13 +1199,39 @@ ghal_net_icmp_login_once: call serial_write64 clc ret + .queue_missing: mov byte [rel physical_proof_error_code], 0x53 stc ret -.invalid_packet: - mov byte [rel physical_proof_error_code], 0x56 - stc + +ghal_net_checksum: +.checksum_words: + cmp ecx, 2 + jb .checksum_tail + movzx eax, word [rsi] + xchg al, ah + add ebx, eax + add rsi, 2 + sub ecx, 2 + jmp .checksum_words +.checksum_tail: + test ecx, ecx + jz .checksum_fold + movzx eax, byte [rsi] + shl eax, 8 + add ebx, eax +.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 ghal_net_io_base: dw 0 diff --git a/product-source/hololake-platform/guanghu-os/scripts/native-layout-nasm-args.sh b/product-source/hololake-platform/guanghu-os/scripts/native-layout-nasm-args.sh index bacce8f..84bf9bd 100755 --- a/product-source/hololake-platform/guanghu-os/scripts/native-layout-nasm-args.sh +++ b/product-source/hololake-platform/guanghu-os/scripts/native-layout-nasm-args.sh @@ -49,6 +49,7 @@ recovery_beacon_lba=$(native_value recovery_beacon_lba_start) gestational_index_lba=$(native_value gestational_index_lba_start) native_ipv4=$(ipv4_dword "$(network_value native_ipv4)") gateway_ipv4=$(ipv4_dword "$(network_value gateway_ipv4)") +relay_ipv4=$(ipv4_dword "$(network_value relay_ipv4)") for value in \ "${kernel_lba}" "${proof_lba}" "${world_store_lba}" \ @@ -68,4 +69,5 @@ printf '%s\n' \ "-dGHOS_NATIVE_RECOVERY_BEACON_LBA=${recovery_beacon_lba}" \ "-dGHOS_NATIVE_GESTATIONAL_INDEX_LBA=${gestational_index_lba}" \ "-dGHOS_NATIVE_IPV4_DWORD=${native_ipv4}" \ - "-dGHOS_GATEWAY_IPV4_DWORD=${gateway_ipv4}" + "-dGHOS_GATEWAY_IPV4_DWORD=${gateway_ipv4}" \ + "-dGHOS_RELAY_IPV4_DWORD=${relay_ipv4}" diff --git a/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py b/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py index 484dd58..cb6e825 100755 --- a/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py +++ b/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py @@ -53,6 +53,35 @@ def verify_reply(packet: bytes, magic: bytes) -> int: return int.from_bytes(packet[6:8], "big") +def verify_native_request(packet: bytes, pipeline: list[bytes]) -> tuple[int, str]: + if not packet or packet[0] >> 4 != 4: + raise RuntimeError("native request is missing its IPv4 header") + header_length = (packet[0] & 0x0F) * 4 + source = socket.inet_ntoa(packet[12:16]) + icmp = packet[header_length:] + if len(icmp) < 48 or icmp[0] != 8 or icmp[1] != 0: + raise RuntimeError("unexpected native ICMP request shape") + if checksum(icmp) != 0: + raise RuntimeError("native ICMP request checksum failed") + sequence = int.from_bytes(icmp[6:8], "big") + if not 1 <= sequence <= len(pipeline): + raise RuntimeError("native ICMP sequence is outside the pipeline") + magic = pipeline[sequence - 1] + if icmp[16:32] != magic or icmp[32:48] != magic: + raise RuntimeError("native ICMP request magic mismatch") + return sequence, source + + +def native_ack_reply(packet: bytes) -> bytes: + header_length = (packet[0] & 0x0F) * 4 + icmp = bytearray(packet[header_length:]) + icmp[0] = 0 + icmp[2:4] = b"\0\0" + icmp[32:48] = NATIVE_ACK_MAGIC + icmp[2:4] = struct.pack("!H", checksum(bytes(icmp))) + return bytes(icmp) + + def exchange( peer: socket.socket, target: tuple[str, int], @@ -80,7 +109,7 @@ def exchange( def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("--target", required=True) + parser.add_argument("--target") parser.add_argument("--receipt", required=True) parser.add_argument("--timeout", type=float, default=90.0) parser.add_argument("--retry-interval", type=float, default=1.0) @@ -92,6 +121,8 @@ def main() -> None: parser.add_argument("--resume-after-branch", action="store_true") parser.add_argument("--resume-resident-count", type=int) parser.add_argument("--resident-pipeline", action="store_true") + parser.add_argument("--native-relay", action="store_true") + parser.add_argument("--allowed-source") args = parser.parse_args() if args.retry_interval < 1.0: raise SystemExit("--retry-interval must be at least 1 second") @@ -108,19 +139,79 @@ def main() -> None: args.resume_after_branch, args.resume_resident_count is not None, args.resident_pipeline, + args.native_relay, ) ) > 1: raise SystemExit( "--resident, --login-only, --resume-after-login, and " "--resume-after-commit, --resume-after-branch, and " - "--resume-resident-count, and --resident-pipeline are mutually exclusive" + "--resume-resident-count, --resident-pipeline, and --native-relay " + "are mutually exclusive" ) socket_type = socket.SOCK_RAW if args.raw_socket else socket.SOCK_DGRAM peer = socket.socket(socket.AF_INET, socket_type, socket.IPPROTO_ICMP) + deadline = time.monotonic() + args.timeout + if args.native_relay: + if not args.raw_socket: + raise SystemExit("--native-relay requires --raw-socket") + if not args.allowed_source: + raise SystemExit("--native-relay requires --allowed-source") + allowed_source = socket.gethostbyname(args.allowed_source) + pipeline = [ + LOGIN_MAGIC, + LOGIN_MAGIC, + LOGIN_MAGIC, + COMMIT_MAGIC, + BRANCH_MAGIC, + *([LOGIN_MAGIC] * 10), + RECOVERY_MAGIC, + ] + peer.settimeout(0.5) + acknowledged: set[int] = set() + sources: set[str] = set() + completed_at: float | None = None + while time.monotonic() < deadline and ( + completed_at is None or time.monotonic() < completed_at + 5.0 + ): + try: + packet, address = peer.recvfrom(4096) + sequence, source = verify_native_request(packet, pipeline) + except (TimeoutError, RuntimeError, IndexError): + continue + if source != allowed_source: + continue + peer.sendto(native_ack_reply(packet), address) + acknowledged.add(sequence) + sources.add(source) + if len(acknowledged) == len(pipeline) and completed_at is None: + completed_at = time.monotonic() + if len(acknowledged) != len(pipeline): + raise TimeoutError( + "native relay pipeline incomplete; acknowledged sequences: " + + ",".join(str(item) for item in sorted(acknowledged)) + ) + receipt = pathlib.Path(args.receipt) + receipt.write_text( + "schema: guanghu.physical-native-icmp-relay/v1\n" + "status: PASS_100\n" + "handshake_direction: NATIVE_INITIATED_OUTBOUND_ICMP\n" + "native_ack_marker: HLDP-NATIVE-ACK!\n" + "acknowledged_sequences: 1-16\n" + f"observed_nat_sources: {','.join(sorted(sources))}\n" + "login_reply_count: 3\n" + "code_commit_reply_verified: true\n" + "branch_move_reply_verified: true\n" + "resident_login_reply_count: 10\n" + "recovery_reply_verified: true\n", + encoding="utf-8", + ) + print(receipt.read_text(encoding="utf-8"), end="") + return + if not args.target: + raise SystemExit("--target is required unless --native-relay is used") peer.settimeout(0.2 if args.resident_pipeline else 1.0) target = (socket.gethostbyname(args.target), 0) - deadline = time.monotonic() + args.timeout if args.resident_pipeline: pipeline = [ LOGIN_MAGIC, @@ -137,19 +228,24 @@ def main() -> None: if time.monotonic() >= deadline: break peer.sendto(request(pipeline_sequence, pipeline_magic), target) - try: - packet, _ = peer.recvfrom(4096) - observed_sequence = reply_sequence_index(packet) - if not 1 <= observed_sequence <= len(pipeline): - raise RuntimeError("native ACK sequence is outside the pipeline") - reply_sequence = verify_reply( - packet, - pipeline[observed_sequence - 1], - ) - except (TimeoutError, RuntimeError, IndexError): - continue - if reply_sequence == pipeline_sequence: + receive_until = min(deadline, time.monotonic() + 0.8) + while time.monotonic() < receive_until: + try: + packet, _ = peer.recvfrom(4096) + observed_sequence = reply_sequence_index(packet) + if not 1 <= observed_sequence <= len(pipeline): + raise RuntimeError( + "native ACK sequence is outside the pipeline" + ) + reply_sequence = verify_reply( + packet, + pipeline[observed_sequence - 1], + ) + except (TimeoutError, RuntimeError, IndexError): + continue acknowledged.add(reply_sequence) + if reply_sequence == pipeline_sequence: + break if len(acknowledged) != len(pipeline): raise TimeoutError( "native pipeline incomplete; acknowledged sequences: " 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 03b8fdb..ade6f98 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 @@ -7,9 +7,9 @@ import time GUEST_MAC = bytes.fromhex("525400267198") PEER_MAC = bytes.fromhex("525400123401") -GUEST_IP = socket.inet_aton("10.0.0.7") -PEER_IP = socket.inet_aton("10.0.0.1") -LOGIN_CLIENT_IP = socket.inet_aton("10.0.0.2") +GUEST_IP = socket.inet_aton("172.16.0.6") +GATEWAY_IP = socket.inet_aton("172.16.0.1") +RELAY_IP = socket.inet_aton("43.153.193.169") LOGIN_MAGIC = b"HLDP-GHOS-LOGIN!" COMMIT_MAGIC = b"HLDP-CODE-COMMIT" BRANCH_MAGIC = b"HLDP-BRANCH-MOVE" @@ -35,68 +35,65 @@ def arp_reply(request: bytes) -> bytes: target_ip = request[38:42] assert sender_mac == GUEST_MAC assert sender_ip == GUEST_IP - assert target_ip == PEER_IP + assert target_ip == GATEWAY_IP return ( sender_mac + PEER_MAC + b"\x08\x06" + b"\x00\x01\x08\x00\x06\x04\x00\x02" + PEER_MAC - + PEER_IP + + GATEWAY_IP + sender_mac + sender_ip ) -def icmp_request(sequence: int, magic: bytes) -> bytes: - payload = b"\0" * 8 + magic + magic - icmp = struct.pack("!BBHHH", 8, 0, 0, 0x4748, sequence) + payload - icmp = icmp[:2] + struct.pack("!H", checksum(icmp)) + icmp[4:] - total_length = 20 + len(icmp) - ip = struct.pack( - "!BBHHHBBH4s4s", - 0x45, - 0, - total_length, - 0x484C, - 0, - 64, - 1, - 0, - LOGIN_CLIENT_IP, - GUEST_IP, - ) - ip = ip[:10] + struct.pack("!H", checksum(ip)) + ip[12:] - return GUEST_MAC + PEER_MAC + b"\x08\x00" + ip + icmp - - -def validate_reply(frame: bytes, magic: bytes) -> None: +def verified_request(frame: bytes, sequence: int, magic: bytes) -> None: assert frame[0:6] == PEER_MAC assert frame[6:12] == GUEST_MAC assert frame[12:14] == b"\x08\x00" assert frame[26:30] == GUEST_IP - assert frame[30:34] == LOGIN_CLIENT_IP - assert frame[34] == 0 + assert frame[30:34] == RELAY_IP + assert checksum(frame[14:34]) == 0 + assert frame[34] == 8 + assert frame[35] == 0 + assert frame[38:40] == b"\x47\x48" + assert int.from_bytes(frame[40:42], "big") == sequence assert frame[50:66] == magic - assert frame[66:82] == NATIVE_ACK_MAGIC - assert checksum(frame[34:]) == 0 + assert frame[66:82] == magic + assert checksum(frame[34:82]) == 0 + + +def authenticated_reply(frame: bytes, magic: bytes) -> bytes: + reply = bytearray(frame) + reply[0:6] = GUEST_MAC + reply[6:12] = PEER_MAC + reply[26:30] = RELAY_IP + reply[30:34] = GUEST_IP + reply[24:26] = b"\0\0" + reply[24:26] = struct.pack("!H", checksum(bytes(reply[14:34]))) + reply[34] = 0 + reply[36:38] = b"\0\0" + reply[66:82] = NATIVE_ACK_MAGIC + reply[36:38] = struct.pack("!H", checksum(bytes(reply[34:82]))) + return bytes(reply) def main() -> None: - global GUEST_IP, PEER_IP, LOGIN_CLIENT_IP + global GUEST_IP, GATEWAY_IP, RELAY_IP parser = argparse.ArgumentParser() parser.add_argument("--listen-port", type=int, required=True) parser.add_argument("--qemu-port", type=int, required=True) parser.add_argument("--receipt", required=True) parser.add_argument("--resident", action="store_true") parser.add_argument("--login-only", action="store_true") - parser.add_argument("--guest-ip", default="10.0.0.7") - parser.add_argument("--peer-ip", default="10.0.0.1") - parser.add_argument("--login-client-ip", default="10.0.0.2") + 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") args = parser.parse_args() GUEST_IP = socket.inet_aton(args.guest_ip) - PEER_IP = socket.inet_aton(args.peer_ip) - LOGIN_CLIENT_IP = socket.inet_aton(args.login_client_ip) + GATEWAY_IP = socket.inet_aton(args.peer_ip) + RELAY_IP = socket.inet_aton(args.relay_ip) peer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) peer.bind(("127.0.0.1", args.listen_port)) @@ -104,101 +101,63 @@ def main() -> None: qemu = ("127.0.0.1", args.qemu_port) deadline = time.monotonic() + 15 arp_verified = False - login_sent = False - reply_count = 0 - login_reply_count = 0 - resident_login_reply_count = 0 - recovery_reply_verified = False - command_phase = "login" - - def write_receipt( - *, - commit_verified: bool, - branch_verified: bool, - ) -> None: - with open(args.receipt, "w", encoding="utf-8") as output: - output.write( - "arp_gateway_reply: VERIFIED\n" - f"icmp_login_request_sent: {str(login_sent).lower()}\n" - "icmp_login_reply_verified: true\n" - f"icmp_login_reply_count: {login_reply_count}\n" - "code_commit_reply_verified: " - f"{str(commit_verified).lower()}\n" - "branch_move_reply_verified: " - f"{str(branch_verified).lower()}\n" - f"resident_login_reply_count: {resident_login_reply_count}\n" - "recovery_reply_verified: " - f"{str(recovery_reply_verified).lower()}\n" - "login_magic: HLDP-GHOS-LOGIN!\n" - ) - - def phase_magic() -> bytes: - return { - "login": LOGIN_MAGIC, - "commit": COMMIT_MAGIC, - "branch": BRANCH_MAGIC, - "resident_login": LOGIN_MAGIC, - "recovery": RECOVERY_MAGIC, - }[command_phase] + login_count = 0 + resident_login_count = 0 + commit_verified = False + branch_verified = False + recovery_verified = False while time.monotonic() < deadline: try: frame = peer.recv(4096) except TimeoutError: - if arp_verified: - peer.sendto( - icmp_request(reply_count + 1, phase_magic()), - qemu, - ) - login_sent = True continue if frame[12:14] == b"\x08\x06": peer.sendto(arp_reply(frame), qemu) arp_verified = True continue - if frame[12:14] == b"\x08\x00": - magic = phase_magic() - validate_reply(frame, magic) - reply_count += 1 - if command_phase == "login": - login_reply_count += 1 - if command_phase == "login" and reply_count < 3: - peer.sendto(icmp_request(reply_count + 1, LOGIN_MAGIC), qemu) - continue - if command_phase == "login": - if args.login_only: - write_receipt( - commit_verified=False, - branch_verified=False, - ) - return - command_phase = "commit" - peer.sendto(icmp_request(4, COMMIT_MAGIC), qemu) - continue - if command_phase == "commit": - command_phase = "branch" - peer.sendto(icmp_request(5, BRANCH_MAGIC), qemu) - continue - if command_phase == "branch" and args.resident: - command_phase = "resident_login" - peer.sendto(icmp_request(6, LOGIN_MAGIC), qemu) - continue - if command_phase == "resident_login": - resident_login_reply_count += 1 - if resident_login_reply_count < 10: - peer.sendto( - icmp_request(6 + resident_login_reply_count, LOGIN_MAGIC), - qemu, - ) - continue - command_phase = "recovery" - peer.sendto(icmp_request(16, RECOVERY_MAGIC), qemu) - continue - if command_phase == "recovery": - recovery_reply_verified = True - write_receipt(commit_verified=True, branch_verified=True) - return - raise SystemExit("timed out waiting for native ICMP login reply") + if frame[12:14] != b"\x08\x00": + continue + sequence = int.from_bytes(frame[40:42], "big") + if sequence <= 3: + magic = LOGIN_MAGIC + login_count += 1 + elif sequence == 4: + magic = COMMIT_MAGIC + commit_verified = True + elif sequence == 5: + magic = BRANCH_MAGIC + branch_verified = True + elif 6 <= sequence <= 15: + magic = LOGIN_MAGIC + resident_login_count += 1 + elif sequence == 16: + magic = RECOVERY_MAGIC + recovery_verified = True + else: + raise AssertionError(f"unexpected native sequence {sequence}") + verified_request(frame, sequence, magic) + peer.sendto(authenticated_reply(frame, magic), qemu) + + terminal_sequence = 3 if args.login_only else (16 if args.resident else 5) + if sequence != terminal_sequence: + continue + with open(args.receipt, "w", encoding="utf-8") as output: + output.write( + "arp_gateway_reply: VERIFIED\n" + "handshake_direction: NATIVE_INITIATED_OUTBOUND_ICMP\n" + "authenticated_relay_reply: VERIFIED\n" + "icmp_login_request_sent: true\n" + "icmp_login_reply_verified: true\n" + f"icmp_login_reply_count: {login_count}\n" + f"code_commit_reply_verified: {str(commit_verified).lower()}\n" + f"branch_move_reply_verified: {str(branch_verified).lower()}\n" + f"resident_login_reply_count: {resident_login_count}\n" + f"recovery_reply_verified: {str(recovery_verified).lower()}\n" + "login_magic: HLDP-GHOS-LOGIN!\n" + ) + return + raise SystemExit("timed out waiting for native outbound ICMP exchange") if __name__ == "__main__": diff --git a/product-source/hololake-platform/guanghu-os/scripts/test-jd-native-resident-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/test-jd-native-resident-candidate.sh new file mode 100755 index 0000000..c9cee95 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/test-jd-native-resident-candidate.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ $# -eq 2 ]] || { + echo "usage: test-jd-native-resident-candidate.sh " >&2 + exit 64 +} + +candidate=$(readlink -f "$1") +receipt=$(readlink -m "$2") +source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +native_root=${source_root}/native/x86_64-bios +test_root=$(mktemp -d) +peer_pid= +cleanup() { + if [[ -n ${peer_pid} ]]; then + kill "${peer_pid}" 2>/dev/null || true + fi + if [[ ${GHOS_KEEP_TEST_ROOT:-0} != 1 ]]; then + rm -rf "${test_root}" + else + echo "GHOS_TEST_ROOT=${test_root}" >&2 + fi +} +trap cleanup EXIT + +[[ $(stat -c %s "${candidate}") -eq 14848 ]] +truncate -s 700M "${test_root}/disk.img" +nasm -f bin -dCANDIDATE_LBA=105 -dPROOF_LBA=134 \ + "${native_root}/physical-test-mbr.asm" -o "${test_root}/mbr.bin" +dd if="${test_root}/mbr.bin" of="${test_root}/disk.img" \ + bs=512 seek=0 conv=notrunc status=none +dd if="${candidate}" of="${test_root}/disk.img" \ + bs=512 seek=105 conv=notrunc status=none + +peer_port=$((32000 + ${BASHPID:-$$} % 1000)) +qemu_port=$((peer_port + 1)) +python3 "${source_root}/scripts/qemu-native-net-peer.py" \ + --listen-port "${peer_port}" \ + --qemu-port "${qemu_port}" \ + --receipt "${test_root}/peer.hldp" \ + --resident >"${test_root}/peer.log" 2>&1 & +peer_pid=$! +set +e +timeout 35 qemu-system-x86_64 \ + -machine pc,accel=tcg \ + -m 64M \ + -drive "if=none,id=ghboot,format=raw,file=${test_root}/disk.img" \ + -device virtio-blk-pci,drive=ghboot,disable-modern=on,bootindex=0 \ + -netdev "dgram,id=ghnet,local.type=inet,local.host=127.0.0.1,local.port=${qemu_port},remote.type=inet,remote.host=127.0.0.1,remote.port=${peer_port}" \ + -device virtio-net-pci,netdev=ghnet,disable-modern=on,mac=52:54:00:26:71:98 \ + -display none \ + -monitor none \ + -serial "file:${test_root}/serial.log" \ + -device isa-debug-exit,iobase=0xf4,iosize=0x04 +qemu_status=$? +set -e +[[ ${qemu_status} -eq 33 ]] +wait "${peer_pid}" +peer_pid= + +grep -q '^handshake_direction: NATIVE_INITIATED_OUTBOUND_ICMP$' \ + "${test_root}/peer.hldp" +grep -q '^authenticated_relay_reply: VERIFIED$' "${test_root}/peer.hldp" +grep -q '^icmp_login_reply_count: 3$' "${test_root}/peer.hldp" +grep -q '^code_commit_reply_verified: true$' "${test_root}/peer.hldp" +grep -q '^branch_move_reply_verified: true$' "${test_root}/peer.hldp" +grep -q '^resident_login_reply_count: 10$' "${test_root}/peer.hldp" +grep -q '^recovery_reply_verified: true$' "${test_root}/peer.hldp" +grep -q '^GHOS_DISK_PROOF_OBSERVED_AFTER_RESET=LBA134' \ + "${test_root}/serial.log" + +python3 - "${test_root}/disk.img" <<'PY' +import pathlib +import sys + +with pathlib.Path(sys.argv[1]).open("rb") as disk: + def sector(lba: int, count: int = 1) -> bytes: + disk.seek(lba * 512) + return disk.read(count * 512) + + proof = sector(134) + assert proof[0] == 0xA7 + assert proof[1:].startswith(b"GHOS_NATIVE_LONG64_DISK_PROOF\0") + assert proof[42:44] == bytes([0x7F, 0]) + assert proof[90] == 13 + assert proof[93:105] == bytes([1] * 12) + assert sector(135).startswith(b"GHOS_HLDP_WORLD_STORE_V1\n") + assert sector(136).startswith(b"GHOS_CODE_CHANNEL_STORE_V1\n") + assert sector(137) == sector(136) + assert b"branch=guanghu/main\n" in sector(138) + assert sector(139, 2).startswith(b"# GRUB Environment Block\n") + assert sector(141).startswith(b"GHOS_GHCIP_INDEX_V1\n") + assert b"GHCIP_PERSONA_STATE=EXISTS\n" in sector(142) +PY + +observed_at=$(date --iso-8601=seconds) +image_sha=$(sha256sum "${candidate}" | awk '{print $1}') +cat >"${receipt}" <