// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // ---------------------------------------------------------------------------- /// // Microsoft Research, Cambridge // // #define DEBUG_IP using NetStack.Common; using System; using System.Collections; using System.Diagnostics; using System.Net.IP; using Drivers.Net; using NetStack.NetDrivers; using NetStack.Protocols; using Microsoft.Singularity; namespace NetStack.Runtime { /// // This module implements the IP protocol // NOTICE: FOR NOW WE DO NOT HANDLE FRAGMENTATION!!!!!!!!!!!!!! // public class IPModule : IProtocol { // the version protected int version; // the icmp protocol handle (for sending control messages) protected IProtocol icmp; protected ArpModule arp; // IP specific stack configuration data HostConfiguration hostConfig; private const string moduleName = "IP"; [ Conditional("DEBUG_IP") ] private static void DebugPrint(string format, params object [] args) { DebugStub.Print("IPModule: {0}", __arglist(string.Format(format, args)) ); } // INetModule interfaces // ------------------------ string INetModule.ModuleName { get { return moduleName; } } ushort INetModule.ModuleVersion { get { return 0x01; } } // 0.1 // called by the runtime when // the protocol should be started public bool StartModule() { // get a ref to the icmp protocol icmp = Core.Instance().GetProtocolByName("ICMP"); if (icmp == null) { return false; } // get a ref to the arp protocol arp = Core.Instance().GetProtocolByName("ARP") as ArpModule; if (arp == null) { return false; } return true; } public bool StopModule() { return true; } public bool DestroyModule() { return true; } // IProtocol interfaces // ------------------------ public bool Initialize(ProtocolParams parameters) { Debug.Assert(parameters == null || parameters["name"] == moduleName); hostConfig = new HostConfiguration(); version = ProtocolParams.LookupInt32(parameters, "version", 4); bool fragment = ProtocolParams.LookupBoolean(parameters, "fragment", false); // save the routing protocol name, if exists if (version != 4 || fragment == true) { DebugPrint("Support only exists for V4 w/o fragments.\n"); return false; } Core core = Core.Instance(); core.RegisterProtocol(this); if (!core.packetTypes.RegisterTypeHandler(PacketTypes.IP, this)) { core.DeregisterProtocol(this); return false; } return true; } public ushort GetProtocolID() { return EthernetFormat.PROTOCOL_IP; } public Session CreateSession() { return null; } // handle incoming IP packets public NetStatus OnProtocolReceive(NetPacket! pkt) { IPFormat.IPHeader! ipHeader; if (IPFormat.ReadIPHeader(pkt, out ipHeader) == false) { DebugPrint("bad header\n"); return NetStatus.Code.PROTOCOL_OK; } pkt.OverlapContext = ipHeader; Debug.Assert(IPFormat.IsMoreFragSet(ipHeader) == false); // check packet checksum if (IPFormat.IsCheckSumOK(ipHeader) == false) { DebugPrint("checksum error, dropping!\n"); return NetStatus.Code.PROTOCOL_DROP_CHKSUM; } // check if the packet is for us... // TBC: for now we ignore broadcasts // (directed + limited bcast addresses) Core core = Core.Instance(); // if its not ours but we are a gateway, we should route it. if (hostConfig.IsLocalAddress(ipHeader.Destination) == false && ipHeader.Destination != IPv4.Broadcast) { if (hostConfig.IsRouter) { // Not supported for the time being. } // DebugPrint("Dropping wrong destination\n"); return NetStatus.Code.RT_DROP_WRONG_DEST; } // we should handle it.. IProtocol prot = core.GetProtocolFromID(ipHeader.protocol); if (prot == null) { DebugPrint("Packet drop no handler for protocol id = {0}\n", (ushort)ipHeader.protocol); return NetStatus.Code.RT_DROP_NO_HANDLER; } else { DebugPrint("Got packet of type {0}\n", ipHeader.protocol); } // clip the packet for the next protocol header + data pkt.Clip(ipHeader.totalLength - IPFormat.Size - 1); return prot.OnProtocolReceive(pkt); } // this method sends an IP packet. It uses the IP header // target address to find route and sets the packet's Mux. // it also sets the target and source mac addresses public NetStatus OnProtocolSend(NetPacket! packet) { Multiplexer mux = (Multiplexer)packet.Mux; if (mux != null) { byte[]! data = (byte[]!) packet.GetRawData(); // Source has already resolved outbound mux. Check // briefly that they've put values in for ethernet headers // (yes, only ethernet is supported here folks) int dstOkay = ((int)data[0] | (int)data[1] | (int)data[2] | (int)data[3] | (int)data[4] | (int)data[5]); int srcOkay = ((int)data[6] | (int)data[7] | (int)data[8] | (int)data[9] | (int)data[10]| (int)data[11]); if (dstOkay != 0 && srcOkay != 0) { mux.SendBuffered(packet); DebugPrint("Packet sent (had mux)\n"); return NetStatus.Code.PROTOCOL_OK; } // Invalid destination or source address. Ignore request // to send via specific mux. } // since we may retransmit this packet, we should // reset the buffer to the IP boundaries! packet.Clip(EthernetFormat.Size, packet.Length - EthernetFormat.Size - 1); IPFormat.IPHeader! ipHeader; if (IPFormat.ReadIPHeader(packet, out ipHeader) == false) { DebugPrint("Packet dropped error)\n"); DebugStub.Break(); return NetStatus.Code.PROTOCOL_DROP_ERROR; } Debug.Assert(ipHeader.Destination != IPv4.Zero); RouteEntry e = HostConfiguration.RoutingTable.Lookup(ipHeader.Destination); if (e == null) { DebugPrint("Packet dropped no route)\n"); return NetStatus.Code.RT_DROP_NO_ROUTE; } // DebugPrint("{0} -> {1}\n", ipHeader.Destination, e); // DebugPrint("Packet source {0}\n", ipHeader.Source); IPv4 ifaddr = e.InterfaceAddress; IPv4 nexthop; if (e.Gateway == e.InterfaceAddress) { nexthop = ipHeader.Destination; } else { nexthop = e.Gateway; } // DebugPrint("Selected destination {0}\n", nexthop); IAdapter adapter = hostConfig.Bindings.GetAdapter(ifaddr); assert adapter != null; Multiplexer targetMux = Core.Instance().GetMuxForAdapter(adapter); assert targetMux != null; packet.Mux = targetMux; EthernetAddress localMac = adapter.HardwareAddress; EthernetAddress remoteMac; IAdapter targetAdapter = hostConfig.Bindings.GetAdapter(nexthop); if (targetAdapter != null) { remoteMac = targetAdapter.HardwareAddress; } else { if (arp.Lookup(nexthop, out remoteMac) == false) { DebugPrint("No ARP Entry for: {0}\n", nexthop); arp.ArpRequest(ipHeader.Source, nexthop, targetMux, new ArpRequestCallback(ArpResponse), packet, TimeSpan.FromSeconds(3)); BlockAssociatedSession(packet); return NetStatus.Code.RT_RETRY_PENDING_ARP; } } return SendResolved(packet, targetMux, localMac, remoteMac); } private NetStatus SendResolved(NetPacket! packet, Multiplexer! targetMux, EthernetAddress localMac, EthernetAddress remoteMac) { // set the ethernet data // TBC: make the ARPModule setup the packet { Session session = packet.SessionContext as Session; if (session != null) { DebugPrint("Sending packet for {0}/{1} ({2}-->{3})\n", session.RemoteAddress, session.RemotePort, localMac, remoteMac); } else { DebugPrint("Sending packet to {0}\n", remoteMac); } } Debug.Assert(packet.IsOneChunk); EthernetFormat.Write((byte[]!)packet.GetRawData(), 0, localMac, remoteMac, EthernetFormat.PROTOCOL_IP); targetMux.SendBuffered(packet); return NetStatus.Code.PROTOCOL_OK; } private void ArpResponse(IPv4 address, EthernetAddress answer, object cookie) { NetPacket! packet = (NetPacket!)cookie; DebugPrint("Got ARP response for {0} -> {1}", address, answer); UnblockAssociatedSession(packet); if (answer != EthernetAddress.Zero) { Multiplexer mux = (Multiplexer!)packet.Mux; SendResolved(packet, mux, mux.Adapter.HardwareAddress, answer); Core.Instance().SignalOutboundPackets(); } else { DebugPrint("ARP timed out for {0}", address); } } private void BlockAssociatedSession(NetPacket! packet) { Session! session = (!)(packet.SessionContext as Session); session.OutQueuePaused = true; DebugPrint("Blocking Session {0}\n", session.Uid); } private void UnblockAssociatedSession(NetPacket! packet) { Session! session = (!)(packet.SessionContext as Session); session.OutQueuePaused = false; DebugPrint("Unblocking session {0}\n", session.Uid); Core.Instance().SignalOutboundPackets(); // Kickstart } public NetStatus SetProtocolSpecific(ushort opcode, byte[]! data) { return NetStatus.Code.PROTOCOL_OK; } public NetStatus GetProtocolSpecific(ushort opcode, out byte[] data) { data = null; return NetStatus.Code.PROTOCOL_OK; } // //protected NetStatus ForwardPacket(IPFormat.IPHeader! rcvIPHeader, // NetPacket! packet, // Multiplexer targetMux) //{ // Debug.Assert(packet != null && packet.IsOneChunk); // // TBC: this is just a preliminary imp, should // // consider MTUs etc. // byte[] data = packet.ToContiguous(); // int start = EthernetFormat.Size; // // // decrease the ttl, if it reaches 0 discard and send // // ICMP message using on the source mux, otherwise forward! // if (--rcvIPHeader.ttl == 0) { // // drop it! and use icmp protocol to return error // // TBC: icmp.OnProtocolSend(..) // will use packetArg.Mux = source mux // return NetStatus.Code.RT_DROP_TTL_EXPIRED; // } // // at last, forward it already! // // modify the header + calc new checksum // IPFormat.WriteIPHeader(data, start, rcvIPHeader); // DebugPrint("Forwarding IP packet.\n"); // // return OnProtocolSend(packet); //} // public HostConfiguration! HostConfiguration { get { return hostConfig; } } // ctor public IPModule() { } } }