diff --git a/source/Cosmos/Cosmos.System/Network/TCPIP/IPv4OutgoingBuffer.cs b/source/Cosmos/Cosmos.System/Network/TCPIP/IPv4OutgoingBuffer.cs index 18a5c2b96..ea3b43dec 100644 --- a/source/Cosmos/Cosmos.System/Network/TCPIP/IPv4OutgoingBuffer.cs +++ b/source/Cosmos/Cosmos.System/Network/TCPIP/IPv4OutgoingBuffer.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using HW = Cosmos.Hardware; +using System; namespace Cosmos.Sys.Network.TCPIP { @@ -7,11 +8,12 @@ namespace Cosmos.Sys.Network.TCPIP { private class BufferEntry { - public enum EntryStatus { ADDED, ARP_SENT, DONE }; + public enum EntryStatus { ADDED, ARP_SENT, ROUTE_ARP_SENT, JUST_SEND, DONE }; public HW.Network.NetworkDevice NIC; public IPPacket Packet; public EntryStatus Status; + public IPv4Address nextHop; public BufferEntry(HW.Network.NetworkDevice nic, IPPacket packet) { @@ -53,6 +55,35 @@ namespace Cosmos.Sys.Network.TCPIP BufferEntry entry = queue[e]; if (entry.Status == BufferEntry.EntryStatus.ADDED) { + if (TCPIPStack.IsLocalAddress(entry.Packet.DestinationIP) == false) + { + entry.nextHop = TCPIPStack.FindRoute(entry.Packet.DestinationIP); + if (entry.nextHop == null) + { + entry.Status = BufferEntry.EntryStatus.DONE; + continue; + } + + if (ARP.ARPCache.Contains(entry.nextHop) == true) + { + entry.Packet.DestinationMAC = ARP.ARPCache.Resolve(entry.nextHop); + + entry.NIC.QueueBytes(entry.Packet.RawData); + + entry.Status = BufferEntry.EntryStatus.DONE; + } + else + { + ARP.ARPRequest_EthernetIPv4 arp_request = new ARP.ARPRequest_EthernetIPv4(entry.NIC.MACAddress, entry.Packet.SourceIP, + HW.Network.MACAddress.Broadcast, entry.nextHop); + + entry.NIC.QueueBytes(arp_request.RawData); + + entry.Status = BufferEntry.EntryStatus.ROUTE_ARP_SENT; + } + continue; + } + if (ARP.ARPCache.Contains(entry.Packet.DestinationIP) == true) { entry.Packet.DestinationMAC = ARP.ARPCache.Resolve(entry.Packet.DestinationIP); @@ -71,6 +102,12 @@ namespace Cosmos.Sys.Network.TCPIP entry.Status = BufferEntry.EntryStatus.ARP_SENT; } } + else if (entry.Status == BufferEntry.EntryStatus.JUST_SEND) + { + entry.NIC.QueueBytes(entry.Packet.RawData); + + entry.Status = BufferEntry.EntryStatus.DONE; + } } int i = 0; @@ -100,7 +137,16 @@ namespace Cosmos.Sys.Network.TCPIP { entry.Packet.DestinationMAC = arp_reply.SenderMAC; - entry.Status = BufferEntry.EntryStatus.ADDED; + entry.Status = BufferEntry.EntryStatus.JUST_SEND; + } + } + else if (entry.Status == BufferEntry.EntryStatus.ROUTE_ARP_SENT) + { + if (entry.nextHop.CompareTo(arp_reply.SenderIP) == 0) + { + entry.Packet.DestinationMAC = arp_reply.SenderMAC; + + entry.Status = BufferEntry.EntryStatus.JUST_SEND; } } } diff --git a/source/Cosmos/Cosmos.System/Network/TCPIPStack.cs b/source/Cosmos/Cosmos.System/Network/TCPIPStack.cs index e0dc98619..e39831e72 100644 --- a/source/Cosmos/Cosmos.System/Network/TCPIPStack.cs +++ b/source/Cosmos/Cosmos.System/Network/TCPIPStack.cs @@ -27,6 +27,12 @@ namespace Cosmos.Sys.Network /// Byte buffer of the received data public delegate void ClientDataReceived(TcpClient client, byte[] data); + /// + /// TCP Client disconnection delegate + /// + /// Instance of the that represents the connection + public delegate void ClientDisconnected(TcpClient client); + /// /// Implements a TCP/IP Stack on top of Cosmos /// @@ -38,7 +44,7 @@ namespace Cosmos.Sys.Network private static List ipConfigs; private static HW.TempDictionary udpClients; private static HW.TempDictionary tcpListeners; - private static List tcpSockets; + internal static List tcpSockets; /// /// Initialize the TCP/IP Stack variables @@ -244,6 +250,19 @@ namespace Cosmos.Sys.Network active_connection.ConnectionState = TCP.TCPConnection.State.ESTABLISHED; } } + else if (active_connection.ConnectionState == TCP.TCPConnection.State.SYN_SENT) + { + if ((tcp_packet.Syn == true) && (tcp_packet.Ack == true) && ((active_connection.LocalSequenceNumber + 1) == tcp_packet.AckNumber)) + { + active_connection.LocalSequenceNumber++; + active_connection.RemoteSequenceNumber = tcp_packet.SequenceNumber + 1; + active_connection.ConnectionState = TCP.TCPConnection.State.ESTABLISHED; + + TCP.TCPPacket ack = new TCP.TCPPacket(active_connection, active_connection.LocalSequenceNumber, + active_connection.RemoteSequenceNumber, 0x10, 8192); + TCPIP.IPv4OutgoingBuffer.AddPacket(ack); + } + } else if (active_connection.ConnectionState == TCP.TCPConnection.State.ESTABLISHED) { if (tcp_packet.Ack == true) @@ -261,6 +280,11 @@ namespace Cosmos.Sys.Network active_connection.client.dataReceived(tcp_packet.TCP_Data); } + + if (tcp_packet.Fin == true) + { + active_connection.client.disconnect(); + } } } @@ -354,5 +378,32 @@ namespace Cosmos.Sys.Network return default_gw; } + + internal static bool IsLocalAddress(IPv4Address destIP) + { + for (int c = 0; c < ipConfigs.Count; c++) + { + if ((ipConfigs[c].IPAddress.To32BitNumber() & ipConfigs[c].SubnetMask.To32BitNumber()) == + (destIP.To32BitNumber() & ipConfigs[c].SubnetMask.To32BitNumber())) + { + return true; + } + } + + return false; + } + + internal static IPv4Address FindRoute(IPv4Address destIP) + { + for (int c = 0; c < ipConfigs.Count; c++) + { + if (ipConfigs[c].DefaultGateway.CompareTo(IPv4Address.Zero) != 0) + { + return ipConfigs[c].DefaultGateway; + } + } + + return null; + } } } diff --git a/source/Cosmos/Cosmos.System/Network/TcpClient.cs b/source/Cosmos/Cosmos.System/Network/TcpClient.cs index c7f46546c..b8269fb65 100644 --- a/source/Cosmos/Cosmos.System/Network/TcpClient.cs +++ b/source/Cosmos/Cosmos.System/Network/TcpClient.cs @@ -1,4 +1,5 @@ using Cosmos.Sys.Network.TCPIP.TCP; +using System; namespace Cosmos.Sys.Network { @@ -9,6 +10,27 @@ namespace Cosmos.Sys.Network { internal TCPConnection connection; internal ClientDataReceived dataCallback; + internal ClientDisconnected disconnectCallback; + + protected static UInt16 NextLocalPort = 33000; + + public TcpClient(IPv4Address dest, UInt16 port) + { + IPv4Address source = TCPIPStack.FindNetwork(dest); + if( source == null ) + { + throw new ArgumentException("Destination host unreachable", "dest"); + } + + this.connection = new TCPConnection(dest, port, source, NextLocalPort++, 0x5656, TCPConnection.State.SYN_SENT); + this.connection.client = this; + + TCPIPStack.tcpSockets.Add(connection); + + TCPPacket packet = new TCPPacket(connection, connection.LocalSequenceNumber, 0, 0x02, 8192); + + TCPIP.IPv4OutgoingBuffer.AddPacket(packet); + } internal TcpClient(TCPConnection connection) { @@ -25,6 +47,15 @@ namespace Cosmos.Sys.Network set { this.dataCallback = value; } } + /// + /// Data Received callback function + /// + public ClientDisconnected Disconnect + { + get { return this.disconnectCallback; } + set { this.disconnectCallback = value; } + } + /// /// Send a string to the remote host /// @@ -52,6 +83,18 @@ namespace Cosmos.Sys.Network TCPIP.IPv4OutgoingBuffer.AddPacket(packet); } + /// + /// Close down an active connection + /// + public void Close() + { + TCPPacket packet = new TCPPacket(connection, connection.LocalSequenceNumber, connection.RemoteSequenceNumber, 0x11, 8192); + + TCPIP.IPv4OutgoingBuffer.AddPacket(packet); + + connection.ConnectionState = TCPConnection.State.TIMEWAIT; + } + internal void dataReceived(byte[] data) { if (this.dataCallback != null) @@ -74,5 +117,21 @@ namespace Cosmos.Sys.Network { get { return this.connection.LocalEndpoint; } } + + /// + /// Returns true if the current connection is active + /// + public bool Connected + { + get { return (this.connection.ConnectionState == TCPConnection.State.ESTABLISHED); } + } + + internal void disconnect() + { + if (this.disconnectCallback != null) + { + this.disconnectCallback(this); + } + } } } diff --git a/source/SSchockeTest/Program.cs b/source/SSchockeTest/Program.cs index 1db33fcda..eaf5f03a6 100644 --- a/source/SSchockeTest/Program.cs +++ b/source/SSchockeTest/Program.cs @@ -46,11 +46,18 @@ namespace Cosmos.Playground.SSchocke { Console.WriteLine("Initializing TCP Stack..."); TCPIPStack.Init(); - TCPIPStack.ConfigIP(nic, new IPv4Config(new IPv4Address(192, 168, 21, 123), new IPv4Address(255, 255, 255, 0))); + TCPIPStack.ConfigIP(nic, new IPv4Config(new IPv4Address(192, 168, 20, 123), + new IPv4Address(255, 255, 255, 0), + new IPv4Address(192, 168, 20, 100))); Console.WriteLine("Initializing TCP Port 80..."); TCPIPStack.AddTcpListener(80, WebServerConnect); + Console.WriteLine("Setup outgoing connection..."); + TcpClient webClient = new TcpClient(new IPv4Address(196, 38, 235, 2), 80); + webClient.DataReceived = WebClient_RecvData; + webClient.Disconnect = WebClientDisconnect; + #region Setup WebServer strings webPage = "

It works! This is a web page being hosted by your Cosmos Operating System

"; int webPageLength = webPage.Length; @@ -69,9 +76,15 @@ namespace Cosmos.Playground.SSchocke { error404 += "404 URL Not found"; #endregion + bool requestDone = false; while (true) { TCPIPStack.Update(); + if ((requestDone == false) && (webClient.Connected == true)) + { + webClient.SendString("GET /\r\n"); + requestDone = true; + } } Console.WriteLine("Press a key to shutdown..."); @@ -83,6 +96,13 @@ namespace Cosmos.Playground.SSchocke { { Console.WriteLine("Client(" + client.RemoteEndpoint.ToString() + ") Connected to port 80..."); client.DataReceived = WebServer_RecvData; + client.Disconnect = WebServerDisconnect; + } + + private static void WebServerDisconnect(TcpClient client) + { + Console.WriteLine("Client(" + client.RemoteEndpoint.ToString() + ") disconnected..."); + client.Close(); } private static void WebServer_RecvData(TcpClient client, byte[] data) @@ -104,5 +124,24 @@ namespace Cosmos.Playground.SSchocke { client.SendString(error404); } } - } + + private static void WebClient_RecvData(TcpClient client, byte[] data) + { + Console.WriteLine("Received reply from " + client.RemoteEndpoint.ToString()); + StringBuilder sb = new StringBuilder(data.Length); + for (int b = 0; b < data.Length; b++) + { + sb.Append((char)data[b]); + } + String dataString = sb.ToString(); + + Console.WriteLine(dataString); + } + + private static void WebClientDisconnect(TcpClient client) + { + Console.WriteLine("Disconnect from " + client.RemoteEndpoint.ToString() + "..."); + client.Close(); + } + } } \ No newline at end of file