UDP.Mod 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE UDP; (** AUTHOR "pjm, mvt"; PURPOSE "UDP protocol"; *)
  3. (*
  4. UDP Header
  5. 00 16 source port
  6. 02 16 destination port
  7. 04 16 UDP length (header and data)
  8. 06 16 UDP checksum (pseudo-header, header and data)
  9. 08 -- optional data
  10. UDP Pseudo-header (for checksum calculation)
  11. 00 32 source address
  12. 04 32 destination address
  13. 08 08 zero = 0
  14. 09 08 protocol = 17
  15. 10 16 UDP length (duplicate)
  16. Notes:
  17. o Bit numbers above are Intel bit order.
  18. o Avoid use of SET because of PPC bit numbering issues.
  19. o Always access fields as 8-, 16- or 32-bit values and use DIV, MOD, ASH, ODD for bit access.
  20. *)
  21. IMPORT Modules, Machine, Objects, Network, IP, ICMP;
  22. CONST
  23. (** Error codes *)
  24. Ok* = 0;
  25. PortInUse* = 3501;
  26. Timeout* = 3502;
  27. BufferOverflow* = 3503;
  28. NoInterface* = 3504;
  29. Closed* = 3505;
  30. NilPort* = 0;
  31. IPTypeUDP = 17; (* UDP type code for IP packets *)
  32. UDPHdrLen = 8;
  33. MaxPseudoHdrLen = 40; (* IPv4: 12, IPv6: 40 *)
  34. MaxUDPDataLen = 10000H-UDPHdrLen;
  35. MinEphemeralPort = 1024;
  36. MaxEphemeralPort = 5000;
  37. QueueSize = 40; (* size (number of packets) of receive queue per socket *)
  38. HashTableSize = 128; (* size of connection lookup hash table *)
  39. TYPE
  40. (** Socket. Stores the state of a UDP communication endpoint. *)
  41. Socket* = OBJECT
  42. VAR
  43. next: Socket; (* link for socket pool *)
  44. lport: LONGINT; (* local port *)
  45. hdr: ARRAY UDPHdrLen OF CHAR; (* UDP prototype header for sending *)
  46. pseudoHdr: ARRAY MaxPseudoHdrLen OF CHAR; (* pseudo header for calculating checksum *)
  47. (* Receive queue (ring buffer) *)
  48. queue: ARRAY QueueSize OF Network.Buffer;
  49. queueFirst: LONGINT; (* index where the new items are queued *)
  50. queueLast: LONGINT; (* index where the items are removed from the queued *)
  51. (* Variables for handling timeout *)
  52. timer: Objects.Timer;
  53. timeout, open: BOOLEAN;
  54. (** Constructor *)
  55. PROCEDURE &Open*(lport: LONGINT; VAR res: WORD);
  56. BEGIN
  57. open := TRUE;
  58. ASSERT((lport >= 0) & (lport < 10000H));
  59. SELF.lport := lport;
  60. IF pool.AddSocket(SELF) THEN
  61. (* set first part of UDP header *)
  62. Network.PutNet2(hdr, 0, SELF.lport);
  63. (* set up buffering and blocking *)
  64. queueFirst := 0;
  65. queueLast := 0;
  66. NEW(timer);
  67. res := Ok;
  68. ELSE
  69. res := PortInUse;
  70. END
  71. END Open;
  72. (** Send a UDP datagram to the foreign address specified by "fip" and "fport".
  73. The data is in "data[ofs..ofs+len-1]". In case of concurrent sends the datagrams are serialized. *)
  74. PROCEDURE Send*(fip: IP.Adr; fport: LONGINT; CONST data: ARRAY OF CHAR; ofs, len: LONGINT; VAR res: WORD);
  75. VAR
  76. int: IP.Interface;
  77. BEGIN {EXCLUSIVE}
  78. ASSERT((fport >= 0) & (fport < 10000H));
  79. ASSERT((len >= 0) & (len <= MaxUDPDataLen));
  80. int := IP.InterfaceByDstIP(fip);
  81. IF int # NIL THEN
  82. DoSend(int, fip, fport, data, ofs, len);
  83. res := Ok;
  84. ELSE
  85. res := NoInterface;
  86. END;
  87. END Send;
  88. (** Send a broadcast UDP datagram via interface "int" to port "lport". Normally only used by DHCP.
  89. The data is in "data[ofs..ofs+len-1]". In case of concurrent sends the datagrams are serialized. *)
  90. PROCEDURE SendBroadcast*(int: IP.Interface; fport: LONGINT; CONST data: ARRAY OF CHAR; ofs, len: LONGINT);
  91. BEGIN {EXCLUSIVE}
  92. ASSERT((fport >= 0) & (fport < 10000H));
  93. ASSERT((len >= 0) & (len <= MaxUDPDataLen));
  94. DoSend(int, int.broadAdr, fport, data, ofs, len);
  95. END SendBroadcast;
  96. (** Receive a UDP datagram. If none is available, wait up to the specified timeout for one to arrive.
  97. "data[ofs..ofs+size-1]" is the data buffer to hold the returned datagram.
  98. "ms" is a wait timeout value in milliseconds, 0 means "don't wait", -1 means "infinite wait".
  99. On return, "fip" and "fport" hold the foreign address and port.
  100. "len" returns the actual datagram size and "data[ofs..ofs+len-1]" returns the data.
  101. "res" returns "Timeout" in case of a timeout and "BufferOverflow" if the received datagram was too big.
  102. *)
  103. PROCEDURE Receive*(VAR data: ARRAY OF CHAR; ofs, size, ms: LONGINT; VAR fip: IP.Adr; VAR fport, len: LONGINT; VAR res: WORD);
  104. VAR
  105. buffer: Network.Buffer;
  106. fragmentBuffer: Network.Buffer;
  107. fragmentOffset: LONGINT;
  108. BEGIN {EXCLUSIVE}
  109. IF ~open THEN res := Closed; RETURN END;
  110. IF queueFirst = queueLast THEN
  111. (* queue empty *)
  112. IF ms > 0 THEN
  113. timeout := FALSE;
  114. Objects.SetTimeout(timer, DoTimeout, ms);
  115. AWAIT((queueFirst # queueLast) OR timeout OR ~open);
  116. IF ~open THEN res := Closed; RETURN END;
  117. IF timeout THEN
  118. res := Timeout;
  119. RETURN;
  120. ELSE
  121. Objects.CancelTimeout(timer)
  122. (* now we can continue *)
  123. END;
  124. ELSIF ms = -1 THEN
  125. (* infinite wait *)
  126. AWAIT((queueFirst # queueLast) OR ~ open);
  127. IF ~open THEN res := Closed; RETURN END;
  128. ELSE
  129. res := Timeout;
  130. RETURN;
  131. END;
  132. END;
  133. (* Here we can get a packet from the queue *)
  134. buffer := queue[queueLast];
  135. queueLast := (queueLast + 1) MOD QueueSize;
  136. fip := IP.SrcAdrFromBuffer(buffer);
  137. fport := Network.GetNet2(buffer.data, buffer.ofs);
  138. fragmentBuffer := buffer;
  139. len := 0;
  140. WHILE fragmentBuffer # NIL DO
  141. INC(len, fragmentBuffer.len);
  142. fragmentBuffer := fragmentBuffer.nextFragment;
  143. END;
  144. DEC(len, UDPHdrLen);
  145. IF len > size THEN
  146. (* packet too big for receive buffer *)
  147. res := BufferOverflow;
  148. ELSE
  149. Network.Copy(buffer.data, data, buffer.ofs+UDPHdrLen, ofs, buffer.len - UDPHdrLen);
  150. fragmentOffset := ofs + buffer.len - UDPHdrLen;
  151. fragmentBuffer := buffer.next;
  152. WHILE fragmentBuffer # NIL DO
  153. Network.Copy(fragmentBuffer.data, data, buffer.ofs, fragmentOffset, buffer.len);
  154. INC(fragmentOffset, buffer.len);
  155. fragmentBuffer := fragmentBuffer.nextFragment;
  156. END;
  157. res := Ok;
  158. END;
  159. Network.ReturnBuffer(buffer);
  160. END Receive;
  161. (* Internal send operation. Called from "Send" and "SendBroadcast". *)
  162. PROCEDURE DoSend(int: IP.Interface; fip: IP.Adr; fport: LONGINT; CONST data: ARRAY OF CHAR; ofs, len: LONGINT);
  163. VAR
  164. sum: LONGINT;
  165. pseudoHdrLen: LONGINT;
  166. BEGIN
  167. (* set UDP header *)
  168. Network.PutNet2(hdr, 2, fport); (* foreign port *)
  169. Network.PutNet2(hdr, 4, len+UDPHdrLen); (* UPD length *)
  170. Network.Put2(hdr, 6, 0); (* checksum := 0 *)
  171. IF ~(Network.ChecksumUDP IN int.dev.calcChecksum) THEN
  172. (* set pseudo header *)
  173. pseudoHdrLen := int.WritePseudoHeader(pseudoHdr, int.localAdr, fip, IPTypeUDP, len+UDPHdrLen);
  174. sum := IP.Checksum1(pseudoHdr, 0, pseudoHdrLen, 0);
  175. sum := IP.Checksum1(hdr, 0, UDPHdrLen, sum);
  176. sum := IP.Checksum2(data, ofs, len, sum);
  177. Network.Put2(hdr, 6, sum); (* checksum := sum *)
  178. END;
  179. int.Send(IPTypeUDP, fip, hdr, data, UDPHdrLen, ofs, len, IP.MaxTTL);
  180. END DoSend;
  181. (* Handle timeout call from Objects *)
  182. PROCEDURE DoTimeout;
  183. BEGIN {EXCLUSIVE}
  184. timeout := TRUE;
  185. END DoTimeout;
  186. (* Input a datagram on this socket. *)
  187. PROCEDURE Input(fip: IP.Adr; buffer: Network.Buffer);
  188. BEGIN {EXCLUSIVE}
  189. IF (queueLast - queueFirst) MOD QueueSize = 1 THEN
  190. (* queue full - discard packet and return buffer *)
  191. Machine.AtomicInc(NUDPQueueOverflow);
  192. Network.ReturnBuffer(buffer);
  193. ELSE
  194. queue[queueFirst] := buffer;
  195. queueFirst := (queueFirst + 1) MOD QueueSize;
  196. Machine.AtomicInc(NUDPQueued);
  197. END;
  198. END Input;
  199. (** Close the Socket, freeing its address for re-use. *)
  200. PROCEDURE Close*;
  201. BEGIN {EXCLUSIVE}
  202. pool.RemoveSocket(SELF);
  203. Objects.CancelTimeout(timer);
  204. open := FALSE;
  205. (* return all queued buffers *)
  206. WHILE queueFirst # queueLast DO
  207. Network.ReturnBuffer(queue[queueLast]);
  208. queueLast := (queueLast + 1) MOD QueueSize;
  209. END;
  210. (* do not touch any other fields, as instance may still be in use via pool.Lookup. *)
  211. END Close;
  212. END Socket;
  213. (* Socket pool *)
  214. SocketPool = OBJECT
  215. VAR
  216. table: ARRAY HashTableSize OF Socket;
  217. eport: LONGINT;
  218. (* Initialize the pool. *)
  219. PROCEDURE &Init*;
  220. VAR i: LONGINT;
  221. BEGIN
  222. FOR i := 0 TO HashTableSize-1 DO
  223. table[i] := NIL;
  224. END;
  225. eport := MinEphemeralPort;
  226. END Init;
  227. (* Look for the specified Socket *)
  228. PROCEDURE Lookup(lport: LONGINT): Socket;
  229. VAR item: Socket;
  230. BEGIN
  231. item := table[lport MOD HashTableSize];
  232. WHILE (item # NIL) & (item.lport # lport) DO
  233. item := item.next;
  234. END;
  235. RETURN item;
  236. END Lookup;
  237. (* Add a socket to the pool. If lport is NilPort, an ephemeral port is assigned. *)
  238. PROCEDURE AddSocket(p: Socket): BOOLEAN;
  239. VAR
  240. ok: BOOLEAN;
  241. i, sport: LONGINT;
  242. BEGIN {EXCLUSIVE}
  243. IF p.lport = NilPort THEN
  244. (* find an unused ephemeral port *)
  245. sport := eport; (* store port where the search started *)
  246. REPEAT
  247. p.lport := eport;
  248. (* check if port is in use *)
  249. ok := (Lookup(eport) = NIL);
  250. INC(eport);
  251. IF eport > MaxEphemeralPort THEN
  252. eport := MinEphemeralPort;
  253. END;
  254. UNTIL ok OR (eport = sport);
  255. (* ok is TRUE here if the port is not used yet *)
  256. ELSE
  257. (* ensure port is not in use *)
  258. ok := (Lookup(p.lport) = NIL);
  259. END;
  260. IF ok THEN
  261. i := p.lport MOD HashTableSize;
  262. p.next := table[i];
  263. table[i] := p;
  264. END;
  265. RETURN ok;
  266. END AddSocket;
  267. (* Remove the Socket from the pool, making its address re-usable. *)
  268. PROCEDURE RemoveSocket(p: Socket);
  269. VAR
  270. i: LONGINT;
  271. item: Socket;
  272. BEGIN {EXCLUSIVE}
  273. i := p.lport MOD HashTableSize;
  274. IF table[i] = NIL THEN
  275. (* not found *)
  276. ELSIF table[i] = p THEN
  277. table[i] := table[i].next;
  278. ELSE
  279. item := table[i];
  280. WHILE (item.next # NIL) & (item.next # p) DO
  281. item := item.next;
  282. END;
  283. IF item.next # NIL THEN
  284. item.next := item.next.next;
  285. END;
  286. END;
  287. (* do not clear p.next, because Lookup may be looking at it *)
  288. END RemoveSocket;
  289. (* Close all sockets that are registered in pool *)
  290. PROCEDURE CloseAll;
  291. VAR i: LONGINT;
  292. BEGIN
  293. FOR i := 0 TO HashTableSize-1 DO
  294. WHILE table[i] # NIL DO
  295. table[i].Close();
  296. END;
  297. END;
  298. END CloseAll;
  299. END SocketPool;
  300. VAR
  301. (* Module variables *)
  302. pool: SocketPool;
  303. (* Statistic variables *)
  304. NUDPRcvTotal-, NUDPTooSmall-, NUDPBadChecksum-, NUDPRcvBroadcast-, NUDPUnknownPort-,
  305. NUDPQueued-, NUDPQueueOverflow-, NUDPTrim-, NUDPBadHdrLen-: LONGINT;
  306. (* Receive a UDP datagram. *)
  307. PROCEDURE Input(int: IP.Interface; type: LONGINT; fip, lip: IP.Adr; buffer: Network.Buffer);
  308. VAR
  309. (* pseudo header for calculating checksum *)
  310. pseudoHdr: ARRAY MaxPseudoHdrLen OF CHAR;
  311. pseudoHdrLen: LONGINT;
  312. sum, tlen: LONGINT;
  313. s: Socket;
  314. reassembledLength: LONGINT;
  315. fragmentBuffer: Network.Buffer;
  316. BEGIN
  317. Machine.AtomicInc(NUDPRcvTotal);
  318. IF buffer.len >= UDPHdrLen THEN
  319. tlen := Network.GetNet2(buffer.data, buffer.ofs+4);
  320. IF (tlen >= UDPHdrLen) & (tlen <= buffer.len) THEN
  321. IF tlen < buffer.len THEN
  322. (* size not used *)
  323. Machine.AtomicInc(NUDPTrim);
  324. buffer.len := tlen;
  325. END;
  326. IF Network.ChecksumUDP IN buffer.calcChecksum THEN
  327. sum := 0;
  328. ELSE
  329. sum := Network.Get2(buffer.data, buffer.ofs+6); (* get checksum from header *)
  330. END;
  331. IF sum # 0 THEN
  332. (* calculate checksum *)
  333. (* set pseudo header *)
  334. reassembledLength := 0;
  335. fragmentBuffer := buffer;
  336. WHILE fragmentBuffer # NIL DO
  337. INC(reassembledLength, fragmentBuffer.len);
  338. fragmentBuffer := fragmentBuffer.nextFragment;
  339. END;
  340. pseudoHdrLen := int.WritePseudoHeader(pseudoHdr, fip, lip, IPTypeUDP, reassembledLength);
  341. sum := IP.Checksum1(pseudoHdr, 0, pseudoHdrLen, 0);
  342. IF buffer.nextFragment # NIL THEN
  343. (* fragmented packets *)
  344. fragmentBuffer := buffer;
  345. WHILE fragmentBuffer.nextFragment # NIL DO
  346. sum := IP.Checksum1(fragmentBuffer.data, fragmentBuffer.ofs, fragmentBuffer.len, sum);
  347. fragmentBuffer := fragmentBuffer.nextFragment;
  348. END;
  349. sum := IP.Checksum2(fragmentBuffer.data, fragmentBuffer.ofs, fragmentBuffer.len, sum);
  350. ELSE
  351. sum := IP.Checksum2(buffer.data, buffer.ofs, buffer.len, sum);
  352. END;
  353. END;
  354. IF sum = 0 THEN
  355. s := pool.Lookup(Network.GetNet2(buffer.data, buffer.ofs+2));
  356. IF s # NIL THEN
  357. s.Input(fip, buffer);
  358. (* Exit here w/o returning buffer because it is passed to Socket.Input *)
  359. RETURN;
  360. ELSIF ~int.IsBroadcast(lip) THEN
  361. Machine.AtomicInc(NUDPUnknownPort);
  362. ICMP.SendICMP (ICMP.ICMPDstUnreachable, fip, buffer);
  363. END;
  364. ELSE
  365. Machine.AtomicInc(NUDPBadChecksum);
  366. END;
  367. ELSE
  368. Machine.AtomicInc(NUDPBadHdrLen);
  369. END;
  370. ELSE
  371. Machine.AtomicInc(NUDPTooSmall);
  372. END;
  373. (* Exit and return buffer here because it is no longer used *)
  374. Network.ReturnBuffer(buffer);
  375. END Input;
  376. PROCEDURE Cleanup;
  377. BEGIN
  378. IP.RemoveReceiver(IPTypeUDP);
  379. pool.CloseAll();
  380. END Cleanup;
  381. BEGIN
  382. NEW(pool);
  383. IP.InstallReceiver(IPTypeUDP, Input);
  384. Modules.InstallTermHandler(Cleanup);
  385. END UDP.
  386. (*
  387. History:
  388. 27.10.2003 mvt Complete internal redesign for new interfaces of Network and IP.
  389. 22.11.2003 mvt Changed SocketPool to work with a hash table.
  390. 02.05.2005 eb Works with fragmented packets & IPv6 ready (WritePseudoHdr)
  391. *)