mirror of
https://git.checksum.fail/alec/Web.git
synced 2026-05-26 16:29:46 +00:00
Add SnailNet files from Shrine, add Virtio-net driver from Erythros
This commit is contained in:
+128
@@ -0,0 +1,128 @@
|
||||
// Not a Network Layer protocol, but it is encapsulated in L2 frames, which
|
||||
// makes it L3 for our purposes
|
||||
|
||||
#define ARP_REQUEST 0x01
|
||||
#define ARP_REPLY 0x02
|
||||
|
||||
class CArpHeader {
|
||||
U16 htype;
|
||||
U16 ptype;
|
||||
U8 hlen;
|
||||
U8 plen;
|
||||
U16 oper;
|
||||
U8 sha[6];
|
||||
U32 spa;
|
||||
U8 tha[6];
|
||||
U32 tpa;
|
||||
};
|
||||
|
||||
class CArpCacheEntry {
|
||||
CArpCacheEntry *next;
|
||||
U32 ip;
|
||||
U8 mac[6];
|
||||
};
|
||||
|
||||
// Stored in network order
|
||||
static U32 arp_my_ipv4_n = 0;
|
||||
|
||||
// TODO: use a Hash table
|
||||
static CArpCacheEntry *arp_cache = NULL;
|
||||
|
||||
// IPs are in network order
|
||||
I64 ArpSend(U16 oper, U8 *dest_mac, U8 *sender_mac, U32 sender_ip_n,
|
||||
U8 *target_mac, U32 target_ip_n) {
|
||||
U8 *frame;
|
||||
|
||||
I64 index = EthernetFrameAlloc(&frame, sender_mac, dest_mac, ETHERTYPE_ARP,
|
||||
sizeof(CArpHeader), 0);
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
CArpHeader *hdr = frame;
|
||||
hdr->htype = htons(1);
|
||||
hdr->ptype = htons(ETHERTYPE_IPV4);
|
||||
hdr->hlen = 6;
|
||||
hdr->plen = 4;
|
||||
hdr->oper = htons(oper);
|
||||
MemCpy(hdr->sha, sender_mac, 6);
|
||||
hdr->spa = sender_ip_n;
|
||||
MemCpy(hdr->tha, target_mac, 6);
|
||||
hdr->tpa = target_ip_n;
|
||||
|
||||
return EthernetFrameFinish(index);
|
||||
}
|
||||
|
||||
U0 ArpSetIPv4Address(U32 addr) {
|
||||
arp_my_ipv4_n = htonl(addr);
|
||||
|
||||
// Broadcast our new address
|
||||
ArpSend(ARP_REPLY, eth_broadcast, EthernetGetAddress(), arp_my_ipv4_n,
|
||||
eth_null, arp_my_ipv4_n);
|
||||
}
|
||||
|
||||
CArpCacheEntry *ArpCacheFindByIP(U32 ip) {
|
||||
CArpCacheEntry *e = arp_cache;
|
||||
|
||||
while (e) {
|
||||
if (e->ip == ip)
|
||||
return e;
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
CArpCacheEntry *ArpCachePut(U32 ip, U8 *mac) {
|
||||
CArpCacheEntry *e = ArpCacheFindByIP(ip);
|
||||
|
||||
if (!e) {
|
||||
//"ARP: add entry for %08X\n", ip;
|
||||
e = MAlloc(sizeof(CArpCacheEntry));
|
||||
e->next = arp_cache;
|
||||
e->ip = ip;
|
||||
MemCpy(e->mac, mac, 6);
|
||||
arp_cache = e;
|
||||
}
|
||||
// FIXME: else replace!
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
I64 ArpHandler(CEthFrame *eth_frame) {
|
||||
if (eth_frame->ethertype != ETHERTYPE_ARP)
|
||||
return -1;
|
||||
|
||||
// FIXME[obecebo]: this blocks responding to ARP_REQUEST? [2019/08/05]
|
||||
if (eth_frame->length < sizeof(CArpHeader))
|
||||
return -1;
|
||||
|
||||
CArpHeader *hdr = eth_frame->data;
|
||||
U16 oper = ntohs(hdr->oper);
|
||||
|
||||
//"ARP: htype %d, ptype %d, hlen %d, plen %d, oper %d\n",
|
||||
// ntohs(hdr->htype), ntohs(hdr->ptype), hdr->hlen, hdr->plen, oper;
|
||||
//" spa %08X, tpa %08X\n", ntohl(hdr->spa), ntohl(hdr->tpa);
|
||||
|
||||
if (ntohs(hdr->htype) != 1 || ntohs(hdr->ptype) != ETHERTYPE_IPV4 ||
|
||||
hdr->hlen != 6 || hdr->plen != 4)
|
||||
return -1;
|
||||
|
||||
if (oper == ARP_REQUEST) {
|
||||
// Not too sure about this line, but it seems necessary in WiFi networks,
|
||||
// because the wireless device won't hear our Ethernet broadcast when we
|
||||
// Request
|
||||
// ArpCachePut(ntohl(hdr->spa), hdr->sha);
|
||||
|
||||
if (hdr->tpa == arp_my_ipv4_n) {
|
||||
ArpSend(ARP_REPLY, hdr->sha, EthernetGetAddress(), arp_my_ipv4_n,
|
||||
hdr->sha, hdr->spa);
|
||||
}
|
||||
} else if (oper == ARP_REPLY) {
|
||||
ArpCachePut(ntohl(hdr->spa), hdr->sha);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RegisterL3Protocol(ETHERTYPE_ARP, &ArpHandler);
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
|
||||
#define BOOTREQUEST 0x01
|
||||
#define BOOTREPLY 0x02
|
||||
|
||||
#define HTYPE_ETHERNET 0x01
|
||||
|
||||
#define HLEN_ETHERNET 6
|
||||
|
||||
#define DHCP_OPTION_SUBNET_MASK 1
|
||||
#define DHCP_OPTION_ROUTER 3
|
||||
#define DHCP_OPTION_DNS 6
|
||||
#define DHCP_OPTION_DOMAIN_NAME 15
|
||||
#define DHCP_OPTION_REQUESTED_IP 50
|
||||
#define DHCP_OPTION_MSGTYPE 53
|
||||
#define DHCP_OPTION_SERVER_ID 54
|
||||
#define DHCP_OPTION_PARAMLIST 55
|
||||
|
||||
#define DHCP_COOKIE 0x63825363
|
||||
#define DHCP_MSGTYPE_DISCOVER 0x01
|
||||
#define DHCP_MSGTYPE_OFFER 0x02
|
||||
#define DHCP_MSGTYPE_REQUEST 0x03
|
||||
#define DHCP_MSGTYPE_ACK 0x05
|
||||
|
||||
class CDhcpHeader {
|
||||
U8 op;
|
||||
U8 htype;
|
||||
U8 hlen;
|
||||
U8 hops;
|
||||
U32 xid;
|
||||
U16 secs;
|
||||
U16 flags;
|
||||
U32 ciaddr;
|
||||
U32 yiaddr;
|
||||
U32 siaddr;
|
||||
U32 giaddr;
|
||||
U8 chaddr[16];
|
||||
U8 sname[64];
|
||||
U8 file[128];
|
||||
};
|
||||
|
||||
class CDhcpDiscoverOptions {
|
||||
U32 cookie;
|
||||
// DHCP Message Type
|
||||
U8 dmt_type;
|
||||
U8 dmt_length;
|
||||
U8 dmt;
|
||||
// DHCP Parameter Request List
|
||||
U8 prl_type;
|
||||
U8 prl_length;
|
||||
U8 prl[4];
|
||||
|
||||
U8 end;
|
||||
};
|
||||
|
||||
class CDhcpRequestOptions {
|
||||
U32 cookie;
|
||||
// DHCP Message Type
|
||||
U8 dmt_type;
|
||||
U8 dmt_length;
|
||||
U8 dmt;
|
||||
// DHCP Requested IP
|
||||
U8 requested_ip_type;
|
||||
U8 requested_ip_length;
|
||||
U32 requested_ip;
|
||||
// DHCP Server Identifier
|
||||
U8 server_id_type;
|
||||
U8 server_id_length;
|
||||
U32 server_id;
|
||||
|
||||
U8 end;
|
||||
};
|
||||
|
||||
U32 DhcpBeginTransaction() { return RandU32(); }
|
||||
|
||||
I64 DhcpSendDiscover(U32 xid) {
|
||||
U8 *frame;
|
||||
I64 index =
|
||||
UdpPacketAlloc(&frame, 0x00000000, 68, 0xffffffff, 67,
|
||||
sizeof(CDhcpHeader) + sizeof(CDhcpDiscoverOptions));
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
CDhcpHeader *dhcp = frame;
|
||||
MemSet(dhcp, 0, sizeof(CDhcpHeader));
|
||||
dhcp->op = BOOTREQUEST;
|
||||
dhcp->htype = HTYPE_ETHERNET;
|
||||
dhcp->hlen = HLEN_ETHERNET;
|
||||
dhcp->hops = 0;
|
||||
dhcp->xid = htonl(xid);
|
||||
dhcp->secs = 0;
|
||||
dhcp->flags = htons(0x8000);
|
||||
dhcp->ciaddr = 0;
|
||||
dhcp->yiaddr = 0;
|
||||
dhcp->siaddr = 0;
|
||||
dhcp->giaddr = 0;
|
||||
MemCpy(dhcp->chaddr, EthernetGetAddress(), 6);
|
||||
|
||||
CDhcpDiscoverOptions *opts = frame + sizeof(CDhcpHeader);
|
||||
opts->cookie = htonl(DHCP_COOKIE);
|
||||
opts->dmt_type = DHCP_OPTION_MSGTYPE;
|
||||
opts->dmt_length = 1;
|
||||
opts->dmt = DHCP_MSGTYPE_DISCOVER;
|
||||
opts->prl_type = DHCP_OPTION_PARAMLIST;
|
||||
opts->prl_length = 4;
|
||||
opts->prl[0] = DHCP_OPTION_SUBNET_MASK;
|
||||
opts->prl[1] = DHCP_OPTION_ROUTER;
|
||||
opts->prl[2] = DHCP_OPTION_DNS;
|
||||
opts->prl[3] = DHCP_OPTION_DOMAIN_NAME;
|
||||
opts->end = 0xff;
|
||||
|
||||
return UdpPacketFinish(index);
|
||||
}
|
||||
|
||||
I64 DhcpSendRequest(U32 xid, U32 requested_ip, U32 siaddr) {
|
||||
U8 *frame;
|
||||
I64 index = UdpPacketAlloc(&frame, 0x00000000, 68, 0xffffffff, 67,
|
||||
sizeof(CDhcpHeader) + sizeof(CDhcpRequestOptions));
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
CDhcpHeader *dhcp = frame;
|
||||
MemSet(dhcp, 0, sizeof(CDhcpHeader));
|
||||
dhcp->op = BOOTREQUEST;
|
||||
dhcp->htype = HTYPE_ETHERNET;
|
||||
dhcp->hlen = HLEN_ETHERNET;
|
||||
dhcp->hops = 0;
|
||||
dhcp->xid = htonl(xid);
|
||||
dhcp->secs = 0;
|
||||
dhcp->flags = htons(0x0000);
|
||||
dhcp->ciaddr = 0;
|
||||
dhcp->yiaddr = 0;
|
||||
dhcp->siaddr = htonl(siaddr);
|
||||
dhcp->giaddr = 0;
|
||||
MemCpy(dhcp->chaddr, EthernetGetAddress(), 6);
|
||||
|
||||
CDhcpRequestOptions *opts = frame + sizeof(CDhcpHeader);
|
||||
opts->cookie = htonl(DHCP_COOKIE);
|
||||
opts->dmt_type = DHCP_OPTION_MSGTYPE;
|
||||
opts->dmt_length = 1;
|
||||
opts->dmt = DHCP_MSGTYPE_REQUEST;
|
||||
opts->requested_ip_type = DHCP_OPTION_REQUESTED_IP;
|
||||
opts->requested_ip_length = 4;
|
||||
opts->requested_ip = htonl(requested_ip);
|
||||
opts->server_id_type = DHCP_OPTION_SERVER_ID;
|
||||
opts->server_id_length = 4;
|
||||
opts->server_id = htonl(siaddr);
|
||||
opts->end = 0xff;
|
||||
|
||||
return UdpPacketFinish(index);
|
||||
}
|
||||
|
||||
I64 DhcpParseBegin(U8 **data_inout, I64 *length_inout, CDhcpHeader **hdr_out) {
|
||||
U8 *data = *data_inout;
|
||||
I64 length = *length_inout;
|
||||
|
||||
if (length < sizeof(CDhcpHeader) + 4) {
|
||||
//"DhcpParseBegin: too short\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
U32 *p_cookie = data + sizeof(CDhcpHeader);
|
||||
|
||||
if (ntohl(*p_cookie) != DHCP_COOKIE) {
|
||||
//"DhcpParseBegin: cookie %08Xh != %08Xh\n", ntohl(*p_cookie), DHCP_COOKIE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*hdr_out = data;
|
||||
*data_inout = data + (sizeof(CDhcpHeader) + 4);
|
||||
*length_inout = length - (sizeof(CDhcpHeader) + 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
I64 DhcpParseOption(U8 **data_inout, I64 *length_inout, U8 *type_out,
|
||||
U8 *value_length_out, U8 **value_out) {
|
||||
U8 *data = *data_inout;
|
||||
I64 length = *length_inout;
|
||||
|
||||
if (length < 2 || length < 2 + data[1]) {
|
||||
//"DhcpParseOption: too short\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data[0] == 0xff)
|
||||
return 0;
|
||||
|
||||
*type_out = data[0];
|
||||
*value_length_out = data[1];
|
||||
*value_out = data + 2;
|
||||
|
||||
*data_inout = data + (2 + *value_length_out);
|
||||
*length_inout = length - (2 + *value_length_out);
|
||||
return data[0];
|
||||
}
|
||||
|
||||
I64 DhcpParseOffer(U32 xid, U8 *data, I64 length, U32 *yiaddr_out,
|
||||
U32 *dns_ip_out, U32 *router_ip_out, U32 *subnet_mask_out) {
|
||||
CDhcpHeader *hdr;
|
||||
I64 error = DhcpParseBegin(&data, &length, &hdr);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (ntohl(hdr->xid) != xid)
|
||||
return -1;
|
||||
|
||||
Bool have_type = FALSE;
|
||||
Bool have_dns = FALSE;
|
||||
Bool have_router = FALSE;
|
||||
Bool have_subnet = FALSE;
|
||||
|
||||
while (length) {
|
||||
U8 type, value_length;
|
||||
U8 *value;
|
||||
|
||||
error = DhcpParseOption(&data, &length, &type, &value_length, &value);
|
||||
//"%d, %02Xh, %d, %02Xh...\n", error, type, value_length, value[0];
|
||||
if (error < 0)
|
||||
return error;
|
||||
if (error == 0)
|
||||
break;
|
||||
|
||||
if (type == DHCP_OPTION_MSGTYPE && value_length == 1 &&
|
||||
value[0] == DHCP_MSGTYPE_OFFER)
|
||||
have_type = TRUE;
|
||||
|
||||
if (type == DHCP_OPTION_DNS && value_length == 4) {
|
||||
*dns_ip_out = ntohl(*(value(U32 *)));
|
||||
have_dns = TRUE;
|
||||
}
|
||||
|
||||
if (type == DHCP_OPTION_ROUTER && value_length == 4) {
|
||||
*router_ip_out = ntohl(*(value(U32 *)));
|
||||
have_router = TRUE;
|
||||
}
|
||||
|
||||
if (type == DHCP_OPTION_SUBNET_MASK && value_length == 4) {
|
||||
*subnet_mask_out = ntohl(*(value(U32 *)));
|
||||
have_subnet = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//"DhcpParseOffer: end %d %d %d %d\n", have_type, have_dns, have_subnet,
|
||||
// have_router;
|
||||
|
||||
// VirtualBox host network doesn't provide DNS or ROUTER, so this has to do
|
||||
if (have_type && have_subnet) {
|
||||
*yiaddr_out = ntohl(hdr->yiaddr);
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 DhcpParseAck(U32 xid, U8 *data, I64 length) {
|
||||
CDhcpHeader *hdr;
|
||||
I64 error = DhcpParseBegin(&data, &length, &hdr);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (ntohl(hdr->xid) != xid)
|
||||
return -1;
|
||||
|
||||
while (length) {
|
||||
U8 type, value_length;
|
||||
U8 *value;
|
||||
|
||||
error = DhcpParseOption(&data, &length, &type, &value_length, &value);
|
||||
//"%d, %02Xh, %d, %02Xh...\n", error, type, value_length, value[0];
|
||||
if (error < 0)
|
||||
return error;
|
||||
if (error == 0)
|
||||
break;
|
||||
|
||||
if (type == DHCP_OPTION_MSGTYPE && value_length == 1 &&
|
||||
value[0] == DHCP_MSGTYPE_ACK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
+539
@@ -0,0 +1,539 @@
|
||||
#define DNS_RCODE_NO_ERROR 0
|
||||
#define DNS_RCODE_FORMAT_ERROR 1
|
||||
#define DNS_RCODE_SERVER_FAILURE 2
|
||||
#define DNS_RCODE_NAME_ERROR 3
|
||||
#define DNS_RCODE_NOT_IMPLEMENTED 5
|
||||
#define DNS_RCODE_REFUSED 6
|
||||
|
||||
#define DNS_FLAG_RA 0x0080
|
||||
#define DNS_FLAG_RD 0x0100
|
||||
#define DNS_FLAG_TC 0x0200
|
||||
#define DNS_FLAG_AA 0x0400
|
||||
|
||||
#define DNS_OP_QUERY 0
|
||||
#define DNS_OP_IQUERY 1
|
||||
#define DNS_OP_STATUS 2
|
||||
|
||||
#define DNS_FLAG_QR 0x8000
|
||||
|
||||
// http://www.freesoft.org/CIE/RFC/1035/14.htm
|
||||
#define DNS_TYPE_A 1
|
||||
#define DNS_TYPE_NS 2
|
||||
#define DNS_TYPE_CNAME 5
|
||||
#define DNS_TYPE_PTR 12
|
||||
#define DNS_TYPE_MX 15
|
||||
#define DNS_TYPE_TXT 16
|
||||
|
||||
// http://www.freesoft.org/CIE/RFC/1035/16.htm
|
||||
#define DNS_CLASS_IN 1
|
||||
|
||||
#define DNS_TIMEOUT 5000
|
||||
#define DNS_MAX_RETRIES 3
|
||||
|
||||
class CDnsCacheEntry {
|
||||
CDnsCacheEntry *next;
|
||||
U8 *hostname;
|
||||
addrinfo info;
|
||||
// TODO: honor TTL
|
||||
};
|
||||
|
||||
class CDnsHeader {
|
||||
U16 id;
|
||||
U16 flags;
|
||||
U16 qdcount;
|
||||
U16 ancount;
|
||||
U16 nscount;
|
||||
U16 arcount;
|
||||
};
|
||||
|
||||
class CDnsDomainName {
|
||||
U8 **labels;
|
||||
I64 num_labels;
|
||||
}
|
||||
|
||||
class CDnsQuestion {
|
||||
CDnsQuestion *next;
|
||||
|
||||
CDnsDomainName qname;
|
||||
U16 qtype;
|
||||
U16 qclass;
|
||||
};
|
||||
|
||||
class CDnsRR {
|
||||
CDnsRR *next;
|
||||
|
||||
CDnsDomainName name;
|
||||
U16 type;
|
||||
U16 class_;
|
||||
U32 ttl;
|
||||
U16 rdlength;
|
||||
U8 *rdata;
|
||||
};
|
||||
|
||||
// TODO: use a Hash table
|
||||
static CDnsCacheEntry *dns_cache = NULL;
|
||||
|
||||
static U32 dns_ip = 0;
|
||||
|
||||
static CDnsCacheEntry *DnsCacheFind(U8 *hostname) {
|
||||
CDnsCacheEntry *e = dns_cache;
|
||||
|
||||
while (e) {
|
||||
if (!StrCmp(e->hostname, hostname))
|
||||
return e;
|
||||
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static CDnsCacheEntry *DnsCachePut(U8 *hostname, addrinfo *info) {
|
||||
CDnsCacheEntry *e = DnsCacheFind(hostname);
|
||||
|
||||
if (!e) {
|
||||
e = MAlloc(sizeof(CDnsCacheEntry));
|
||||
e->next = dns_cache;
|
||||
e->hostname = StrNew(hostname);
|
||||
AddrInfoCopy(&e->info, info);
|
||||
|
||||
dns_cache = e;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static I64 DnsCalcQuestionSize(CDnsQuestion *question) {
|
||||
I64 size = 0;
|
||||
I64 i;
|
||||
for (i = 0; i < question->qname.num_labels; i++) {
|
||||
size += 1 + StrLen(question->qname.labels[i]);
|
||||
}
|
||||
return size + 1 + 4;
|
||||
}
|
||||
|
||||
static U0 DnsSerializeQuestion(U8 *buf, CDnsQuestion *question) {
|
||||
I64 i;
|
||||
|
||||
for (i = 0; i < question->qname.num_labels; i++) {
|
||||
U8 *label = question->qname.labels[i];
|
||||
*(buf++) = StrLen(label);
|
||||
|
||||
while (*label)
|
||||
*(buf++) = *(label++);
|
||||
}
|
||||
|
||||
*(buf++) = 0;
|
||||
*(buf++) = (question->qtype >> 8);
|
||||
*(buf++) = (question->qtype & 0xff);
|
||||
*(buf++) = (question->qclass >> 8);
|
||||
*(buf++) = (question->qclass & 0xff);
|
||||
}
|
||||
|
||||
static I64 DnsSendQuestion(U16 id, U16 local_port, CDnsQuestion *question) {
|
||||
if (!dns_ip)
|
||||
return -1;
|
||||
|
||||
U8 *frame;
|
||||
I64 index =
|
||||
UdpPacketAlloc(&frame, IPv4GetAddress(), local_port, dns_ip, 53,
|
||||
sizeof(CDnsHeader) + DnsCalcQuestionSize(question));
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
U16 flags = (DNS_OP_QUERY << 11) | DNS_FLAG_RD;
|
||||
|
||||
CDnsHeader *hdr = frame;
|
||||
hdr->id = htons(id);
|
||||
hdr->flags = htons(flags);
|
||||
hdr->qdcount = htons(1);
|
||||
hdr->ancount = 0;
|
||||
hdr->nscount = 0;
|
||||
hdr->arcount = 0;
|
||||
|
||||
DnsSerializeQuestion(frame + sizeof(CDnsHeader), question);
|
||||
|
||||
return UdpPacketFinish(index);
|
||||
}
|
||||
|
||||
static I64 DnsParseDomainName(U8 *packet_data, I64 packet_length,
|
||||
U8 **data_inout, I64 *length_inout,
|
||||
CDnsDomainName *name_out) {
|
||||
U8 *data = *data_inout;
|
||||
I64 length = *length_inout;
|
||||
Bool jump_taken = FALSE;
|
||||
|
||||
if (length < 1) {
|
||||
//"DnsParseDomainName: EOF\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
name_out->labels = MAlloc(16 * sizeof(U8 *));
|
||||
name_out->num_labels = 0;
|
||||
|
||||
U8 *name_buf = MAlloc(256);
|
||||
name_out->labels[0] = name_buf;
|
||||
|
||||
while (length) {
|
||||
I64 label_len = *(data++);
|
||||
length--;
|
||||
|
||||
if (label_len == 0) {
|
||||
break;
|
||||
} else if (label_len >= 192) {
|
||||
label_len &= 0x3f;
|
||||
|
||||
if (!jump_taken) {
|
||||
*data_inout = data + 1;
|
||||
*length_inout = length - 1;
|
||||
jump_taken = TRUE;
|
||||
}
|
||||
|
||||
//"jmp %d\n", ((label_len << 8) | *data);
|
||||
|
||||
data = packet_data + ((label_len << 8) | *data);
|
||||
length = packet_data + packet_length - data;
|
||||
} else {
|
||||
if (length < label_len)
|
||||
return -1;
|
||||
|
||||
MemCpy(name_buf, data, label_len);
|
||||
data += label_len;
|
||||
length -= label_len;
|
||||
|
||||
name_buf[label_len] = 0;
|
||||
//"%d bytes => %s\n", label_len, name_buf;
|
||||
name_out->labels[name_out->num_labels++] = name_buf;
|
||||
|
||||
name_buf += label_len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!jump_taken) {
|
||||
*data_inout = data;
|
||||
*length_inout = length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static I64 DnsParseQuestion(U8 *packet_data, I64 packet_length, U8 **data_inout,
|
||||
I64 *length_inout, CDnsQuestion *question_out) {
|
||||
I64 error = DnsParseDomainName(packet_data, packet_length, data_inout,
|
||||
length_inout, &question_out->qname);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
U8 *data = *data_inout;
|
||||
I64 length = *length_inout;
|
||||
|
||||
if (length < 4)
|
||||
return -1;
|
||||
|
||||
question_out->next = NULL;
|
||||
question_out->qtype = (data[1] << 8) | data[0];
|
||||
question_out->qclass = (data[3] << 8) | data[2];
|
||||
|
||||
//"DnsParseQuestion: qtype %d, qclass %d\n", ntohs(question_out->qtype),
|
||||
// ntohs(question_out->qclass);
|
||||
|
||||
*data_inout = data + 4;
|
||||
*length_inout = length - 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static I64 DnsParseRR(U8 *packet_data, I64 packet_length, U8 **data_inout,
|
||||
I64 *length_inout, CDnsRR *rr_out) {
|
||||
I64 error = DnsParseDomainName(packet_data, packet_length, data_inout,
|
||||
length_inout, &rr_out->name);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
U8 *data = *data_inout;
|
||||
I64 length = *length_inout;
|
||||
|
||||
if (length < 10)
|
||||
return -1;
|
||||
|
||||
rr_out->next = NULL;
|
||||
MemCpy(&rr_out->type, data, 10);
|
||||
|
||||
I64 record_length = 10 + ntohs(rr_out->rdlength);
|
||||
|
||||
if (length < record_length)
|
||||
return -1;
|
||||
|
||||
rr_out->rdata = data + 10;
|
||||
|
||||
//"DnsParseRR: type %d, class %d\n, ttl %d, rdlength %d\n",
|
||||
// ntohs(rr_out->type), ntohs(rr_out->class_), ntohl(rr_out->ttl),
|
||||
// ntohs(rr_out->rdlength);
|
||||
|
||||
*data_inout = data + record_length;
|
||||
*length_inout = length - record_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static I64 DnsParseResponse(U16 id, U8 *data, I64 length, CDnsHeader **hdr_out,
|
||||
CDnsQuestion **questions_out,
|
||||
CDnsRR **answers_out) {
|
||||
U8 *packet_data = data;
|
||||
I64 packet_length = length;
|
||||
|
||||
if (length < sizeof(CDnsHeader)) {
|
||||
//"DnsParseResponse: too short\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
CDnsHeader *hdr = data;
|
||||
data += sizeof(CDnsHeader);
|
||||
|
||||
if (id != 0 && ntohs(hdr->id) != id) {
|
||||
//"DnsParseResponse: id %04Xh != %04Xh\n", ntohs(hdr->id), id;
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 i;
|
||||
|
||||
for (i = 0; i < htons(hdr->qdcount); i++) {
|
||||
CDnsQuestion *question = MAlloc(sizeof(CDnsQuestion));
|
||||
if (DnsParseQuestion(packet_data, packet_length, &data, &length, question) <
|
||||
0)
|
||||
return -1;
|
||||
|
||||
question->next = *questions_out;
|
||||
*questions_out = question;
|
||||
}
|
||||
|
||||
for (i = 0; i < htons(hdr->ancount); i++) {
|
||||
CDnsRR *answer = MAlloc(sizeof(CDnsRR));
|
||||
if (DnsParseRR(packet_data, packet_length, &data, &length, answer) < 0)
|
||||
return -1;
|
||||
|
||||
answer->next = *answers_out;
|
||||
*answers_out = answer;
|
||||
}
|
||||
|
||||
*hdr_out = hdr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static U0 DnsBuildQuestion(CDnsQuestion *question, U8 *name) {
|
||||
question->next = NULL;
|
||||
question->qname.labels = MAlloc(16 * sizeof(U8 *));
|
||||
question->qname.labels[0] = 0;
|
||||
question->qname.num_labels = 0;
|
||||
question->qtype = DNS_TYPE_A;
|
||||
question->qclass = DNS_CLASS_IN;
|
||||
|
||||
U8 *copy = StrNew(name);
|
||||
|
||||
while (*copy) {
|
||||
question->qname.labels[question->qname.num_labels++] = copy;
|
||||
U8 *dot = StrFirstOcc(copy, ".");
|
||||
|
||||
if (dot) {
|
||||
*dot = 0;
|
||||
copy = dot + 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static U0 DnsFreeQuestion(CDnsQuestion *question) {
|
||||
Free(question->qname.labels[0]);
|
||||
}
|
||||
|
||||
static U0 DnsFreeRR(CDnsRR *rr) { Free(rr->name.labels[0]); }
|
||||
|
||||
static U0 DnsFreeQuestionChain(CDnsQuestion *questions) {
|
||||
while (questions) {
|
||||
CDnsQuestion *next = questions->next;
|
||||
DnsFreeQuestion(questions);
|
||||
Free(questions);
|
||||
questions = next;
|
||||
}
|
||||
}
|
||||
|
||||
static U0 DnsFreeRRChain(CDnsRR *rrs) {
|
||||
while (rrs) {
|
||||
CDnsQuestion *next = rrs->next;
|
||||
DnsFreeRR(rrs);
|
||||
Free(rrs);
|
||||
rrs = next;
|
||||
}
|
||||
}
|
||||
|
||||
static I64 DnsRunQuery(I64 sock, U8 *name, U16 port, addrinfo **res_out) {
|
||||
I64 retries = 0;
|
||||
I64 timeout = DNS_TIMEOUT;
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_MS, &timeout, sizeof(timeout)) <
|
||||
0) {
|
||||
"$FG,6$DnsRunQuery: setsockopt failed\n$FG$";
|
||||
}
|
||||
|
||||
U16 local_port = RandU16();
|
||||
|
||||
sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(local_port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(sock, &addr, sizeof(addr)) < 0) {
|
||||
"$FG,4$DnsRunQuery: failed to bind\n$FG$";
|
||||
return -1;
|
||||
}
|
||||
|
||||
U8 buffer[2048];
|
||||
|
||||
I64 count;
|
||||
sockaddr_in addr_in;
|
||||
|
||||
U16 id = RandU16();
|
||||
I64 error = 0;
|
||||
|
||||
CDnsQuestion question;
|
||||
DnsBuildQuestion(&question, name);
|
||||
|
||||
while (1) {
|
||||
error = DnsSendQuestion(id, local_port, &question);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
count =
|
||||
recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
|
||||
|
||||
if (count > 0) {
|
||||
//"Try parse response\n";
|
||||
CDnsHeader *hdr = NULL;
|
||||
CDnsQuestion *questions = NULL;
|
||||
CDnsRR *answers = NULL;
|
||||
|
||||
error = DnsParseResponse(id, buffer, count, &hdr, &questions, &answers);
|
||||
|
||||
if (error >= 0) {
|
||||
Bool have = FALSE;
|
||||
|
||||
// Look for a suitable A-record in the answer
|
||||
CDnsRR *answer = answers;
|
||||
while (answer) {
|
||||
// TODO: if there are multiple acceptable answers,
|
||||
// we should pick one at random -- not just the first one
|
||||
if (htons(answer->type) == DNS_TYPE_A &&
|
||||
htons(answer->class_) == DNS_CLASS_IN &&
|
||||
htons(answer->rdlength) == 4) {
|
||||
addrinfo *res = MAlloc(sizeof(addrinfo));
|
||||
res->ai_flags = 0;
|
||||
res->ai_family = AF_INET;
|
||||
res->ai_socktype = 0;
|
||||
res->ai_protocol = 0;
|
||||
res->ai_addrlen = sizeof(sockaddr_in);
|
||||
res->ai_addr = MAlloc(sizeof(sockaddr_in));
|
||||
res->ai_canonname = NULL;
|
||||
res->ai_next = NULL;
|
||||
|
||||
sockaddr_in *sa = res->ai_addr;
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = port;
|
||||
MemCpy(&sa->sin_addr.s_addr, answers->rdata, 4);
|
||||
|
||||
DnsCachePut(name, res);
|
||||
*res_out = res;
|
||||
have = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
answer = answer->next;
|
||||
}
|
||||
|
||||
DnsFreeQuestionChain(questions);
|
||||
DnsFreeRRChain(answers);
|
||||
|
||||
if (have)
|
||||
break;
|
||||
|
||||
// At this point we could try iterative resolution,
|
||||
// but all end-user DNS servers would have tried that already
|
||||
|
||||
"$FG,6$DnsParseResponse: no suitable answer in reply\n$FG$";
|
||||
error = -1;
|
||||
} else {
|
||||
"$FG,6$DnsParseResponse: error %d\n$FG$", error;
|
||||
}
|
||||
}
|
||||
|
||||
if (++retries == DNS_MAX_RETRIES) {
|
||||
"$FG,4$DnsRunQuery: max retries reached\n$FG$";
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DnsFreeQuestion(&question);
|
||||
return error;
|
||||
}
|
||||
|
||||
I64 DnsGetaddrinfo(U8 *node, U8 *service, addrinfo *hints, addrinfo **res) {
|
||||
no_warn service;
|
||||
no_warn hints;
|
||||
|
||||
CDnsCacheEntry *cached = DnsCacheFind(node);
|
||||
|
||||
if (cached) {
|
||||
*res = MAlloc(sizeof(addrinfo));
|
||||
AddrInfoCopy(*res, &cached->info);
|
||||
(*res)->ai_flags |= AI_CACHED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
I64 sock = socket(AF_INET, SOCK_DGRAM);
|
||||
I64 error = 0;
|
||||
|
||||
if (sock >= 0) {
|
||||
// TODO: service should be parsed as int, specifying port number
|
||||
error = DnsRunQuery(sock, node, 0, res);
|
||||
|
||||
close(sock);
|
||||
} else
|
||||
error = -1;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
U0 DnsSetResolverIPv4(U32 ip) { dns_ip = ip; }
|
||||
|
||||
public
|
||||
U0 Host(U8 *hostname) {
|
||||
addrinfo *res = NULL;
|
||||
I64 error = getaddrinfo(hostname, NULL, NULL, &res);
|
||||
|
||||
if (error < 0) {
|
||||
"$FG,4$getaddrinfo: error %d\n", error;
|
||||
} else {
|
||||
addrinfo *curr = res;
|
||||
while (curr) {
|
||||
U8 buffer[INET_ADDRSTRLEN];
|
||||
"flags %04Xh, family %d, socktype %d, proto %d, addrlen %d, addr %s\n",
|
||||
curr->ai_flags, curr->ai_family, curr->ai_socktype, curr->ai_protocol,
|
||||
curr->ai_addrlen,
|
||||
inet_ntop(AF_INET, &(curr->ai_addr(sockaddr_in *))->sin_addr, buffer,
|
||||
sizeof(buffer));
|
||||
curr = curr->ai_next;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
U0 DnsInit() {
|
||||
static CAddrResolver dns_addr_resolver;
|
||||
dns_addr_resolver.getaddrinfo = &DnsGetaddrinfo;
|
||||
|
||||
socket_addr_resolver = &dns_addr_resolver;
|
||||
}
|
||||
|
||||
DnsInit;
|
||||
@@ -0,0 +1,57 @@
|
||||
class CEthFrame {
|
||||
U8 source_addr[6];
|
||||
U8 padding[2];
|
||||
U8 dest_addr[6];
|
||||
U16 ethertype;
|
||||
|
||||
U8 *data;
|
||||
I64 length;
|
||||
};
|
||||
|
||||
class CL3Protocol {
|
||||
CL3Protocol *next;
|
||||
|
||||
U16 ethertype;
|
||||
U8 padding[6];
|
||||
|
||||
I64 (*handler)(CEthFrame *frame);
|
||||
};
|
||||
|
||||
static CL3Protocol *l3_protocols = NULL;
|
||||
|
||||
U8 eth_null[6] = {0, 0, 0, 0, 0, 0};
|
||||
U8 eth_broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
|
||||
I64 EthernetFrameParse(CEthFrame *frame_out, U8 *frame, U16 length) {
|
||||
// FIXME: check length
|
||||
// TODO: MemCpy has high overhead, get rid of it
|
||||
MemCpy(frame_out->dest_addr, frame, 6);
|
||||
MemCpy(frame_out->source_addr, frame + 6, 6);
|
||||
frame_out->ethertype = frame[13] | (frame[12] << 8);
|
||||
|
||||
/*"Rx dst: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
frame_out->dest_addr[0], frame_out->dest_addr[1],
|
||||
frame_out->dest_addr[2], frame_out->dest_addr[3], frame_out->dest_addr[4],
|
||||
frame_out->dest_addr[5];
|
||||
|
||||
"Rx src: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
frame_out->source_addr[0], frame_out->source_addr[1],
|
||||
frame_out->source_addr[2], frame_out->source_addr[3],
|
||||
frame_out->source_addr[4], frame_out->source_addr[5];
|
||||
|
||||
"Rx ethertype: %02X\n", frame_out->ethertype;*/
|
||||
|
||||
frame_out->data = frame + 14;
|
||||
frame_out->length = length - 14 - 4; // ??
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 RegisterL3Protocol(U16 ethertype, I64 (*handler)(CEthFrame *frame)) {
|
||||
CL3Protocol *p = MAlloc(sizeof(CL3Protocol));
|
||||
|
||||
p->next = l3_protocols;
|
||||
p->ethertype = ethertype;
|
||||
p->handler = handler;
|
||||
|
||||
l3_protocols = p;
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
#define IP_PROTO_ICMP 0x01
|
||||
#define IP_PROTO_TCP 0x06
|
||||
#define IP_PROTO_UDP 0x11
|
||||
|
||||
#define IPV4_EADDR_INVALID (-200001)
|
||||
#define IPV4_EHOST_UNREACHABLE (-200002)
|
||||
|
||||
#define IPV4_TTL 64
|
||||
|
||||
class CIPv4Packet {
|
||||
CEthFrame *l2_frame;
|
||||
|
||||
U32 source_ip;
|
||||
U32 dest_ip;
|
||||
U8 proto;
|
||||
U8 padding[7];
|
||||
|
||||
U8 *data;
|
||||
I64 length;
|
||||
I64 ttl;
|
||||
};
|
||||
|
||||
class CIPv4Header {
|
||||
U8 version_ihl;
|
||||
U8 dscp_ecn;
|
||||
U16 total_length;
|
||||
U16 ident;
|
||||
U16 flags_fragoff;
|
||||
U8 ttl;
|
||||
U8 proto;
|
||||
U16 header_checksum;
|
||||
U32 source_ip;
|
||||
U32 dest_ip;
|
||||
};
|
||||
|
||||
class CL4Protocol {
|
||||
CL4Protocol *next;
|
||||
|
||||
U8 proto;
|
||||
U8 padding[7];
|
||||
|
||||
U0 (*handler)(CIPv4Packet *packet);
|
||||
};
|
||||
|
||||
// *_n = stored in network order
|
||||
static U32 my_ip = 0;
|
||||
static U32 my_ip_n = 0;
|
||||
|
||||
static U32 ipv4_router_addr = 0;
|
||||
static U32 ipv4_subnet_mask = 0;
|
||||
|
||||
static CL4Protocol *l4_protocols = NULL;
|
||||
|
||||
// http://stackoverflow.com/q/26774761/2524350
|
||||
static U16 IPv4Checksum(U8 *header, I64 length) {
|
||||
I64 nleft = length;
|
||||
U16 *w = header;
|
||||
I64 sum = 0;
|
||||
|
||||
while (nleft > 1) {
|
||||
sum += *(w++);
|
||||
nleft -= 2;
|
||||
}
|
||||
|
||||
// mop up an odd byte, if necessary
|
||||
if (nleft == 1) {
|
||||
sum += ((*w) & 0x00ff);
|
||||
}
|
||||
|
||||
// add back carry outs from top 16 bits to low 16 bits
|
||||
sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16
|
||||
sum += (sum >> 16); // add carry
|
||||
return (~sum) & 0xffff;
|
||||
}
|
||||
|
||||
static I64 GetEthernetAddressForIP(U32 ip, U8 **mac_out) {
|
||||
// invalid
|
||||
if (ip == 0) {
|
||||
return IPV4_EADDR_INVALID;
|
||||
}
|
||||
// broadcast
|
||||
else if (ip == 0xffffffff) {
|
||||
*mac_out = eth_broadcast;
|
||||
return 0;
|
||||
}
|
||||
// outside this subnet; needs routing
|
||||
else if ((ip & ipv4_subnet_mask) != (my_ip & ipv4_subnet_mask)) {
|
||||
// no gateway
|
||||
if (ipv4_router_addr == 0) {
|
||||
return IPV4_EADDR_INVALID;
|
||||
}
|
||||
|
||||
// FIXME: infinite loop if mis-configured
|
||||
|
||||
return GetEthernetAddressForIP(ipv4_router_addr, mac_out);
|
||||
}
|
||||
// local network
|
||||
else {
|
||||
// FIXME: this can stall NetHandlerTask, we might need a flag to bail early
|
||||
|
||||
CArpCacheEntry *e = ArpCacheFindByIP(ip);
|
||||
|
||||
if (e) {
|
||||
*mac_out = e->mac;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//"Not in cache, requesting\n";
|
||||
|
||||
// Up to 4 retries, 500 ms each
|
||||
I64 retries = 4;
|
||||
|
||||
while (retries) {
|
||||
ArpSend(ARP_REQUEST, eth_broadcast, EthernetGetAddress(), my_ip_n,
|
||||
eth_null, htonl(ip));
|
||||
|
||||
I64 try_ = 0;
|
||||
|
||||
for (try_ = 0; try_ < 50; try_++) {
|
||||
Sleep(10);
|
||||
|
||||
e = ArpCacheFindByIP(ip);
|
||||
if (e)
|
||||
break;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
*mac_out = e->mac;
|
||||
return 0;
|
||||
}
|
||||
|
||||
retries--;
|
||||
}
|
||||
|
||||
in_addr in;
|
||||
in.s_addr = htonl(ip);
|
||||
U8 buffer[INET_ADDRSTRLEN];
|
||||
"$FG,6$IPv4: Failed to resolve address %s\n$FG$",
|
||||
inet_ntop(AF_INET, &in.s_addr, buffer, sizeof(buffer));
|
||||
return IPV4_EHOST_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
I64 IPv4PacketAlloc(U8 **frame_out, U8 proto, U32 source_ip, U32 dest_ip,
|
||||
I64 length) {
|
||||
U8 *frame;
|
||||
U8 *dest_mac;
|
||||
|
||||
I64 error = GetEthernetAddressForIP(dest_ip, &dest_mac);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
I64 index =
|
||||
EthernetFrameAlloc(&frame, EthernetGetAddress(), dest_mac, ETHERTYPE_IPV4,
|
||||
sizeof(CIPv4Header) + length, 0);
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
I64 internet_header_length = 5;
|
||||
|
||||
CIPv4Header *hdr = frame;
|
||||
hdr->version_ihl = internet_header_length | (4 << 4);
|
||||
hdr->dscp_ecn = 0;
|
||||
hdr->total_length = htons(internet_header_length * 4 + length);
|
||||
hdr->ident = 0;
|
||||
hdr->flags_fragoff = 0;
|
||||
hdr->ttl = IPV4_TTL;
|
||||
hdr->proto = proto;
|
||||
hdr->header_checksum = 0;
|
||||
hdr->source_ip = htonl(source_ip);
|
||||
hdr->dest_ip = htonl(dest_ip);
|
||||
|
||||
hdr->header_checksum = IPv4Checksum(hdr, internet_header_length * 4);
|
||||
|
||||
*frame_out = frame + sizeof(CIPv4Header);
|
||||
return index;
|
||||
}
|
||||
|
||||
I64 IPv4PacketFinish(I64 index) { return EthernetFrameFinish(index); }
|
||||
|
||||
U32 IPv4GetAddress() { return my_ip; }
|
||||
|
||||
U0 IPv4SetAddress(U32 addr) {
|
||||
my_ip = addr;
|
||||
my_ip_n = htonl(addr);
|
||||
|
||||
ArpSetIPv4Address(addr);
|
||||
}
|
||||
|
||||
U0 IPv4SetSubnet(U32 router_addr, U32 subnet_mask) {
|
||||
ipv4_router_addr = router_addr;
|
||||
ipv4_subnet_mask = subnet_mask;
|
||||
}
|
||||
|
||||
I64 IPv4ParsePacket(CIPv4Packet *packet_out, CEthFrame *eth_frame) {
|
||||
if (eth_frame->ethertype != ETHERTYPE_IPV4)
|
||||
return -1;
|
||||
|
||||
// FIXME: check eth_frame->length etc.
|
||||
|
||||
CIPv4Header *hdr = eth_frame->data;
|
||||
I64 header_length = (hdr->version_ihl & 0x0f) * 4;
|
||||
//"IPv4: hdr %d, proto %02X, source %08X, dest %08X, len %d\n",
|
||||
// header_length, hdr->proto, ntohl(hdr->source_ip), ntohl(hdr->dest_ip),
|
||||
// eth_frame->length - header_length;
|
||||
|
||||
U16 total_length = ntohs(hdr->total_length);
|
||||
|
||||
packet_out->l2_frame = eth_frame;
|
||||
packet_out->source_ip = ntohl(hdr->source_ip);
|
||||
packet_out->dest_ip = ntohl(hdr->dest_ip);
|
||||
packet_out->proto = hdr->proto;
|
||||
|
||||
packet_out->data = eth_frame->data + header_length;
|
||||
packet_out->length = total_length - header_length;
|
||||
packet_out->ttl = hdr->ttl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 RegisterL4Protocol(U8 proto, I64 (*handler)(CIPv4Packet *frame)) {
|
||||
CL4Protocol *p = MAlloc(sizeof(CL4Protocol));
|
||||
|
||||
p->next = l4_protocols;
|
||||
p->proto = proto;
|
||||
p->handler = handler;
|
||||
|
||||
l4_protocols = p;
|
||||
}
|
||||
|
||||
I64 IPv4Handler(CEthFrame *eth_frame) {
|
||||
CIPv4Packet packet;
|
||||
|
||||
I64 error = IPv4ParsePacket(&packet, eth_frame);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
// This seems necessary to receive connections under VBox NAT,
|
||||
// but is also pretty slow, so should be optimized to use a better
|
||||
// struct than linked list.
|
||||
ArpCachePut(packet.source_ip, eth_frame->source_addr);
|
||||
|
||||
CL4Protocol *l4 = l4_protocols;
|
||||
|
||||
while (l4) {
|
||||
if (l4->proto == packet.proto) {
|
||||
l4->handler(&packet);
|
||||
break;
|
||||
}
|
||||
l4 = l4->next;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
RegisterL3Protocol(ETHERTYPE_IPV4, &IPv4Handler);
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#define ICMP_TYPE_ECHO_REPLY 0
|
||||
#define ICMP_TYPE_ECHO_REQUEST 8
|
||||
|
||||
class CIcmpHeader {
|
||||
U8 type;
|
||||
U8 code;
|
||||
U16 checksum;
|
||||
U16 identifier;
|
||||
U16 seq_number;
|
||||
};
|
||||
|
||||
U64 *icmp_reply = CAlloc(sizeof(U64) * 65536);
|
||||
|
||||
U16 IcmpComputeChecksum(U8 *buf, I64 size) {
|
||||
I64 i;
|
||||
U64 sum = 0;
|
||||
|
||||
for (i = 0; i < size; i += 2) {
|
||||
sum += *buf(U16 *);
|
||||
buf += 2;
|
||||
}
|
||||
if (size - i > 0) {
|
||||
sum += *buf;
|
||||
}
|
||||
|
||||
while ((sum >> 16) != 0) {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
|
||||
return ~sum(U16);
|
||||
}
|
||||
|
||||
I64 IcmpSendReply(U32 dest_ip, U16 identifier, U16 seq_number,
|
||||
U16 request_checksum, U8 *payload, I64 length) {
|
||||
U8 *frame;
|
||||
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_ICMP, IPv4GetAddress(), dest_ip,
|
||||
sizeof(CIcmpHeader) + length);
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
CIcmpHeader *hdr = frame;
|
||||
hdr->type = ICMP_TYPE_ECHO_REPLY;
|
||||
hdr->code = 0;
|
||||
hdr->checksum = htons(ntohs(request_checksum) + 0x0800); // hack alert!
|
||||
hdr->identifier = identifier;
|
||||
hdr->seq_number = seq_number;
|
||||
|
||||
MemCpy(frame + sizeof(CIcmpHeader), payload, length);
|
||||
return IPv4PacketFinish(index);
|
||||
}
|
||||
|
||||
I64 IcmpSendRequest(U32 dest_ip, U16 identifier, U16 seq_number,
|
||||
U16 request_checksum, U8 *payload, I64 length) {
|
||||
no_warn request_checksum;
|
||||
U8 *frame;
|
||||
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_ICMP, IPv4GetAddress(), dest_ip,
|
||||
sizeof(CIcmpHeader) + length);
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
CIcmpHeader *hdr = frame;
|
||||
hdr->type = ICMP_TYPE_ECHO_REQUEST;
|
||||
hdr->code = 0;
|
||||
hdr->checksum = 0;
|
||||
hdr->identifier = identifier;
|
||||
hdr->seq_number = seq_number;
|
||||
|
||||
hdr->checksum = IcmpComputeChecksum(hdr, sizeof(CIcmpHeader));
|
||||
|
||||
MemCpy(frame + sizeof(CIcmpHeader), payload, length);
|
||||
return IPv4PacketFinish(index);
|
||||
}
|
||||
|
||||
I64 IcmpHandler(CIPv4Packet *packet) {
|
||||
if (packet->proto != IP_PROTO_ICMP)
|
||||
return -1;
|
||||
|
||||
if (packet->length < sizeof(CIcmpHeader))
|
||||
return -1;
|
||||
|
||||
CIcmpHeader *hdr = packet->data;
|
||||
|
||||
if (hdr->type == ICMP_TYPE_ECHO_REPLY && hdr->code == 0) {
|
||||
icmp_reply[hdr->identifier] = packet;
|
||||
}
|
||||
|
||||
if (hdr->type == ICMP_TYPE_ECHO_REQUEST && hdr->code == 0) {
|
||||
// This also makes sure that we don't stall NetHandlerTask
|
||||
ArpCachePut(packet->source_ip, packet->l2_frame->source_addr);
|
||||
|
||||
IcmpSendReply(packet->source_ip, hdr->identifier, hdr->seq_number,
|
||||
hdr->checksum, packet->data + sizeof(CIcmpHeader),
|
||||
packet->length - sizeof(CIcmpHeader));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RegisterL4Protocol(IP_PROTO_ICMP, &IcmpHandler);
|
||||
@@ -0,0 +1,327 @@
|
||||
#define SOCK_STREAM 1
|
||||
#define SOCK_DGRAM 2
|
||||
#define SOCK_RAW 3
|
||||
|
||||
#define AF_UNSPEC 0
|
||||
#define AF_INET 2
|
||||
#define AF_INET6 10
|
||||
|
||||
#define INADDR_ANY 0
|
||||
|
||||
#define INET_ADDRSTRLEN 16
|
||||
|
||||
#define NS_INADDRSZ 4
|
||||
|
||||
#define SOL_SOCKET 1
|
||||
|
||||
// optval = I64*
|
||||
#define SO_RCVTIMEO_MS 1
|
||||
|
||||
#define AI_CACHED 0x8000
|
||||
|
||||
class in_addr {
|
||||
U32 s_addr;
|
||||
};
|
||||
|
||||
class sockaddr {
|
||||
U16 sa_family;
|
||||
U8 sa_data[14];
|
||||
};
|
||||
|
||||
class sockaddr_in {
|
||||
I16 sin_family;
|
||||
U16 sin_port;
|
||||
in_addr sin_addr;
|
||||
U8 sin_zero[8];
|
||||
};
|
||||
|
||||
class addrinfo {
|
||||
I32 ai_flags;
|
||||
I32 ai_family;
|
||||
I32 ai_socktype;
|
||||
I32 ai_protocol;
|
||||
I64 ai_addrlen;
|
||||
sockaddr *ai_addr;
|
||||
U8 *ai_canonname;
|
||||
addrinfo *ai_next;
|
||||
};
|
||||
|
||||
I64 inet_pton(I64 af, U8 *src, U8 *dst) {
|
||||
I64 saw_digit, octets, ch;
|
||||
U8 tmp[NS_INADDRSZ], *tp;
|
||||
|
||||
if (af != AF_INET) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
saw_digit = 0;
|
||||
octets = 0;
|
||||
*(tp = tmp) = 0;
|
||||
while (*src) {
|
||||
ch = *src++;
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
U64 new = *tp * 10 + (ch - '0');
|
||||
if (saw_digit && *tp == 0)
|
||||
return 0;
|
||||
if (new > 255)
|
||||
return 0;
|
||||
*tp = new;
|
||||
if (!saw_digit) {
|
||||
if (++octets > 4)
|
||||
return 0;
|
||||
saw_digit = 1;
|
||||
}
|
||||
} else if (ch == '.' && saw_digit) {
|
||||
if (octets == 4)
|
||||
return 0;
|
||||
*++tp = 0;
|
||||
saw_digit = 0;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
if (octets < 4)
|
||||
return 0;
|
||||
MemCpy(dst, tmp, NS_INADDRSZ);
|
||||
return 1;
|
||||
}
|
||||
|
||||
U8 *inet_ntop(I64 af, U8 *src, U8 *dst, I64 size) {
|
||||
if (af == AF_INET && size >= INET_ADDRSTRLEN) {
|
||||
StrPrint(dst, "%d.%d.%d.%d", src[0], src[1], src[2], src[3]);
|
||||
return dst;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class CSocket {
|
||||
I64 (*accept)(CSocket *s, sockaddr *src_addr, I64 addrlen);
|
||||
I64 (*bind)(CSocket *s, sockaddr *addr, I64 addrlen);
|
||||
I64 (*close)(CSocket *s);
|
||||
I64 (*connect)(CSocket *s, sockaddr *addr, I64 addrlen);
|
||||
I64 (*listen)(CSocket *s, I64 backlog);
|
||||
I64(*recvfrom)
|
||||
(CSocket *s, U8 *buf, I64 len, I64 flags, sockaddr *src_addr, I64 addrlen);
|
||||
I64(*sendto)
|
||||
(CSocket *s, U8 *buf, I64 len, I64 flags, sockaddr *dest_addr, I64 addrlen);
|
||||
I64 (*setsockopt)(CSocket *s, I64 level, I64 optname, U8 *optval, I64 optlen);
|
||||
};
|
||||
|
||||
class CSocketClass {
|
||||
CSocketClass *next;
|
||||
|
||||
U16 domain;
|
||||
U16 type;
|
||||
U8 padding[4];
|
||||
|
||||
CSocket *(*socket)(U16 domain, U16 type);
|
||||
};
|
||||
|
||||
class CAddrResolver {
|
||||
// TODO: allow different resolvers for different socket domains
|
||||
|
||||
I64 (*getaddrinfo)(U8 *node, U8 *service, addrinfo *hints, addrinfo **res);
|
||||
};
|
||||
|
||||
static CSocketClass *socket_classes = NULL;
|
||||
static CAddrResolver *socket_addr_resolver = NULL;
|
||||
|
||||
static CSocketClass *FindSocketClass(U16 domain, U16 type) {
|
||||
CSocketClass *cls = socket_classes;
|
||||
|
||||
while (cls) {
|
||||
if (cls->domain == domain && cls->type == type)
|
||||
return cls;
|
||||
|
||||
cls = cls->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
I64 SocketInit() { return 0; }
|
||||
|
||||
I64 socket(I64 domain, I64 type) {
|
||||
CSocketClass *cls = FindSocketClass(domain, type);
|
||||
|
||||
if (cls)
|
||||
return cls->socket(domain, type)(I64);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 accept(I64 sockfd, sockaddr *addr, I64 addrlen) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->accept(sock, addr, addrlen);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 close(I64 sockfd) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->close(sock);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 bind(I64 sockfd, sockaddr *addr, I64 addrlen) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->bind(sock, addr, addrlen);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 connect(I64 sockfd, sockaddr *addr, I64 addrlen) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->connect(sock, addr, addrlen);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 listen(I64 sockfd, I64 backlog) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->listen(sock, backlog);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 recv(I64 sockfd, U8 *buf, I64 len, I64 flags) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->recvfrom(sock, buf, len, flags, NULL, 0);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 recvfrom(I64 sockfd, U8 *buf, I64 len, I64 flags, sockaddr *src_addr,
|
||||
I64 addrlen) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->recvfrom(sock, buf, len, flags, src_addr, addrlen);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 send(I64 sockfd, U8 *buf, I64 len, I64 flags) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->sendto(sock, buf, len, flags, NULL, 0);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 sendto(I64 sockfd, U8 *buf, I64 len, I64 flags, sockaddr *dest_addr,
|
||||
I64 addrlen) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->sendto(sock, buf, len, flags, dest_addr, addrlen);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 setsockopt(I64 sockfd, I64 level, I64 optname, U8 *optval, I64 optlen) {
|
||||
CSocket *sock = sockfd(CSocket *);
|
||||
if (sockfd > 0)
|
||||
return sock->setsockopt(sock, level, optname, optval, optlen);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 getaddrinfo(U8 *node, U8 *service, addrinfo *hints, addrinfo **res) {
|
||||
if (socket_addr_resolver)
|
||||
return socket_addr_resolver->getaddrinfo(node, service, hints, res);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
U0 freeaddrinfo(addrinfo *res) {
|
||||
while (res) {
|
||||
addrinfo *next = res->ai_next;
|
||||
Free(res->ai_addr);
|
||||
Free(res->ai_canonname);
|
||||
Free(res);
|
||||
res = next;
|
||||
}
|
||||
}
|
||||
|
||||
U0 AddrInfoCopy(addrinfo *ai_out, addrinfo *ai_in) {
|
||||
MemCpy(ai_out, ai_in, sizeof(addrinfo));
|
||||
|
||||
if (ai_in->ai_addr) {
|
||||
ai_out->ai_addr = MAlloc(ai_in->ai_addrlen);
|
||||
MemCpy(ai_out->ai_addr, ai_in->ai_addr, ai_in->ai_addrlen);
|
||||
}
|
||||
|
||||
if (ai_in->ai_canonname) {
|
||||
ai_out->ai_canonname = StrNew(ai_in->ai_canonname);
|
||||
}
|
||||
}
|
||||
|
||||
U8 *gai_strerror(I64 errcode) {
|
||||
no_warn errcode;
|
||||
return "Unspecified error";
|
||||
}
|
||||
|
||||
// Inspired by
|
||||
// https://docs.python.org/3.7/library/socket.html#socket.create_connection
|
||||
I64 create_connection(U8 *hostname, U16 port) {
|
||||
sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = 0;
|
||||
|
||||
addrinfo *res;
|
||||
I64 error = getaddrinfo(hostname, NULL, NULL, &res);
|
||||
|
||||
if (error < 0) {
|
||||
"$FG,4$getaddrinfo: error %d\n$FG$", error;
|
||||
} else {
|
||||
addrinfo *curr = res;
|
||||
|
||||
while (curr) {
|
||||
if (curr->ai_family == AF_INET &&
|
||||
(curr->ai_socktype == 0 || curr->ai_socktype == SOCK_STREAM)) {
|
||||
addr.sin_addr.s_addr = (curr->ai_addr(sockaddr_in *))->sin_addr.s_addr;
|
||||
freeaddrinfo(res);
|
||||
|
||||
I64 sockfd = socket(AF_INET, SOCK_STREAM);
|
||||
|
||||
if (sockfd < 0)
|
||||
return sockfd;
|
||||
|
||||
error = connect(sockfd, &addr, sizeof(addr));
|
||||
|
||||
if (error < 0) {
|
||||
close(sockfd);
|
||||
return error;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
curr = curr->ai_next;
|
||||
}
|
||||
|
||||
"$FG,4$create_connection: no suitable address\n$FG$";
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
U0 RegisterSocketClass(U16 domain, U16 type,
|
||||
CSocket *(*socket)(U16 domain, U16 type)) {
|
||||
CSocketClass *cls = MAlloc(sizeof(CSocketClass));
|
||||
|
||||
cls->next = socket_classes;
|
||||
cls->domain = domain;
|
||||
cls->type = type;
|
||||
cls->socket = socket;
|
||||
|
||||
socket_classes = cls;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Warning: terrible code ahead. this still needs a lot of work
|
||||
|
||||
// In the future we'll probably have 2 FIFOs (pending frames & empty buffers)
|
||||
// TODO: check if FIFO implementation is suitable for high throughput
|
||||
|
||||
#define NET_FIFO_DEPTH 1024
|
||||
|
||||
#define ETHERNET_FRAME_SIZE 1548
|
||||
|
||||
#define ETHERTYPE_IPV4 0x0800
|
||||
#define ETHERTYPE_ARP 0x0806
|
||||
|
||||
class CNetFifoEntry {
|
||||
I64 length;
|
||||
U8 frame[ETHERNET_FRAME_SIZE];
|
||||
};
|
||||
|
||||
static CFifoI64 *netfifo;
|
||||
|
||||
static CNetFifoEntry *entries;
|
||||
static I64 next_entry = 0;
|
||||
|
||||
CTask *netfifo_handler_task = NULL;
|
||||
|
||||
// TODO: asm optimization? or perhaps use EndianU*?
|
||||
// These don't belong here in the first place,
|
||||
// but it's convenient for Ethernet drivers
|
||||
// We'll probably split it off along with ETHERTYPE_* constants
|
||||
|
||||
U16 htons(U16 h) { return ((h >> 8) | (h << 8)) & 0xffff; }
|
||||
|
||||
U16 ntohs(U16 h) { return ((h >> 8) | (h << 8)) & 0xffff; }
|
||||
|
||||
U32 htonl(U32 h) {
|
||||
return ((h >> 24) | ((h & 0x00ff0000) >> 8) | ((h & 0x0000ff00) << 8) |
|
||||
(h << 24)) &
|
||||
0xffffffff;
|
||||
}
|
||||
|
||||
U32 ntohl(U32 h) {
|
||||
return ((h >> 24) | ((h & 0x00ff0000) >> 8) | ((h & 0x0000ff00) << 8) |
|
||||
(h << 24)) &
|
||||
0xffffffff;
|
||||
}
|
||||
|
||||
CNetFifoEntry *NetFifoPull() {
|
||||
CNetFifoEntry *entry;
|
||||
|
||||
if (FifoI64Rem(netfifo, &entry))
|
||||
return entry;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
I64 NetFifoPushCopy(U8 *data, I64 length) {
|
||||
CNetFifoEntry *entry = &entries[next_entry];
|
||||
next_entry = (next_entry + 1) & (NET_FIFO_DEPTH - 1);
|
||||
|
||||
entry->length = length;
|
||||
MemCpy(entry->frame, data, length);
|
||||
|
||||
if (!FifoI64Ins(netfifo, entry))
|
||||
return -1;
|
||||
|
||||
// Wake up Handler Task
|
||||
if (netfifo_handler_task)
|
||||
LBtr(&netfifo_handler_task->task_flags, TASKf_IDLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 NetFifoInit() {
|
||||
netfifo = FifoI64New(NET_FIFO_DEPTH);
|
||||
entries = MAlloc(NET_FIFO_DEPTH * sizeof(CNetFifoEntry));
|
||||
}
|
||||
|
||||
NetFifoInit;
|
||||
@@ -0,0 +1,58 @@
|
||||
U0 @virtio_net_handle_net_fifo_entry(CNetFifoEntry *e) {
|
||||
CEthFrame l2_frame;
|
||||
|
||||
if (EthernetFrameParse(&l2_frame, e->frame, e->length) < 0)
|
||||
return;
|
||||
|
||||
CL3Protocol *l3 = l3_protocols;
|
||||
|
||||
while (l3) {
|
||||
if (l3->ethertype == l2_frame.ethertype) {
|
||||
l3->handler(&l2_frame);
|
||||
break;
|
||||
}
|
||||
l3 = l3->next;
|
||||
}
|
||||
}
|
||||
|
||||
U0 @virtio_net_handler_task() {
|
||||
I64 idx_used, idx_rec;
|
||||
I64 i, j;
|
||||
@virtio_used_item *item;
|
||||
U8 *buffer;
|
||||
I64 length;
|
||||
while (1) {
|
||||
idx_rec = VirtioNet.rq_index;
|
||||
idx_used = VirtioNet.rq->used.index;
|
||||
|
||||
if (idx_used < idx_rec) {
|
||||
idx_used += 0x10000;
|
||||
}
|
||||
|
||||
if (idx_rec != idx_used && idx_used) {
|
||||
|
||||
j = 0;
|
||||
for (i = idx_rec; i < idx_used; i++) {
|
||||
item = VirtioNet.rq->used.ring;
|
||||
buffer = VirtioNet.rq->buffers[item[i % 256].index + 1];
|
||||
length = item[i % 256].length;
|
||||
NetFifoPushCopy(buffer, length - 10);
|
||||
j++;
|
||||
VirtioNet.rx_packets++;
|
||||
VirtioNet.rx_bytes += length - 10;
|
||||
}
|
||||
VirtioNet.rq_index = idx_used % 0x10000;
|
||||
VirtioNet.rq->available.index += j;
|
||||
OutU16(VirtioNet.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
|
||||
}
|
||||
CNetFifoEntry *e = NetFifoPull;
|
||||
if (e) {
|
||||
@virtio_net_handle_net_fifo_entry(e);
|
||||
}
|
||||
Busy(200);
|
||||
}
|
||||
}
|
||||
|
||||
Spawn(&@virtio_net_handler_task, NULL, "NetHandlerTask", 2);
|
||||
|
||||
"[OK] NetHandler \n";
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
|
||||
#define CLIENT_START 0
|
||||
#define CLIENT_DISCOVER 1
|
||||
#define CLIENT_REQUEST 2
|
||||
#define CLIENT_REQUEST_ACCEPTED 3
|
||||
|
||||
#define DHCP_TIMEOUT 3000
|
||||
#define MAX_RETRIES 3
|
||||
|
||||
I64 DhcpConfigureInner(I64 sock, U32 *yiaddr_out, U32 *dns_ip_out,
|
||||
U32 *router_ip_out, U32 *subnet_mask_out) {
|
||||
I64 state = CLIENT_START;
|
||||
I64 retries = 0;
|
||||
|
||||
I64 timeout = DHCP_TIMEOUT;
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_MS, &timeout, sizeof(timeout)) <
|
||||
0) {
|
||||
"$FG,6$DhcpConfigure: setsockopt failed\n$FG$";
|
||||
}
|
||||
|
||||
sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(68);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(sock, &addr, sizeof(addr)) < 0) {
|
||||
"$FG,4$DhcpConfigure: failed to bind\n$FG$";
|
||||
return -1;
|
||||
}
|
||||
|
||||
U32 xid = DhcpBeginTransaction();
|
||||
|
||||
I64 error = 0;
|
||||
|
||||
U32 dhcp_addr;
|
||||
U8 buffer[2048];
|
||||
|
||||
I64 count;
|
||||
sockaddr_in addr_in;
|
||||
|
||||
while (state != CLIENT_REQUEST_ACCEPTED) {
|
||||
if (state == CLIENT_START) {
|
||||
state = CLIENT_DISCOVER;
|
||||
retries = 0;
|
||||
} else if (state == CLIENT_DISCOVER) {
|
||||
error = DhcpSendDiscover(xid);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
count =
|
||||
recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
|
||||
|
||||
if (count > 0) {
|
||||
//"Try parse Offer\n";
|
||||
error = DhcpParseOffer(xid, buffer, count, yiaddr_out, dns_ip_out,
|
||||
router_ip_out, subnet_mask_out);
|
||||
|
||||
if (error < 0) {
|
||||
"$FG,6$DhcpParseOffer1: error %d\n$FG$", error;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0 && error >= 0) {
|
||||
dhcp_addr = ntohl(addr_in.sin_addr.s_addr);
|
||||
//"DHCP Offer from %08X: YIAddr %08X,\n\tDNS %08X, Router %08X, Subnet
|
||||
//%08X\n",
|
||||
// dhcp_addr, *yiaddr_out, dns_ip, router_ip, subnet_mask;
|
||||
|
||||
state = CLIENT_REQUEST;
|
||||
retries = 0;
|
||||
} else if (++retries == MAX_RETRIES) {
|
||||
"$FG,4$DhcpConfigure: max retries for DISCOVER\n$FG$";
|
||||
return -1;
|
||||
}
|
||||
} else if (state == CLIENT_REQUEST) {
|
||||
error = DhcpSendRequest(xid, *yiaddr_out, dhcp_addr);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
count =
|
||||
recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
|
||||
|
||||
if (count > 0) {
|
||||
//"Try parse Ack\n";
|
||||
error = DhcpParseAck(xid, buffer, count);
|
||||
|
||||
if (error < 0) {
|
||||
"$FG,6$DhcpParseOffer: error %d\n$FG$", error;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0 && error >= 0) {
|
||||
dhcp_addr = ntohl(addr_in.sin_addr.s_addr);
|
||||
//"DHCP Ack from %08X\n", dhcp_addr;
|
||||
|
||||
state = CLIENT_REQUEST_ACCEPTED;
|
||||
} else if (++retries == MAX_RETRIES) {
|
||||
"$FG,4$DhcpConfigure: max retries for REQUEST\n$FG$";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
I64 DhcpConfigure() {
|
||||
I64 sock = socket(AF_INET, SOCK_DGRAM);
|
||||
|
||||
if (sock < 0)
|
||||
return -1;
|
||||
|
||||
U32 yiaddr, dns_ip, router_ip, subnet_mask;
|
||||
I64 state =
|
||||
DhcpConfigureInner(sock, &yiaddr, &dns_ip, &router_ip, &subnet_mask);
|
||||
|
||||
close(sock);
|
||||
|
||||
if (state == CLIENT_REQUEST_ACCEPTED) {
|
||||
in_addr in;
|
||||
in.s_addr = htonl(yiaddr);
|
||||
U8 buffer[INET_ADDRSTRLEN];
|
||||
"$FG,2$Obtained IP address %s\n$FG$",
|
||||
inet_ntop(AF_INET, &in.s_addr, buffer, sizeof(buffer));
|
||||
IPv4SetAddress(yiaddr);
|
||||
IPv4SetSubnet(router_ip, subnet_mask);
|
||||
DnsSetResolverIPv4(dns_ip);
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
U0 Netcfg() {
|
||||
SocketInit();
|
||||
|
||||
"$FG,7$Netcfg: Configuring network...\n$FG$";
|
||||
|
||||
I64 error = DhcpConfigure();
|
||||
if (error < 0)
|
||||
"$FG,4$DhcpConfigure: error %d\n$FG$", error;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#exe {
|
||||
if (SNAILNET_NATIVE_DRIVER == NULL) {
|
||||
StreamPrint("#include \"::/Adam/Net/SnailLib\"");
|
||||
}
|
||||
}
|
||||
|
||||
// Higher-level, utility functions
|
||||
|
||||
I64 recvLine(I64 sock, U8 *buffer, I64 size, I64 flags) {
|
||||
I64 got = 0;
|
||||
while (got + 1 < size) {
|
||||
if (!recv(sock, buffer + got, 1, flags))
|
||||
return -1;
|
||||
|
||||
if (buffer[got] == '\n')
|
||||
break;
|
||||
else if (buffer[got] != '\r')
|
||||
got++;
|
||||
}
|
||||
// FIXME: safe but incorrect behavior on overflow
|
||||
buffer[got] = 0;
|
||||
return got;
|
||||
}
|
||||
|
||||
I64 sendall(I64 sockfd, U8 *buf, I64 len, I64 flags) {
|
||||
I64 total = 0;
|
||||
|
||||
while (len) {
|
||||
I64 sent = send(sockfd, buf, len, flags);
|
||||
if (sent > 0) {
|
||||
buf += sent;
|
||||
total += sent;
|
||||
len -= sent;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
I64 sendString(I64 sockfd, U8 *str, I64 flags) {
|
||||
return sendall(sockfd, str, StrLen(str), flags);
|
||||
}
|
||||
+1108
File diff suppressed because it is too large
Load Diff
+247
@@ -0,0 +1,247 @@
|
||||
class CUdpHeader {
|
||||
U16 source_port;
|
||||
U16 dest_port;
|
||||
U16 length;
|
||||
U16 checksum;
|
||||
};
|
||||
|
||||
class CUdpSocket {
|
||||
CSocket sock;
|
||||
|
||||
I64 rcvtimeo_ms;
|
||||
I64 recv_maxtime;
|
||||
|
||||
U8 *recv_buf;
|
||||
I64 recv_len;
|
||||
|
||||
sockaddr_in recv_addr;
|
||||
U16 bound_to;
|
||||
};
|
||||
|
||||
// TODO: this takes up half a meg, change it to a binary tree or something
|
||||
static CUdpSocket **udp_bound_sockets;
|
||||
|
||||
I64 UdpPacketAlloc(U8 **frame_out, U32 source_ip, U16 source_port, U32 dest_ip,
|
||||
U16 dest_port, I64 length) {
|
||||
U8 *frame;
|
||||
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_UDP, source_ip, dest_ip,
|
||||
sizeof(CUdpHeader) + length);
|
||||
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
CUdpHeader *hdr = frame;
|
||||
hdr->source_port = htons(source_port);
|
||||
hdr->dest_port = htons(dest_port);
|
||||
hdr->length = htons(sizeof(CUdpHeader) + length);
|
||||
hdr->checksum = 0;
|
||||
|
||||
*frame_out = frame + sizeof(CUdpHeader);
|
||||
return index;
|
||||
}
|
||||
|
||||
I64 UdpPacketFinish(I64 index) { return IPv4PacketFinish(index); }
|
||||
|
||||
I64 UdpParsePacket(U16 *source_port_out, U16 *dest_port_out, U8 **data_out,
|
||||
I64 *length_out, CIPv4Packet *packet) {
|
||||
if (packet->proto != IP_PROTO_UDP)
|
||||
return -1;
|
||||
|
||||
CUdpHeader *hdr = packet->data;
|
||||
//"UDP: from %d, to %d, len %d, chksum %d\n",
|
||||
// ntohs(hdr->source_port), ntohs(hdr->dest_port), ntohs(hdr->length),
|
||||
// ntohs(hdr->checksum);
|
||||
|
||||
// FIXME: validate packet->length
|
||||
|
||||
*source_port_out = ntohs(hdr->source_port);
|
||||
*dest_port_out = ntohs(hdr->dest_port);
|
||||
// ntohs(hdr->length)
|
||||
// ntohs(hdr->checksum)
|
||||
|
||||
*data_out = packet->data + sizeof(CUdpHeader);
|
||||
*length_out = packet->length - sizeof(CUdpHeader);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
I64 UdpSocketAccept(CUdpSocket *s, sockaddr *addr, I64 addrlen) {
|
||||
no_warn s;
|
||||
no_warn addr;
|
||||
no_warn addrlen;
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 UdpSocketBind(CUdpSocket *s, sockaddr *addr, I64 addrlen) {
|
||||
if (addrlen < sizeof(sockaddr_in))
|
||||
return -1;
|
||||
|
||||
if (s->bound_to)
|
||||
return -1;
|
||||
|
||||
sockaddr_in *addr_in = addr;
|
||||
U16 port = ntohs(addr_in->sin_port);
|
||||
|
||||
// TODO: address & stuff
|
||||
if (udp_bound_sockets[port] != NULL)
|
||||
return -1;
|
||||
|
||||
udp_bound_sockets[port] = s;
|
||||
s->bound_to = port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
I64 UdpSocketClose(CUdpSocket *s) {
|
||||
if (s->bound_to)
|
||||
udp_bound_sockets[s->bound_to] = NULL;
|
||||
|
||||
Free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
I64 UdpSocketConnect(CUdpSocket *s, sockaddr *addr, I64 addrlen) {
|
||||
// FIXME: implement
|
||||
no_warn s;
|
||||
no_warn addr;
|
||||
no_warn addrlen;
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 UdpSocketListen(CUdpSocket *s, I64 backlog) {
|
||||
no_warn s;
|
||||
no_warn backlog;
|
||||
return -1;
|
||||
}
|
||||
|
||||
I64 UdpSocketRecvfrom(CUdpSocket *s, U8 *buf, I64 len, I64 flags,
|
||||
sockaddr *src_addr, I64 addrlen) {
|
||||
no_warn flags;
|
||||
|
||||
s->recv_buf = buf;
|
||||
s->recv_len = len;
|
||||
|
||||
if (s->rcvtimeo_ms != 0)
|
||||
s->recv_maxtime = cnts.jiffies + s->rcvtimeo_ms * JIFFY_FREQ / 1000;
|
||||
|
||||
while (s->recv_buf != NULL) {
|
||||
// Check for timeout
|
||||
if (s->rcvtimeo_ms != 0 && cnts.jiffies > s->recv_maxtime) {
|
||||
// TODO: seterror(EWOULDBLOCK)
|
||||
s->recv_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
Yield;
|
||||
}
|
||||
|
||||
// TODO: addrlen
|
||||
if (src_addr) {
|
||||
// wtf? can't copy structs with '='?
|
||||
MemCpy((src_addr(sockaddr_in *)), &s->recv_addr, addrlen);
|
||||
}
|
||||
|
||||
return s->recv_len;
|
||||
}
|
||||
|
||||
I64 UdpSocketSendto(CSocket *s, U8 *buf, I64 len, I64 flags,
|
||||
sockaddr_in *dest_addr, I64 addrlen) {
|
||||
no_warn s;
|
||||
no_warn flags;
|
||||
|
||||
if (addrlen < sizeof(sockaddr_in))
|
||||
return -1;
|
||||
|
||||
U8 *frame;
|
||||
|
||||
I64 index = UdpPacketAlloc(&frame, IPv4GetAddress(), 0,
|
||||
ntohl(dest_addr->sin_addr.s_addr),
|
||||
ntohs(dest_addr->sin_port), len);
|
||||
|
||||
if (index < 0)
|
||||
return -1;
|
||||
|
||||
MemCpy(frame, buf, len);
|
||||
return UdpPacketFinish(index);
|
||||
}
|
||||
|
||||
I64 UdpSocketSetsockopt(CUdpSocket *s, I64 level, I64 optname, U8 *optval,
|
||||
I64 optlen) {
|
||||
if (level == SOL_SOCKET && optname == SO_RCVTIMEO_MS && optlen == 8) {
|
||||
s->rcvtimeo_ms = *(optval(I64 *));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
CUdpSocket *UdpSocket(U16 domain, U16 type) {
|
||||
if (domain != AF_INET || type != SOCK_DGRAM)
|
||||
return NULL;
|
||||
|
||||
CUdpSocket *s = MAlloc(sizeof(CUdpSocket));
|
||||
s->sock.accept = &UdpSocketAccept;
|
||||
s->sock.bind = &UdpSocketBind;
|
||||
s->sock.close = &UdpSocketClose;
|
||||
s->sock.connect = &UdpSocketConnect;
|
||||
s->sock.listen = &UdpSocketListen;
|
||||
s->sock.recvfrom = &UdpSocketRecvfrom;
|
||||
s->sock.sendto = &UdpSocketSendto;
|
||||
s->sock.setsockopt = &UdpSocketSetsockopt;
|
||||
|
||||
s->rcvtimeo_ms = 0;
|
||||
s->recv_maxtime = 0;
|
||||
|
||||
s->recv_buf = NULL;
|
||||
s->recv_len = 0;
|
||||
s->recv_addr.sin_family = AF_INET;
|
||||
s->bound_to = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
I64 UdpHandler(CIPv4Packet *packet) {
|
||||
U16 source_port;
|
||||
U16 dest_port;
|
||||
U8 *data;
|
||||
I64 length;
|
||||
|
||||
I64 error = UdpParsePacket(&source_port, &dest_port, &data, &length, packet);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
//"%u => %p\n", dest_port, udp_bound_sockets[dest_port];
|
||||
|
||||
CUdpSocket *s = udp_bound_sockets[dest_port];
|
||||
|
||||
// FIXME: should also check that bound address is INADDR_ANY,
|
||||
// OR packet dest IP matches bound address
|
||||
if (s != NULL) {
|
||||
if (s->recv_buf) {
|
||||
I64 num_recv = s->recv_len;
|
||||
|
||||
if (num_recv > length)
|
||||
num_recv = length;
|
||||
|
||||
MemCpy(s->recv_buf, data, num_recv);
|
||||
|
||||
// signal that we received something
|
||||
s->recv_buf = NULL;
|
||||
s->recv_len = num_recv;
|
||||
|
||||
// TODO: we keep converting n>h>n, fuck that
|
||||
s->recv_addr.sin_port = htons(source_port);
|
||||
s->recv_addr.sin_addr.s_addr = htonl(packet->source_ip);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
U0 UdpInit() {
|
||||
udp_bound_sockets = MAlloc(65536 * sizeof(CUdpSocket *));
|
||||
MemSet(udp_bound_sockets, 0, 65536 * sizeof(CUdpSocket *));
|
||||
}
|
||||
|
||||
UdpInit;
|
||||
RegisterL4Protocol(IP_PROTO_UDP, &UdpHandler);
|
||||
RegisterSocketClass(AF_INET, SOCK_DGRAM, &UdpSocket);
|
||||
@@ -0,0 +1,185 @@
|
||||
/* NOTE: This driver uses SnailNet naming conventions for required Ethernet
|
||||
function calls. */
|
||||
|
||||
extern U16 htons(U16 h);
|
||||
extern I64 NetFifoPushCopy(U8 *data, I64 length);
|
||||
#define ETHERNET_FRAME_SIZE 1548
|
||||
U8 *SNAILNET_NATIVE_DRIVER = "Virtio-net";
|
||||
|
||||
// Current Rx/Tx buffer
|
||||
I64 rx_buffer_ptr = 0;
|
||||
I64 tx_buffer_ptr = 0;
|
||||
|
||||
I64 rx_buffer_count = 256;
|
||||
I64 tx_buffer_count = 256;
|
||||
|
||||
U64 rx_buffers = MAlloc(ETHERNET_FRAME_SIZE * 256);
|
||||
U64 tx_buffers = MAlloc(ETHERNET_FRAME_SIZE * 256);
|
||||
|
||||
class @virtio_net {
|
||||
U16 port;
|
||||
U8 mac[6];
|
||||
@virtio_queue *rq;
|
||||
@virtio_queue *sq;
|
||||
I64 rq_size;
|
||||
I64 rq_index;
|
||||
I64 sq_size;
|
||||
I64 sq_index;
|
||||
I64 rx_packets;
|
||||
I64 rx_bytes;
|
||||
I64 tx_packets;
|
||||
I64 tx_bytes;
|
||||
};
|
||||
|
||||
class @virtio_net_header {
|
||||
U8 flags;
|
||||
U8 gso_type;
|
||||
U16 header_length;
|
||||
U16 gso_size;
|
||||
U16 checksum_start;
|
||||
U16 checksum_offset;
|
||||
};
|
||||
|
||||
@virtio_net VirtioNet;
|
||||
MemSet(&VirtioNet, 0, sizeof(@virtio_net));
|
||||
|
||||
@virtio_net_header *def_pkt_hdr = CAlloc(sizeof(@virtio_net_header));
|
||||
|
||||
static I64 @virtio_net_alloc_tx_packet(U8 **buffer_out, I64 length, I64 flags) {
|
||||
// FIXME: validate length
|
||||
flags = flags;
|
||||
I64 sq_idx = VirtioNet.sq->available.index % 256;
|
||||
I64 sq_idx2 = sq_idx % 128;
|
||||
I64 index = tx_buffer_ptr;
|
||||
tx_buffer_ptr = (tx_buffer_ptr + 1) & (tx_buffer_count - 1);
|
||||
*buffer_out = tx_buffers + index * ETHERNET_FRAME_SIZE;
|
||||
|
||||
VirtioNet.sq->buffers[sq_idx2 * 2].address = def_pkt_hdr;
|
||||
VirtioNet.sq->buffers[sq_idx2 * 2].length = sizeof(@virtio_net_header);
|
||||
VirtioNet.sq->buffers[sq_idx2 * 2].flags = VRING_DESC_F_NEXT;
|
||||
VirtioNet.sq->buffers[sq_idx2 * 2].next = (sq_idx2 * 2) + 1;
|
||||
VirtioNet.sq->buffers[(sq_idx2 * 2) + 1].address = *buffer_out;
|
||||
VirtioNet.sq->buffers[(sq_idx2 * 2) + 1].length = length;
|
||||
VirtioNet.sq->buffers[(sq_idx2 * 2) + 1].flags = NULL;
|
||||
VirtioNet.sq->buffers[(sq_idx2 * 2) + 1].next = 0;
|
||||
VirtioNet.sq->available.ring[sq_idx] = sq_idx2 * 2;
|
||||
|
||||
VirtioNet.sq->available.index++;
|
||||
|
||||
VirtioNet.tx_packets++;
|
||||
VirtioNet.tx_bytes += length;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static I64 @virtio_net_finish_tx_packet(I64) {
|
||||
OutU16(VirtioNet.port + VIRTIO_PCI_QUEUE_NOTIFY, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U8 *loopback_frame = MAlloc(ETHERNET_FRAME_SIZE);
|
||||
I64 loopback_length = 0;
|
||||
|
||||
I64 EthernetFrameAlloc(U8 **buffer_out, U8 *src_addr, U8 *dst_addr,
|
||||
U16 ethertype, I64 length, I64 flags) {
|
||||
|
||||
U8 *frame;
|
||||
|
||||
// APAD_XMT doesn't seem to work in VirtualBox, so we have to pad the frame
|
||||
// ourselves
|
||||
if (length < 46)
|
||||
length = 46;
|
||||
|
||||
I64 index;
|
||||
|
||||
if (!MemCmp(dst_addr, &VirtioNet.mac, 6)) {
|
||||
frame = loopback_frame;
|
||||
loopback_length = length;
|
||||
index = I64_MAX;
|
||||
} else {
|
||||
index = @virtio_net_alloc_tx_packet(&frame, 14 + length, flags);
|
||||
if (index < 0)
|
||||
return index;
|
||||
}
|
||||
|
||||
MemCpy(frame + 0, dst_addr, 6);
|
||||
MemCpy(frame + 6, src_addr, 6);
|
||||
frame[12] = (ethertype >> 8);
|
||||
frame[13] = (ethertype & 0xff);
|
||||
|
||||
*buffer_out = frame + 14;
|
||||
return index;
|
||||
}
|
||||
|
||||
I64 EthernetFrameFinish(I64 index) {
|
||||
if (index == I64_MAX && loopback_frame && loopback_length) {
|
||||
NetFifoPushCopy(loopback_frame, loopback_length);
|
||||
loopback_length = 0;
|
||||
return 0;
|
||||
}
|
||||
return @virtio_net_finish_tx_packet(index);
|
||||
}
|
||||
|
||||
U8 *EthernetGetAddress() { return &VirtioNet.mac; }
|
||||
|
||||
I64 @virtio_net_init() {
|
||||
I64 i, j;
|
||||
|
||||
// Scan for device
|
||||
j = PCIClassFind(0x020000, 0);
|
||||
if (j < 0) {
|
||||
"\nVirtio-net device not found.\n";
|
||||
return -1;
|
||||
}
|
||||
VirtioNet.port = PCIReadU32(j.u8[2], j.u8[1], j.u8[0], 0x10) & 0xFFFFFFFC;
|
||||
for (i = 0; i < 6; i++) {
|
||||
VirtioNet.mac[i] = InU8(VirtioNet.port + VIRTIO_PCI_CONFIG + i);
|
||||
}
|
||||
|
||||
// Reset Device
|
||||
OutU8(VirtioNet.port + VIRTIO_PCI_STATUS, 0);
|
||||
|
||||
// Found Driver
|
||||
OutU8(VirtioNet.port + VIRTIO_PCI_STATUS,
|
||||
InU8(VirtioNet.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_ACKNOWLEDGE |
|
||||
VIRTIO_CONFIG_S_DRIVER);
|
||||
|
||||
// Set up receive queue
|
||||
OutU16(VirtioNet.port + VIRTIO_PCI_QUEUE_SEL, 0);
|
||||
VirtioNet.rq_size = InU16(VirtioNet.port + VIRTIO_PCI_QUEUE_SIZE); // 256
|
||||
VirtioNet.rq = CAllocAligned(sizeof(@virtio_queue), 4096, Fs->code_heap);
|
||||
OutU32(VirtioNet.port + VIRTIO_PCI_QUEUE_PFN, VirtioNet.rq / 4096);
|
||||
|
||||
// Set up send queue
|
||||
OutU16(VirtioNet.port + VIRTIO_PCI_QUEUE_SEL, 1);
|
||||
VirtioNet.sq_size = InU16(VirtioNet.port + VIRTIO_PCI_QUEUE_SIZE); // 256
|
||||
VirtioNet.sq = CAllocAligned(sizeof(@virtio_queue), 4096, Fs->code_heap);
|
||||
OutU32(VirtioNet.port + VIRTIO_PCI_QUEUE_PFN, VirtioNet.sq / 4096);
|
||||
|
||||
for (i = 0; i < 128; i++) {
|
||||
VirtioNet.rq->buffers[i * 2].address = CAlloc(sizeof(@virtio_net_header));
|
||||
VirtioNet.rq->buffers[i * 2].length = sizeof(@virtio_net_header);
|
||||
VirtioNet.rq->buffers[i * 2].flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE;
|
||||
VirtioNet.rq->buffers[i * 2].next = (i * 2) + 1;
|
||||
VirtioNet.rq->buffers[(i * 2) + 1].address = CAlloc(ETHERNET_FRAME_SIZE);
|
||||
VirtioNet.rq->buffers[(i * 2) + 1].length = ETHERNET_FRAME_SIZE;
|
||||
VirtioNet.rq->buffers[(i * 2) + 1].flags = VRING_DESC_F_WRITE;
|
||||
VirtioNet.rq->buffers[(i * 2) + 1].next = 0;
|
||||
VirtioNet.rq->available.ring[i] = i * 2;
|
||||
VirtioNet.rq->available.ring[i + 128] = i * 2;
|
||||
}
|
||||
VirtioNet.rq->available.index = 1;
|
||||
|
||||
// Init OK
|
||||
OutU8(VirtioNet.port + VIRTIO_PCI_STATUS,
|
||||
InU8(VirtioNet.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
OutU16(VirtioNet.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
|
||||
"[Virtio-net] device detected, MAC address "
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
VirtioNet.mac[0], VirtioNet.mac[1], VirtioNet.mac[2], VirtioNet.mac[3],
|
||||
VirtioNet.mac[4], VirtioNet.mac[5];
|
||||
}
|
||||
|
||||
@virtio_net_init;
|
||||
|
||||
"[OK] virtio-net \n";
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// PCI virtio I/O registers.
|
||||
//
|
||||
|
||||
#define VIRTIO_PCI_HOST_FEATURES 0 // Features supported by the host
|
||||
#define VIRTIO_PCI_GUEST_FEATURES 4 // Features activated by the guest
|
||||
#define VIRTIO_PCI_QUEUE_PFN 8 // PFN for the currently selected queue
|
||||
#define VIRTIO_PCI_QUEUE_SIZE 12 // Queue size for the currently selected queue
|
||||
#define VIRTIO_PCI_QUEUE_SEL 14 // Queue selector
|
||||
#define VIRTIO_PCI_QUEUE_NOTIFY 16 // Queue notifier
|
||||
#define VIRTIO_PCI_STATUS 18 // Device status register
|
||||
#define VIRTIO_PCI_ISR 19 // Interrupt status register
|
||||
#define VIRTIO_PCI_CONFIG 20 // Configuration data block
|
||||
|
||||
//
|
||||
// PCI virtio status register bits
|
||||
//
|
||||
|
||||
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
|
||||
#define VIRTIO_CONFIG_S_DRIVER 2
|
||||
#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
||||
#define VIRTIO_CONFIG_S_FAILED 0x80
|
||||
|
||||
//
|
||||
// Ring descriptor flags
|
||||
//
|
||||
|
||||
#define VRING_DESC_F_NEXT 1 // Buffer continues via the next field
|
||||
#define VRING_DESC_F_WRITE 2 // Buffer is write-only (otherwise read-only)
|
||||
#define VRING_DESC_F_INDIRECT 4 // Buffer contains a list of buffer descriptors
|
||||
|
||||
class @virtio_queue_buf {
|
||||
U64 address;
|
||||
U32 length;
|
||||
U16 flags;
|
||||
U16 next;
|
||||
};
|
||||
class @virtio_avail {
|
||||
U16 flags;
|
||||
U16 index;
|
||||
U16 ring[256];
|
||||
U16 int_index;
|
||||
};
|
||||
class @virtio_used_item {
|
||||
U32 index;
|
||||
U32 length;
|
||||
};
|
||||
class @virtio_used {
|
||||
U16 flags;
|
||||
U16 index;
|
||||
@virtio_used_item ring[256];
|
||||
U16 int_index;
|
||||
};
|
||||
class @virtio_queue {
|
||||
@virtio_queue_buf buffers[256];
|
||||
@virtio_avail available;
|
||||
U8 padding[3578];
|
||||
@virtio_used used;
|
||||
};
|
||||
|
||||
class @virtio_avail_buf {
|
||||
U32 index;
|
||||
U64 address;
|
||||
U32 length;
|
||||
};
|
||||
|
||||
class @virtio_buf_info {
|
||||
U8 *buffer;
|
||||
U64 size;
|
||||
U8 flags;
|
||||
|
||||
// If the user wants to keep same buffer as passed in this struct, use "true".
|
||||
// otherwise, the supplied buffer will be copied in the queues' buffer
|
||||
Bool copy;
|
||||
};
|
||||
|
||||
"[OK] virtio \n";
|
||||
Reference in New Issue
Block a user