singrdk/base/Services/NetStack/Runtime/ICMPSession.cs

229 lines
7.8 KiB
C#

// ----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ----------------------------------------------------------------------------
using NetStack.Common;
using System;
using System.Threading;
using System.Collections;
using System.Diagnostics;
#if !SINGULARITY
using System.Net;
#endif
using System.Net.IP;
using Drivers.Net;
namespace NetStack.Runtime
{
using Protocols;
// an ICMP session
public class IcmpSession : Session
{
// the session's transmit q size
public const int TxQSize = 100;
// the session's receive q size
public const int RcvQSize = 100;
public IcmpSession(IProtocol! protocol)
: base(protocol, TxQSize, RcvQSize)
{
}
// override the OnReceive
override internal NetStatus OnReceive(object sender,
NetPacket! pkt,
object ctx)
{
NetStatus res = base.OnReceive(sender, pkt, ctx);
// if the packet can be delivered to the user
if (res != NetStatus.Code.PROTOCOL_PROCESSING) {
// get the packet and put it on the incoming queue
// if there is no room, drop it. this method is called
// by the runtime when a packet destination is this session.
this.PutPacket(inQueue, pkt, false);
}
return res;
}
// the user should create an IP+ICMP buffer
override public int WriteData(byte[]! data)
{
if (!IsSessionValidForUserWrite()) {
return -1;
}
byte[] pktData = new byte[EthernetFormat.Size+IPFormat.Size+data.Length];
Array.Copy(data,0,pktData,EthernetFormat.Size,data.Length);
NetPacket pkt=new NetPacket(pktData);
PutPacket(outQueue,pkt,true);
return data.Length;
}
override public bool Close()
{
Core.Instance().DeregisterSession(Protocol, this);
return true;
}
}
#if Qos
// an Qos ICMP session
public class QosIcmpSession : Session
{
// the session's transmit q size
public const int TxQSize=100;
// the session's receive q size
public const int RcvQSize=100;
public QosIcmpSession(IProtocol protocol)
: base(protocol, TxQSize, RcvQSize)
{
}
// override the OnReceive (no state machine here, so don't call base)
override internal NetStatus OnReceive(object sender,NetPacket pkt,object ctx)
{
#if DEBUG
Console.Out.WriteLine("QosIcmpSession:OnReceived, flow capacity={0}",capacity);
#endif
if (!this.PutPacket(inQueue,inQueueMonitor,pkt,false)) {
pkt.ReleaseRef();
// increment the session capacity
capacity++;
return NetStatus.Code.PROTOCOL_OK;
}
// the packet is in the Q so, return it only after the user consumed it!
return NetStatus.Code.PROTOCOL_PROCESSING;
}
override public byte[] ReadData()
{
NetPacket pkt = GetPacketFromQ(inQueue,inQueueMonitor,true,Timeout.Infinite,true);
byte[] toUser=null;
if (pkt != null)
toUser = pkt.ToUser();
// now the packet can be returned!
pkt.ReleaseRef();
Core.Instance().TheDemux.TakeFreePacket(pkt);
// increment the session capacity
capacity++;
#if DEBUG
Console.Out.WriteLine("QosIcmpSession::ReadData, session capacity={0}",capacity);
#endif
return toUser;
}
override public byte[] PollCopyData(int timeout)
{
NetPacket pkt = GetPacketFromQ(inQueue,inQueueMonitor,true,timeout,true);
byte[] toUser=null;
if (pkt != null) {
toUser = pkt.ToUser();
// now the packet can be returned!
pkt.ReleaseRef();
Core.Instance().TheDemux.TakeFreePacket(pkt);
// increment the session capacity
capacity++;
}
return toUser;
}
// the user should create an IP+ICMP buffer
override public int WriteData(byte[] data)
{
if (!IsSessionValidForUserWrite()) {
return -1;
}
byte[] pktData = new byte[EthernetFormat.Size+IPFormat.Size+data.Length];
Array.Copy(data,0,pktData,EthernetFormat.Size,data.Length);
NetPacket pkt=new NetPacket(pktData);
PutPacket(outQueue,outQueueMonitor,pkt,true);
return data.Length;
}
override public bool Close()
{
Core.Instance().DeregisterSession(prot,this);
return true;
}
}
#region PrivateIcmpSession
// -------------------------------------------------------
// The internal ICMP Session (owned by the stack)
// -------------------------------------------------------
public class PrivateIcmpSession : QosIcmpSession
{
public PrivateIcmpSession(IProtocol p) : base(p)
{
}
// override the OnReceive
override internal NetStatus OnReceive(object sender,NetPacket pkt,object ctx)
{
#if DEBUG
Console.Out.WriteLine("PrivateIcmpSession:OnReceived, flow capacity={0}",capacity);
#endif
Debug.Assert(ctx!=null);
NetStatus res = NetStatus.Code.PROTOCOL_OK;
IcmpFormat.IcmpHeader icmpHeader = (IcmpFormat.IcmpHeader)ctx;
IPFormat.IPHeader ipHeader = pkt.OverlapContext as IPFormat.IPHeader;
Multiplexer mux = pkt.AdapterContext as Multiplexer; // source mux
Debug.Assert(ipHeader!=null && mux!=null);
bool handled=false;
// handle various types
switch (icmpHeader.type) {
case (byte)IcmpFormat.IcmpType.ECHO_REQUEST:
if (icmpHeader.code == 0) {
#if DEBUG_IP
Console.WriteLine("PrivateIcmpSession: Handling ECHO_REQUEST From: {0}", ipHeader.srcAddrIP);
#endif
// send reply
// clone it since some session may use it (it is readonly!)
int length = pkt.Length();
byte [] pktData = new byte [length];
Array.Copy(pkt.GetRawData(), 0, pktData, 0, length);
IcmpFormat.CreateFastEchoReply(
pktData,
ipHeader.totalLength-IPFormat.Size
);
NetPacket reply = new NetPacket(pktData);
reply.SessionContext=this;
mux.Send(reply);
handled=true;
}
break;
default:
break;
}
// return the original packet (no use for it anymore)
pkt.ReleaseRef();
// only increment the session capacity
// if this is not ours..
// this way we apply back pressure and only
// increase the capacity once sending is done!
if (!handled)
capacity++;
#if DEBUG
Console.Out.WriteLine("PrivateIcmpSession:OnReceived FINISHED, flow capacity={0}",capacity);
#endif
return res;
}
}
#endregion
#endif
}