PageRenderTime 65ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/verify/src/Checked/Libraries/NetStack2/TCP/TCP.cs

#
C# | 2164 lines | 1691 code | 278 blank | 195 comment | 396 complexity | 8d77311fa08705dae54112f9875a5e1c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Microsoft Research Singularity
  4. //
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. //
  7. // File:
  8. //
  9. // Note: Goal -- implement TCP RENO
  10. //
  11. //#define DEBUG_TCP
  12. //#define TCP_THREAD_POOL
  13. using System;
  14. using Drivers.Net;
  15. using System.Net.IP;
  16. using System.Threading;
  17. using System.Diagnostics;
  18. using System.Collections;
  19. using System.Runtime.CompilerServices;
  20. using Microsoft.Singularity;
  21. using Microsoft.Singularity.Channels;
  22. using Microsoft.Singularity.Io;
  23. using Microsoft.Singularity.Io.Net;
  24. using Microsoft.Singularity.V1.Services;
  25. using Microsoft.SingSharp;
  26. using Microsoft.Contracts;
  27. using TcpError = NetStack.Contracts.TcpError;
  28. namespace Microsoft.Singularity.NetStack2
  29. {
  30. public enum TcpState : short
  31. {
  32. //we break camel case here for the sake of tradition
  33. CLOSED = 0,
  34. LISTEN = 1,
  35. SYN_SENT = 2,
  36. SYN_RECEIVED = 3,
  37. ESTABLISHED = 4,
  38. FIN_WAIT_1 = 5,
  39. FIN_WAIT_2 = 6,
  40. CLOSE_WAIT = 7,
  41. CLOSING = 8,
  42. LAST_ACK = 9,
  43. TIME_WAIT = 10,
  44. }
  45. [FlagsAttribute]
  46. public enum TcpFlags : byte
  47. {
  48. FIN = 0x01,
  49. SYN = 0x02,
  50. RST = 0x04,
  51. PSH = 0x08,
  52. ACK = 0x10,
  53. URG = 0x20,
  54. ECE = 0x40,
  55. CWR = 0x80,
  56. }
  57. enum TcpOptions : byte
  58. {
  59. EOL = 0,
  60. NOP = 1,
  61. MSS = 2, //set the MSS
  62. WSF = 3, //windows scaling
  63. TIMESTAMP = 8, //use timestamps
  64. }
  65. public struct TcpHeader
  66. {
  67. public const int MinSize = 20;
  68. public ushort srcPort;
  69. public ushort destPort;
  70. public uint seqNumber;
  71. public uint ackNumber;
  72. public byte dataOffsetBytes; //offset counted in 4 byte intervals
  73. public byte dataOffset; //offset where data begins in 32 bit words
  74. public byte flags;
  75. public ushort windowSize;
  76. public ushort checksum;
  77. public ushort urgentPointer;
  78. // Compare two TCP sequence numbers:
  79. // - means A < B, 0 means A == B and + means A > B
  80. //[Pure]
  81. [Inline]
  82. private static int SequenceNumberCompare(uint seqA, uint seqB)
  83. {
  84. // Exploit integer underflow to compare correctly even in the case
  85. // of sequence number wraparound. This assumes the two numbers are
  86. // always in the same half of the numberspace.
  87. return unchecked((int)(unchecked(seqA - seqB)));
  88. }
  89. //[Pure]
  90. public static bool SeqEQ(uint seqA, uint seqB)
  91. {
  92. return TcpHeader.SequenceNumberCompare(seqA, seqB) == 0;
  93. }
  94. //[Pure]
  95. public static bool SeqNEQ(uint seqA, uint seqB)
  96. {
  97. return TcpHeader.SequenceNumberCompare(seqA, seqB) != 0;
  98. }
  99. //[Pure]
  100. public static bool SeqLEQ(uint seqA, uint seqB)
  101. {
  102. return TcpHeader.SequenceNumberCompare(seqA, seqB) <= 0;
  103. }
  104. //[Pure]
  105. public static bool SeqLess(uint seqA, uint seqB)
  106. {
  107. return TcpHeader.SequenceNumberCompare(seqA, seqB) < 0;
  108. }
  109. //[Pure]
  110. public static bool SeqGEQ(uint seqA, uint seqB)
  111. {
  112. return TcpHeader.SequenceNumberCompare(seqA, seqB) >= 0;
  113. }
  114. //[Pure]
  115. public static bool SeqGreater(uint seqA, uint seqB)
  116. {
  117. return TcpHeader.SequenceNumberCompare(seqA, seqB) > 0;
  118. }
  119. public TcpHeader(Bytes packet, int index)
  120. {
  121. srcPort = NetworkBitConverter.ToUInt16(packet, index);
  122. index += 2;
  123. destPort = NetworkBitConverter.ToUInt16(packet, index);
  124. index += 2;
  125. seqNumber = NetworkBitConverter.ToUInt32(packet, index);
  126. index += 4;
  127. ackNumber = NetworkBitConverter.ToUInt32(packet, index);
  128. index += 4;
  129. dataOffsetBytes = packet[index++];
  130. dataOffset = (byte) ((dataOffsetBytes >> 4) * 4);
  131. flags = packet[index++];
  132. windowSize = NetworkBitConverter.ToUInt16(packet, index);
  133. index += 2;
  134. checksum = NetworkBitConverter.ToUInt16(packet, index);
  135. index += 2;
  136. urgentPointer = NetworkBitConverter.ToUInt16(packet, index);
  137. }
  138. public static ushort SumHeader(TcpHeader tcpHeader)
  139. {
  140. int checksum = tcpHeader.srcPort;
  141. checksum += tcpHeader.destPort;
  142. checksum += (int) (tcpHeader.seqNumber >> 16);
  143. checksum += (int) (tcpHeader.seqNumber & 0xffff);
  144. checksum += (int) (tcpHeader.ackNumber >> 16);
  145. checksum += (int) (tcpHeader.ackNumber & 0xffff);
  146. checksum += (((int)tcpHeader.dataOffsetBytes) << 8) | (int)tcpHeader.flags;
  147. checksum += (int) tcpHeader.windowSize;
  148. checksum += (int) tcpHeader.urgentPointer;
  149. // Double-wrap checksum
  150. checksum = (checksum & 0xFFFF) + (checksum >> 16);
  151. checksum = (checksum & 0xFFFF) + (checksum >> 16);
  152. return (ushort)checksum;
  153. }
  154. public static bool IsChecksumValid(IpHeader ipHeader,
  155. TcpHeader tcpHeader,
  156. Bytes buffer)
  157. {
  158. int ipOffset = EthernetHeader.Size;
  159. ushort ipPayloadSize = 0;
  160. // Compute partial checksums of headers
  161. ushort chksum = IpHeader.SumPseudoHeader(buffer, ipOffset, ref ipPayloadSize);
  162. chksum = IpHeader.SumShortValues(chksum, SumHeader(tcpHeader));
  163. // Checksum payload (without potential odd byte)
  164. //XXX we include tcp options in the 'payload' for checksumming...related to legacy code from
  165. //old netstack...should be cleaned up.
  166. int payloadOffset = TcpHeader.MinSize + IpHeader.Size + EthernetHeader.Size;
  167. int payloadLength = ipPayloadSize - TcpHeader.MinSize;
  168. //DebugStub.WriteLine("payload length {0}\n", DebugStub.ArgList(payloadLength));
  169. ushort payloadSum = IpHeader.SumShortValues(buffer, payloadOffset, payloadLength);
  170. chksum = IpHeader.SumShortValues(chksum, payloadSum);
  171. unchecked {
  172. chksum = (ushort)~chksum;
  173. }
  174. //who know.....
  175. if (chksum == (ushort) 0xFFFF) {
  176. chksum = 0;
  177. }
  178. else if (chksum == (ushort) 0x0) {
  179. chksum = 0xFFFF;
  180. }
  181. // Check for match.
  182. bool checksumMatch = (tcpHeader.checksum == chksum);
  183. // If checksum error, unconditionally output message to debugger.
  184. if (checksumMatch == false) {
  185. DebugStub.WriteLine("Bad TCP checksum {0:x4} != {1:x4}: SEQ {2:x8} ACK {3:x8}",
  186. DebugStub.ArgList(tcpHeader.checksum, chksum,
  187. tcpHeader.seqNumber,
  188. tcpHeader.ackNumber
  189. ));
  190. }
  191. // IsValid is a Match.
  192. // return checksumMatch;
  193. return true;
  194. }
  195. public static void SetTcpChecksum(Bytes buffer,
  196. Bytes payload,
  197. int ipOffset,
  198. ushort tcpLength)
  199. {
  200. // sum IP pseudo
  201. ushort ipPayloadSize = 0;
  202. ushort headerSum = IpHeader.SumPseudoHeader(buffer, ipOffset,
  203. ref ipPayloadSize);
  204. DebugStub.Assert((tcpLength + payload.Length) == ipPayloadSize);
  205. // now add it to the udp header + data
  206. int ipHeaderSize = (buffer[ipOffset] & 0xf) * 4;
  207. int tcpOffset = ipOffset + ipHeaderSize;
  208. ushort tcpsum = IpHeader.SumShortValues(buffer, ipOffset+IpHeader.Size, tcpLength);
  209. DebugStub.Assert(buffer[tcpOffset + 16] == 0);
  210. DebugStub.Assert(buffer[tcpOffset + 17] == 0);
  211. ushort payloadSum = IpHeader.SumShortValues(payload, 0,
  212. payload.Length);
  213. ushort hdrSum = IpHeader.SumShortValues(headerSum, tcpsum);
  214. ushort chksum;
  215. chksum = (ushort) ~(
  216. IpHeader.SumShortValues(hdrSum, payloadSum)
  217. );
  218. if (chksum == 0) {
  219. chksum = (ushort) 0xFFFF;
  220. }
  221. buffer[tcpOffset + 16] = (byte) (chksum >> 8);
  222. buffer[tcpOffset + 17] = (byte) (chksum & 0xff);
  223. }
  224. public static void SetTcpChecksum(Bytes header,
  225. int ipOffset,
  226. ushort tcpLength)
  227. {
  228. // sum IP pseudo
  229. ushort ipPayloadSize = 0;
  230. ushort headerSum = IpHeader.SumPseudoHeader(header, ipOffset,
  231. ref ipPayloadSize);
  232. DebugStub.Assert(tcpLength == ipPayloadSize);
  233. // now add it to the tcp header + data
  234. int ipHeaderSize = (header[ipOffset] & 0xf) * 4;
  235. int tcpOffset = ipOffset + ipHeaderSize;
  236. // DebugStub.WriteLine("ipHeaderSize {0} tcpOffset {1}\n", DebugStub.ArgList(ipHeaderSize, tcpOffset));
  237. ushort tcpsum = IpHeader.SumShortValues(header, ipOffset+IpHeader.Size, tcpLength);
  238. DebugStub.Assert(header[tcpOffset + 16] == 0);
  239. DebugStub.Assert(header[tcpOffset + 17] == 0);
  240. ushort hdrSum = IpHeader.SumShortValues(headerSum, tcpsum);
  241. ushort chksum = (ushort) ~hdrSum;
  242. if (chksum == 0) {
  243. chksum = (ushort) 0xFFFF;
  244. }
  245. header[tcpOffset + 16] = (byte) (chksum >> 8);
  246. header[tcpOffset + 17] = (byte) (chksum & 0xff);
  247. }
  248. }
  249. public class TCP
  250. {
  251. private const uint DefaultRxWinSize = 65535;
  252. private const uint DefaultTxWinSize = 65535;
  253. private static ushort MaxPortNumber = 65535;
  254. //global data structures
  255. private static ushort srcMSS = 1460; //assuming ethernet
  256. private static uint nextISS = 1; //XXX wrong.
  257. private static MonitorLock thisLock = new MonitorLock();
  258. private static MonitorLock tcpPortsMtx;
  259. private static ushort nextPortNumber; //assigning ports on demand
  260. public ushort localPort;
  261. public ushort remotePort;
  262. public IPv4 localIPAddress;
  263. public IPv4 remoteIPAddress;
  264. public LinkedListNode chainedHashLLNode; //pointer into location in chained hash table
  265. public int acceptQueueMaxSize;
  266. public int acceptQueueLength;
  267. public int acceptQueueHead;
  268. public int acceptQueueTail;
  269. public TCP[] acceptQueue;
  270. public TCP listenSocket;
  271. private static TimerWheel slowWheel;
  272. private static TimerWheel fastWheel;
  273. private static ChainedHash activeHash;
  274. private static ChainedHash passiveHash;
  275. #if TCP_THREAD_POOL
  276. private static NetStackThreadPool threadPool;
  277. #endif
  278. public bool activeOpen;
  279. //per connection data structures
  280. private ushort defaultMaxSegSize = 536; //default unless notified in SYN options
  281. private TcpState currentState; //where we are in the state machine
  282. private short currentRxt; //current retransmission timeout
  283. private short dupAckCount; //number consecutive dup acks received
  284. private ushort maxSegSize; //max segement size to send
  285. //what about push?
  286. private bool ackNow; //send ack right away
  287. private bool delayAck; //use del ack timer
  288. private bool noDelay; //disable nagle
  289. private bool needFin;
  290. private bool needAck;
  291. //TCP options
  292. private bool rcvdScale; //did we receive the windows scaling option?
  293. private bool rcvdTimestamp; //did we receive request for echo timestamps?
  294. private bool isBound;
  295. private bool isConnected;
  296. //pointers into timer wheels
  297. private LinkedListNode delayAckTimeoutNode;
  298. private LinkedListNode retransmitTimeoutNode;
  299. private LinkedListNode connectionTimeoutNode;
  300. private LinkedListNode fin2MslTimeoutNode; //used for both fin2 and TimeWait
  301. //delegates for timer wheel
  302. public TimerWheel.TimerDelegate connectionTimeoutDelegate;
  303. public TimerWheel.TimerDelegate delayAckTimeoutDelegate;
  304. public TimerWheel.TimerDelegate retransmitTimeoutDelegate;
  305. public TimerWheel.TimerDelegate fin2MslTimeoutDelegate;
  306. public AutoResetEvent tcpWriterWait;
  307. public AutoResetEvent tcpReaderWait;
  308. public AutoResetEvent tcpConnWait;
  309. public bool finacked;
  310. public bool readerWaiting;
  311. public bool writerWaiting;
  312. public bool disposed;
  313. //RFC 793
  314. //Send Sequence Variables
  315. private uint sndUna; // send unacknowledged
  316. private uint sndNxt; // send next
  317. private uint sndMax;
  318. private uint sndWnd; // send window
  319. private uint sndUp ; // send urgent pointer
  320. private uint sndWn1; // segment sequence number used for last window update
  321. private uint sndWn2; // segment acknowledgment number used for last window update
  322. private uint iss; // initial send sequence number
  323. //Receive Sequence Variables
  324. private uint rcvNxt; // receive next
  325. private uint rcvWnd; // receive window
  326. private uint rcvUp; // receive urgent pointer
  327. private uint irs; // initial receive sequence number
  328. private uint rcvAdv; //windows advertised by other end
  329. private ushort sndMss; //mss received in tcp options
  330. private long timeWaiting;
  331. #if false
  332. //XXX implement later when congestion matters
  333. //congestion control comes later
  334. private uint cwnd; //congestion control window
  335. private uint sndSSThresh; //size threshold for slow start
  336. //rtt estimators
  337. //these are set in 'ticks' of the slowTimer
  338. private short rttEstimate; //rtt estiamte
  339. private short rttSequenceNumber; //seq currently timed
  340. private short smoothedRtt; //smoothed rtt ticks x 8
  341. private short rttVariance; //ticks x 4
  342. private ushort rttMinimum; //minimum allowed rtt
  343. private uint maxSndWindow //largest window offered by peer
  344. #endif
  345. private static uint rxBufferSize; //size of buffers for apps
  346. private static uint txBufferSize;
  347. private int timeoutValue;
  348. private int maxTimeoutValue;
  349. private TContainerTcpVectorQueueByte txContainer; //send buffer
  350. private TContainerTcpVectorQueueByte rxAssemblyContainer; //reassemble out of order segments
  351. private TContainerTcpVectorQueueByte rxContainer; //receieve buffer
  352. [Conditional("DEBUG_TCP")]
  353. internal static void DebugPrint(string format,
  354. params object [] arguments)
  355. {
  356. DebugStub.Print("TCP: {0}",
  357. DebugStub.ArgList(
  358. string.Format(format, arguments))
  359. );
  360. }
  361. [Conditional("DEBUG_TCP")]
  362. internal static void DebugPrint(string format)
  363. {
  364. DebugStub.Print("TCP: {0}",
  365. DebugStub.ArgList(format));
  366. }
  367. public TcpState CurrentState
  368. {
  369. get { return currentState; }
  370. }
  371. public bool AckNow
  372. {
  373. get { return this.ackNow; }
  374. }
  375. public static void Initialize()
  376. {
  377. //initialize timer wheels
  378. fastWheel = new TimerWheel(10, 200); //200 ms per 'tick'
  379. slowWheel = new TimerWheel(500); //500ms per 'tick'
  380. //initialize chained hash tables for demultiplexing
  381. activeHash = new ChainedHash(true);
  382. passiveHash = new ChainedHash(false);
  383. rxBufferSize = DefaultRxWinSize;
  384. txBufferSize = DefaultTxWinSize;
  385. tcpPortsMtx = new MonitorLock();
  386. nextPortNumber = 1024;
  387. #if TCP_THREAD_POOL
  388. threadPool = new NetStackThreadPool(16, 16*128);
  389. #endif
  390. }
  391. #if TCP_THREAD_POOL
  392. public static NetStackThreadPool ThreadPool {
  393. get { return threadPool; }
  394. }
  395. #endif
  396. internal class ConnectionTimeout: TimerWheel.TimerDelegate
  397. {
  398. public override void Run(TCP tcpSession)
  399. {
  400. DebugStub.Print("Got connection timeout!\n");
  401. if (slowWheel.DeleteTimerEvent(ref tcpSession.retransmitTimeoutNode) == true) {
  402. DisposeTcpSession(tcpSession);
  403. }
  404. }
  405. }
  406. public class DelayAckTimeout: TimerWheel.TimerDelegate
  407. {
  408. TCP tcp;
  409. internal DelayAckTimeout(TCP tcp) {this.tcp = tcp;}
  410. public override void Run(TCP tcpSession)
  411. {
  412. DebugStub.Print("Got ack delay timeout...sending latest ack\n");
  413. if (fastWheel.DeleteTimerEvent(ref tcpSession.delayAckTimeoutNode) == true) {
  414. tcp.delayAckTimeoutNode = null;
  415. tcp.ackNow = true;
  416. tcp.SendPacket(TcpFlags.ACK, false);
  417. }
  418. }
  419. }
  420. public class RetransmitTimeout: TimerWheel.TimerDelegate
  421. {
  422. TCP tcp;
  423. internal RetransmitTimeout(TCP tcp) {this.tcp = tcp;}
  424. public override void Run(TCP tcpSession)
  425. {
  426. DebugStub.Print("Got retransmit timeout\n");
  427. if (slowWheel.DeleteTimerEvent(ref tcpSession.retransmitTimeoutNode) == true) {
  428. //set timer to new backoff value
  429. if (tcp.timeoutValue > tcp.maxTimeoutValue) {
  430. DebugStub.Print("attempted final rentransmit....FAILED!\n");
  431. DebugStub.Assert(false);
  432. DebugStub.Break();
  433. }
  434. tcp.retransmitTimeoutNode = null;
  435. tcp.timeoutValue = tcp.timeoutValue * 2;
  436. if (tcp.currentState == TcpState.SYN_SENT) {
  437. tcp.SendPacket(TcpFlags.SYN, true);
  438. }
  439. else if (tcp.currentState == TcpState.SYN_RECEIVED) {
  440. tcp.SendPacket(TcpFlags.SYN | TcpFlags.ACK, true);
  441. }
  442. else {
  443. tcp.ackNow = true;
  444. tcp.SendPacket(TcpFlags.ACK, true);
  445. }
  446. }
  447. }
  448. }
  449. internal class Fin2MslTimeout: TimerWheel.TimerDelegate
  450. {
  451. public override void Run(TCP tcpSession)
  452. {
  453. DebugPrint("Got Fin2Msl timeout!\n");
  454. if (slowWheel.DeleteTimerEvent(ref tcpSession.fin2MslTimeoutNode) == true) {
  455. tcpSession.fin2MslTimeoutNode = null;
  456. DisposeTcpSession(tcpSession);
  457. }
  458. }
  459. }
  460. public IPv4 RemoteAddress { get { return remoteIPAddress; } }
  461. public ushort RemotePort { get { return remotePort; } }
  462. public IPv4 LocalAddress { get { return localIPAddress; } }
  463. public ushort LocalPort { get { return localPort; } }
  464. public TCP()
  465. {
  466. //begin with wildcards
  467. localIPAddress = IPv4.Any;
  468. localPort = 0;
  469. remotePort = 0;
  470. remoteIPAddress = IPv4.Any;
  471. iss = nextISS;
  472. nextISS += 64000;
  473. maxSegSize = defaultMaxSegSize;
  474. maxTimeoutValue = 80000;
  475. activeOpen = false;
  476. readerWaiting = false;
  477. writerWaiting = false;
  478. disposed = false;
  479. timeWaiting = 0;
  480. //default rtt estimates
  481. #if false
  482. rttEstimate = 0;
  483. smoothedRtt = 0;
  484. rttVariance = 24;
  485. rttMinimum = 1; //minimum of 500 ms retransmission timeout... ugh
  486. currentRxt = 12; //6 second initial retransmission timeout
  487. #endif
  488. //flags
  489. noDelay = true; //ignore nagle by default for now
  490. isBound = false;
  491. isConnected = false;
  492. finacked = false;
  493. ackNow = false;
  494. needFin = false;
  495. needAck = false;
  496. //timers
  497. //Note -- we're currently not using the persist timer
  498. retransmitTimeoutNode = null;
  499. connectionTimeoutNode = null;
  500. fin2MslTimeoutNode = null;
  501. delayAckTimeoutNode = null;
  502. connectionTimeoutDelegate = new ConnectionTimeout();
  503. delayAckTimeoutDelegate = new DelayAckTimeout(this);
  504. fin2MslTimeoutDelegate = new Fin2MslTimeout();
  505. retransmitTimeoutDelegate = new RetransmitTimeout(this);
  506. listenSocket = null;
  507. //window sizes
  508. rcvWnd = DefaultRxWinSize;
  509. rcvUp = 0;
  510. //initialize the per connection buffers
  511. txContainer = new TContainerTcpVectorQueueByte(
  512. new TcpVectorQueueByte()
  513. );
  514. rxContainer = new TContainerTcpVectorQueueByte(
  515. new TcpVectorQueueByte()
  516. );
  517. rxAssemblyContainer = new TContainerTcpVectorQueueByte(
  518. new TcpVectorQueueByte()
  519. );
  520. currentState = TcpState.CLOSED;
  521. tcpWriterWait = new AutoResetEvent(false);
  522. tcpReaderWait = new AutoResetEvent(false);
  523. tcpConnWait = new AutoResetEvent(false);
  524. }
  525. //XXX Right now we get a port and then grok the hashtable
  526. //to see if it exists, and then ask for another port....
  527. private bool AssignNextAvailablePort()
  528. {
  529. using (tcpPortsMtx.Lock()) {
  530. this.localPort = nextPortNumber;
  531. //Sing# complains if we just allow it to roll over?
  532. if (nextPortNumber + 1 > MaxPortNumber) {
  533. nextPortNumber = 1;
  534. }
  535. else {
  536. nextPortNumber++;
  537. }
  538. }
  539. return true;
  540. }
  541. //add read/write/poll read etc etc etc.
  542. //write send function. make changes to Nic so that we
  543. //can get a packetfifo a fill it with everything in the send buffer
  544. //finish writing timer delegates.
  545. public bool InternalConnect(IPv4 destIP, ushort destPort)
  546. {
  547. remoteIPAddress = destIP;
  548. remotePort = destPort;
  549. return true;
  550. }
  551. //interface calls
  552. //active open
  553. public bool Connect(IPv4 destIP, ushort destPort, out TcpError error)
  554. {
  555. error = TcpError.Unknown;
  556. if (isConnected == true) {
  557. error = TcpError.AlreadyConnected;
  558. return false;
  559. }
  560. InternalConnect(destIP, destPort);
  561. if (isBound == false) {
  562. if(AssignNextAvailablePort() == false) {
  563. DebugPrint("Ran out of ports!\n");
  564. error = TcpError.ResourcesExhausted;
  565. return false;
  566. }
  567. HostConfiguration hostConfiguration = IP.GetHostConfiguration();
  568. RouteEntry e = hostConfiguration.RoutingTable.Lookup(destIP);
  569. if (e != null) {
  570. localIPAddress = e.InterfaceAddress;
  571. DebugPrint("local address now {0}\n", localIPAddress);
  572. }
  573. else {
  574. DebugPrint("No route for {0}\n", destIP);
  575. DebugStub.Assert(false);
  576. //need better errors?
  577. error = TcpError.ResourcesExhausted;
  578. return false;
  579. }
  580. }
  581. DebugPrint("Starting connection src ip {0} port {1} dest ip {2} port {3}\n",
  582. localIPAddress, localPort, destIP, destPort);
  583. chainedHashLLNode = activeHash.InsertNewSession(this);
  584. this.sndUna = iss;
  585. this.sndNxt = iss;
  586. this.sndMax = iss;;
  587. slowWheel.AddTimerEvent(75000, this,
  588. this.connectionTimeoutDelegate,
  589. ref this.connectionTimeoutNode);
  590. this.timeoutValue = 500;
  591. slowWheel.AddTimerEvent(this.timeoutValue, this,
  592. this.retransmitTimeoutDelegate,
  593. ref this.retransmitTimeoutNode);
  594. currentState = TcpState.SYN_SENT;
  595. SendPacket(TcpFlags.SYN, false);
  596. DebugPrint("Tcp.Connect...waiting for response...\n");
  597. tcpConnWait.WaitOne();
  598. DebugPrint("Connection complete!\n");
  599. activeOpen = true;
  600. return true;
  601. }
  602. public int GetNumWaitingListenSessions()
  603. {
  604. DebugPrint("GetNumWaitingListenSessions!\n");
  605. DebugStub.Break();
  606. return 0;
  607. }
  608. public bool IsDataAvailable()
  609. {
  610. bool rc;
  611. TcpVectorQueueByte rxBuffer = rxContainer.Acquire();
  612. rc = (rxBuffer.Size() > 0);
  613. rxContainer.Release(rxBuffer);
  614. return rc;
  615. }
  616. public bool Listen(int backlog)
  617. {
  618. DebugPrint("TCP: Listen\n");
  619. if (isBound == false) {
  620. DebugStub.Print("socket isn't bound...rejecting\n");
  621. return false;
  622. }
  623. if (backlog < 1 || backlog > 50) {
  624. DebugPrint("Backlog out of bounds...silently changing to 50\n");
  625. backlog = 50;
  626. }
  627. TCP tcpSession = passiveHash.GetTCPSession(this.localIPAddress, this.localPort,
  628. IPv4.Any, 0);
  629. if (tcpSession != null) {
  630. if (tcpSession.currentState != TcpState.LISTEN) {
  631. DebugStub.Print("Calling listen over existing contract.....overwriting\n");
  632. DisposeTcpSession(tcpSession);
  633. return false;
  634. } else {
  635. DebugStub.Print("Failed! Already listening....\n");
  636. return false;
  637. }
  638. }
  639. this.acceptQueue = new TCP[backlog];
  640. this.acceptQueueHead = 0;
  641. this.acceptQueueTail = 0;
  642. for (int i = 0; i < backlog; i++) {
  643. this.acceptQueue[i] = null;
  644. }
  645. this.listenSocket = null;
  646. this.acceptQueueLength = 0;
  647. this.acceptQueueMaxSize = backlog;
  648. this.chainedHashLLNode = passiveHash.InsertNewSession(this);
  649. this.activeOpen = false;
  650. this.currentState = TcpState.LISTEN;
  651. return true;
  652. }
  653. public bool Bind(IPv4 sourceAddr, ushort sourcePort)
  654. {
  655. if (sourceAddr != IPv4.Any && !IsLocalAddress(sourceAddr)) {
  656. // Invalid address
  657. //set error
  658. return false;
  659. }
  660. localIPAddress = sourceAddr;
  661. DebugPrint("TCP port bound to {0}\n", localIPAddress);
  662. //check to see if this port/ip is in use.
  663. if ((this.localPort != sourcePort) && (sourcePort != 0)) {
  664. TCP tcpSession = activeHash.GetTCPSession(sourceAddr, sourcePort,
  665. IPv4.Any, 0);
  666. if (tcpSession == null) {
  667. tcpSession = passiveHash.GetTCPSession(sourceAddr, sourcePort,
  668. IPv4.Any, 0);
  669. if (tcpSession == null) {
  670. this.localPort = sourcePort;
  671. isBound = true;
  672. return true;
  673. }
  674. }
  675. isBound = false;
  676. return false;
  677. }
  678. isBound = true;
  679. return true;
  680. }
  681. public TCP Accept()
  682. {
  683. while (this.acceptQueue[this.acceptQueueHead] == null &&
  684. this.currentState == TcpState.LISTEN) {
  685. bool rc;
  686. rc = this.tcpConnWait.WaitOne(TimeSpan.FromSeconds(5));
  687. if (rc == false) {
  688. return null;
  689. }
  690. }
  691. if (this.currentState != TcpState.LISTEN) {
  692. DebugStub.Print("ack! no longer listening!!\n");
  693. return null;
  694. }
  695. TCP newConnection = this.acceptQueue[this.acceptQueueHead];
  696. DebugStub.Assert(newConnection != null);
  697. if (newConnection == null) {
  698. DebugStub.Break();
  699. }
  700. this.acceptQueueLength--;
  701. this.acceptQueue[this.acceptQueueHead] = null;
  702. this.acceptQueueHead =
  703. (this.acceptQueueHead + 1) % this.acceptQueueMaxSize;
  704. DebugPrint("AcceptQueueHead incremented now {0} " +
  705. " acceptMaxQueueSize {1}\n",
  706. this.acceptQueueHead, this.acceptQueueMaxSize);
  707. return newConnection;
  708. }
  709. public Bytes Read()
  710. {
  711. DebugPrint("TCP: Read\n");
  712. Bytes buff;
  713. while (((buff = PopData()) == null) &&
  714. (currentState == TcpState.ESTABLISHED)) {
  715. tcpReaderWait.WaitOne();
  716. }
  717. return buff;
  718. }
  719. public Bytes[] ReadV()
  720. {
  721. DebugPrint("TCP:ReadV");
  722. Bytes[] buff = null;
  723. while (((buff = PopAllData()) == null) &&
  724. (this.currentState == TcpState.ESTABLISHED)) {
  725. this.tcpReaderWait.WaitOne();
  726. }
  727. return buff;
  728. }
  729. public Bytes PollRead(TimeSpan timeout)
  730. {
  731. Bytes buff;
  732. bool rc = true;
  733. if ((buff = PopData()) == null) {
  734. rc = tcpReaderWait.WaitOne(timeout);
  735. if (rc == true) {
  736. buff = PopData();
  737. }
  738. }
  739. return buff;
  740. }
  741. public Bytes[] PollReadV(TimeSpan timeout)
  742. {
  743. Bytes[] buff;
  744. bool rc = true;
  745. if ((buff = PopAllData()) == null) {
  746. rc = tcpReaderWait.WaitOne(timeout);
  747. if (rc == true) {
  748. buff = PopAllData();
  749. }
  750. }
  751. return buff;
  752. }
  753. public void Close()
  754. {
  755. if (currentState == TcpState.ESTABLISHED) {
  756. needFin = true;
  757. SendPacket(TcpFlags.ACK, false);
  758. }
  759. else if (currentState == TcpState.CLOSE_WAIT) {
  760. //XXX might need a timer here
  761. needFin = true;
  762. SendPacket(TcpFlags.ACK, false);
  763. }
  764. else if (currentState == TcpState.CLOSED) {
  765. DisposeTcpSession(this);
  766. }
  767. else {
  768. DebugStub.WriteLine("Got close in state {0}\n", DebugStub.ArgList(currentState));
  769. }
  770. }
  771. public bool IsLocalAddress(IPv4 ipAddress)
  772. {
  773. return IP.IsLocalAddress(ipAddress);
  774. }
  775. private static Bytes GetHeader(int size)
  776. {
  777. //DebugPrint("Allocating header size {0}\n", size);
  778. byte[] header = new byte[size];
  779. return new Bytes(header);
  780. }
  781. //XXX need offset
  782. public static void BuildHeader(Bytes header, IPv4 destAddress,
  783. IPv4 srcAddress, ushort destPort, ushort srcPort,
  784. byte flags, uint seqNumber, uint ackNumber,
  785. uint windowSize, int optlen,
  786. short payloadLength, uint urgPtr, int ipHeaderOffset)
  787. {
  788. // DebugPrint("BuildHeader optlen {0}\n", optlen);
  789. int totalLength = IpHeader.Size + TcpHeader.MinSize + payloadLength + optlen;
  790. //write ip header
  791. int offset = ipHeaderOffset;
  792. int o = ipHeaderOffset;
  793. header[o++] = 0x45; //default version 4, header_len 5
  794. header[o++] = 0; //tos
  795. header[o++] = (byte) (((ushort)totalLength) >> 8); //total length of the ip header
  796. header[o++] = (byte) (((ushort)totalLength) & 0xff);
  797. header[o++] = (byte) (((ushort)0) >> 8); //fragment id
  798. header[o++] = (byte) (((ushort)0) & 0xff); //fragment id
  799. header[o++] = (byte) (((ushort)0) >> 8); //fragment offset
  800. header[o++] = (byte) (((ushort)0) & 0xff); //fragment offset
  801. header[o++] = 128; //default ttl
  802. header[o++] = 6; // protocol ID --> tcp
  803. header[o++] = 0; //ipchecksum (fill it in later)
  804. header[o++] = 0; //ipchecksum
  805. srcAddress.CopyOut(header.Array, header.Start + o);
  806. o += IPv4.Length;
  807. // set the ip addresses
  808. destAddress.CopyOut(header.Array, header.Start + o);
  809. o += IPv4.Length;
  810. // calculate ip checksum
  811. ushort chk = IpHeader.CalculateChecksum(header, offset, IpHeader.Size);
  812. header[offset + 10] = (byte) (((ushort)chk) >> 8);
  813. header[offset + 11] = (byte) (((ushort)chk) & 0xff);
  814. //write tcp segment header
  815. header[o++] = (byte)(((ushort)srcPort) >> 8);
  816. header[o++] = (byte) (((ushort)srcPort) & 0xff);
  817. header[o++] = (byte)(((ushort)destPort) >> 8);
  818. header[o++] = (byte) (((ushort)destPort) & 0xff);
  819. header[o++] = (byte)(((uint)seqNumber) >> 24);
  820. header[o++] = (byte)(((uint)seqNumber) >> 16);
  821. header[o++] = (byte)(((uint)seqNumber) >> 8);
  822. header[o++] = (byte) (((uint)seqNumber) & 0xff);
  823. header[o++] = (byte)(((uint)ackNumber) >> 24);
  824. header[o++] = (byte)(((uint)ackNumber) >> 16);
  825. header[o++] = (byte)(((uint)ackNumber) >> 8);
  826. header[o++] = (byte) (((uint)ackNumber) & 0xff);
  827. byte off = (byte) ((TcpHeader.MinSize + optlen) >> 2);
  828. // DebugPrint("BuildHeader: offset is {0}\n", off);
  829. header[o++] = (byte) (off << (byte) 4);
  830. header[o++] = flags;
  831. header[o++] = (byte)(((ushort)windowSize) >> 8);
  832. header[o++] = (byte) (((ushort)windowSize) & 0xff);
  833. // checksum
  834. header[o++] = 0;
  835. header[o++] = 0;
  836. header[o++] = (byte)(((ushort)urgPtr) >> 8);
  837. header[o++] = (byte) (((ushort)urgPtr) & 0xff);
  838. // DebugPrint("BuildHeader: Complete\n");
  839. }
  840. public static void DeleteTimers(TCP tcpSession)
  841. {
  842. slowWheel.DeleteTimerEvent(ref tcpSession.retransmitTimeoutNode);
  843. slowWheel.DeleteTimerEvent(ref tcpSession.connectionTimeoutNode);
  844. slowWheel.DeleteTimerEvent(ref tcpSession.fin2MslTimeoutNode);
  845. fastWheel.DeleteTimerEvent(ref tcpSession.delayAckTimeoutNode);
  846. }
  847. public static void DisposeTcpSession(TCP tcpSession)
  848. {
  849. if (tcpSession.disposed == true) {
  850. return;
  851. }
  852. tcpSession.disposed = true;
  853. //check to see whether this was a passive or active open
  854. if (tcpSession.activeOpen) {
  855. activeHash.RemoveTcpSession(tcpSession, tcpSession.chainedHashLLNode);
  856. }
  857. else {
  858. passiveHash.RemoveTcpSession(tcpSession, tcpSession.chainedHashLLNode);
  859. }
  860. tcpSession.currentState = TcpState.CLOSED;
  861. DeleteTimers(tcpSession);
  862. TcpVectorQueueByte txBuffer = tcpSession.txContainer.Acquire();
  863. uint bufferSize = txBuffer.Size();
  864. if (bufferSize > 0) {
  865. txBuffer.ReleaseDataFromTxBuffer(bufferSize);
  866. txBuffer.SetTxBuffTotalOffset(tcpSession.sndNxt - tcpSession.sndUna);
  867. }
  868. tcpSession.txContainer.Release(txBuffer);
  869. }
  870. public long TimeWaiting()
  871. {
  872. return this.timeWaiting;
  873. }
  874. //app level send
  875. public int Send(Bytes data)
  876. {
  877. TcpVectorQueueByte txBuffer = txContainer.Acquire();
  878. int bytesLeft = data.Length;
  879. int rc = bytesLeft;
  880. DebugPrint("Sending {0} bytes\n", data.Length);
  881. while (bytesLeft != 0) {
  882. Bytes dataToSave = null;
  883. if (bytesLeft > (txBufferSize - txBuffer.Size())) {
  884. uint offset = txBufferSize - txBuffer.Size();
  885. if (offset > 0) {
  886. dataToSave = Bitter.SplitOff(ref data, (int) offset);
  887. txBuffer.AddTail(data, 0, offset);
  888. bytesLeft -= (int) offset;
  889. }
  890. else {
  891. dataToSave = data;
  892. }
  893. }
  894. else {
  895. txBuffer.AddTail(data, 0, (uint) bytesLeft);
  896. bytesLeft = 0;
  897. }
  898. //execute internal send
  899. if (bytesLeft > 0) {
  900. txContainer.Release(txBuffer);
  901. SendPacket(TcpFlags.ACK, false);
  902. tcpWriterWait.WaitOne();
  903. if (currentState != TcpState.ESTABLISHED) {
  904. DebugPrint("Connection closed!\n");
  905. //delete dataToSave;
  906. return -1;
  907. }
  908. txBuffer = txContainer.Acquire();
  909. VTable.Assert(dataToSave != null);
  910. data = dataToSave;
  911. dataToSave = null;
  912. }
  913. }
  914. txContainer.Release(txBuffer);
  915. SendPacket(TcpFlags.ACK, false);
  916. return rc;
  917. }
  918. public void ProcessOptions(Bytes packet, int optionsSize)
  919. {
  920. int offset = EthernetHeader.Size + IpHeader.Size + TcpHeader.MinSize;
  921. int limit = offset + optionsSize;
  922. DebugPrint("Options size {0}\n", optionsSize);
  923. for(int i = offset; i < limit; i++) {
  924. byte p = packet[i];
  925. if (p == (byte) TcpOptions.EOL || p == (byte) TcpOptions.NOP) {
  926. continue;
  927. }
  928. if (p == (byte) TcpOptions.MSS) {
  929. i++;
  930. byte s = packet[i++];
  931. DebugStub.Assert(s == 4);
  932. VTable.Assert(s == 4);
  933. byte[] toConvert;
  934. toConvert = new byte[2];
  935. toConvert[0] = packet[i++];
  936. toConvert[1] = packet[i];
  937. ushort mss;
  938. mss = NetworkBitConverter.ToUInt16(toConvert, 0);
  939. DebugPrint("Reveived MSS option of {0}\n", mss);
  940. sndMss = mss;
  941. }
  942. else {
  943. byte s = packet[i + 1];
  944. DebugPrint("Unhandled options: {0} size {1}...\n", (int)p, (int)s);
  945. i += ((int)s-1);
  946. }
  947. }
  948. }
  949. public static void SendError(IpHeader ipHeader, TcpHeader tcpHeader, TcpFlags flags)
  950. {
  951. Bytes header;
  952. Bytes ethernetHeader;
  953. if (flags == TcpFlags.RST) {
  954. DebugPrint("Sending RST\n");
  955. header = new Bytes(new byte[IpHeader.Size + TcpHeader.MinSize]);
  956. ethernetHeader = new Bytes(new byte[EthernetHeader.Size]);
  957. BuildHeader (header, ipHeader.destAddress, ipHeader.srcAddress,
  958. tcpHeader.destPort, tcpHeader.srcPort, (byte) TcpFlags.RST,
  959. tcpHeader.ackNumber, 0, 0, 0, 0, 0, 0);
  960. TcpHeader.SetTcpChecksum(header, 0, TcpHeader.MinSize);
  961. IP.SendOutgoingPacket(ethernetHeader, header, ipHeader.srcAddress);
  962. }
  963. else if (((TcpFlags.RST & flags) != 0) &&
  964. ((TcpFlags.ACK & flags) != 0)) {
  965. DebugPrint("Sending RST | ACK\n");
  966. header = new Bytes(new byte[IpHeader.Size + TcpHeader.MinSize]);
  967. ethernetHeader = new Bytes(new byte[EthernetHeader.Size]);
  968. uint headerLength = (uint) (EthernetHeader.Size + IpHeader.Size + tcpHeader.dataOffset);
  969. uint segmentLength = (uint) (ipHeader.totalLength - IpHeader.Size) - headerLength;
  970. BuildHeader (header, ipHeader.destAddress, ipHeader.srcAddress, tcpHeader.destPort,
  971. tcpHeader.srcPort, (byte) (TcpFlags.RST | TcpFlags.ACK),
  972. 0, tcpHeader.seqNumber + segmentLength, 0, 0, 0, 0, 0);
  973. }
  974. else {
  975. DebugStub.Assert(false);
  976. }
  977. }
  978. private Bytes GetNextBuffer(out uint seqNmb)
  979. {
  980. TcpVectorQueueByte txBuffer = txContainer.Acquire();
  981. uint txBufferOffset = (this.sndNxt - this.sndUna);
  982. uint numBytes = (txBuffer.Size() - txBufferOffset);
  983. DebugPrint("size {0} offset {1} bytes {2}\n",
  984. txBuffer.Size(), txBufferOffset, numBytes);
  985. seqNmb = this.sndNxt;
  986. if (txBuffer.Size() == 0 || numBytes == 0 ||
  987. (this.sndWnd < 2*this.sndMss)) {
  988. txContainer.Release(txBuffer);
  989. return null;
  990. }
  991. int bufLen;
  992. int bufStart;
  993. int bufByteToSend;
  994. Bytes targetBuffer = txBuffer.GetCurrentBuffer(out bufStart, out bufLen, out bufByteToSend,
  995. (int) txBufferOffset);
  996. //"bufStart" represents the oficial start of the buffer (unacknowledged data to be sent
  997. //"bufByteToSend" represents that next byte to be sent
  998. if (bufByteToSend != bufStart) {
  999. if (bufByteToSend < bufStart) {
  1000. DebugStub.Break();
  1001. }
  1002. bufLen = bufLen - (bufByteToSend - bufStart);
  1003. }
  1004. //we ended at the end of a previous buffer
  1005. //on a new buffer, bufByteToSend is always equal to bufStart
  1006. if (bufLen == 0) {
  1007. targetBuffer = txBuffer.GetNextBuffer(out bufStart, out bufLen);
  1008. DebugPrint("GetNextBuffer result start {0} length {1}\n",
  1009. bufStart, bufLen);
  1010. bufByteToSend = bufStart;
  1011. }
  1012. DebugPrint("GetCurrentBuffer result start {0} length {1} offset {2}\n",
  1013. bufStart, bufLen, bufByteToSend);
  1014. DebugStub.Assert(targetBuffer != null);
  1015. VTable.Assert(targetBuffer != null);
  1016. int pcktLen = Math.Min((int) numBytes, this.sndMss);
  1017. if(pcktLen > 1460 || pcktLen < 0) {
  1018. DebugStub.WriteLine("sndNxt {0} sndUna {1} sndMax {2} buffer size {3}",
  1019. DebugStub.ArgList(this.sndNxt, this.sndUna, this.sndMax, txBuffer.Size()));
  1020. DebugStub.WriteLine("bufStart {0} bufLen {1} bufByteToSend{2}",
  1021. DebugStub.ArgList(bufStart, bufLen, bufByteToSend));
  1022. DebugStub.Break();
  1023. }
  1024. Bytes cpBuf = new Bytes(new byte[pcktLen]);
  1025. while(pcktLen > 0) {
  1026. //copy the lesser of the space left in
  1027. //the send buffer and the space left in the packet
  1028. int cpLen = Math.Min(bufLen, pcktLen);
  1029. int cpOff = 0;
  1030. try {
  1031. Bitter.Copy(cpBuf, cpOff, cpLen, targetBuffer, (int) bufByteToSend);
  1032. }
  1033. catch (Exception e) {
  1034. DebugStub.Print("copy failed exception {0}\n", DebugStub.ArgList(e.ToString()));
  1035. DebugStub.Print("cpOff {0} cplen {1} cp.Len {2} targetoffset {3} targetlen {4}length {5}\n",
  1036. DebugStub.ArgList(cpOff, cpLen, cpBuf.Length, bufByteToSend, (targetBuffer).Length,
  1037. pcktLen));
  1038. DebugStub.Break();
  1039. DebugStub.Assert(false);
  1040. VTable.Assert(false);
  1041. }
  1042. // DebugStub.WriteLine("pcktLen {0} cpOff {1} bufLen {2} cpLen {3}\n",
  1043. // DebugStub.ArgList(pcktLen, cpOff, bufLen, cpLen));
  1044. pcktLen -= cpLen;
  1045. cpOff += cpLen;
  1046. bufLen -= cpLen;
  1047. //we've run out of buffer to copy from and we still have space
  1048. //in this packet...
  1049. if (bufLen == 0) {
  1050. txBuffer.AdvanceBufferOffset((uint) cpLen);
  1051. if (pcktLen != 0) {
  1052. targetBuffer = txBuffer.GetNextBuffer(out bufStart, out bufLen);
  1053. DebugPrint("GetNextBuffer result start {0} length {1}\n",
  1054. bufStart, bufLen);
  1055. bufByteToSend = bufStart;
  1056. }
  1057. }
  1058. else {
  1059. txBuffer.AdvanceBufferOffset((uint) cpLen);
  1060. bufByteToSend += cpLen;
  1061. }
  1062. }
  1063. this.sndWnd -= (uint) cpBuf.Length;
  1064. this.sndNxt += (uint) cpBuf.Length;
  1065. if (TcpHeader.SeqGreater(this.sndNxt, this.sndMax)) {
  1066. this.sndMax = this.sndNxt;
  1067. }
  1068. txBuffer.SetTxBuffTotalOffset(this.sndNxt - this.sndUna);
  1069. txContainer.Release(txBuffer);
  1070. return cpBuf;
  1071. }
  1072. public void SendPacket(TcpFlags flags, bool resetSndNxt)
  1073. {
  1074. using (thisLock.Lock()) {
  1075. //three factors decide how many packets we generate
  1076. //1) the amount of data waiting to be sent in the txBuffer
  1077. //2) how many bytes are available in the remote window
  1078. //3) the number of packets available in the NIC
  1079. //we want the min of these three
  1080. bool dataToSend = false;
  1081. if (resetSndNxt == true) {
  1082. sndNxt = sndUna;
  1083. DebugStub.Print("SendPacket: sendWnd {0} rcvNxt {1} rcvWnd {2} " +
  1084. " flags 0x{4:x}, resetSndNxt {5}\n",
  1085. DebugStub.ArgList(this.sndWnd, this.rcvNxt, this.rcvWnd,
  1086. (uint) flags, sndNxt, resetSndNxt));
  1087. }
  1088. DebugPrint("SendPacket: sendWnd {0} rcvNxt {1} rcvWnd {2} " +
  1089. " flags 0x{3:x}, resetSndNxt {4}\n",
  1090. this.sndWnd, this.rcvNxt, this.rcvWnd, (uint) flags, sndNxt);
  1091. #if false
  1092. int bufferedBytes = (int) Math.Min (sndWnd, txBuffer.Size() - txBufferOffset);
  1093. if (bufferedBytes == 0 &&
  1094. sndWnd == 0 &&
  1095. TcpHeader.SeqLess(sndNxt, sndMax)) {
  1096. DebugStub.Print("zero window but resending....\n");
  1097. bufferedBytes = (int) (txBuffer.Size() - txBufferOffset);
  1098. }
  1099. #endif
  1100. uint seqNmb = 0;
  1101. Bytes pckt = GetNextBuffer(out seqNmb);
  1102. bool ackOnly = false;
  1103. while(pckt != null) {
  1104. dataToSend = true;
  1105. Bytes header =

Large files files are truncated, but you can click here to view the full file