Implemented proof-of-concept UDP chat program.

Added Port Control Protocol prototyping in Tests folder.
ARP Handler now warns when seeing requests with non-matching target IPs, and reports error on unrecognized operations.
ICMP Handler now warns when seeing ICMP packets with unrecognized type or code.
Set up NetLog to log info using colors and labels for clarity.
Fixed IPV4AddressParse string parameter bug, also put in a missing CompCtrlDel at the end of the method.
Fixed PresentationToNetwork not erroring when IPV4AddressParse fails.
Implemented UDP Sockets allowing 0 ms timeout and immediately returning on UDPSocketReceiveFrom if no messages were waiting at the socket.
Fixed UDP Handler using wrong address of packet to search bound socket addresses.
This commit is contained in:
TomAwezome 2021-02-06 06:05:14 -05:00
parent 79e7caaad4
commit 40b85644ba
10 changed files with 286 additions and 42 deletions

Binary file not shown.

View file

@ -91,7 +91,7 @@ CARPHash *ARPCacheFindByIP(U32 ip_address)
if (entry == NULL)
{
NetWarn("ARP CACHE FIND BY IP: Could not find an IP in ARP cache.");
NetLog("ARP CACHE FIND BY IP: Could not find an IP in ARP cache.");
}
Free(ip_string);
@ -100,17 +100,20 @@ CARPHash *ARPCacheFindByIP(U32 ip_address)
CARPHash *ARPCachePut(U32 ip_address, U8 *mac_address)
{
CARPHash *entry = ARPCacheFindByIP(ip_address);
CARPHash *entry;
NetLog("ARP CACHE PUT: Attempting to look for entry in ARP Cache.");
//Free(entry); // something seems wrong about this...
entry = ARPCacheFindByIP(ip_address);
if (!entry)
{
entry = CAlloc(sizeof(CARPHash));
NetLog("ARP CACHE PUT: Attempting add to cache: addr, mac:");
NetLog(" 0x%0X, 0x%0X 0x%0X 0x%0X 0x%0X 0x%0X 0x%0X",
ip_address, mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
entry->str = MStrPrint("%X", ip_address);
entry->type = HTT_ARP;
MemCopy(entry->mac_address, mac_address, 6);
@ -135,64 +138,46 @@ U0 ARPSetIPV4Address(U32 ip_address)
arp_globals.local_ipv4);
}
//ARP Handler? Takes an eth_frame CEthFrame* ?
//Seems like a weird way to do this,
//and then it goes into a a RegisterL3Protocol
//function that does some function-handler
//magic. Idk why it _has_ to do that, it seems
//like a choice they made. I really do wonder if
//their is a slicker way to implement this.
//Less of this fucky function syntax mess.
//We'll just make the ARP handle function
//and directly call it based on a switch in
//the NetQueueHandler
I64 ARPHandler(CEthernetFrame *ethernet_frame)
{
{ // Use of ARPHandler must account for -1 error codes.
CARPHeader *header;
U16 operation;
NetLog("ARP HANDLER: Entering ARP Handler.");
// shrine checks if frame ethertype is ARP and ensures length is not less than CARPHeader
// since revising Shrine implement, will do same checks for now ..
if (ethernet_frame->ethertype != ETHERTYPE_ARP)
{
NetErr("ARP HANDLER: Caught wrong frame ethertype.");
return -1; // External use of ARPHandler must account for -1 error codes
return -1;
}
if (ethernet_frame->length < sizeof(CARPHeader))
{
NetErr("ARP HANDLER: Caught wrong frame length.");
return -1; // External use of ARPHandler must account for -1 error codes
return -1;
}
header = ethernet_frame->data;
operation = EndianU16(header->operation);
// shrine checks to ensure header->htype is 1,
// EndianU16(header->protocol_type) == ethertype ipv4,
// hlen(?) != 6(?), and that plen(?) == 4 (?)
if (EndianU16(header->hardware_type) != HTYPE_ETHERNET)
{
NetErr("ARP HANDLER: Caught wrong frame hardware type.");
return -1; // External use of ARPHandler must account for -1 error codes
return -1;
}
if (EndianU16(header->protocol_type) != ETHERTYPE_IPV4)
{
NetErr("ARP HANDLER: Caught wrong frame protocol type.");
return -1; // External use of ARPHandler must account for -1 error codes
return -1;
}
if (header->hardware_addr_len != HLEN_ETHERNET)
{
NetErr("ARP HANDLER: Caught wrong frame hardware address length.");
return -1; // External use of ARPHandler must account for -1 error codes
return -1;
}
if (header->protocol_addr_len != PLEN_IPV4)
{
NetErr("ARP HANDLER: Caught wrong frame protocol address length.");
return -1; // External use of ARPHandler must account for -1 error codes
return -1;
}
switch (operation)
@ -208,11 +193,18 @@ I64 ARPHandler(CEthernetFrame *ethernet_frame)
header->sender_hardware_addr,
header->sender_protocol_addr);
}
else
NetWarn("ARP HANDLER: Saw request, target IP address is not this machine.");
break;
case ARP_REPLY:
NetLog("ARP HANDLER: Saw reply, putting into ARP Cache.");
ARPCachePut(EndianU32(header->sender_protocol_addr), header->sender_hardware_addr);
break;
default:
NetErr("ARP HANDLER: Unrecognized operation: 0x%X", operation);
break;
}
NetLog("ARP HANDLER: Exiting.");
@ -232,12 +224,16 @@ U0 ARPRep()
while (temp_hash)
{
"ARP Hash @ $FG,3$0x%X$FG$:\n", temp_hash;
address = EndianU32(Str2I64(temp_hash->str, 16)(U32));
" IP Address: $FG,6$%d.%d.%d.%d$FG$\n",
(&address)(U8 *)[0], (&address)(U8 *)[1], (&address)(U8 *)[2], (&address)(U8 *)[3]; // todo: kludge
" MAC Address: $FG,6$";$FG$
for (j = 0; j < MAC_ADDRESS_LENGTH; j++)
"%02X ", temp_hash->mac_address[j];
"$FG$\n\n";
temp_hash = temp_hash->next;

View file

@ -524,7 +524,7 @@ U0 NetConfigure()
{
I64 error;
NetLog("==== Configuring Network. ====");
NetLog("\n==== Configuring Network. ====\n");
error = DHCPConfigure();
if (error < 0)

View file

@ -74,6 +74,8 @@ I64 ICMPHandler(CIPV4Packet *packet)
packet->data + sizeof(CICMPHeader),
packet->length - sizeof(CICMPHeader)); // TODO: ??
}
else
NetWarn("ICMP HANDLER: Unhandled ICMP packet. type, code: 0x%X, 0x%X", header->type, header->code);
NetLog("ICMP HANDLER: Exiting.");

