using System; using System.Collections.Generic; using HW = Cosmos.Hardware2; using ARP = Cosmos.Sys.Network.TCPIP.ARP; using ICMP = Cosmos.Sys.Network.TCPIP.ICMP; using UDP = Cosmos.Sys.Network.TCPIP.UDP; using TCP = Cosmos.Sys.Network.TCPIP.TCP; namespace Cosmos.Sys.Network { /// /// Data Received function delegate for UDP protocol /// /// Endpoint that data was received from /// Data received in the packet public delegate void DataReceived(IPv4EndPoint source, byte[] data); /// /// New TCP Client connection delegate /// /// Instance of the that represents the connection public delegate void ClientConnected(TcpClient client); /// /// TCP Client Data Received delegate /// /// instance that data was received on /// 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 /// public static class TCPIPStack { private static UInt16 sNextFragmentID; private static HW.TempDictionary addressMap; private static List ipConfigs; private static HW.TempDictionary udpClients; private static HW.TempDictionary tcpListeners; internal static List tcpSockets; /// /// Initialize the TCP/IP Stack variables /// public static void Init() { addressMap = new HW.TempDictionary(); ipConfigs = new List(); udpClients = new HW.TempDictionary(); tcpListeners = new HW.TempDictionary(); tcpSockets = new List(); } /// /// Configure the IP address setup for a given network card /// /// Network Device to configure /// Configuration public static void ConfigIP(HW.Network.NetworkDevice nic, IPv4Config config) { addressMap.Add(config.IPAddress.To32BitNumber(), nic); ipConfigs.Add(config); nic.DataReceived += HandlePacket; } /// /// This function must be called repeatedly to keep the TCP/IP Stack going. /// Will later be called by a background thread in the kernel /// public static void Update() { TCPIP.IPv4OutgoingBuffer.Send(); } /// /// Can be used to test pinging to a network address from Cosmos /// /// IP Address of destination public static void Ping(IPv4Address dest) { IPv4Address source = FindNetwork(dest); if (source == null) { Console.WriteLine("Destination Network Unreachable!!"); return; } ICMP.ICMPEchoRequest request = new ICMP.ICMPEchoRequest(source, dest, 0x10, 1); TCPIP.IPv4OutgoingBuffer.AddPacket(request); } /// /// Subscribe to a UDP port to listen to data received on a specific port number /// Only one listener allowed /// /// Port number to listen on /// delegate to call when data is received public static void SubscribeUDPPort(UInt16 port, DataReceived callback) { if (udpClients.ContainsKey(port) == true) { throw new ArgumentException("Port is already subscribed to", "port"); } udpClients.Add(port, callback); } /// /// Unsubscribe from existing subscription to a UDP port /// /// Port number to unsubscribe from public static void UnsubscribeUDPPort(UInt16 port) { udpClients.Remove(port); } /// /// Send a UDP packet to a destination device /// /// IP address of destination /// Source port /// Destination port to send data to /// Data to be sent public static void SendUDP(IPv4Address dest, UInt16 srcPort, UInt16 destPort, byte[] data) { IPv4Address source = FindNetwork(dest); if (source == null) { Console.WriteLine("Destination Network Unreachable!!"); return; } UDP.UDPPacket outgoing = new UDP.UDPPacket(source, dest, srcPort, destPort, data); TCPIP.IPv4OutgoingBuffer.AddPacket(outgoing); } /// /// Add a TCP listener to the specified port /// /// Port number to listen on /// Callback function that is called when a new client connects public static void AddTcpListener(UInt16 port, ClientConnected connectCallback) { if (tcpListeners.ContainsKey(port) == true) { throw new ArgumentException("Port is already subscribed to", "port"); } tcpListeners.Add(port, connectCallback); } internal static UInt16 NextIPFragmentID() { return sNextFragmentID++; } internal static void HandlePacket(byte[] packetData) { UInt16 etherType = (UInt16)((packetData[12] << 8) | packetData[13]); switch (etherType) { case 0x0806: ARPHandler(packetData); break; case 0x0800: IPv4Handler(packetData); break; } } private static void IPv4Handler(byte[] packetData) { TCPIP.IPPacket ip_packet = new TCPIP.IPPacket(packetData); ARP.ARPCache.Update(ip_packet.SourceIP, ip_packet.SourceMAC); if ((addressMap.ContainsKey(ip_packet.DestinationIP.To32BitNumber()) == true) || (ip_packet.DestinationIP.address[3] == 255)) { switch (ip_packet.Protocol) { case 1: IPv4_ICMPHandler(packetData); break; case 6: IPv4_TCPHandler(packetData); break; case 17: IPv4_UDPHandler(packetData); break; } } } private static void IPv4_TCPHandler(byte[] packetData) { TCP.TCPPacket tcp_packet = new TCP.TCPPacket(packetData); if (tcp_packet.Syn == true) { if (tcpListeners.ContainsKey(tcp_packet.DestinationPort) == true) { TCP.TCPConnection connection = new TCP.TCPConnection(tcp_packet.SourceIP, tcp_packet.SourcePort, tcp_packet.DestinationIP, tcp_packet.DestinationPort, tcp_packet.SequenceNumber, TCP.TCPConnection.State.SYN_RECVD); tcpSockets.Add(connection); TCP.TCPPacket syn_ack = new TCP.TCPPacket(connection, connection.LocalSequenceNumber, ++connection.RemoteSequenceNumber, 0x12, 8192, 2); syn_ack.AddMSSOption(1360); syn_ack.AddSACKOption(); TCPIP.IPv4OutgoingBuffer.AddPacket(syn_ack); return; } } TCP.TCPConnection active_connection = null; for (int c = 0; c < tcpSockets.Count; c++) { if ((tcpSockets[c].RemoteIP.CompareTo(tcp_packet.SourceIP) == 0) && (tcpSockets[c].RemotePort == tcp_packet.SourcePort) && (tcpSockets[c].LocalPort == tcp_packet.DestinationPort)) { active_connection = tcpSockets[c]; break; } } if (active_connection == null) { TCP.TCPPacket reset_packet = new TCP.TCPPacket(tcp_packet.DestinationIP, tcp_packet.SourceIP, tcp_packet.DestinationPort, tcp_packet.SourcePort, 0, (tcp_packet.SequenceNumber + 1), 0x14, 8192); TCPIP.IPv4OutgoingBuffer.AddPacket(reset_packet); return; } if (active_connection.ConnectionState == TCP.TCPConnection.State.SYN_RECVD) { if ((tcp_packet.Ack == true) && ((active_connection.LocalSequenceNumber + 1) == tcp_packet.AckNumber)) { active_connection.LocalSequenceNumber++; active_connection.ConnectionState = TCP.TCPConnection.State.ESTABLISHED; ClientConnected connectCallback = tcpListeners[tcp_packet.DestinationPort]; connectCallback(new TcpClient(active_connection)); } } 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) { //active_connection.LocalSequenceNumber = tcp_packet.AckNumber; } if (tcp_packet.TCP_DataLength > 0) { active_connection.RemoteSequenceNumber += tcp_packet.TCP_DataLength; TCP.TCPPacket ack = new TCP.TCPPacket(active_connection, active_connection.LocalSequenceNumber, active_connection.RemoteSequenceNumber, 0x10, 8192); TCPIP.IPv4OutgoingBuffer.AddPacket(ack); active_connection.client.dataReceived(tcp_packet.TCP_Data); } if (tcp_packet.Fin == true) { active_connection.client.disconnect(); } } } private static void IPv4_UDPHandler(byte[] packetData) { UDP.UDPPacket udp_packet = new UDP.UDPPacket(packetData); if (udpClients.ContainsKey(udp_packet.DestinationPort) == true) { DataReceived dlgt = udpClients[udp_packet.DestinationPort]; if (dlgt != null) { dlgt(new IPv4EndPoint(udp_packet.SourceIP, udp_packet.SourcePort), udp_packet.UDP_Data); } } } private static void IPv4_ICMPHandler(byte[] packetData) { ICMP.ICMPPacket icmp_packet = new ICMP.ICMPPacket(packetData); switch (icmp_packet.ICMP_Type) { case 0: ICMP.ICMPEchoReply recvd_reply = new ICMP.ICMPEchoReply(packetData); Console.WriteLine("Received ICMP Echo reply from " + recvd_reply.SourceIP.ToString()); break; case 8: ICMP.ICMPEchoRequest request = new ICMP.ICMPEchoRequest(packetData); ICMP.ICMPEchoReply reply = new ICMP.ICMPEchoReply(request); Console.WriteLine("Sending ICMP Echo reply to " + reply.DestinationIP.ToString()); TCPIP.IPv4OutgoingBuffer.AddPacket(reply); break; } } private static void ARPHandler(byte[] packetData) { ARP.ARPPacket arp_packet = new ARP.ARPPacket(packetData); if (arp_packet.Operation == 0x01) { if ((arp_packet.HardwareType == 1) && (arp_packet.ProtocolType == 0x0800)) { ARP.ARPRequest_EthernetIPv4 arp_request = new ARP.ARPRequest_EthernetIPv4(packetData); ARP.ARPCache.Update(arp_request.SenderIP, arp_request.SenderMAC); if (addressMap.ContainsKey(arp_request.TargetIP.To32BitNumber()) == true) { //Console.WriteLine("ARP Request Recvd from " + arp_request.SenderIP.ToString()); HW.Network.NetworkDevice nic = addressMap[arp_request.TargetIP.To32BitNumber()]; ARP.ARPReply_EthernetIPv4 reply = new ARP.ARPReply_EthernetIPv4(nic.MACAddress, arp_request.TargetIP, arp_request.SenderMAC, arp_request.SenderIP); nic.QueueBytes(reply.RawData); } } } else if (arp_packet.Operation == 0x02) { if ((arp_packet.HardwareType == 1) && (arp_packet.ProtocolType == 0x0800)) { ARP.ARPReply_EthernetIPv4 arp_reply = new ARP.ARPReply_EthernetIPv4(packetData); ARP.ARPCache.Update(arp_reply.SenderIP, arp_reply.SenderMAC); //Console.WriteLine("ARP Reply Recvd for IP=" + arp_reply.SenderIP.ToString()); TCPIP.IPv4OutgoingBuffer.ARPCache_Update(arp_reply); } } } internal static HW.Network.NetworkDevice FindInterface(IPv4Address sourceIP) { return addressMap[sourceIP.To32BitNumber()]; } internal static IPv4Address FindNetwork(IPv4Address destIP) { IPv4Address default_gw = null; for (int c = 0; c < ipConfigs.Count; c++) { if ((ipConfigs[c].IPAddress.To32BitNumber() & ipConfigs[c].SubnetMask.To32BitNumber()) == (destIP.To32BitNumber() & ipConfigs[c].SubnetMask.To32BitNumber())) { return ipConfigs[c].IPAddress; } if ((default_gw == null) && (ipConfigs[c].DefaultGateway.CompareTo(IPv4Address.Zero) != 0)) { default_gw = ipConfigs[c].IPAddress; } } 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; } } }