/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Netstack / Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: UdpModule.cs // // Note: This is a port of the existing code TCP Session code to a UDP // Session. // // #define DEBUG_UDP using NetStack.Common; using System; using System.Diagnostics; using System.Threading; #if SINGULARITY using Microsoft.Singularity; using Microsoft.Singularity.Channels; #else using System.Net; #endif using System.Net.IP; using Drivers.Net; namespace NetStack.Runtime { using Protocols; public class UdpSession : Session { // the session's transmit q size (num of packets) public const int TxQueueSize = 100; // the session's receive queue size (num of packets) public const int RxQueueSize = 100; protected bool blocking = true; // Blocking / Non-Blocking private bool closed = false; public UdpSession(IProtocol! p) : base (p, TxQueueSize, RxQueueSize) { } ~UdpSession() { } [ Conditional("DEBUG_UDP") ] private static void DebugPrint(string format, params object [] args) { Core.Log(format, args); } public bool BindLocalEndPoint(IPv4 address, ushort port) { return SetLocalEndPoint(address, port); } public void BindRemoteEndPoint(IPv4 address, ushort port) { SetRemoteEndPoint(address, port); } public void BindEndPoints(IPv4 newLocalAddress, ushort newLocalPort, IPv4 remoteAddress, ushort remotePort) { BindRemoteEndPoint(remoteAddress, remotePort); if (BindLocalEndPoint(newLocalAddress, newLocalPort) == true) { String msg = String.Format("Successful bind to {0} (bound to {1})\n", newLocalPort, LocalPort); Tracing.Log(Tracing.Debug,msg); } else { String msg = String.Format("Failed to bind to {0} (bound to {1})\n", newLocalPort, LocalPort); Tracing.Log(Tracing.Debug,msg); } } override internal NetStatus OnReceive(object sender, NetPacket! packet, object context) { if (this.PutPacket(this.inQueue, packet, false) == true) { // Packet is queued for user. Needs to be held onto. return NetStatus.Code.PROTOCOL_PROCESSING; } return NetStatus.Code.PROTOCOL_OK; } public bool Blocking { get { return blocking; } set { blocking = value; } } override public byte[] ReadCopyData() { if (!IsSessionValidForUserRead()) { DebugPrint("ReadData on closed UDP session"); return null; } NetPacket netPacket = DequeuePacket(inQueue, blocking ? -1 /* forever */: 0 /* don't sleep */); if (netPacket != null) { byte [] userData = netPacket.ToUser(); Core.Instance().TheDemux.TakeFreePacket(netPacket); return userData; } return null; } override public int WriteData(byte[]! data) { if (this.RemoteAddress == IPv4.Any || this.RemotePort == 0) { Core.Log("Attempting to send UDP data to an invalid endpoint: " + "{0}/{1}\n", this.RemoteAddress, this.RemotePort); String msg = String.Format("Attempting to send UDP data to an invalid endpoint: {0}/{1}\n", this.RemoteAddress, this.RemotePort); Tracing.Log(Tracing.Debug,msg); // Log sending to incomplete endpoint return 0; } return WriteDataTo(this.RemoteAddress, this.RemotePort, data); } public int WriteDataTo(IPv4 remoteAddress, ushort remotePort, byte[]! data) { if (!IsSessionValidForUserWrite()) { DebugPrint("WriteDataTo on closed UDP session"); return -1; } int headerSize = EthernetFormat.Size + IPFormat.Size + UdpFormat.Size; byte[] packet = new byte [headerSize + data.Length]; int ipStart = EthernetFormat.Size; if (this.LocalAddress == IPv4.Any) this.SetLocalEndPoint(this.LocalAddress, this.LocalPort); UdpFormat.WriteUdpPacket(packet, ipStart, this.LocalAddress, this.LocalPort, remoteAddress, remotePort, data, 0, data.Length); NetPacket netPacket = new NetPacket(packet); if (base.PutPacket(outQueue, netPacket, blocking) == false) { Core.Log("Failed to enqueue packet for {0}/{1}", remoteAddress, remotePort); String msg = String.Format ("Failed to enqueue packet for {0}/{1}", remoteAddress, remotePort); Tracing.Log(Tracing.Debug,msg); return 0; } return data.Length; } public int WriteDataTo(IPv4 remoteAddress, ushort remotePort, byte[]! in ExHeap data) { if (!IsSessionValidForUserWrite()) { DebugPrint("WriteDataTo on closed UDP session"); return -1; } int headerSize = EthernetFormat.Size + IPFormat.Size + UdpFormat.Size; int dataLength = Math.Min(1514 - headerSize, data.Length); byte[] packet = new byte [headerSize + dataLength]; int ipStart = EthernetFormat.Size; if (this.LocalAddress == IPv4.Any) { this.SetLocalEndPoint(this.LocalAddress, this.LocalPort); } UdpFormat.WriteUdpPacket(packet, ipStart, this.LocalAddress, this.LocalPort, remoteAddress, remotePort, data, 0, dataLength); NetPacket netPacket = new NetPacket(packet); if (base.PutPacket(outQueue, netPacket, blocking) == false) { Core.Log("Failed to enqueue packet for {0}/{1}", remoteAddress, remotePort); String msg = String.Format ("Failed to enqueue packet for {0}/{1}", remoteAddress, remotePort); Tracing.Log(Tracing.Debug,msg); return 0; } return dataLength; } public int WriteData(byte[]! in ExHeap buffer) { if (this.RemoteAddress == IPv4.Any || this.RemotePort == 0) { Core.Log("Attempting to send UDP data to an invalid endpoint: " + "{0}/{1}\n", this.RemoteAddress, this.RemotePort); String msg = String.Format("Attempting to send UDP data to an invalid endpoint: {0}/{1}\n", this.RemoteAddress, this.RemotePort); Tracing.Log(Tracing.Debug,msg); // Log sending to incomplete endpoint return 0; } return WriteDataTo(this.RemoteAddress, this.RemotePort, buffer); } public bool WritePacket(NetPacket! netPacket) { if (!IsSessionValidForUserWrite()) { DebugStub.Break(); return false; } return base.PutPacket(outQueue, netPacket, blocking); } public override bool Close() { if (closed == false) { DebugPrint("Closing Session"); this.Dispose(); closed = true; } return closed; } public bool PollSelectRead(int timeoutMillis) { return PollQueueNotEmpty(this.inQueue, timeoutMillis); } public bool PollSelectWrite(int timeoutMillis) { return PollQueueNotFull(this.outQueue, timeoutMillis); } internal override bool IsSessionValidForUserRead() { return !closed; } internal override bool IsSessionValidForUserWrite() { return !closed; } } }