View file

@ -64,6 +64,13 @@ interrupt U0 NetHandler()
{
CNetQueueEntry *entry;
NetLog("$$BG,DKGRAY$$$$FG,BLACK$$"
"=== NET HANDLER ==="
"$$BG$$$$FG$$");
NetLog("$$BD,LTGRAY$$$$FD,WHITE$$"
"NET HANDLER: Entering interrupt.");
while (entry = NetQueuePull())
{
NetLog("NET HANDLER: Caught NetQueue Entry, handling.");
@ -73,7 +80,11 @@ interrupt U0 NetHandler()
}
NetLog("NET HANDLER: NetQueue empty, exiting interrupt.");
NetLog("NET HANDLER: NetQueue empty, exiting interrupt.\n"
"$$BD,WHITE$$$$FD,LTGRAY$$"
"$$BG,DKGRAY$$$$FG,BLACK$$"
"==================="
"$$BG$$$$FG$$");
*(dev.uncached_alias + LAPIC_EOI)(U32*) = 0;
}

View file

@ -512,7 +512,7 @@ U0 PCNetFinishTransmitPacket(I64 de_index)
CPCNetDescriptorEntry *entry = &pcnet.tx_de_buffer[de_index * sizeof(CPCNetDescriptorEntry)];
Bts(&entry->status1, PCNET_DESCRIPTORf_OWN);
NetLog("PCNet FINISH TX PACKET: TX DE index: %X, OWN bit of entry at entry: %b.",
NetLog("PCNET FINISH TX PACKET: TX DE index: %X, OWN bit of entry at entry: %b.",
de_index, Bt(&entry->status1, PCNET_DESCRIPTORf_OWN));
}
@ -535,7 +535,7 @@ I64 PCNetReceivePacket(U8 **packet_buffer_out, U16 *packet_length_out)
I64 de_index = pcnet.current_rx_de_index;
U16 packet_length;
NetLog("PCNET RECEIVE PACKET: PCNet received packet. %X , %X ", packet_buffer_out, packet_length_out);
NetLog("PCNET RECEIVE PACKET: Output buffer ptr, output length ptr: %X, %X ", packet_buffer_out, packet_length_out);
CPCNetDescriptorEntry *entry = &pcnet.rx_de_buffer[de_index * sizeof(CPCNetDescriptorEntry)];
@ -572,13 +572,18 @@ interrupt U0 PCNetIRQ()
while (PCNetDriverOwns(&entry[pcnet.current_rx_de_index]))
{
NetLog("PCNET IRQ: Saw owned RX DE index %d.", pcnet.current_rx_de_index);
NetLog("$$BG,LTCYAN$$$$FG,WHITE$$"
"==== PCNET IRQ ===="
"$$BG$$$$FG$$");
NetLog("$$BD,CYAN$$$$FD,WHITE$$"
"PCNET IRQ: Saw owned RX DE index %d.", pcnet.current_rx_de_index);
de_index = PCNetReceivePacket(&packet_buffer, &packet_length);
if (de_index >= 0) // necessary? check increment logic in PCNetReceivePacket.
if (de_index >= 0) // todo: necessary? check increment logic in PCNetReceivePacket.
{
NetLog("PCNET IRQ: Pushing copy into Net Queue, Releasing Receive Packet");
NetLog("PCNET IRQ: Pushing copy into Net Queue, releasing receive packet.");
NetQueuePushCopy(packet_buffer, packet_length);
PCNetReleaseReceivePacket(de_index);
}
@ -586,6 +591,12 @@ interrupt U0 PCNetIRQ()
Bts(&csr, PCNET_CTRL_RINT);
PCNetWriteCSR(PCNET_CSR_CTRLSTATUS, csr);
NetLog("PCNET IRQ: Exiting.\n"
"$$BD,WHITE$$$$FD,LTGRAY$$"
"$$BG,LTCYAN$$$$FG,WHITE$$"
"==================="
"$$BG$$$$FG$$");
}
*(dev.uncached_alias + LAPIC_EOI)(U32*) = 0;

