fix(baremetal,net): fix bare-metal boot freezes and implement real internet stack
- Zero user PML4 and allocated page tables in VMM to prevent dirty RAM crash on real hardware - Probe UART existence and add timeout to prevent infinite freeze on motherboards without COM1 - Clamp SMP AP boot to MAX_CPUS to prevent GDT/TSS corruption on multi-core CPUs - Add kernel early framebuffer console (FbConsole) with 8x16 font for direct HDMI/DP screen output - Configure Limine resolution and dynamic 4K draw buffers in gui_server to fix 1080p black screen - Implement RFC 793 TCP pseudo-header checksum and full TCP 3-way handshake - Implement DHCP client (UDP 67/68) and DNS client (UDP 53) - Add dns, dhcp commands to shell and update web browser with live URL loading
This commit is contained in:
+522
-115
@@ -2,23 +2,41 @@
|
||||
#include "rtl8139.h"
|
||||
#include "string.h"
|
||||
#include "stdio.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// Default QEMU User Networking (SLIRP) Configuration
|
||||
// Guest IP: 10.0.2.15
|
||||
// Gateway: 10.0.2.2 (Default QEMU MAC: 52:55:0a:00:02:02)
|
||||
// DNS: 10.0.2.3
|
||||
net_config_t g_net_cfg = {
|
||||
.ip = (10) | (0 << 8) | (2 << 16) | (15 << 24),
|
||||
.gateway = (10) | (0 << 8) | (2 << 16) | (2 << 24),
|
||||
.dns_server = (10) | (0 << 8) | (2 << 16) | (3 << 24),
|
||||
.netmask = (255) | (255 << 8) | (255 << 16) | (0 << 24),
|
||||
.mac = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}
|
||||
};
|
||||
|
||||
static uint8_t g_gateway_mac[6] = {0x52, 0x55, 0x0a, 0x00, 0x02, 0x02};
|
||||
|
||||
static uint16_t htons(uint16_t v) {
|
||||
static inline uint16_t htons(uint16_t v) {
|
||||
return (uint16_t)(((v & 0xFF) << 8) | ((v >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
static inline uint16_t ntohs(uint16_t v) {
|
||||
return htons(v);
|
||||
}
|
||||
|
||||
static inline uint32_t htonl(uint32_t v) {
|
||||
return (((v & 0x000000FF) << 24) |
|
||||
((v & 0x0000FF00) << 8) |
|
||||
((v & 0x00FF0000) >> 8) |
|
||||
((v & 0xFF000000) >> 24));
|
||||
}
|
||||
|
||||
static inline uint32_t ntohl(uint32_t v) {
|
||||
return htonl(v);
|
||||
}
|
||||
|
||||
uint16_t net_checksum(const void *buf, size_t len) {
|
||||
const uint16_t *data = (const uint16_t *)buf;
|
||||
uint32_t sum = 0;
|
||||
@@ -36,6 +54,43 @@ uint16_t net_checksum(const void *buf, size_t len) {
|
||||
return (uint16_t)(~sum);
|
||||
}
|
||||
|
||||
// RFC 793 TCP Pseudo-Header Checksum
|
||||
uint16_t net_tcp_checksum(uint32_t src_ip, uint32_t dst_ip, const tcp_hdr_t *tcp, size_t tcp_len) {
|
||||
struct __attribute__((packed)) {
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
uint8_t zero;
|
||||
uint8_t proto;
|
||||
uint16_t length;
|
||||
} ph;
|
||||
|
||||
ph.src = src_ip;
|
||||
ph.dst = dst_ip;
|
||||
ph.zero = 0;
|
||||
ph.proto = IP_PROTO_TCP;
|
||||
ph.length = htons((uint16_t)tcp_len);
|
||||
|
||||
uint32_t sum = 0;
|
||||
const uint16_t *p = (const uint16_t *)&ph;
|
||||
for (size_t i = 0; i < sizeof(ph) / 2; i++) {
|
||||
sum += p[i];
|
||||
}
|
||||
|
||||
const uint16_t *data = (const uint16_t *)tcp;
|
||||
size_t len = tcp_len;
|
||||
while (len > 1) {
|
||||
sum += *data++;
|
||||
len -= 2;
|
||||
}
|
||||
if (len == 1) {
|
||||
sum += *(const uint8_t *)data;
|
||||
}
|
||||
while (sum >> 16) {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
return (uint16_t)(~sum);
|
||||
}
|
||||
|
||||
void net_init(void) {
|
||||
rtl8139_init();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
@@ -50,17 +105,15 @@ int net_send_arp_request(uint32_t target_ip) {
|
||||
eth_hdr_t *eth = (eth_hdr_t *)packet;
|
||||
arp_hdr_t *arp = (arp_hdr_t *)(packet + sizeof(eth_hdr_t));
|
||||
|
||||
// Broadcast Ethernet Header
|
||||
memset(eth->dest_mac, 0xFF, 6);
|
||||
memcpy(eth->src_mac, g_net_cfg.mac, 6);
|
||||
eth->ethertype = htons(ETHERTYPE_ARP);
|
||||
|
||||
// ARP Payload (Who has target_ip? Tell sender_ip)
|
||||
arp->hw_type = htons(1); // Ethernet
|
||||
arp->proto_type = htons(0x0800); // IPv4
|
||||
arp->hw_type = htons(1);
|
||||
arp->proto_type = htons(0x0800);
|
||||
arp->hw_len = 6;
|
||||
arp->proto_len = 4;
|
||||
arp->opcode = htons(1); // Request
|
||||
arp->opcode = htons(1); // ARP Request
|
||||
memcpy(arp->sender_mac, g_net_cfg.mac, 6);
|
||||
arp->sender_ip = g_net_cfg.ip;
|
||||
memset(arp->target_mac, 0x00, 6);
|
||||
@@ -76,17 +129,15 @@ int net_send_icmp_ping(uint32_t target_ip, uint16_t seq) {
|
||||
icmp_hdr_t *icmp = (icmp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
uint8_t *payload = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t);
|
||||
|
||||
// 1. Ethernet Header (Direct unicast to resolved gateway MAC)
|
||||
memcpy(eth->dest_mac, g_gateway_mac, 6);
|
||||
memcpy(eth->src_mac, g_net_cfg.mac, 6);
|
||||
eth->ethertype = htons(ETHERTYPE_IPv4);
|
||||
|
||||
// 2. IPv4 Header
|
||||
ip->version_ihl = 0x45; // Version 4, Header Length 5 (20 bytes)
|
||||
ip->version_ihl = 0x45;
|
||||
ip->tos = 0;
|
||||
ip->total_length = htons(20 + 8 + 32); // IP + ICMP + 32-byte payload = 60 bytes
|
||||
ip->total_length = htons(20 + 8 + 32);
|
||||
ip->id = htons(seq);
|
||||
ip->flags_fragment = htons(0x4000); // Don't Fragment
|
||||
ip->flags_fragment = htons(0x4000);
|
||||
ip->ttl = 64;
|
||||
ip->protocol = IP_PROTO_ICMP;
|
||||
ip->src_ip = g_net_cfg.ip;
|
||||
@@ -94,120 +145,352 @@ int net_send_icmp_ping(uint32_t target_ip, uint16_t seq) {
|
||||
ip->checksum = 0;
|
||||
ip->checksum = net_checksum(ip, 20);
|
||||
|
||||
// 3. ICMP Echo Request Header
|
||||
icmp->type = ICMP_ECHO_REQUEST;
|
||||
icmp->code = 0;
|
||||
icmp->id = htons(0x1234);
|
||||
icmp->sequence = htons(seq);
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
payload[i] = (uint8_t)('a' + (i % 26));
|
||||
payload[i] = (uint8_t)('A' + (i % 26));
|
||||
}
|
||||
|
||||
icmp->checksum = 0;
|
||||
icmp->checksum = net_checksum(icmp, 8 + 32);
|
||||
|
||||
size_t total_len = sizeof(eth_hdr_t) + 20 + 8 + 32;
|
||||
return rtl8139_send_packet(packet, (uint16_t)total_len);
|
||||
return rtl8139_send_packet(packet, sizeof(eth_hdr_t) + 20 + 8 + 32);
|
||||
}
|
||||
|
||||
int net_poll_icmp_reply(uint32_t *from_ip, uint16_t *seq) {
|
||||
uint8_t buffer[1600];
|
||||
int len = rtl8139_poll_packet(buffer, sizeof(buffer));
|
||||
|
||||
if (len < (int)sizeof(eth_hdr_t)) {
|
||||
return 0;
|
||||
uint8_t buf[1600];
|
||||
int len = rtl8139_poll_packet(buf, sizeof(buf));
|
||||
if (len < (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
eth_hdr_t *eth = (eth_hdr_t *)buffer;
|
||||
|
||||
// Handle ARP Reply to dynamically update gateway MAC
|
||||
if (htons(eth->ethertype) == ETHERTYPE_ARP && len >= (int)(sizeof(eth_hdr_t) + sizeof(arp_hdr_t))) {
|
||||
arp_hdr_t *arp = (arp_hdr_t *)(buffer + sizeof(eth_hdr_t));
|
||||
if (htons(arp->opcode) == 2) { // ARP Reply
|
||||
eth_hdr_t *eth = (eth_hdr_t *)buf;
|
||||
if (htons(eth->ethertype) == ETHERTYPE_ARP) {
|
||||
arp_hdr_t *arp = (arp_hdr_t *)(buf + sizeof(eth_hdr_t));
|
||||
if (htons(arp->opcode) == 2 && arp->sender_ip == g_net_cfg.gateway) {
|
||||
memcpy(g_gateway_mac, arp->sender_mac, 6);
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (htons(eth->ethertype) != ETHERTYPE_IPv4 || len < (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ipv4_hdr_t *ip = (ipv4_hdr_t *)(buffer + sizeof(eth_hdr_t));
|
||||
if (ip->protocol != IP_PROTO_ICMP) {
|
||||
return 0;
|
||||
}
|
||||
if (htons(eth->ethertype) != ETHERTYPE_IPv4) return -1;
|
||||
ipv4_hdr_t *ip = (ipv4_hdr_t *)(buf + sizeof(eth_hdr_t));
|
||||
if (ip->protocol != IP_PROTO_ICMP) return -1;
|
||||
|
||||
int ip_hdr_len = (ip->version_ihl & 0x0F) * 4;
|
||||
icmp_hdr_t *icmp = (icmp_hdr_t *)(buffer + sizeof(eth_hdr_t) + ip_hdr_len);
|
||||
icmp_hdr_t *icmp = (icmp_hdr_t *)(buf + sizeof(eth_hdr_t) + ip_hdr_len);
|
||||
|
||||
if (icmp->type == ICMP_ECHO_REPLY) {
|
||||
if (from_ip) *from_ip = ip->src_ip;
|
||||
if (seq) *seq = htons(icmp->sequence);
|
||||
return 1; // Genuine Echo Reply received!
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t parse_ip(const char *str) {
|
||||
uint32_t parts[4] = {0};
|
||||
int idx = 0;
|
||||
while (*str && idx < 4) {
|
||||
uint32_t ip = 0;
|
||||
int octet = 0;
|
||||
int shift = 0;
|
||||
|
||||
while (*str) {
|
||||
if (*str >= '0' && *str <= '9') {
|
||||
parts[idx] = parts[idx] * 10 + (*str - '0');
|
||||
octet = octet * 10 + (*str - '0');
|
||||
} else if (*str == '.') {
|
||||
idx++;
|
||||
ip |= ((uint32_t)(octet & 0xFF) << shift);
|
||||
shift += 8;
|
||||
octet = 0;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
return (parts[0]) | (parts[1] << 8) | (parts[2] << 16) | (parts[3] << 24);
|
||||
ip |= ((uint32_t)(octet & 0xFF) << shift);
|
||||
return ip;
|
||||
}
|
||||
|
||||
void format_ip(uint32_t ip, char *buf) {
|
||||
uint8_t *b = (uint8_t *)&ip;
|
||||
int len = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint8_t val = b[i];
|
||||
if (val >= 100) {
|
||||
buf[len++] = '0' + (val / 100);
|
||||
val %= 100;
|
||||
buf[len++] = '0' + (val / 10);
|
||||
val %= 10;
|
||||
} else if (val >= 10) {
|
||||
buf[len++] = '0' + (val / 10);
|
||||
val %= 10;
|
||||
uint8_t b = (uint8_t)((ip >> (i * 8)) & 0xFF);
|
||||
if (b >= 100) {
|
||||
buf[len++] = '0' + (b / 100);
|
||||
b %= 100;
|
||||
buf[len++] = '0' + (b / 10);
|
||||
b %= 10;
|
||||
} else if (b >= 10) {
|
||||
buf[len++] = '0' + (b / 10);
|
||||
b %= 10;
|
||||
}
|
||||
buf[len++] = '0' + val;
|
||||
buf[len++] = '0' + b;
|
||||
if (i < 3) buf[len++] = '.';
|
||||
}
|
||||
buf[len] = '\0';
|
||||
}
|
||||
|
||||
int net_http_get(uint32_t target_ip, uint16_t port, const char *path, char *out_body, size_t max_body_len) {
|
||||
if (!out_body || max_body_len == 0) return -1;
|
||||
if (!path || !*path) path = "/";
|
||||
// =========================================================================
|
||||
// DNS CLIENT (UDP Port 53) - Resolves Hostnames to Real IPv4 Addresses
|
||||
// =========================================================================
|
||||
int net_dns_resolve(const char *domain, uint32_t *out_ip) {
|
||||
if (!domain || !*domain || !out_ip) return -1;
|
||||
|
||||
// 1. Craft TCP SYN packet
|
||||
uint8_t packet[128] = {0};
|
||||
eth_hdr_t *eth = (eth_hdr_t *)packet;
|
||||
ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t));
|
||||
tcp_hdr_t *tcp = (tcp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
// Check if domain is already a numerical IPv4 address
|
||||
bool is_numeric = true;
|
||||
for (const char *p = domain; *p; p++) {
|
||||
if (!((*p >= '0' && *p <= '9') || *p == '.')) {
|
||||
is_numeric = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_numeric) {
|
||||
*out_ip = parse_ip(domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t local_port = 49152 + (uint16_t)(target_ip & 0x3FF);
|
||||
uint32_t my_seq = 0x10002000;
|
||||
uint8_t packet[512] = {0};
|
||||
eth_hdr_t *eth = (eth_hdr_t *)packet;
|
||||
ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t));
|
||||
udp_hdr_t *udp = (udp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
uint8_t *dns_payload = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t);
|
||||
|
||||
// Build DNS Header
|
||||
dns_hdr_t *dns = (dns_hdr_t *)dns_payload;
|
||||
uint16_t query_id = 0x5A5A;
|
||||
dns->id = htons(query_id);
|
||||
dns->flags = htons(0x0100); // Standard query with recursion desired
|
||||
dns->qdcount = htons(1); // 1 question
|
||||
dns->ancount = 0;
|
||||
dns->nscount = 0;
|
||||
dns->arcount = 0;
|
||||
|
||||
// Format QNAME: e.g. "httpbin.org" -> \x07httpbin\x03org\x00
|
||||
uint8_t *qname = dns_payload + sizeof(dns_hdr_t);
|
||||
const char *src = domain;
|
||||
while (*src) {
|
||||
const char *dot = strchr(src, '.');
|
||||
size_t len = dot ? (size_t)(dot - src) : strlen(src);
|
||||
*qname++ = (uint8_t)len;
|
||||
memcpy(qname, src, len);
|
||||
qname += len;
|
||||
src += len;
|
||||
if (*src == '.') src++;
|
||||
}
|
||||
*qname++ = 0x00; // Null terminator for QNAME
|
||||
|
||||
// QTYPE (A = 1) and QCLASS (IN = 1)
|
||||
*qname++ = 0x00; *qname++ = 0x01; // Type A
|
||||
*qname++ = 0x00; *qname++ = 0x01; // Class IN
|
||||
|
||||
size_t dns_len = (size_t)(qname - dns_payload);
|
||||
size_t udp_len = sizeof(udp_hdr_t) + dns_len;
|
||||
size_t ip_len = sizeof(ipv4_hdr_t) + udp_len;
|
||||
size_t total_packet_len = sizeof(eth_hdr_t) + ip_len;
|
||||
|
||||
// Ethernet Header
|
||||
memcpy(eth->dest_mac, g_gateway_mac, 6);
|
||||
uint8_t target_mac[6];
|
||||
if ((g_net_cfg.dns_server & 0x00FFFFFF) == (g_net_cfg.ip & 0x00FFFFFF)) {
|
||||
// Local subnet (e.g. QEMU 10.0.2.3): match last octet
|
||||
memcpy(target_mac, g_gateway_mac, 6);
|
||||
target_mac[5] = (uint8_t)(g_net_cfg.dns_server >> 24);
|
||||
} else {
|
||||
// External DNS (e.g. 1.1.1.1 or 8.8.8.8): route via Gateway
|
||||
memcpy(target_mac, g_gateway_mac, 6);
|
||||
}
|
||||
memcpy(eth->dest_mac, target_mac, 6);
|
||||
memcpy(eth->src_mac, g_net_cfg.mac, 6);
|
||||
eth->ethertype = htons(ETHERTYPE_IPv4);
|
||||
|
||||
// IPv4 Header
|
||||
ip->version_ihl = 0x45;
|
||||
ip->tos = 0;
|
||||
ip->total_length = htons((uint16_t)ip_len);
|
||||
ip->id = htons(0x7788);
|
||||
ip->flags_fragment = htons(0x4000);
|
||||
ip->ttl = 64;
|
||||
ip->protocol = IP_PROTO_UDP;
|
||||
ip->src_ip = g_net_cfg.ip;
|
||||
ip->dst_ip = g_net_cfg.dns_server;
|
||||
ip->checksum = 0;
|
||||
ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t));
|
||||
|
||||
// UDP Header
|
||||
uint16_t src_port = 52345;
|
||||
udp->src_port = htons(src_port);
|
||||
udp->dst_port = htons(53); // DNS Port
|
||||
udp->length = htons((uint16_t)udp_len);
|
||||
udp->checksum = 0; // Optional in IPv4 UDP
|
||||
|
||||
// Transmit DNS Query (ensure minimum Ethernet frame size 60 bytes)
|
||||
rtl8139_send_packet(packet, total_packet_len < 60 ? 60 : total_packet_len);
|
||||
|
||||
// Poll for DNS response with realistic WAN timeout (~1.5 seconds)
|
||||
uint8_t rx_buf[1600];
|
||||
for (int retry = 0; retry < 300; retry++) {
|
||||
int rx_len = rtl8139_poll_packet(rx_buf, sizeof(rx_buf));
|
||||
if (rx_len > (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t) + sizeof(dns_hdr_t))) {
|
||||
eth_hdr_t *rx_eth = (eth_hdr_t *)rx_buf;
|
||||
if (htons(rx_eth->ethertype) == ETHERTYPE_IPv4) {
|
||||
ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx_buf + sizeof(eth_hdr_t));
|
||||
if (rx_ip->protocol == IP_PROTO_UDP) {
|
||||
udp_hdr_t *rx_udp = (udp_hdr_t *)(rx_buf + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
if (htons(rx_udp->dst_port) == src_port) {
|
||||
uint8_t *resp_dns = rx_buf + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t);
|
||||
dns_hdr_t *rx_dns_hdr = (dns_hdr_t *)resp_dns;
|
||||
|
||||
if (ntohs(rx_dns_hdr->id) == query_id && (ntohs(rx_dns_hdr->flags) & 0x8000) && ntohs(rx_dns_hdr->ancount) > 0) {
|
||||
// Skip DNS Question section to reach Answer section
|
||||
uint8_t *ptr = resp_dns + sizeof(dns_hdr_t);
|
||||
// Skip QNAME
|
||||
while (*ptr && ptr < rx_buf + rx_len) {
|
||||
if ((*ptr & 0xC0) == 0xC0) { ptr += 2; break; } // Compression pointer
|
||||
ptr += (*ptr + 1);
|
||||
}
|
||||
if (*ptr == 0) ptr++;
|
||||
ptr += 4; // Skip QTYPE (2) + QCLASS (2)
|
||||
|
||||
// Parse Answers
|
||||
int answers = ntohs(rx_dns_hdr->ancount);
|
||||
for (int a = 0; a < answers && ptr < rx_buf + rx_len - 10; a++) {
|
||||
// Name: either pointer (0xC0XX) or label
|
||||
if ((*ptr & 0xC0) == 0xC0) {
|
||||
ptr += 2;
|
||||
} else {
|
||||
while (*ptr && ptr < rx_buf + rx_len) ptr += (*ptr + 1);
|
||||
if (*ptr == 0) ptr++;
|
||||
}
|
||||
uint16_t a_type = (uint16_t)((ptr[0] << 8) | ptr[1]);
|
||||
uint16_t a_len = (uint16_t)((ptr[8] << 8) | ptr[9]);
|
||||
ptr += 10; // Type(2), Class(2), TTL(4), DataLen(2)
|
||||
|
||||
if (a_type == 1 && a_len == 4) { // IPv4 Address (Type A)
|
||||
uint32_t resolved = 0;
|
||||
memcpy(&resolved, ptr, 4);
|
||||
*out_ip = resolved;
|
||||
return 0; // Success!
|
||||
}
|
||||
ptr += a_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (volatile int d = 0; d < 20000; d++);
|
||||
}
|
||||
|
||||
return -1; // Timeout
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DHCP AUTO-CONFIGURATION (UDP Port 67/68)
|
||||
// =========================================================================
|
||||
int net_dhcp_discover(void) {
|
||||
// Send DHCP Discover packet broadcast
|
||||
uint8_t packet[350] = {0};
|
||||
eth_hdr_t *eth = (eth_hdr_t *)packet;
|
||||
ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t));
|
||||
udp_hdr_t *udp = (udp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
uint8_t *dhcp = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t);
|
||||
|
||||
memset(eth->dest_mac, 0xFF, 6);
|
||||
memcpy(eth->src_mac, g_net_cfg.mac, 6);
|
||||
eth->ethertype = htons(ETHERTYPE_IPv4);
|
||||
|
||||
ip->version_ihl = 0x45;
|
||||
ip->tos = 0;
|
||||
ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t) + 300);
|
||||
ip->id = htons(0x1010);
|
||||
ip->flags_fragment = 0;
|
||||
ip->ttl = 64;
|
||||
ip->protocol = IP_PROTO_UDP;
|
||||
ip->src_ip = 0x00000000;
|
||||
ip->dst_ip = 0xFFFFFFFF; // 255.255.255.255
|
||||
ip->checksum = 0;
|
||||
ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t));
|
||||
|
||||
udp->src_port = htons(68); // DHCP Client
|
||||
udp->dst_port = htons(67); // DHCP Server
|
||||
udp->length = htons(sizeof(udp_hdr_t) + 300);
|
||||
udp->checksum = 0;
|
||||
|
||||
// BOOTP / DHCP Request fields
|
||||
dhcp[0] = 1; // Boot Request
|
||||
dhcp[1] = 1; // 10Mb Ethernet
|
||||
dhcp[2] = 6; // HW Address length
|
||||
dhcp[3] = 0; // Hops
|
||||
dhcp[4] = 0x39; dhcp[5] = 0x03; dhcp[6] = 0xF3; dhcp[7] = 0x26; // Transaction ID
|
||||
memcpy(dhcp + 28, g_net_cfg.mac, 6); // Client MAC
|
||||
// Magic Cookie: 99, 130, 83, 99
|
||||
dhcp[236] = 99; dhcp[237] = 130; dhcp[238] = 83; dhcp[239] = 99;
|
||||
// Option 53: DHCP Discover
|
||||
dhcp[240] = 53; dhcp[241] = 1; dhcp[242] = 1;
|
||||
// Option 55: Parameter Request List (Subnet Mask, Router, DNS)
|
||||
dhcp[243] = 55; dhcp[244] = 3; dhcp[245] = 1; dhcp[246] = 3; dhcp[247] = 6;
|
||||
dhcp[248] = 255; // End
|
||||
|
||||
rtl8139_send_packet(packet, sizeof(packet));
|
||||
|
||||
// Listen for DHCP Offer
|
||||
uint8_t rx[1600];
|
||||
for (int retry = 0; retry < 40; retry++) {
|
||||
int rx_len = rtl8139_poll_packet(rx, sizeof(rx));
|
||||
if (rx_len > (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t) + 240)) {
|
||||
ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx + sizeof(eth_hdr_t));
|
||||
if (rx_ip->protocol == IP_PROTO_UDP) {
|
||||
udp_hdr_t *rx_udp = (udp_hdr_t *)(rx + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
if (htons(rx_udp->dst_port) == 68) {
|
||||
uint8_t *rx_dhcp = rx + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t);
|
||||
uint32_t offered_ip = 0;
|
||||
memcpy(&offered_ip, rx_dhcp + 16, 4); // "Your (client) IP address" (yiaddr)
|
||||
if (offered_ip != 0) {
|
||||
g_net_cfg.ip = offered_ip;
|
||||
// Parse options
|
||||
uint8_t *opt = rx_dhcp + 240;
|
||||
while (*opt != 255 && opt < rx + rx_len) {
|
||||
uint8_t code = *opt++;
|
||||
uint8_t len = *opt++;
|
||||
if (code == 1 && len == 4) memcpy(&g_net_cfg.netmask, opt, 4);
|
||||
if (code == 3 && len == 4) memcpy(&g_net_cfg.gateway, opt, 4);
|
||||
if (code == 6 && len >= 4) memcpy(&g_net_cfg.dns_server, opt, 4);
|
||||
opt += len;
|
||||
}
|
||||
return 0; // Success!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (volatile int d = 0; d < 20000; d++);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// FULL TCP 3-WAY HANDSHAKE & HTTP CLIENT (Live Internet Access)
|
||||
// =========================================================================
|
||||
int net_http_get(uint32_t target_ip, uint16_t port, const char *path, const char *host_header, char *out_body, size_t max_body_len) {
|
||||
if (!out_body || max_body_len == 0) return -1;
|
||||
if (!path || !*path) path = "/";
|
||||
|
||||
uint16_t local_port = 49152 + (uint16_t)(target_ip & 0x1FF) + (uint16_t)(port & 0x1F);
|
||||
uint32_t my_seq = 0x20001000;
|
||||
uint32_t srv_seq = 0;
|
||||
|
||||
uint8_t packet[1500] = {0};
|
||||
eth_hdr_t *eth = (eth_hdr_t *)packet;
|
||||
ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t));
|
||||
tcp_hdr_t *tcp = (tcp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t));
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// STEP 1: SEND TCP SYN (with RFC 793 TCP Checksum)
|
||||
// -------------------------------------------------------------
|
||||
memcpy(eth->dest_mac, g_gateway_mac, 6);
|
||||
memcpy(eth->src_mac, g_net_cfg.mac, 6);
|
||||
eth->ethertype = htons(ETHERTYPE_IPv4);
|
||||
|
||||
ip->version_ihl = 0x45;
|
||||
ip->tos = 0;
|
||||
ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t));
|
||||
ip->id = htons(0x4321);
|
||||
ip->id = htons(0x1122);
|
||||
ip->flags_fragment = htons(0x4000);
|
||||
ip->ttl = 64;
|
||||
ip->protocol = IP_PROTO_TCP;
|
||||
@@ -216,73 +499,197 @@ int net_http_get(uint32_t target_ip, uint16_t port, const char *path, char *out_
|
||||
ip->checksum = 0;
|
||||
ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t));
|
||||
|
||||
// TCP SYN Header
|
||||
tcp->src_port = htons(local_port);
|
||||
tcp->dst_port = htons(port);
|
||||
tcp->seq_num = (uint32_t)(((my_seq & 0xFF) << 24) | ((my_seq & 0xFF00) << 8) | ((my_seq >> 8) & 0xFF00) | ((my_seq >> 24) & 0xFF));
|
||||
tcp->seq_num = htonl(my_seq);
|
||||
tcp->ack_num = 0;
|
||||
tcp->data_offset_reserved = (5 << 4); // 20 bytes header
|
||||
tcp->data_offset_reserved = (5 << 4);
|
||||
tcp->flags = TCP_FLAG_SYN;
|
||||
tcp->window_size = htons(65535);
|
||||
tcp->checksum = 0;
|
||||
tcp->urgent_ptr = 0;
|
||||
tcp->checksum = net_tcp_checksum(ip->src_ip, ip->dst_ip, tcp, sizeof(tcp_hdr_t));
|
||||
|
||||
// Send SYN
|
||||
rtl8139_send_packet(packet, sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t));
|
||||
|
||||
// Poll for packet
|
||||
uint8_t rx_buf[1600];
|
||||
for (int retry = 0; retry < 30; retry++) {
|
||||
int rx_len = rtl8139_poll_packet(rx_buf, sizeof(rx_buf));
|
||||
// -------------------------------------------------------------
|
||||
// STEP 2: WAIT FOR TCP SYN-ACK
|
||||
// -------------------------------------------------------------
|
||||
uint8_t rx[1600];
|
||||
bool got_synack = false;
|
||||
|
||||
for (int retry = 0; retry < 300; retry++) {
|
||||
int rx_len = rtl8139_poll_packet(rx, sizeof(rx));
|
||||
if (rx_len > (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t))) {
|
||||
eth_hdr_t *rx_eth = (eth_hdr_t *)rx_buf;
|
||||
eth_hdr_t *rx_eth = (eth_hdr_t *)rx;
|
||||
if (htons(rx_eth->ethertype) == ETHERTYPE_IPv4) {
|
||||
ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx_buf + sizeof(eth_hdr_t));
|
||||
ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx + sizeof(eth_hdr_t));
|
||||
if (rx_ip->protocol == IP_PROTO_TCP) {
|
||||
int ip_hl = (rx_ip->version_ihl & 0x0F) * 4;
|
||||
tcp_hdr_t *rx_tcp = (tcp_hdr_t *)(rx_buf + sizeof(eth_hdr_t) + ip_hl);
|
||||
tcp_hdr_t *rx_tcp = (tcp_hdr_t *)(rx + sizeof(eth_hdr_t) + ip_hl);
|
||||
if (htons(rx_tcp->dst_port) == local_port) {
|
||||
int tcp_hl = (rx_tcp->data_offset_reserved >> 4) * 4;
|
||||
int data_offset = sizeof(eth_hdr_t) + ip_hl + tcp_hl;
|
||||
int payload_len = rx_len - data_offset;
|
||||
if (payload_len > 0) {
|
||||
int copy_bytes = (payload_len < (int)max_body_len - 1) ? payload_len : ((int)max_body_len - 1);
|
||||
memcpy(out_body, rx_buf + data_offset, copy_bytes);
|
||||
out_body[copy_bytes] = '\0';
|
||||
return 200;
|
||||
if ((rx_tcp->flags & (TCP_FLAG_SYN | TCP_FLAG_ACK)) == (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
|
||||
srv_seq = ntohl(rx_tcp->seq_num) + 1;
|
||||
my_seq++;
|
||||
got_synack = true;
|
||||
break;
|
||||
} else if (rx_tcp->flags & TCP_FLAG_RST) {
|
||||
printf("[TCP] Connection refused by remote host (RST received)\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (volatile int d = 0; d < 30000; d++) {}
|
||||
for (volatile int d = 0; d < 60000; d++);
|
||||
}
|
||||
|
||||
// Default Fallback Demo Page
|
||||
const char *demo_html =
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
" <title>opencoreC Web Browser</title>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
" <h1>Welcome to opencoreC Web!</h1>\n"
|
||||
" <p>Status: <b>200 OK</b> (Connected via TCP/IPv4)</p>\n"
|
||||
" <p>Local IP: <b>10.0.2.15</b> | Gateway: <b>10.0.2.2</b></p>\n"
|
||||
" <p>Driver: Realtek RTL8139 Fast Ethernet PCI Bus Master</p>\n"
|
||||
" <hr/>\n"
|
||||
" <h3>Available Links & Subsystems:</h3>\n"
|
||||
" <ul>\n"
|
||||
" <li><b>Architecture:</b> 4-Core SMP x86_64 Microkernel</li>\n"
|
||||
" <li><b>IPC:</b> Fast-Path Capability & Synchronous Rendezvous</li>\n"
|
||||
" <li><b>Compositor:</b> LVGL v8.3 with SSE2 SIMD Acceleration</li>\n"
|
||||
" <li><b>Binaries:</b> Native .OPC Binary Format with Sandboxing</li>\n"
|
||||
" <li><b>Hardware:</b> PCI GPU Detection & VRAM Management</li>\n"
|
||||
" </ul>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
if (!got_synack) {
|
||||
// Fallback response for offline environments
|
||||
snprintf(out_body, max_body_len,
|
||||
"<!DOCTYPE html>\n<html>\n<head><title>opencoreC Web</title></head>\n"
|
||||
"<body>\n<h1>opencoreC Web Browser</h1>\n"
|
||||
"<p>Host unreachable via TCP port %u (SYN-ACK timeout).</p>\n"
|
||||
"<p>Target IP: 0x%08x</p>\n</body>\n</html>\n",
|
||||
(unsigned int)port, (unsigned int)target_ip);
|
||||
return 504; // Gateway Timeout
|
||||
}
|
||||
|
||||
strncpy(out_body, demo_html, max_body_len - 1);
|
||||
out_body[max_body_len - 1] = '\0';
|
||||
return 200;
|
||||
// -------------------------------------------------------------
|
||||
// STEP 3: SEND ACK + HTTP GET REQUEST
|
||||
// -------------------------------------------------------------
|
||||
char http_req[512];
|
||||
snprintf(http_req, sizeof(http_req),
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"User-Agent: opencoreC/1.0 (x86_64 SMP)\r\n"
|
||||
"Accept: */*\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
path, host_header ? host_header : "10.0.2.2");
|
||||
|
||||
size_t req_len = strlen(http_req);
|
||||
size_t tcp_payload_len = sizeof(tcp_hdr_t) + req_len;
|
||||
|
||||
ip->total_length = htons((uint16_t)(sizeof(ipv4_hdr_t) + tcp_payload_len));
|
||||
ip->id = htons(0x1123);
|
||||
ip->checksum = 0;
|
||||
ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t));
|
||||
|
||||
tcp->seq_num = htonl(my_seq);
|
||||
tcp->ack_num = htonl(srv_seq);
|
||||
tcp->data_offset_reserved = (5 << 4);
|
||||
tcp->flags = TCP_FLAG_ACK | TCP_FLAG_PSH;
|
||||
tcp->checksum = 0;
|
||||
|
||||
uint8_t *payload_ptr = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t);
|
||||
memcpy(payload_ptr, http_req, req_len);
|
||||
tcp->checksum = net_tcp_checksum(ip->src_ip, ip->dst_ip, tcp, tcp_payload_len);
|
||||
|
||||
rtl8139_send_packet(packet, sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + tcp_payload_len);
|
||||
my_seq += (uint32_t)req_len;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// STEP 4: RECEIVE HTTP DATA STREAM
|
||||
// -------------------------------------------------------------
|
||||
size_t total_body_read = 0;
|
||||
out_body[0] = '\0';
|
||||
|
||||
int empty_polls = 0;
|
||||
for (int retry = 0; retry < 400; retry++) {
|
||||
int rx_len = rtl8139_poll_packet(rx, sizeof(rx));
|
||||
if (rx_len > (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t))) {
|
||||
eth_hdr_t *rx_eth = (eth_hdr_t *)rx;
|
||||
if (htons(rx_eth->ethertype) == ETHERTYPE_IPv4) {
|
||||
ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx + sizeof(eth_hdr_t));
|
||||
if (rx_ip->protocol == IP_PROTO_TCP && rx_ip->src_ip == target_ip) {
|
||||
int ip_hl = (rx_ip->version_ihl & 0x0F) * 4;
|
||||
tcp_hdr_t *rx_tcp = (tcp_hdr_t *)(rx + sizeof(eth_hdr_t) + ip_hl);
|
||||
if (htons(rx_tcp->dst_port) == local_port) {
|
||||
int tcp_hl = (rx_tcp->data_offset_reserved >> 4) * 4;
|
||||
int data_offset = sizeof(eth_hdr_t) + ip_hl + tcp_hl;
|
||||
uint16_t ip_total_len = ntohs(rx_ip->total_length);
|
||||
int data_bytes = (int)ip_total_len - (ip_hl + tcp_hl);
|
||||
|
||||
if (data_bytes > 0) {
|
||||
empty_polls = 0;
|
||||
size_t space = max_body_len - 1 - total_body_read;
|
||||
size_t to_copy = (size_t)data_bytes < space ? (size_t)data_bytes : space;
|
||||
memcpy(out_body + total_body_read, rx + data_offset, to_copy);
|
||||
total_body_read += to_copy;
|
||||
out_body[total_body_read] = '\0';
|
||||
|
||||
// Acknowledge received data
|
||||
srv_seq = ntohl(rx_tcp->seq_num) + (uint32_t)data_bytes;
|
||||
tcp->seq_num = htonl(my_seq);
|
||||
tcp->ack_num = htonl(srv_seq);
|
||||
tcp->flags = TCP_FLAG_ACK;
|
||||
tcp->checksum = 0;
|
||||
tcp->checksum = net_tcp_checksum(ip->src_ip, ip->dst_ip, tcp, sizeof(tcp_hdr_t));
|
||||
ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t));
|
||||
ip->checksum = 0;
|
||||
ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t));
|
||||
rtl8139_send_packet(packet, sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t));
|
||||
}
|
||||
|
||||
if (rx_tcp->flags & TCP_FLAG_FIN) {
|
||||
// Server closed connection cleanly
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (total_body_read > 0) {
|
||||
empty_polls++;
|
||||
if (empty_polls > 25) {
|
||||
break; // Finished receiving body stream
|
||||
}
|
||||
}
|
||||
}
|
||||
for (volatile int d = 0; d < 60000; d++);
|
||||
}
|
||||
|
||||
if (total_body_read > 0) {
|
||||
return 200; // Success HTTP 200 OK
|
||||
}
|
||||
|
||||
// Default response if connected but empty
|
||||
snprintf(out_body, max_body_len,
|
||||
"<!DOCTYPE html>\n<html>\n<head><title>opencoreC Web</title></head>\n"
|
||||
"<body>\n<h1>opencoreC Web Browser</h1>\n"
|
||||
"<p>Connected to %s via TCP/IPv4, but server returned 0 bytes.</p>\n</body>\n</html>\n",
|
||||
host_header ? host_header : "remote host");
|
||||
return 204;
|
||||
}
|
||||
|
||||
int net_http_get_url(const char *url, char *out_body, size_t max_body_len) {
|
||||
if (!url || !*url) return -1;
|
||||
if (strncmp(url, "http://", 7) == 0) url += 7;
|
||||
|
||||
char host[128] = {0};
|
||||
uint16_t port = 80;
|
||||
const char *path = "/";
|
||||
|
||||
int hi = 0;
|
||||
while (*url && *url != '/' && *url != ':' && hi < 127) {
|
||||
host[hi++] = *url++;
|
||||
}
|
||||
if (*url == ':') {
|
||||
url++;
|
||||
port = 0;
|
||||
while (*url >= '0' && *url <= '9') {
|
||||
port = port * 10 + (*url - '0');
|
||||
url++;
|
||||
}
|
||||
}
|
||||
if (*url == '/') {
|
||||
path = url;
|
||||
}
|
||||
|
||||
uint32_t target_ip = 0;
|
||||
if (net_dns_resolve(host, &target_ip) != 0) {
|
||||
target_ip = g_net_cfg.gateway; // Fallback to gateway
|
||||
}
|
||||
|
||||
return net_http_get(target_ip, port, path, host, out_body, max_body_len);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user