#include #include #include "network.h" #include "e1000.h" #include "pci.h" #undef IP_PROTO_UDP // Avoid redefinition warning from net_defs.h #include "net_defs.h" static int network_initialized = 0; static mac_address_t our_mac; static ipv4_address_t ip_address = {{0,0,0,0}}; static ipv4_address_t gateway_ip = {{0,0,0,0}}; static ipv4_address_t subnet_mask = {{0,0,0,0}}; static ipv4_address_t dns_server_ip = {{0,0,0,0}}; static uint16_t ipv4_id_counter = 0; typedef struct { ipv4_address_t ip; mac_address_t mac; uint32_t timestamp; int valid; } arp_cache_entry_t; #define ARP_CACHE_SIZE 16 static arp_cache_entry_t arp_cache[ARP_CACHE_SIZE]; static int arp_cache_initialized = 0; #define UDP_MAX_CALLBACKS 8 typedef struct { uint16_t port; udp_callback_t callback; int valid; } udp_callback_entry_t; static udp_callback_entry_t udp_callbacks[UDP_MAX_CALLBACKS]; static int frames_received_count = 0; static int udp_packets_received_count = 0; static int udp_callbacks_called_count = 0; static int e1000_receive_calls = 0; static int e1000_receive_empty = 0; static int network_process_calls = 0; static void* kmemcpy(void* d, const void* s, size_t n){uint8_t*D=d;const uint8_t*S=s;for(size_t i=0;i>16) sum=(sum&0xFFFF)+(sum>>16); return (uint16_t)(~sum); } static void arp_cache_init(void){ if(arp_cache_initialized) return; kmemset(arp_cache,0,sizeof(arp_cache)); arp_cache_initialized=1; } static arp_cache_entry_t* arp_cache_find(const ipv4_address_t* ip){ for(int i=0;imac,mac,sizeof(mac_address_t)); e->timestamp=0; return;} for(int i=0;imac_address; return 0; } int network_get_ipv4_address(ipv4_address_t* ip){ if(!network_initialized) return -1; *ip=ip_address; return 0; } int network_set_ipv4_address(const ipv4_address_t* ip){ if(!network_initialized) return -1; ip_address=*ip; return 0; } int network_get_gateway_ip(ipv4_address_t* ip){ if(!network_initialized) return -1; *ip=gateway_ip; return 0; } int network_get_dns_ip(ipv4_address_t* ip){ if(!network_initialized) return -1; *ip=dns_server_ip; return 0; } ipv4_address_t get_local_ip(void) { return ip_address; } ipv4_address_t get_dns_server_ip(void) { return dns_server_ip; } int ip_send_packet(ipv4_address_t dst, uint8_t protocol, const void *data, uint16_t len) { return ipv4_send_packet(&dst, protocol, data, len); } int network_send_frame(const void* data,size_t length){ if(!network_initialized) return -1; if(length>ETH_FRAME_MAX_SIZE) return -1; return e1000_send_packet(data,length); } int network_receive_frame(void* buffer,size_t buffer_size){ if(!network_initialized) return 0; e1000_receive_calls++; int result=e1000_receive_packet(buffer,buffer_size); if(result==0) e1000_receive_empty++; return result; } void network_process_frames(void){ network_process_calls++; if(!network_initialized) return; uint8_t frame_buffer[ETH_FRAME_MAX_SIZE]; int frame_length; while((frame_length=network_receive_frame(frame_buffer,sizeof(frame_buffer)))>0){ frames_received_count++; if(frame_length<(int)sizeof(eth_header_t)) continue; eth_header_t* eth=(eth_header_t*)frame_buffer; uint16_t ethertype=ntohs(eth->ethertype); int is_broadcast=1; int is_for_us=1; for(int i=0;i<6;i++){ if(eth->dest_mac[i]!=0xFF) is_broadcast=0; if(eth->dest_mac[i]!=our_mac.bytes[i]) is_for_us=0; } if(!is_broadcast && !is_for_us) continue; void* payload=frame_buffer+sizeof(eth_header_t); size_t payload_length=frame_length-sizeof(eth_header_t); if(ethertype==ETH_ETHERTYPE_ARP){ if(payload_length>=sizeof(arp_header_t)) arp_process_packet((arp_header_t*)payload,payload_length); } else if(ethertype==ETH_ETHERTYPE_IPV4){ if(payload_length>=sizeof(ipv4_header_t)){ ipv4_header_t* ip=(ipv4_header_t*)payload; uint16_t checksum=ip->checksum; ip->checksum=0; uint16_t calc=ipv4_checksum(ip); ip->checksum=checksum; if(checksum==calc){ int for_our_ip=1; for(int i=0;i<4;i++){ if(ip->dest_ip[i]!=ip_address.bytes[i]) { for_our_ip=0; break; } } if(for_our_ip || ip->dest_ip[0]==255){ mac_address_t src_mac; kmemcpy(src_mac.bytes,eth->src_mac,6); ipv4_address_t src_ip_struct; kmemcpy(src_ip_struct.bytes, ip->src_ip, 4); arp_cache_add(&src_ip_struct, &src_mac); ipv4_process_packet(ip,&src_mac,payload_length); } } } } } } int arp_send_request(const ipv4_address_t* target_ip){ if(!network_initialized) return -1; uint8_t frame[ETH_FRAME_MAX_SIZE]; eth_header_t* eth=(eth_header_t*)frame; arp_header_t* arp=(arp_header_t*)(frame+sizeof(eth_header_t)); for(int i=0;i<6;i++) eth->dest_mac[i]=0xFF; kmemcpy(eth->src_mac,our_mac.bytes,6); eth->ethertype=htons(ETH_ETHERTYPE_ARP); arp->hw_type=htons(1); arp->proto_type=htons(ETH_ETHERTYPE_IPV4); arp->hw_len=6; arp->proto_len=4; arp->opcode=htons(ARP_OP_REQUEST); kmemcpy(arp->sender_mac,our_mac.bytes,6); kmemcpy(arp->sender_ip,ip_address.bytes,4); for(int i=0;i<6;i++) arp->target_mac[i]=0; kmemcpy(arp->target_ip,target_ip->bytes,4); size_t frame_length=sizeof(eth_header_t)+sizeof(arp_header_t); return network_send_frame(frame,frame_length); } int arp_lookup(const ipv4_address_t* ip,mac_address_t* mac){ if(!network_initialized) return -1; arp_cache_entry_t* e=arp_cache_find(ip); if(e && e->valid){ *mac=e->mac; return 0; } arp_send_request(ip); return -1; } void arp_process_packet(const arp_header_t* arp,size_t length){ if(lengthhw_type)!=1 || ntohs(arp->proto_type)!=ETH_ETHERTYPE_IPV4) return; uint16_t opcode=ntohs(arp->opcode); ipv4_address_t sender_ip; mac_address_t sender_mac; kmemcpy(sender_ip.bytes,arp->sender_ip,4); kmemcpy(sender_mac.bytes,arp->sender_mac,6); arp_cache_add(&sender_ip,&sender_mac); if(opcode==ARP_OP_REQUEST){ int is_for_us=1; for(int i=0;i<4;i++){ if(arp->target_ip[i]!=ip_address.bytes[i]) { is_for_us=0; break; } } if(is_for_us){ uint8_t frame[ETH_FRAME_MAX_SIZE]; eth_header_t* eth=(eth_header_t*)frame; arp_header_t* r=(arp_header_t*)(frame+sizeof(eth_header_t)); kmemcpy(eth->dest_mac,arp->sender_mac,6); kmemcpy(eth->src_mac,our_mac.bytes,6); eth->ethertype=htons(ETH_ETHERTYPE_ARP); r->hw_type=htons(1); r->proto_type=htons(ETH_ETHERTYPE_IPV4); r->hw_len=6; r->proto_len=4; r->opcode=htons(ARP_OP_REPLY); kmemcpy(r->sender_mac,our_mac.bytes,6); kmemcpy(r->sender_ip,ip_address.bytes,4); kmemcpy(r->target_mac,arp->sender_mac,6); kmemcpy(r->target_ip,arp->sender_ip,4); size_t frame_length=sizeof(eth_header_t)+sizeof(arp_header_t); network_send_frame(frame,frame_length); } } } int ipv4_send_packet(const ipv4_address_t* dest_ip,uint8_t protocol,const void* data,size_t data_length){ if(!network_initialized) return -1; mac_address_t dest_mac; int is_bcast=(dest_ip->bytes[0]==255 && dest_ip->bytes[1]==255 && dest_ip->bytes[2]==255 && dest_ip->bytes[3]==255); ipv4_address_t target_ip = *dest_ip; // Routing Logic: If dest is not local, send to Gateway if (!is_bcast && gateway_ip.bytes[0] != 0 && subnet_mask.bytes[0] != 0) { int is_local = 1; for(int i=0; i<4; i++) { if ((dest_ip->bytes[i] & subnet_mask.bytes[i]) != (ip_address.bytes[i] & subnet_mask.bytes[i])) { is_local = 0; break; } } if (!is_local) { target_ip = gateway_ip; } } if(is_bcast){ for(int i=0;i<6;i++) dest_mac.bytes[i]=0xFF; } else { int ok=arp_lookup(&target_ip,&dest_mac); if(ok!=0){ for(int i=0;i<6;i++) dest_mac.bytes[i]=0xFF; } } uint8_t frame[ETH_FRAME_MAX_SIZE]; eth_header_t* eth=(eth_header_t*)frame; ipv4_header_t* ip=(ipv4_header_t*)(frame+sizeof(eth_header_t)); void* ip_payload=frame+sizeof(eth_header_t)+sizeof(ipv4_header_t); kmemcpy(eth->dest_mac,dest_mac.bytes,6); kmemcpy(eth->src_mac,our_mac.bytes,6); eth->ethertype=htons(ETH_ETHERTYPE_IPV4); ip->version_ihl=(4<<4)|5; ip->tos=0; ip->total_length=htons(sizeof(ipv4_header_t)+data_length); ip->id=htons(ipv4_id_counter++); ip->flags_frag=0; ip->ttl=64; ip->protocol=protocol; ip->checksum=0; kmemcpy(ip->src_ip,ip_address.bytes,4); kmemcpy(ip->dest_ip,dest_ip->bytes,4); ip->checksum=ipv4_checksum(ip); kmemcpy(ip_payload,data,data_length); size_t frame_length=sizeof(eth_header_t)+sizeof(ipv4_header_t)+data_length; return network_send_frame(frame,frame_length); } int ipv4_send_packet_to_mac(const ipv4_address_t* dest_ip,const mac_address_t* dest_mac,uint8_t protocol,const void* data,size_t data_length){ if(!network_initialized) return -1; uint8_t frame[ETH_FRAME_MAX_SIZE]; eth_header_t* eth=(eth_header_t*)frame; ipv4_header_t* ip=(ipv4_header_t*)(frame+sizeof(eth_header_t)); void* ip_payload=frame+sizeof(eth_header_t)+sizeof(ipv4_header_t); kmemcpy(eth->dest_mac,dest_mac->bytes,6); kmemcpy(eth->src_mac,our_mac.bytes,6); eth->ethertype=htons(ETH_ETHERTYPE_IPV4); ip->version_ihl=(4<<4)|5; ip->tos=0; ip->total_length=htons(sizeof(ipv4_header_t)+data_length); ip->id=htons(ipv4_id_counter++); ip->flags_frag=0; ip->ttl=64; ip->protocol=protocol; ip->checksum=0; kmemcpy(ip->src_ip,ip_address.bytes,4); kmemcpy(ip->dest_ip,dest_ip->bytes,4); ip->checksum=ipv4_checksum(ip); kmemcpy(ip_payload,data,data_length); size_t frame_length=sizeof(eth_header_t)+sizeof(ipv4_header_t)+data_length; return network_send_frame(frame,frame_length); } void ipv4_process_packet(const ipv4_header_t* ip,const mac_address_t* src_mac,size_t length){ if(lengthversion_ihl & 0x0F)*4; if(ihl<20 || lengthtotal_length); if(total_length>length) return; void* payload=(void*)ip+ihl; size_t payload_length=total_length-ihl; if(ip->protocol==IP_PROTO_UDP){ if(payload_length>=sizeof(udp_header_t)){ udp_packets_received_count++; ipv4_address_t src_ip; kmemcpy(src_ip.bytes,ip->src_ip,4); udp_header_t* udp=(udp_header_t*)payload; uint16_t dest_port=ntohs(udp->dest_port); uint16_t src_port=ntohs(udp->src_port); uint16_t udp_length=ntohs(udp->length); if(udp_length>payload_length) return; void* udp_payload=(void*)udp+sizeof(udp_header_t); size_t udp_payload_length=udp_length-sizeof(udp_header_t); for(int i=0;iprotocol == IP_PROTO_ICMP) { ipv4_address_t src_ip; kmemcpy(src_ip.bytes, ip->src_ip, 4); icmp_handle_packet(src_ip, payload, payload_length); } else if (ip->protocol == IP_PROTO_TCP) { ipv4_address_t src_ip; kmemcpy(src_ip.bytes, ip->src_ip, 4); tcp_handle_packet(src_ip, payload, payload_length); } } 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){ if(!network_initialized) return -1; uint8_t udp_packet[ETH_FRAME_MAX_SIZE]; udp_header_t* udp=(udp_header_t*)udp_packet; void* udp_payload=udp_packet+sizeof(udp_header_t); udp->src_port=htons(src_port); udp->dest_port=htons(dest_port); udp->length=htons(sizeof(udp_header_t)+data_length); udp->checksum=0; kmemcpy(udp_payload,data,data_length); size_t udp_packet_length=sizeof(udp_header_t)+data_length; return ipv4_send_packet(dest_ip,IP_PROTO_UDP,udp_packet,udp_packet_length); } int udp_send_packet_to_mac(const ipv4_address_t* dest_ip,const mac_address_t* dest_mac,uint16_t dest_port,uint16_t src_port,const void* data,size_t data_length){ if(!network_initialized) return -1; uint8_t udp_packet[ETH_FRAME_MAX_SIZE]; udp_header_t* udp=(udp_header_t*)udp_packet; void* udp_payload=udp_packet+sizeof(udp_header_t); udp->src_port=htons(src_port); udp->dest_port=htons(dest_port); udp->length=htons(sizeof(udp_header_t)+data_length); udp->checksum=0; kmemcpy(udp_payload,data,data_length); size_t udp_packet_length=sizeof(udp_header_t)+data_length; return ipv4_send_packet_to_mac(dest_ip,dest_mac,IP_PROTO_UDP,udp_packet,udp_packet_length); } int udp_register_callback(uint16_t port,udp_callback_t callback){ if(!network_initialized) return -1; for(int i=0;i>8)&0xFF00)|((v>>24)&0xFF);} static uint32_t ntohl32(uint32_t v){return htonl32(v);} static uint16_t htons16(uint16_t v){return (uint16_t)(((v&0xFF)<<8)|((v>>8)&0xFF));} static void dhcp_build_discover(dhcp_packet_t* pkt){ kmemset(pkt,0,sizeof(dhcp_packet_t)); pkt->op=DHCP_OP_BOOTREQUEST; pkt->htype=DHCP_HTYPE_ETHERNET; pkt->hlen=DHCP_HLEN_ETHERNET; pkt->xid=htonl32(dhcp_xid); pkt->flags=htons16(0x8000); kmemcpy(pkt->chaddr,our_mac.bytes,6); pkt->magic_cookie=htonl32(DHCP_MAGIC_COOKIE); uint8_t* opt=pkt->options; *opt++=DHCP_OPT_MSG_TYPE; *opt++=1; *opt++=DHCP_MSG_DISCOVER; *opt++=DHCP_OPT_PARAM_REQ_LIST; *opt++=3; *opt++=1; *opt++=3; *opt++=6; *opt++=DHCP_OPT_END; } static void dhcp_build_request(dhcp_packet_t* pkt){ kmemset(pkt,0,sizeof(dhcp_packet_t)); pkt->op=DHCP_OP_BOOTREQUEST; pkt->htype=DHCP_HTYPE_ETHERNET; pkt->hlen=DHCP_HLEN_ETHERNET; pkt->xid=htonl32(dhcp_xid); pkt->flags=htons16(0x8000); kmemcpy(pkt->chaddr,our_mac.bytes,6); pkt->magic_cookie=htonl32(DHCP_MAGIC_COOKIE); uint8_t* opt=pkt->options; *opt++=DHCP_OPT_MSG_TYPE; *opt++=1; *opt++=DHCP_MSG_REQUEST; *opt++=DHCP_OPT_REQ_IP; *opt++=4; *opt++=dhcp_offered_ip.bytes[0]; *opt++=dhcp_offered_ip.bytes[1]; *opt++=dhcp_offered_ip.bytes[2]; *opt++=dhcp_offered_ip.bytes[3]; *opt++=DHCP_OPT_SERVER_ID; *opt++=4; *opt++=(uint8_t)((dhcp_server_id>>24)&0xFF); *opt++=(uint8_t)((dhcp_server_id>>16)&0xFF); *opt++=(uint8_t)((dhcp_server_id>>8)&0xFF); *opt++=(uint8_t)(dhcp_server_id&0xFF); *opt++=DHCP_OPT_END; } static uint8_t dhcp_get_option(const uint8_t* opts,uint8_t code){ const uint8_t* p=opts; while(*p!=DHCP_OPT_END){ uint8_t c=*p++; uint8_t l=*p++; if(c==code) return p[0]; p+=l; } return 0; } static void dhcp_udp_callback(const ipv4_address_t* src_ip,uint16_t src_port,const mac_address_t* src_mac,const void* payload,size_t payload_length){ (void)src_ip; (void)src_mac; if(src_port!=DHCP_SERVER_PORT || payload_lengthop!=DHCP_OP_BOOTREPLY) return; if(ntohl32(pkt->xid)!=dhcp_xid) return; if(ntohl32(pkt->magic_cookie)!=DHCP_MAGIC_COOKIE) return; uint8_t mtype=dhcp_get_option(pkt->options,DHCP_OPT_MSG_TYPE); if(mtype==DHCP_MSG_OFFER){ uint32_t yi_host=ntohl32(pkt->yiaddr); dhcp_offered_ip.bytes[0]=(uint8_t)((yi_host>>24)&0xFF); dhcp_offered_ip.bytes[1]=(uint8_t)((yi_host>>16)&0xFF); dhcp_offered_ip.bytes[2]=(uint8_t)((yi_host>>8)&0xFF); dhcp_offered_ip.bytes[3]=(uint8_t)(yi_host&0xFF); const uint8_t* p=pkt->options; dhcp_server_id=0; while(*p!=DHCP_OPT_END){ uint8_t c=*p++; uint8_t l=*p++; if(c==DHCP_OPT_SERVER_ID && l==4){ dhcp_server_id=((uint32_t)p[0]<<24)|((uint32_t)p[1]<<16)|((uint32_t)p[2]<<8)|(uint32_t)p[3]; break; } p+=l; } if(dhcp_server_id!=0) dhcp_state=1; } else if(mtype==DHCP_MSG_ACK){ uint32_t yi_host=ntohl32(pkt->yiaddr); ip_address.bytes[0]=(uint8_t)((yi_host>>24)&0xFF); ip_address.bytes[1]=(uint8_t)((yi_host>>16)&0xFF); ip_address.bytes[2]=(uint8_t)((yi_host>>8)&0xFF); ip_address.bytes[3]=(uint8_t)(yi_host&0xFF); // Parse Options for Gateway, Subnet, DNS const uint8_t* p=pkt->options; while(*p!=DHCP_OPT_END){ uint8_t c=*p++; uint8_t l=*p++; if(c==DHCP_OPT_SUBNET_MASK && l==4) { kmemcpy(subnet_mask.bytes, p, 4); } else if(c==DHCP_OPT_ROUTER && l>=4) { // Take first router kmemcpy(gateway_ip.bytes, p, 4); } else if(c==DHCP_OPT_DNS && l>=4) { // Take first DNS kmemcpy(dns_server_ip.bytes, p, 4); } p+=l; } dhcp_state=2; } else if(mtype==DHCP_MSG_NAK){ dhcp_state=-1; } } int network_dhcp_acquire(void){ if(!network_initialized) return -1; if(udp_register_callback(DHCP_CLIENT_PORT,dhcp_udp_callback)!=0) return -1; dhcp_xid += 0x12345u + (uint32_t)ipv4_id_counter; dhcp_state=0; dhcp_server_id=0; dhcp_packet_t pkt; dhcp_build_discover(&pkt); ipv4_address_t bcast={{255,255,255,255}}; udp_send_packet(&bcast,DHCP_SERVER_PORT,DHCP_CLIENT_PORT,&pkt,sizeof(dhcp_packet_t)); for(int i=0;i<500000 && dhcp_state==0;i++){ network_process_frames(); if(i%1000==0){ for(volatile int d=0; d<100000; d++){} } } if(dhcp_state!=1) return -1; dhcp_build_request(&pkt); udp_send_packet(&bcast,DHCP_SERVER_PORT,DHCP_CLIENT_PORT,&pkt,sizeof(dhcp_packet_t)); for(int i=0;i<500000 && dhcp_state==1;i++){ network_process_frames(); if(i%1000==0){ for(volatile int d=0; d<100000; d++){} } } return (dhcp_state==2)?0:-1; }