View file

@ -154,7 +154,8 @@ Bool IPV4AddressParse(U8 *string, U32 *destination)
// U8* lexable_string;
// lexable_string = StrReplace(string, ",", ","); // swap any commas with an unexpected value
U8 *lexable_string = StrReplace(string, ".", ","); // swap dots with commas since Lex is easier with them.
U8 *lexable_string = StrNew(string);
lexable_string = StrReplace(lexable_string, ".", ","); // swap dots with commas since Lex is easier with them.
CCompCtrl *cc = CompCtrlNew(lexable_string);
//Bts(&cc->opts, OPTf_DECIMAL_ONLY);
@ -215,6 +216,8 @@ Bool IPV4AddressParse(U8 *string, U32 *destination)
}
}
CompCtrlDel(cc);
temp_destination = EndianU32(temp_destination); // store the address in Network Byte Order (Big-Endian)
*destination = temp_destination;
"\n\n%X\n\n", temp_destination;
@ -250,7 +253,8 @@ I64 PresentationToNetwork(I64 address_family, U8 *string, CIPAddressStorage *des
}
ipv4_address = destination;
IPV4AddressParse(string, &ipv4_address->address);
if (!IPV4AddressParse(string, &ipv4_address->address))
return -1;
break;

116
src/Home/Net/Tests/Chat.CC Executable file
View file

