mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
537 lines
17 KiB
C
537 lines
17 KiB
C
#include "network.h"
|
|
#include "lwip/init.h"
|
|
#include "lwip/timeouts.h"
|
|
#include "lwip/etharp.h"
|
|
#include "lwip/dhcp.h"
|
|
#include "lwip/dns.h"
|
|
#include "lwip/tcp.h"
|
|
#include "lwip/udp.h"
|
|
#include "lwip/stats.h"
|
|
#include "lwip/raw.h"
|
|
#include "lwip/sys.h"
|
|
#include "netif/ethernet.h"
|
|
#include "nic_netif.h"
|
|
#include "kutils.h"
|
|
#include "pci.h"
|
|
#include "e1000.h"
|
|
#include "nic.h"
|
|
|
|
static struct netif nic_netif;
|
|
static int lwip_initialized = 0;
|
|
|
|
static struct tcp_pcb *current_tcp_pcb = NULL;
|
|
static struct pbuf *tcp_recv_queue = NULL;
|
|
static int tcp_connect_done = 0;
|
|
static int tcp_connect_error = 0;
|
|
static int tcp_closed = 0;
|
|
static uint32_t tcp_owner_pid = 0;
|
|
|
|
static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
|
|
(void)arg; (void)tpcb; (void)err;
|
|
if (p == NULL) {
|
|
// Connection closed
|
|
tcp_closed = 1;
|
|
return ERR_OK;
|
|
}
|
|
if (tcp_recv_queue == NULL) {
|
|
tcp_recv_queue = p;
|
|
} else {
|
|
pbuf_chain(tcp_recv_queue, p);
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
static void tcp_err_callback(void *arg, err_t err) {
|
|
(void)arg; (void)err;
|
|
current_tcp_pcb = NULL;
|
|
tcp_connect_error = 1;
|
|
}
|
|
|
|
static err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) {
|
|
(void)arg; (void)tpcb;
|
|
if (err == ERR_OK) {
|
|
tcp_connect_done = 1;
|
|
} else {
|
|
tcp_connect_error = 1;
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
int network_init(void) {
|
|
if (lwip_initialized) return 0;
|
|
|
|
// First, find and initialize the generic NIC device if not already done
|
|
if (nic_init() != 0) {
|
|
return -1; // No supported NIC found
|
|
}
|
|
|
|
lwip_init();
|
|
dns_init(); // Explicitly init DNS just in case
|
|
|
|
ip4_addr_t ipaddr, netmask, gw;
|
|
ip4_addr_set_zero(&ipaddr);
|
|
ip4_addr_set_zero(&netmask);
|
|
ip4_addr_set_zero(&gw);
|
|
|
|
if (netif_add(&nic_netif, &ipaddr, &netmask, &gw, NULL, nic_netif_init, ethernet_input) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
netif_set_default(&nic_netif);
|
|
netif_set_up(&nic_netif);
|
|
|
|
lwip_initialized = 1;
|
|
|
|
// Automate DHCP by default and return its result
|
|
int dhcp_res = network_dhcp_acquire();
|
|
|
|
if (dhcp_res == 0) {
|
|
ipv4_address_t ip;
|
|
network_get_ipv4_address(&ip);
|
|
extern void serial_write(const char *str);
|
|
serial_write("[NET] IP Assigned: ");
|
|
char buf[32];
|
|
k_itoa(ip.bytes[0], buf); serial_write(buf); serial_write(".");
|
|
k_itoa(ip.bytes[1], buf); serial_write(buf); serial_write(".");
|
|
k_itoa(ip.bytes[2], buf); serial_write(buf); serial_write(".");
|
|
k_itoa(ip.bytes[3], buf); serial_write(buf); serial_write("\n");
|
|
} else {
|
|
extern void serial_write(const char *str);
|
|
serial_write("[NET] DHCP Failed during init\n");
|
|
}
|
|
|
|
// Set default DNS server (1.1.1.1)
|
|
ip_addr_t dns1;
|
|
IP_ADDR4(&dns1, 1, 1, 1, 1);
|
|
dns_setserver(0, &dns1);
|
|
|
|
return dhcp_res;
|
|
}
|
|
|
|
int network_get_mac_address(mac_address_t* mac) {
|
|
if (!lwip_initialized) return -1;
|
|
return nic_get_mac_address(mac->bytes);
|
|
}
|
|
|
|
int network_get_nic_name(char* name_out) {
|
|
if (!lwip_initialized) return -1;
|
|
extern const char* nic_get_active_name(void);
|
|
const char* n = nic_get_active_name();
|
|
if (!n) return -1;
|
|
while (*n) *name_out++ = *n++;
|
|
*name_out = 0;
|
|
return 0;
|
|
}
|
|
|
|
int network_get_ipv4_address(ipv4_address_t* ip) {
|
|
if (!lwip_initialized) return -1;
|
|
u32_t addr = ip4_addr_get_u32(netif_ip4_addr(&nic_netif));
|
|
ip->bytes[0] = (addr >> 0) & 0xFF;
|
|
ip->bytes[1] = (addr >> 8) & 0xFF;
|
|
ip->bytes[2] = (addr >> 16) & 0xFF;
|
|
ip->bytes[3] = (addr >> 24) & 0xFF;
|
|
return 0;
|
|
}
|
|
|
|
int network_set_ipv4_address(const ipv4_address_t* ip) {
|
|
if (!lwip_initialized) return -1;
|
|
ip4_addr_t ipaddr;
|
|
IP4_ADDR(&ipaddr, ip->bytes[0], ip->bytes[1], ip->bytes[2], ip->bytes[3]);
|
|
netif_set_ipaddr(&nic_netif, &ipaddr);
|
|
return 0;
|
|
}
|
|
|
|
static volatile int network_processing = 0;
|
|
static void network_poll_internal(void) {
|
|
nic_netif_poll(&nic_netif);
|
|
sys_check_timeouts();
|
|
}
|
|
|
|
void network_process_frames(void) {
|
|
if (!lwip_initialized || network_processing) return;
|
|
network_processing = 1;
|
|
network_poll_internal();
|
|
network_processing = 0;
|
|
}
|
|
|
|
void network_force_unlock(void) {
|
|
network_processing = 0;
|
|
}
|
|
|
|
void network_cleanup(void) {
|
|
extern uint32_t process_get_current_pid(void);
|
|
uint32_t my_pid = process_get_current_pid();
|
|
if (tcp_owner_pid != 0 && tcp_owner_pid != my_pid) return;
|
|
|
|
asm volatile("cli");
|
|
if (tcp_recv_queue) {
|
|
pbuf_free(tcp_recv_queue);
|
|
tcp_recv_queue = NULL;
|
|
}
|
|
if (current_tcp_pcb) {
|
|
tcp_abort(current_tcp_pcb);
|
|
current_tcp_pcb = NULL;
|
|
}
|
|
tcp_owner_pid = 0;
|
|
network_processing = 0;
|
|
asm volatile("sti");
|
|
}
|
|
|
|
int network_dhcp_acquire(void) {
|
|
if (!lwip_initialized) return -1;
|
|
if (network_processing) {
|
|
serial_write("[DHCP] Busy, skipping\n");
|
|
return -1;
|
|
}
|
|
network_processing = 1;
|
|
serial_write("[DHCP] Starting...\n");
|
|
dhcp_start(&nic_netif);
|
|
|
|
uint32_t start = sys_now();
|
|
asm volatile("sti");
|
|
int loops = 0;
|
|
while (sys_now() - start < 10000) { // 10 second timeout
|
|
network_poll_internal();
|
|
if (dhcp_supplied_address(&nic_netif)) {
|
|
asm volatile("cli");
|
|
serial_write("[DHCP] Bound!\n");
|
|
network_processing = 0;
|
|
return 0;
|
|
}
|
|
k_delay(500); // 5ms delay
|
|
}
|
|
asm volatile("cli");
|
|
serial_write("[DHCP] Timeout at t=");
|
|
char buf[32]; k_itoa((int)(sys_now() - start), buf); serial_write(buf); serial_write("\n");
|
|
network_processing = 0;
|
|
return -1;
|
|
}
|
|
|
|
int network_tcp_connect(const ipv4_address_t *ip, uint16_t port) {
|
|
if (!lwip_initialized || network_processing) return -1;
|
|
network_processing = 1;
|
|
if (current_tcp_pcb) tcp_abort(current_tcp_pcb);
|
|
|
|
current_tcp_pcb = tcp_new();
|
|
if (!current_tcp_pcb) { network_processing = 0; return -1; }
|
|
|
|
extern uint32_t process_get_current_pid(void);
|
|
tcp_owner_pid = process_get_current_pid();
|
|
|
|
ip4_addr_t dest_addr;
|
|
IP4_ADDR(&dest_addr, ip->bytes[0], ip->bytes[1], ip->bytes[2], ip->bytes[3]);
|
|
|
|
tcp_connect_done = 0;
|
|
tcp_connect_error = 0;
|
|
tcp_closed = 0;
|
|
tcp_recv_queue = NULL;
|
|
|
|
tcp_recv(current_tcp_pcb, tcp_recv_callback);
|
|
tcp_err(current_tcp_pcb, tcp_err_callback);
|
|
err_t err = tcp_connect(current_tcp_pcb, &dest_addr, port, tcp_connected_callback);
|
|
if (err != ERR_OK) { network_processing = 0; return -1; }
|
|
|
|
uint32_t start = sys_now();
|
|
asm volatile("sti");
|
|
while (sys_now() - start < 15000) { // 15 second timeout
|
|
network_poll_internal();
|
|
if (tcp_connect_done) { asm volatile("cli"); network_processing = 0; return 0; }
|
|
if (tcp_connect_error) { asm volatile("cli"); network_processing = 0; return -1; }
|
|
k_delay(10);
|
|
}
|
|
asm volatile("cli");
|
|
network_processing = 0;
|
|
return -1;
|
|
}
|
|
|
|
int network_tcp_send(const void *data, size_t len) {
|
|
if (!current_tcp_pcb || network_processing) return -1;
|
|
network_processing = 1;
|
|
err_t err = tcp_write(current_tcp_pcb, data, len, TCP_WRITE_FLAG_COPY);
|
|
if (err != ERR_OK) { network_processing = 0; return -1; }
|
|
tcp_output(current_tcp_pcb);
|
|
network_processing = 0;
|
|
return (int)len;
|
|
}
|
|
|
|
int network_tcp_recv(void *buf, size_t max_len) {
|
|
if (network_processing) return -1;
|
|
network_processing = 1;
|
|
|
|
if (!tcp_recv_queue) {
|
|
if (tcp_closed) { network_processing = 0; return 0; } // End of stream
|
|
uint32_t start = sys_now();
|
|
asm volatile("sti");
|
|
while (sys_now() - start < 30000) { // 30 second timeout
|
|
network_poll_internal();
|
|
if (tcp_recv_queue) break;
|
|
if (tcp_closed) break;
|
|
if (tcp_connect_error) { asm volatile("cli"); network_processing = 0; return -1; }
|
|
k_delay(10);
|
|
}
|
|
asm volatile("cli");
|
|
if (!tcp_recv_queue) { network_processing = 0; return 0; }
|
|
}
|
|
|
|
// We already have data or we timed out (return 0 handled above)
|
|
size_t to_copy = max_len;
|
|
if (to_copy > tcp_recv_queue->tot_len) to_copy = tcp_recv_queue->tot_len;
|
|
if (to_copy > 0xFFFF) to_copy = 0xFFFF; // pbuf_copy_partial limit
|
|
|
|
size_t copied = pbuf_copy_partial(tcp_recv_queue, buf, (u16_t)to_copy, 0);
|
|
struct pbuf *remainder = pbuf_free_header(tcp_recv_queue, (u16_t)copied);
|
|
if (current_tcp_pcb) tcp_recved(current_tcp_pcb, (u16_t)copied);
|
|
tcp_recv_queue = remainder;
|
|
network_processing = 0;
|
|
return (int)copied;
|
|
}
|
|
|
|
int network_tcp_recv_nb(void *buf, size_t max_len) {
|
|
if (network_processing) return -1;
|
|
network_processing = 1;
|
|
|
|
network_poll_internal();
|
|
|
|
if (!tcp_recv_queue) {
|
|
if (tcp_closed) { network_processing = 0; return -2; }
|
|
network_processing = 0;
|
|
return 0;
|
|
}
|
|
|
|
size_t to_copy = max_len;
|
|
if (to_copy > tcp_recv_queue->tot_len) to_copy = tcp_recv_queue->tot_len;
|
|
if (to_copy > 0xFFFF) to_copy = 0xFFFF; // pbuf_copy_partial limit
|
|
|
|
size_t copied = pbuf_copy_partial(tcp_recv_queue, buf, (u16_t)to_copy, 0);
|
|
struct pbuf *remainder = pbuf_free_header(tcp_recv_queue, (u16_t)copied);
|
|
if (current_tcp_pcb) tcp_recved(current_tcp_pcb, (u16_t)copied);
|
|
tcp_recv_queue = remainder;
|
|
network_processing = 0;
|
|
return (int)copied;
|
|
}
|
|
|
|
int network_tcp_close(void) {
|
|
if (network_processing) return 0;
|
|
network_processing = 1;
|
|
|
|
// Free any pending receive buffers first
|
|
if (tcp_recv_queue) {
|
|
pbuf_free(tcp_recv_queue);
|
|
tcp_recv_queue = NULL;
|
|
}
|
|
|
|
if (current_tcp_pcb) {
|
|
// Use tcp_abort for immediate cleanup — tcp_close leaves PCBs in TIME_WAIT
|
|
// which pile up since sys_check_timeouts isn't called between page loads
|
|
tcp_abort(current_tcp_pcb);
|
|
current_tcp_pcb = NULL;
|
|
}
|
|
|
|
tcp_closed = 0;
|
|
tcp_connect_done = 0;
|
|
tcp_connect_error = 0;
|
|
network_processing = 0;
|
|
return 0;
|
|
}
|
|
|
|
static ip_addr_t dns_resolved_ip;
|
|
static int dns_done = 0;
|
|
static void dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
|
|
(void)name; (void)callback_arg;
|
|
serial_write("[DNS] Callback triggered\n");
|
|
if (ipaddr) {
|
|
dns_resolved_ip = *ipaddr;
|
|
dns_done = 1;
|
|
} else {
|
|
dns_done = -1;
|
|
}
|
|
}
|
|
|
|
int network_dns_lookup(const char *name, ipv4_address_t *out_ip) {
|
|
if (network_processing) {
|
|
// If we are already processing, we can't start a new DNS lookup
|
|
// because it might deadlock with the caller.
|
|
return -1;
|
|
}
|
|
|
|
dns_done = 0;
|
|
extern void serial_write(const char *str);
|
|
serial_write("[DNS] Lookup: "); serial_write(name); serial_write("\n");
|
|
|
|
network_processing = 1;
|
|
serial_write("[DNS] Path: Calling lwIP dns_gethostbyname...\n");
|
|
asm volatile("cli");
|
|
err_t err = dns_gethostbyname(name, &dns_resolved_ip, dns_callback, NULL);
|
|
serial_write("[DNS] Path: lwIP returned code: ");
|
|
char ebuf[32]; k_itoa((int)err, ebuf); serial_write(ebuf); serial_write("\n");
|
|
|
|
if (err == ERR_OK) {
|
|
serial_write("[DNS] Cache Hit\n");
|
|
u32_t addr = ip4_addr_get_u32(ip_2_ip4(&dns_resolved_ip));
|
|
out_ip->bytes[0] = (addr >> 0) & 0xFF;
|
|
out_ip->bytes[1] = (addr >> 8) & 0xFF;
|
|
out_ip->bytes[2] = (addr >> 16) & 0xFF;
|
|
out_ip->bytes[3] = (addr >> 24) & 0xFF;
|
|
network_processing = 0;
|
|
return 0;
|
|
} else if (err == ERR_INPROGRESS) {
|
|
serial_write("[DNS] In Progress...\n");
|
|
uint32_t start = sys_now();
|
|
asm volatile("sti");
|
|
while (sys_now() - start < 10000) { // 10 second timeout
|
|
network_processing = 0;
|
|
network_poll_internal();
|
|
network_processing = 1;
|
|
|
|
if (dns_done == 1) {
|
|
serial_write("[DNS] Success\n");
|
|
asm volatile("cli");
|
|
u32_t addr = ip4_addr_get_u32(ip_2_ip4(&dns_resolved_ip));
|
|
out_ip->bytes[0] = (addr >> 0) & 0xFF;
|
|
out_ip->bytes[1] = (addr >> 8) & 0xFF;
|
|
out_ip->bytes[2] = (addr >> 16) & 0xFF;
|
|
out_ip->bytes[3] = (addr >> 24) & 0xFF;
|
|
network_processing = 0;
|
|
return 0;
|
|
}
|
|
if (dns_done == -1) {
|
|
serial_write("[DNS] Failed (callback)\n");
|
|
asm volatile("cli");
|
|
network_processing = 0;
|
|
return -1;
|
|
}
|
|
k_delay(10);
|
|
}
|
|
serial_write("[DNS] Timeout!\n");
|
|
asm volatile("cli");
|
|
} else {
|
|
serial_write("[DNS] Error: ");
|
|
char buf[32];
|
|
k_itoa((int)err, buf); serial_write(buf); serial_write("\n");
|
|
}
|
|
network_processing = 0;
|
|
return -1;
|
|
}
|
|
|
|
int network_set_dns_server(const ipv4_address_t *ip) {
|
|
ip_addr_t addr;
|
|
IP4_ADDR(ip_2_ip4(&addr), ip->bytes[0], ip->bytes[1], ip->bytes[2], ip->bytes[3]);
|
|
dns_setserver(0, &addr);
|
|
return 0;
|
|
}
|
|
|
|
int network_get_gateway_ip(ipv4_address_t *ip) {
|
|
if (!lwip_initialized) return -1;
|
|
u32_t addr = ip4_addr_get_u32(netif_ip4_gw(&nic_netif));
|
|
ip->bytes[0] = (addr >> 0) & 0xFF;
|
|
ip->bytes[1] = (addr >> 8) & 0xFF;
|
|
ip->bytes[2] = (addr >> 16) & 0xFF;
|
|
ip->bytes[3] = (addr >> 24) & 0xFF;
|
|
return 0;
|
|
}
|
|
|
|
int network_get_dns_ip(ipv4_address_t *ip) {
|
|
const ip_addr_t *dns = dns_getserver(0);
|
|
u32_t addr = ip4_addr_get_u32(ip_2_ip4(dns));
|
|
ip->bytes[0] = (addr >> 0) & 0xFF;
|
|
ip->bytes[1] = (addr >> 8) & 0xFF;
|
|
ip->bytes[2] = (addr >> 16) & 0xFF;
|
|
ip->bytes[3] = (addr >> 24) & 0xFF;
|
|
return 0;
|
|
}
|
|
|
|
int network_is_initialized(void) { return lwip_initialized; }
|
|
int network_has_ip(void) { return lwip_initialized && !ip4_addr_isany_val(*netif_ip4_addr(&nic_netif)); }
|
|
|
|
int network_send_frame(const void* data, size_t length) { return nic_send_packet(data, length); }
|
|
int network_receive_frame(void* buffer, size_t buffer_size) { return nic_receive_packet(buffer, buffer_size); }
|
|
|
|
static u16_t icmp_cksum(void *data, int len) {
|
|
u32_t sum = 0;
|
|
u16_t *p = (u16_t *)data;
|
|
while (len > 1) {
|
|
sum += *p++;
|
|
len -= 2;
|
|
}
|
|
if (len == 1) {
|
|
sum += *(u8_t *)p;
|
|
}
|
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
|
sum += (sum >> 16);
|
|
return (u16_t)(~sum);
|
|
}
|
|
|
|
int udp_send_packet(const ipv4_address_t* dest_ip, uint16_t dest_port, uint16_t src_port, const void* data, size_t data_length) {
|
|
(void)dest_ip; (void)dest_port; (void)src_port; (void)data; (void)data_length;
|
|
return -1;
|
|
}
|
|
|
|
static int ping_replies = 0;
|
|
static u16_t ping_seq = 0;
|
|
static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) {
|
|
(void)arg; (void)pcb; (void)addr;
|
|
if (p->tot_len >= 8) {
|
|
u8_t *data = (u8_t *)p->payload;
|
|
// Raw PCBs for ICMP usually include the IP header.
|
|
// Check for ICMP Echo Reply (Type 0) at offset 0 (no IP header) or offset 20 (standard IP header)
|
|
if (data[0] == 0) {
|
|
ping_replies++;
|
|
} else if (p->tot_len >= 28 && data[0] == 0x45 && data[20] == 0) {
|
|
ping_replies++;
|
|
}
|
|
}
|
|
pbuf_free(p);
|
|
return 1;
|
|
}
|
|
|
|
int network_icmp_single_ping(ipv4_address_t *dest) {
|
|
if (!lwip_initialized || network_processing) return -2;
|
|
network_processing = 1;
|
|
struct raw_pcb *pcb = raw_new(IP_PROTO_ICMP);
|
|
if (!pcb) { network_processing = 0; return -1; }
|
|
raw_recv(pcb, ping_recv, NULL);
|
|
raw_bind(pcb, IP_ADDR_ANY);
|
|
ip_addr_t dest_addr;
|
|
IP4_ADDR(ip_2_ip4(&dest_addr), dest->bytes[0], dest->bytes[1], dest->bytes[2], dest->bytes[3]);
|
|
|
|
struct pbuf *p = pbuf_alloc(PBUF_IP, 8 + 56, PBUF_RAM); // 64 bytes total
|
|
if (!p) { raw_remove(pcb); network_processing = 0; return -1; }
|
|
u8_t *data = (u8_t *)p->payload;
|
|
data[0] = 8; data[1] = 0; data[2] = 0; data[3] = 0;
|
|
data[4] = 0; data[5] = 1; data[6] = (u8_t)(ping_seq >> 8); data[7] = (u8_t)(ping_seq & 0xFF);
|
|
ping_seq++;
|
|
for (int j = 0; j < 56; j++) data[8+j] = (u8_t)('a' + (j % 26));
|
|
|
|
// Calculate ICMP Checksum
|
|
u16_t chk = icmp_cksum(data, 8 + 56);
|
|
data[2] = (u8_t)(chk & 0xFF);
|
|
data[3] = (u8_t)(chk >> 8);
|
|
|
|
ping_replies = 0;
|
|
uint32_t start = sys_now();
|
|
raw_sendto(pcb, p, &dest_addr);
|
|
pbuf_free(p);
|
|
|
|
asm volatile("sti");
|
|
int rtt = -1;
|
|
while (sys_now() - start < 1000) {
|
|
network_poll_internal();
|
|
if (ping_replies > 0) {
|
|
rtt = (int)(sys_now() - start);
|
|
break;
|
|
}
|
|
k_delay(10);
|
|
}
|
|
asm volatile("cli");
|
|
raw_remove(pcb);
|
|
network_processing = 0;
|
|
return rtt;
|
|
}
|
|
|
|
int network_get_frames_received(void) { return (int)lwip_stats.link.recv; }
|
|
int network_get_udp_packets_received(void) { return (int)lwip_stats.udp.recv; }
|
|
int network_get_frames_sent(void) { return (int)lwip_stats.link.xmit; }
|
|
int network_get_udp_callbacks_called(void) { return 0; }
|
|
int network_get_e1000_receive_calls(void) { return 0; }
|
|
int network_get_e1000_receive_empty(void) { return 0; }
|
|
int network_get_process_calls(void) { return (int)lwip_stats.link.drop; }
|