384 lines
14 KiB
C#
384 lines
14 KiB
C#
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Microsoft Research, Cambridge
|
||
|
* author: Yaron Weinsberg, Richard Black
|
||
|
*/
|
||
|
|
||
|
// #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()
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
}
|