@ -0,0 +1,116 @@
/*
Chat program proof-of-concept using UDP.
Needs Bridged Mode working on two VMs under the same router.
*/
U8 *dest_ip_string = StrGet("Destination IPV4: "),
*port_string = StrGet("RX & TX UDP Port: ");
CIPV4Address *dest_address = CAlloc(sizeof(CIPV4Address));
I64 port = Str2I64(port_string);
CTask *chat_display_task = NULL;
CTask *chat_message_task = NULL;
U0 ChatDisplayTask(I64)
{ // display received messages.
DocTermNew;
DocCursor;
DocPrint(, "$$WW,1$$");
while (TaskValidate(chat_message_task))
{
Refresh;
}
}
U0 ChatMessageTask(I64)
{ // take in text.
U8 *message;
I64 de_index;
U8 *payload;
DocTermNew;
DocPrint(, "$$WW,1$$");
while (message = StrGet("> ",, SGF_SHIFT_ESC_EXIT))
{
DocBottom(chat_display_task->put_doc);
DocPrint(chat_display_task->put_doc, "$$BG,BLUE$$$$BLACK$$<local>$$FG$$$$BG$$ %s\n", message);
de_index = UDPPacketAllocate(&payload, ipv4_globals.local_ip, port, dest_address->address, port, StrLen(message));
MemCopy(payload, message, StrLen(message));
UDPPacketFinish(de_index);
DocClear;
DocPrint(, "$$WW,1$$");
}
}
U0 ChatInit()
{
chat_message_task = Spawn(&ChatMessageTask, NULL, "UDP Chat Message");
chat_display_task = Spawn(&ChatDisplayTask, NULL, "UDP Chat");
chat_message_task->win_inhibit = WIG_USER_TASK_DEFAULT;
chat_display_task->win_inhibit = WIG_USER_TASK_DEFAULT;
LBts(&chat_message_task->display_flags, DISPLAYf_SHOW);
LBts(&chat_display_task->display_flags, DISPLAYf_SHOW);
WinFocus(chat_display_task);
WinFocus(chat_message_task);
chat_display_task->win_top = 2;
chat_display_task->win_bottom = TEXT_ROWS / 3;
chat_display_task->win_left = TEXT_COLS / 3;
chat_display_task->win_right = TEXT_COLS / 3 * 2;
chat_message_task->win_top = chat_display_task->win_bottom + 2;
chat_message_task->win_bottom = chat_message_task->win_top + 3;
chat_message_task->win_left = chat_display_task->win_left;
chat_message_task->win_right = chat_display_task->win_right;
}
U0 Chat()
{
CUDPSocket *udp_socket = UDPSocket(AF_INET);
CSocketAddressIPV4 *sock_addr = CAlloc(sizeof(CSocketAddressIPV4));
U8 *buffer[ETHERNET_FRAME_SIZE];
// MemSet(buffer, 0, ETHERNET_FRAME_SIZE);
"\nIP entered: %s\n", dest_ip_string;
while (PresentationToNetwork(AF_INET, dest_ip_string, dest_address) == -1)
{
"ERROR: Bad IP entered. Retry.\n";
dest_ip_string = StrGet("Destination IPV4: ");
}
ChatInit;
sock_addr->port = EndianU16(port);
sock_addr->family = AF_INET;
sock_addr->address.address = dest_address->address;
UDPSocketBind(udp_socket, sock_addr);
"\nIP entered: %s\n", dest_ip_string;
while (TaskValidate(chat_message_task))
{
if (UDPSocketReceiveFrom(udp_socket, buffer, ETHERNET_FRAME_SIZE, NULL) != -1)
{
DocBottom(chat_display_task->put_doc);
DocPrint(chat_display_task->put_doc, "$$BG,PURPLE$$$$BLACK$$<%s>$$FG$$$$BG$$ %s\n", dest_ip_string, buffer);//NetworkToPresentation(AF_INET, dest_address));
MemSet(buffer, 0, ETHERNET_FRAME_SIZE);
}
Refresh;
}
}
Chat;

99
src/Home/Net/Tests/PCP.CC Executable file
View file

@ -0,0 +1,99 @@
// Port Control Protocol prototyping.
// RFC 6887
class CPCPRequestHeader
{
U8 version;
U8 opcode; // includes R bit.
U16 reserved; // must be all 0 on TX
U32 req_lifetime; // seconds. 0 interpreted as 'delete'.
U8 pcp_client_ip[16]; // source ipv4 addr converted to ipv4-mapped ipv6 as per spec. 128 bits
};
class CPCPMapRequest
{
CPCPRequestHeader header;
U8 nonce[12]; // "Mapping Nonce" 96 bits of random vals.
U8 protocol; // 6 TCP. 17 UDP. 0 'all protocols'.
U8 reserved[3]; // 24 bits, TX must be 0, ignore on RX.
U16 internal_port;
/* internal_port. 0 'all ports' legal only on lifetime 0 (delete) or client req 'all ports'.
must be ignored on RX. */
U16 external_port;
/* external_port. 'suggested'. if client doesnt know it or has no preference it must be 0.*/
U8 external_ip[16];
/* external_ip. 'suggested'. if client doesnt know it or no preference,
it must use 'address-family-specific all-zero address. */
};
I64 PCPSendMapRequest(U16 internal_port, U16 external_port=NULL, U32 external_ip=NULL)
{
U8 *frame;
CPCPMapRequest *request;
I64 de_index;
I64 i;
de_index = UDPPacketAllocate(&frame,
IPV4GetAddress(),
5351,
ipv4_globals.ipv4_router_address,
5351,
sizeof(CPCPMapRequest));
if (de_index < 0)
{
NetErr("PCP SEND MAP REQ: Failed, UDPPacketAllocate returned error.");
return de_index;
}
request = frame;
request->header.version = 2;
request->header.opcode = 1; // MAP
request->header.req_lifetime = 60; // live for 1 min.
// force local IPV4 addr into IPV4-mapped-IPV6
for (i = 0; i < 10; i++) // first 80 bits 0
request->header.pcp_client_ip[i] = 0;
for (; i < 10 + 2; i++) // next 16 bits 1
request->header.pcp_client_ip[i] = 0xFF;
*(&request->header.pcp_client_ip[i](U32 *)) = EndianU32(IPV4GetAddress());
//
// set random 'nonce'
for (i = 0; i < 12; i++)
request->nonce[i] = RandU8;
request->protocol = IP_PROTOCOL_UDP;
request->internal_port = internal_port;
request->external_port = external_port;
if (!external_ip)
{ // if no specified ext ip, use specific ipv4-mapped-ipv6 all-zeroes address.
for (i = 0; i < 10; i++)
request->external_ip[i] = 0;
for (; i < 10 + 2; i++)
request->external_ip[i] = 0xFF;
*(&request->external_ip[i](U32 *)) = 0;
}
else
{ // else, map to param ip
for (i = 0; i < 10; i++)
request->external_ip[i] = 0;
for (; i < 10 + 2; i++)
request->external_ip[i] = 0xFF;
*(&request->external_ip[i](U32 *)) = EndianU32(external_ip);
}
UDPPacketFinish(de_index);
return 0;
}
PCPSendMapRequest(0xDEAD, 0xBEEF);

View file

@ -261,12 +261,12 @@ CUDPTreeQueue *UDPTreeNodeQueueIPV4Find(U32 address, CUDPTreeNode *node)
if (temp_queue->socket->receive_address.family == AF_INET)
{
temp_ip = &temp_queue->socket->receive_address;
NetLog("UDPTreeNodeQueueIPV4Find: addr, nodequeue addr: %08X, %08X",
NetLog("UDPTreeNodeQueueIPV4Find: Comparing: addr, nodequeue addr: %08X, %08X",
address, temp_ip->address.address);
if (temp_ip->address.address == address || temp_ip->address.address == INADDR_ANY)
{
NetLog("UDPTreeNodeQueueIPV4Find: Address match: addr, nodequeue: %08X, %08X ",
NetLog("UDPTreeNodeQueueIPV4Find: Address match: addr, nodequeue addr: %08X, %08X ",
address, temp_ip->address.address);
return temp_queue;
}
@ -658,7 +658,11 @@ I64 UDPSocketReceiveFrom(CUDPSocket *udp_socket, U8 *buffer, I64 len, CSocketAdd
while (message == message->next)
{ // wait for a message to be added to queue. head is non-message.
if (udp_socket->receive_timeout_ms != 0 && counts.jiffies > udp_socket->receive_max_timeout)
// if (udp_socket->receive_timeout_ms != 0 && counts.jiffies > udp_socket->receive_max_timeout)
if (udp_socket->receive_timeout_ms == 0)
return -1; // if no timeout set and didn't see message, bail early
if (counts.jiffies > udp_socket->receive_max_timeout)
{ // Shrine has TODO: 'seterror(EWOULDBLOCK)' investigate this
NetErr("UDP SOCKET RECEIVE FROM: Timed out.");
return -1;
@ -802,7 +806,8 @@ I64 UDPHandler(CIPV4Packet *packet)
node = UDPTreeNodeFind(destination_port, head);
if (node)
{
queue = UDPTreeNodeQueueIPV4Find(packet->destination_ip_address, node); // TODO: make sure bit order is correct here!!
// queue = UDPTreeNodeQueueIPV4Find(packet->destination_ip_address, node); // TODO: make sure bit order is correct here!!
queue = UDPTreeNodeQueueIPV4Find(packet->source_ip_address, node); // TODO: make sure bit order is correct here!!
if (queue)
{
udp_socket = queue->socket;