clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.IO;
32 using System.Net;
33 using System.Net.Sockets;
34 using System.Reflection;
35 using System.Threading;
36 using log4net;
37 using NDesk.Options;
38 using Nini.Config;
39 using OpenMetaverse.Packets;
40 using OpenSim.Framework;
41 using OpenSim.Framework.Console;
42 using OpenSim.Framework.Monitoring;
43 using OpenSim.Region.Framework.Scenes;
44 using OpenMetaverse;
45  
46 using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket;
47  
48 namespace OpenSim.Region.ClientStack.LindenUDP
49 {
50 /// <summary>
51 /// A shim around LLUDPServer that implements the IClientNetworkServer interface
52 /// </summary>
53 public sealed class LLUDPServerShim : IClientNetworkServer
54 {
55 LLUDPServer m_udpServer;
56  
57 public LLUDPServerShim()
58 {
59 }
60  
61 public void Initialise(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
62 {
63 m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager);
64 }
65  
66 public void AddScene(IScene scene)
67 {
68 m_udpServer.AddScene(scene);
69  
70 StatsManager.RegisterStat(
71 new Stat(
72 "ClientLogoutsDueToNoReceives",
73 "Number of times a client has been logged out because no packets were received before the timeout.",
74 "",
75 "",
76 "clientstack",
77 scene.Name,
78 StatType.Pull,
79 MeasuresOfInterest.None,
80 stat => stat.Value = m_udpServer.ClientLogoutsDueToNoReceives,
81 StatVerbosity.Debug));
82  
83 StatsManager.RegisterStat(
84 new Stat(
85 "IncomingUDPReceivesCount",
86 "Number of UDP receives performed",
87 "",
88 "",
89 "clientstack",
90 scene.Name,
91 StatType.Pull,
92 MeasuresOfInterest.AverageChangeOverTime,
93 stat => stat.Value = m_udpServer.UdpReceives,
94 StatVerbosity.Debug));
95  
96 StatsManager.RegisterStat(
97 new Stat(
98 "IncomingPacketsProcessedCount",
99 "Number of inbound LL protocol packets processed",
100 "",
101 "",
102 "clientstack",
103 scene.Name,
104 StatType.Pull,
105 MeasuresOfInterest.AverageChangeOverTime,
106 stat => stat.Value = m_udpServer.IncomingPacketsProcessed,
107 StatVerbosity.Debug));
108  
109 StatsManager.RegisterStat(
110 new Stat(
111 "IncomingPacketsMalformedCount",
112 "Number of inbound UDP packets that could not be recognized as LL protocol packets.",
113 "",
114 "",
115 "clientstack",
116 scene.Name,
117 StatType.Pull,
118 MeasuresOfInterest.AverageChangeOverTime,
119 stat => stat.Value = m_udpServer.IncomingMalformedPacketCount,
120 StatVerbosity.Info));
121  
122 StatsManager.RegisterStat(
123 new Stat(
124 "IncomingPacketsOrphanedCount",
125 "Number of inbound packets that were not initial connections packets and could not be associated with a viewer.",
126 "",
127 "",
128 "clientstack",
129 scene.Name,
130 StatType.Pull,
131 MeasuresOfInterest.AverageChangeOverTime,
132 stat => stat.Value = m_udpServer.IncomingOrphanedPacketCount,
133 StatVerbosity.Info));
134  
135 StatsManager.RegisterStat(
136 new Stat(
137 "IncomingPacketsResentCount",
138 "Number of inbound packets that clients indicate are resends.",
139 "",
140 "",
141 "clientstack",
142 scene.Name,
143 StatType.Pull,
144 MeasuresOfInterest.AverageChangeOverTime,
145 stat => stat.Value = m_udpServer.IncomingPacketsResentCount,
146 StatVerbosity.Debug));
147  
148 StatsManager.RegisterStat(
149 new Stat(
150 "OutgoingUDPSendsCount",
151 "Number of UDP sends performed",
152 "",
153 "",
154 "clientstack",
155 scene.Name,
156 StatType.Pull,
157 MeasuresOfInterest.AverageChangeOverTime,
158 stat => stat.Value = m_udpServer.UdpSends,
159 StatVerbosity.Debug));
160  
161 StatsManager.RegisterStat(
162 new Stat(
163 "OutgoingPacketsResentCount",
164 "Number of packets resent because a client did not acknowledge receipt",
165 "",
166 "",
167 "clientstack",
168 scene.Name,
169 StatType.Pull,
170 MeasuresOfInterest.AverageChangeOverTime,
171 stat => stat.Value = m_udpServer.PacketsResentCount,
172 StatVerbosity.Debug));
173  
174 StatsManager.RegisterStat(
175 new Stat(
176 "AverageUDPProcessTime",
177 "Average number of milliseconds taken to process each incoming UDP packet in a sample.",
178 "This is for initial receive processing which is separate from the later client LL packet processing stage.",
179 "ms",
180 "clientstack",
181 scene.Name,
182 StatType.Pull,
183 MeasuresOfInterest.None,
184 stat => stat.Value = m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond,
185 // stat =>
186 // stat.Value = Math.Round(m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond, 7),
187 StatVerbosity.Debug));
188 }
189  
190 public bool HandlesRegion(Location x)
191 {
192 return m_udpServer.HandlesRegion(x);
193 }
194  
195 public void Start()
196 {
197 m_udpServer.Start();
198 }
199  
200 public void Stop()
201 {
202 m_udpServer.Stop();
203 }
204 }
205  
206 /// <summary>
207 /// The LLUDP server for a region. This handles incoming and outgoing
208 /// packets for all UDP connections to the region
209 /// </summary>
210 public class LLUDPServer : OpenSimUDPBase
211 {
212 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
213  
214 /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary>
215 public const int MTU = 1400;
216  
217 /// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary>
218 public int ClientLogoutsDueToNoReceives { get; private set; }
219  
220 /// <summary>
221 /// Default packet debug level given to new clients
222 /// </summary>
223 public int DefaultClientPacketDebugLevel { get; set; }
224  
225 /// <summary>The measured resolution of Environment.TickCount</summary>
226 public readonly float TickCountResolution;
227  
228 /// <summary>Number of prim updates to put on the queue each time the
229 /// OnQueueEmpty event is triggered for updates</summary>
230 public readonly int PrimUpdatesPerCallback;
231  
232 /// <summary>Number of texture packets to put on the queue each time the
233 /// OnQueueEmpty event is triggered for textures</summary>
234 public readonly int TextureSendLimit;
235  
236 /// <summary>Handlers for incoming packets</summary>
237 //PacketEventDictionary packetEvents = new PacketEventDictionary();
238 /// <summary>Incoming packets that are awaiting handling</summary>
239 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
240  
241 /// <summary></summary>
242 //private UDPClientCollection m_clients = new UDPClientCollection();
243 /// <summary>Bandwidth throttle for this UDP server</summary>
244 protected TokenBucket m_throttle;
245  
246 /// <summary>Bandwidth throttle rates for this UDP server</summary>
247 public ThrottleRates ThrottleRates { get; private set; }
248  
249 /// <summary>Manages authentication for agent circuits</summary>
250 private AgentCircuitManager m_circuitManager;
251  
252 /// <summary>Reference to the scene this UDP server is attached to</summary>
253 public Scene Scene { get; private set; }
254  
255 /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
256 private Location m_location;
257  
258 /// <summary>The size of the receive buffer for the UDP socket. This value
259 /// is passed up to the operating system and used in the system networking
260 /// stack. Use zero to leave this value as the default</summary>
261 private int m_recvBufferSize;
262  
263 /// <summary>Flag to process packets asynchronously or synchronously</summary>
264 private bool m_asyncPacketHandling;
265  
266 /// <summary>Tracks whether or not a packet was sent each round so we know
267 /// whether or not to sleep</summary>
268 private bool m_packetSent;
269  
270 /// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
271 private int m_elapsedMSSinceLastStatReport = 0;
272  
273 /// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
274 private int m_tickLastOutgoingPacketHandler;
275  
276 /// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
277 private int m_elapsedMSOutgoingPacketHandler;
278  
279 /// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
280 private int m_elapsed100MSOutgoingPacketHandler;
281  
282 /// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
283 private int m_elapsed500MSOutgoingPacketHandler;
284  
285 /// <summary>Flag to signal when clients should check for resends</summary>
286 protected bool m_resendUnacked;
287  
288 /// <summary>Flag to signal when clients should send ACKs</summary>
289 protected bool m_sendAcks;
290  
291 /// <summary>Flag to signal when clients should send pings</summary>
292 protected bool m_sendPing;
293  
294 /// <summary>
295 /// Event used to signal when queued packets are available for sending.
296 /// </summary>
297 /// <remarks>
298 /// This allows the outbound loop to only operate when there is data to send rather than continuously polling.
299 /// Some data is sent immediately and not queued. That data would not trigger this event.
300 /// </remarks>
301 private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false);
302  
303 private Pool<IncomingPacket> m_incomingPacketPool;
304  
305 /// <summary>
306 /// Stat for number of packets in the main pool awaiting use.
307 /// </summary>
308 private Stat m_poolCountStat;
309  
310 /// <summary>
311 /// Stat for number of packets in the inbound packet pool awaiting use.
312 /// </summary>
313 private Stat m_incomingPacketPoolStat;
314  
315 private int m_defaultRTO = 0;
316 private int m_maxRTO = 0;
317 private int m_ackTimeout = 0;
318 private int m_pausedAckTimeout = 0;
319 private bool m_disableFacelights = false;
320  
321 public Socket Server { get { return null; } }
322  
323 /// <summary>
324 /// Record how many packets have been resent
325 /// </summary>
326 internal int PacketsResentCount { get; set; }
327  
328 /// <summary>
329 /// Record how many packets have been sent
330 /// </summary>
331 internal int PacketsSentCount { get; set; }
332  
333 /// <summary>
334 /// Record how many incoming packets are indicated as resends by clients.
335 /// </summary>
336 internal int IncomingPacketsResentCount { get; set; }
337  
338 /// <summary>
339 /// Record how many inbound packets could not be recognized as LLUDP packets.
340 /// </summary>
341 public int IncomingMalformedPacketCount { get; private set; }
342  
343 /// <summary>
344 /// Record how many inbound packets could not be associated with a simulator circuit.
345 /// </summary>
346 public int IncomingOrphanedPacketCount { get; private set; }
347  
348 /// <summary>
349 /// Record current outgoing client for monitoring purposes.
350 /// </summary>
351 private IClientAPI m_currentOutgoingClient;
352  
353 /// <summary>
354 /// Recording current incoming client for monitoring purposes.
355 /// </summary>
356 private IClientAPI m_currentIncomingClient;
357  
358 /// <summary>
359 /// Experimental facility to run queue empty processing within a controlled number of threads rather than
360 /// requiring massive numbers of short-lived threads from the threadpool when there are a high number of
361 /// connections.
362 /// </summary>
363 public OutgoingQueueRefillEngine OqrEngine { get; private set; }
364  
365 public LLUDPServer(
366 IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port,
367 IConfigSource configSource, AgentCircuitManager circuitManager)
368 : base(listenIP, (int)port)
369 {
370 #region Environment.TickCount Measurement
371  
372 // Measure the resolution of Environment.TickCount
373 TickCountResolution = 0f;
374 for (int i = 0; i < 5; i++)
375 {
376 int start = Environment.TickCount;
377 int now = start;
378 while (now == start)
379 now = Environment.TickCount;
380 TickCountResolution += (float)(now - start) * 0.2f;
381 }
382 m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms");
383 TickCountResolution = (float)Math.Ceiling(TickCountResolution);
384  
385 #endregion Environment.TickCount Measurement
386  
387 m_circuitManager = circuitManager;
388 int sceneThrottleBps = 0;
389 bool usePools = false;
390  
391 IConfig config = configSource.Configs["ClientStack.LindenUDP"];
392 if (config != null)
393 {
394 m_asyncPacketHandling = config.GetBoolean("async_packet_handling", true);
395 m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0);
396 sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0);
397  
398 PrimUpdatesPerCallback = config.GetInt("PrimUpdatesPerCallback", 100);
399 TextureSendLimit = config.GetInt("TextureSendLimit", 20);
400  
401 m_defaultRTO = config.GetInt("DefaultRTO", 0);
402 m_maxRTO = config.GetInt("MaxRTO", 0);
403 m_disableFacelights = config.GetBoolean("DisableFacelights", false);
404 m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60);
405 m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300);
406 }
407 else
408 {
409 PrimUpdatesPerCallback = 100;
410 TextureSendLimit = 20;
411 m_ackTimeout = 1000 * 60; // 1 minute
412 m_pausedAckTimeout = 1000 * 300; // 5 minutes
413 }
414  
415 // FIXME: This actually only needs to be done once since the PacketPool is shared across all servers.
416 // However, there is no harm in temporarily doing it multiple times.
417 IConfig packetConfig = configSource.Configs["PacketPool"];
418 if (packetConfig != null)
419 {
420 PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
421 PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
422 usePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", usePools);
423 }
424  
425 #region BinaryStats
426 config = configSource.Configs["Statistics.Binary"];
427 m_shouldCollectStats = false;
428 if (config != null)
429 {
430 m_shouldCollectStats = config.GetBoolean("Enabled", false);
431 binStatsMaxFilesize = TimeSpan.FromSeconds(config.GetInt("packet_headers_period_seconds", 300));
432 binStatsDir = config.GetString("stats_dir", ".");
433 m_aggregatedBWStats = config.GetBoolean("aggregatedBWStats", false);
434 }
435 #endregion BinaryStats
436  
437 m_throttle = new TokenBucket(null, sceneThrottleBps);
438 ThrottleRates = new ThrottleRates(configSource);
439  
440 if (usePools)
441 EnablePools();
442  
443 OqrEngine = new OutgoingQueueRefillEngine(this);
444 }
445  
446 public void Start()
447 {
448 StartInbound();
449 StartOutbound();
450 OqrEngine.Start();
451  
452 m_elapsedMSSinceLastStatReport = Environment.TickCount;
453 }
454  
455 private void StartInbound()
456 {
457 m_log.InfoFormat(
458 "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
459 m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools);
460  
461 base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
462  
463 // This thread will process the packets received that are placed on the packetInbox
464 Watchdog.StartThread(
465 IncomingPacketHandler,
466 string.Format("Incoming Packets ({0})", Scene.Name),
467 ThreadPriority.Normal,
468 false,
469 true,
470 GetWatchdogIncomingAlarmData,
471 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
472 }
473  
474 private new void StartOutbound()
475 {
476 m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
477  
478 base.StartOutbound();
479  
480 Watchdog.StartThread(
481 OutgoingPacketHandler,
482 string.Format("Outgoing Packets ({0})", Scene.Name),
483 ThreadPriority.Normal,
484 false,
485 true,
486 GetWatchdogOutgoingAlarmData,
487 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
488 }
489  
490 public void Stop()
491 {
492 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name);
493 base.StopOutbound();
494 base.StopInbound();
495 OqrEngine.Stop();
496 }
497  
498 protected override bool EnablePools()
499 {
500 if (!UsePools)
501 {
502 base.EnablePools();
503  
504 m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500);
505  
506 return true;
507 }
508  
509 return false;
510 }
511  
512 protected override bool DisablePools()
513 {
514 if (UsePools)
515 {
516 base.DisablePools();
517  
518 StatsManager.DeregisterStat(m_incomingPacketPoolStat);
519  
520 // We won't null out the pool to avoid a race condition with code that may be in the middle of using it.
521  
522 return true;
523 }
524  
525 return false;
526 }
527  
528 /// <summary>
529 /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
530 /// stats.
531 /// </summary>
532 private void EnablePoolStats()
533 {
534 m_poolCountStat
535 = new Stat(
536 "UDPPacketBufferPoolCount",
537 "Objects within the UDPPacketBuffer pool",
538 "The number of objects currently stored within the UDPPacketBuffer pool",
539 "",
540 "clientstack",
541 Scene.Name,
542 StatType.Pull,
543 stat => stat.Value = Pool.Count,
544 StatVerbosity.Debug);
545  
546 StatsManager.RegisterStat(m_poolCountStat);
547  
548 m_incomingPacketPoolStat
549 = new Stat(
550 "IncomingPacketPoolCount",
551 "Objects within incoming packet pool",
552 "The number of objects currently stored within the incoming packet pool",
553 "",
554 "clientstack",
555 Scene.Name,
556 StatType.Pull,
557 stat => stat.Value = m_incomingPacketPool.Count,
558 StatVerbosity.Debug);
559  
560 StatsManager.RegisterStat(m_incomingPacketPoolStat);
561 }
562  
563 /// <summary>
564 /// Disables pool stats.
565 /// </summary>
566 private void DisablePoolStats()
567 {
568 StatsManager.DeregisterStat(m_poolCountStat);
569 m_poolCountStat = null;
570  
571 StatsManager.DeregisterStat(m_incomingPacketPoolStat);
572 m_incomingPacketPoolStat = null;
573 }
574  
575 /// <summary>
576 /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
577 /// </summary>
578 /// <returns></returns>
579 private string GetWatchdogIncomingAlarmData()
580 {
581 return string.Format(
582 "Client is {0}",
583 m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none");
584 }
585  
586 /// <summary>
587 /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
588 /// </summary>
589 /// <returns></returns>
590 private string GetWatchdogOutgoingAlarmData()
591 {
592 return string.Format(
593 "Client is {0}",
594 m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
595 }
596  
597 public void AddScene(IScene scene)
598 {
599 if (Scene != null)
600 {
601 m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene");
602 return;
603 }
604  
605 if (!(scene is Scene))
606 {
607 m_log.Error("[LLUDPSERVER]: AddScene() called with an unrecognized scene type " + scene.GetType());
608 return;
609 }
610  
611 Scene = (Scene)scene;
612 m_location = new Location(Scene.RegionInfo.RegionHandle);
613  
614 StatsManager.RegisterStat(
615 new Stat(
616 "InboxPacketsCount",
617 "Number of LL protocol packets waiting for the second stage of processing after initial receive.",
618 "Number of LL protocol packets waiting for the second stage of processing after initial receive.",
619 "",
620 "clientstack",
621 scene.Name,
622 StatType.Pull,
623 MeasuresOfInterest.AverageChangeOverTime,
624 stat => stat.Value = packetInbox.Count,
625 StatVerbosity.Debug));
626  
627 // XXX: These stats are also pool stats but we register them separately since they are currently not
628 // turned on and off by EnablePools()/DisablePools()
629 StatsManager.RegisterStat(
630 new PercentageStat(
631 "PacketsReused",
632 "Packets reused",
633 "Number of packets reused out of all requests to the packet pool",
634 "clientstack",
635 Scene.Name,
636 StatType.Pull,
637 stat =>
638 { PercentageStat pstat = (PercentageStat)stat;
639 pstat.Consequent = PacketPool.Instance.PacketsRequested;
640 pstat.Antecedent = PacketPool.Instance.PacketsReused; },
641 StatVerbosity.Debug));
642  
643 StatsManager.RegisterStat(
644 new PercentageStat(
645 "PacketDataBlocksReused",
646 "Packet data blocks reused",
647 "Number of data blocks reused out of all requests to the packet pool",
648 "clientstack",
649 Scene.Name,
650 StatType.Pull,
651 stat =>
652 { PercentageStat pstat = (PercentageStat)stat;
653 pstat.Consequent = PacketPool.Instance.BlocksRequested;
654 pstat.Antecedent = PacketPool.Instance.BlocksReused; },
655 StatVerbosity.Debug));
656  
657 StatsManager.RegisterStat(
658 new Stat(
659 "PacketsPoolCount",
660 "Objects within the packet pool",
661 "The number of objects currently stored within the packet pool",
662 "",
663 "clientstack",
664 Scene.Name,
665 StatType.Pull,
666 stat => stat.Value = PacketPool.Instance.PacketsPooled,
667 StatVerbosity.Debug));
668  
669 StatsManager.RegisterStat(
670 new Stat(
671 "PacketDataBlocksPoolCount",
672 "Objects within the packet data block pool",
673 "The number of objects currently stored within the packet data block pool",
674 "",
675 "clientstack",
676 Scene.Name,
677 StatType.Pull,
678 stat => stat.Value = PacketPool.Instance.BlocksPooled,
679 StatVerbosity.Debug));
680  
681 // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
682 // scene name
683 if (UsePools)
684 EnablePoolStats();
685  
686 MainConsole.Instance.Commands.AddCommand(
687 "Debug", false, "debug lludp packet",
688 "debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
689 "Turn on packet debugging",
690 "If level > 255 then all incoming and outgoing packets are logged.\n"
691 + "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
692 + "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
693 + "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
694 + "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
695 + "If level <= 0 then no packets are logged.\n"
696 + "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
697 + "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
698 + "In these cases, you cannot also specify an avatar name.\n"
699 + "If an avatar name is given then only packets from that avatar are logged.",
700 HandlePacketCommand);
701  
702 MainConsole.Instance.Commands.AddCommand(
703 "Debug", false, "debug lludp drop",
704 "debug lludp drop <in|out> <add|remove> <packet-name>",
705 "Drop all in or outbound packets that match the given name",
706 "For test purposes.",
707 HandleDropCommand);
708  
709 MainConsole.Instance.Commands.AddCommand(
710 "Debug",
711 false,
712 "debug lludp start",
713 "debug lludp start <in|out|all>",
714 "Control LLUDP packet processing.",
715 "No effect if packet processing has already started.\n"
716 + "in - start inbound processing.\n"
717 + "out - start outbound processing.\n"
718 + "all - start in and outbound processing.\n",
719 HandleStartCommand);
720  
721 MainConsole.Instance.Commands.AddCommand(
722 "Debug",
723 false,
724 "debug lludp stop",
725 "debug lludp stop <in|out|all>",
726 "Stop LLUDP packet processing.",
727 "No effect if packet processing has already stopped.\n"
728 + "in - stop inbound processing.\n"
729 + "out - stop outbound processing.\n"
730 + "all - stop in and outbound processing.\n",
731 HandleStopCommand);
732  
733 MainConsole.Instance.Commands.AddCommand(
734 "Debug",
735 false,
736 "debug lludp pool",
737 "debug lludp pool <on|off>",
738 "Turn object pooling within the lludp component on or off.",
739 HandlePoolCommand);
740  
741 MainConsole.Instance.Commands.AddCommand(
742 "Debug",
743 false,
744 "debug lludp status",
745 "debug lludp status",
746 "Return status of LLUDP packet processing.",
747 HandleStatusCommand);
748  
749 MainConsole.Instance.Commands.AddCommand(
750 "Debug",
751 false,
752 "debug lludp toggle agentupdate",
753 "debug lludp toggle agentupdate",
754 "Toggle whether agentupdate packets are processed or simply discarded.",
755 HandleAgentUpdateCommand);
756 }
757  
758 private void HandlePacketCommand(string module, string[] args)
759 {
760 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
761 return;
762  
763 bool setAsDefaultLevel = false;
764 bool setAll = false;
765 OptionSet optionSet = new OptionSet()
766 .Add("default", o => setAsDefaultLevel = (o != null))
767 .Add("all", o => setAll = (o != null));
768 List<string> filteredArgs = optionSet.Parse(args);
769  
770 string name = null;
771  
772 if (filteredArgs.Count == 6)
773 {
774 if (!(setAsDefaultLevel || setAll))
775 {
776 name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
777 }
778 else
779 {
780 MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
781 return;
782 }
783 }
784  
785 if (filteredArgs.Count > 3)
786 {
787 int newDebug;
788 if (int.TryParse(filteredArgs[3], out newDebug))
789 {
790 if (setAsDefaultLevel || setAll)
791 {
792 DefaultClientPacketDebugLevel = newDebug;
793  
794 MainConsole.Instance.OutputFormat(
795 "Packet debug for {0} clients set to {1} in {2}",
796 (setAll ? "all" : "future"), DefaultClientPacketDebugLevel, Scene.Name);
797  
798 if (setAll)
799 {
800 Scene.ForEachScenePresence(sp =>
801 {
802 MainConsole.Instance.OutputFormat(
803 "Packet debug for {0} ({1}) set to {2} in {3}",
804 sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
805  
806 sp.ControllingClient.DebugPacketLevel = newDebug;
807 });
808 }
809 }
810 else
811 {
812 Scene.ForEachScenePresence(sp =>
813 {
814 if (name == null || sp.Name == name)
815 {
816 MainConsole.Instance.OutputFormat(
817 "Packet debug for {0} ({1}) set to {2} in {3}",
818 sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
819  
820 sp.ControllingClient.DebugPacketLevel = newDebug;
821 }
822 });
823 }
824 }
825 else
826 {
827 MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
828 }
829 }
830 }
831  
832 private void HandleDropCommand(string module, string[] args)
833 {
834 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
835 return;
836  
837 if (args.Length != 6)
838 {
839 MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
840 return;
841 }
842  
843 string direction = args[3];
844 string subCommand = args[4];
845 string packetName = args[5];
846  
847 if (subCommand == "add")
848 {
849 MainConsole.Instance.OutputFormat(
850 "Adding packet {0} to {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
851  
852 Scene.ForEachScenePresence(
853 sp =>
854 {
855 LLClientView llcv = (LLClientView)sp.ControllingClient;
856  
857 if (direction == "in")
858 llcv.AddInPacketToDropSet(packetName);
859 else if (direction == "out")
860 llcv.AddOutPacketToDropSet(packetName);
861 }
862 );
863 }
864 else if (subCommand == "remove")
865 {
866 MainConsole.Instance.OutputFormat(
867 "Removing packet {0} from {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
868  
869 Scene.ForEachScenePresence(
870 sp =>
871 {
872 LLClientView llcv = (LLClientView)sp.ControllingClient;
873  
874 if (direction == "in")
875 llcv.RemoveInPacketFromDropSet(packetName);
876 else if (direction == "out")
877 llcv.RemoveOutPacketFromDropSet(packetName);
878 }
879 );
880 }
881 }
882  
883 private void HandleStartCommand(string module, string[] args)
884 {
885 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
886 return;
887  
888 if (args.Length != 4)
889 {
890 MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
891 return;
892 }
893  
894 string subCommand = args[3];
895  
896 if (subCommand == "in" || subCommand == "all")
897 StartInbound();
898  
899 if (subCommand == "out" || subCommand == "all")
900 StartOutbound();
901 }
902  
903 private void HandleStopCommand(string module, string[] args)
904 {
905 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
906 return;
907  
908 if (args.Length != 4)
909 {
910 MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
911 return;
912 }
913  
914 string subCommand = args[3];
915  
916 if (subCommand == "in" || subCommand == "all")
917 StopInbound();
918  
919 if (subCommand == "out" || subCommand == "all")
920 StopOutbound();
921 }
922  
923 private void HandlePoolCommand(string module, string[] args)
924 {
925 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
926 return;
927  
928 if (args.Length != 4)
929 {
930 MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
931 return;
932 }
933  
934 string enabled = args[3];
935  
936 if (enabled == "on")
937 {
938 if (EnablePools())
939 {
940 EnablePoolStats();
941 MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", Scene.Name);
942 }
943 }
944 else if (enabled == "off")
945 {
946 if (DisablePools())
947 {
948 DisablePoolStats();
949 MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", Scene.Name);
950 }
951 }
952 else
953 {
954 MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
955 }
956 }
957  
958 bool m_discardAgentUpdates;
959  
960 private void HandleAgentUpdateCommand(string module, string[] args)
961 {
962 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
963 return;
964  
965 m_discardAgentUpdates = !m_discardAgentUpdates;
966  
967 MainConsole.Instance.OutputFormat(
968 "Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, Scene.Name);
969 }
970  
971 private void HandleStatusCommand(string module, string[] args)
972 {
973 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
974 return;
975  
976 MainConsole.Instance.OutputFormat(
977 "IN LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningInbound ? "enabled" : "disabled");
978  
979 MainConsole.Instance.OutputFormat(
980 "OUT LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningOutbound ? "enabled" : "disabled");
981  
982 MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", Scene.Name, UsePools ? "on" : "off");
983  
984 MainConsole.Instance.OutputFormat(
985 "Packet debug level for new clients is {0}", DefaultClientPacketDebugLevel);
986 }
987  
988 public bool HandlesRegion(Location x)
989 {
990 return x == m_location;
991 }
992  
993 // public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
994 // {
995 // // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way
996 // if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting)
997 // allowSplitting = false;
998 //
999 // if (allowSplitting && packet.HasVariableBlocks)
1000 // {
1001 // byte[][] datas = packet.ToBytesMultiple();
1002 // int packetCount = datas.Length;
1003 //
1004 // if (packetCount < 1)
1005 // m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length);
1006 //
1007 // for (int i = 0; i < packetCount; i++)
1008 // {
1009 // byte[] data = datas[i];
1010 // m_scene.ForEachClient(
1011 // delegate(IClientAPI client)
1012 // {
1013 // if (client is LLClientView)
1014 // SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
1015 // }
1016 // );
1017 // }
1018 // }
1019 // else
1020 // {
1021 // byte[] data = packet.ToBytes();
1022 // m_scene.ForEachClient(
1023 // delegate(IClientAPI client)
1024 // {
1025 // if (client is LLClientView)
1026 // SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
1027 // }
1028 // );
1029 // }
1030 // }
1031  
1032 /// <summary>
1033 /// Start the process of sending a packet to the client.
1034 /// </summary>
1035 /// <param name="udpClient"></param>
1036 /// <param name="packet"></param>
1037 /// <param name="category"></param>
1038 /// <param name="allowSplitting"></param>
1039 /// <param name="method">
1040 /// The method to call if the packet is not acked by the client. If null, then a standard
1041 /// resend of the packet is done.
1042 /// </param>
1043 public virtual void SendPacket(
1044 LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method)
1045 {
1046 // CoarseLocationUpdate packets cannot be split in an automated way
1047 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
1048 allowSplitting = false;
1049  
1050 bool packetQueued = false;
1051  
1052 if (allowSplitting && packet.HasVariableBlocks)
1053 {
1054 byte[][] datas = packet.ToBytesMultiple();
1055 int packetCount = datas.Length;
1056  
1057 if (packetCount < 1)
1058 m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length);
1059  
1060 for (int i = 0; i < packetCount; i++)
1061 {
1062 byte[] data = datas[i];
1063 if (!SendPacketData(udpClient, data, packet.Type, category, method))
1064 packetQueued = true;
1065 }
1066 }
1067 else
1068 {
1069 byte[] data = packet.ToBytes();
1070 if (!SendPacketData(udpClient, data, packet.Type, category, method))
1071 packetQueued = true;
1072 }
1073  
1074 PacketPool.Instance.ReturnPacket(packet);
1075  
1076 if (packetQueued)
1077 m_dataPresentEvent.Set();
1078 }
1079  
1080 /// <summary>
1081 /// Start the process of sending a packet to the client.
1082 /// </summary>
1083 /// <param name="udpClient"></param>
1084 /// <param name="data"></param>
1085 /// <param name="type"></param>
1086 /// <param name="category"></param>
1087 /// <param name="method">
1088 /// The method to call if the packet is not acked by the client. If null, then a standard
1089 /// resend of the packet is done.
1090 /// </param>
1091 /// <returns>true if the data was sent immediately, false if it was queued for sending</returns>
1092 public bool SendPacketData(
1093 LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method)
1094 {
1095 int dataLength = data.Length;
1096 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
1097 bool doCopy = true;
1098  
1099 // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
1100 // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
1101 // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
1102 // to accomodate for both common scenarios and provide ample room for ACK appending in both
1103 int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200;
1104  
1105 UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
1106  
1107 // Zerocode if needed
1108 if (doZerocode)
1109 {
1110 try
1111 {
1112 dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data);
1113 doCopy = false;
1114 }
1115 catch (IndexOutOfRangeException)
1116 {
1117 // The packet grew larger than the bufferSize while zerocoding.
1118 // Remove the MSG_ZEROCODED flag and send the unencoded data
1119 // instead
1120 m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength +
1121 " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag");
1122 data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
1123 }
1124 }
1125  
1126 // If the packet data wasn't already copied during zerocoding, copy it now
1127 if (doCopy)
1128 {
1129 if (dataLength <= buffer.Data.Length)
1130 {
1131 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
1132 }
1133 else
1134 {
1135 bufferSize = dataLength;
1136 buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
1137  
1138 // m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" +
1139 // type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet");
1140 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
1141 }
1142 }
1143  
1144 buffer.DataLength = dataLength;
1145  
1146 #region Queue or Send
1147  
1148 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null);
1149  
1150 // If we were not provided a method for handling unacked, use the UDPServer default method
1151 if ((outgoingPacket.Buffer.Data[0] & Helpers.MSG_RELIABLE) != 0)
1152 outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method);
1153  
1154 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will
1155 // continue to display the deleted object until relog. Therefore, we need to always queue a kill object
1156 // packet so that it isn't sent before a queued update packet.
1157 bool forceQueue = (type == PacketType.KillObject);
1158  
1159 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue))
1160 {
1161 SendPacketFinal(outgoingPacket);
1162 return true;
1163 }
1164 else
1165 {
1166 return false;
1167 }
1168  
1169 #endregion Queue or Send
1170 }
1171  
1172 public void SendAcks(LLUDPClient udpClient)
1173 {
1174 uint ack;
1175  
1176 if (udpClient.PendingAcks.Dequeue(out ack))
1177 {
1178 List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>();
1179 PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock();
1180 block.ID = ack;
1181 blocks.Add(block);
1182  
1183 while (udpClient.PendingAcks.Dequeue(out ack))
1184 {
1185 block = new PacketAckPacket.PacketsBlock();
1186 block.ID = ack;
1187 blocks.Add(block);
1188 }
1189  
1190 PacketAckPacket packet = new PacketAckPacket();
1191 packet.Header.Reliable = false;
1192 packet.Packets = blocks.ToArray();
1193  
1194 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true, null);
1195 }
1196 }
1197  
1198 public void SendPing(LLUDPClient udpClient)
1199 {
1200 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
1201 pc.Header.Reliable = false;
1202  
1203 pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++;
1204 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
1205 pc.PingID.OldestUnacked = 0;
1206  
1207 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null);
1208 }
1209  
1210 public void CompletePing(LLUDPClient udpClient, byte pingID)
1211 {
1212 CompletePingCheckPacket completePing = new CompletePingCheckPacket();
1213 completePing.PingID.PingID = pingID;
1214 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
1215 }
1216  
1217 public void HandleUnacked(LLClientView client)
1218 {
1219 LLUDPClient udpClient = client.UDPClient;
1220  
1221 if (!udpClient.IsConnected)
1222 return;
1223  
1224 // Disconnect an agent if no packets are received for some time
1225 int timeoutTicks = m_ackTimeout;
1226  
1227 // Allow more slack if the client is "paused" eg file upload dialogue is open
1228 // Some sort of limit is needed in case the client crashes, loses its network connection
1229 // or some other disaster prevents it from sendung the AgentResume
1230 if (udpClient.IsPaused)
1231 timeoutTicks = m_pausedAckTimeout;
1232  
1233 if (client.IsActive &&
1234 (Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks)
1235 {
1236 // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
1237 // though it's set later on by LLClientView.Close()
1238 client.IsActive = false;
1239  
1240 // Fire this out on a different thread so that we don't hold up outgoing packet processing for
1241 // everybody else if this is being called due to an ack timeout.
1242 // This is the same as processing as the async process of a logout request.
1243 Util.FireAndForget(o => DeactivateClientDueToTimeout(client, timeoutTicks));
1244  
1245 return;
1246 }
1247  
1248 // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
1249 List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO);
1250  
1251 if (expiredPackets != null)
1252 {
1253 //m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
1254 // Exponential backoff of the retransmission timeout
1255 udpClient.BackoffRTO();
1256 for (int i = 0; i < expiredPackets.Count; ++i)
1257 expiredPackets[i].UnackedMethod(expiredPackets[i]);
1258 }
1259 }
1260  
1261 public void ResendUnacked(OutgoingPacket outgoingPacket)
1262 {
1263 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
1264 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
1265  
1266 // Set the resent flag
1267 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
1268 outgoingPacket.Category = ThrottleOutPacketType.Resend;
1269  
1270 // Bump up the resend count on this packet
1271 Interlocked.Increment(ref outgoingPacket.ResendCount);
1272  
1273 // Requeue or resend the packet
1274 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false))
1275 SendPacketFinal(outgoingPacket);
1276 }
1277  
1278 public void Flush(LLUDPClient udpClient)
1279 {
1280 // FIXME: Implement?
1281 }
1282  
1283 /// <summary>
1284 /// Actually send a packet to a client.
1285 /// </summary>
1286 /// <param name="outgoingPacket"></param>
1287 internal void SendPacketFinal(OutgoingPacket outgoingPacket)
1288 {
1289 UDPPacketBuffer buffer = outgoingPacket.Buffer;
1290 byte flags = buffer.Data[0];
1291 bool isResend = (flags & Helpers.MSG_RESENT) != 0;
1292 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
1293 bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0;
1294 LLUDPClient udpClient = outgoingPacket.Client;
1295  
1296 if (!udpClient.IsConnected)
1297 return;
1298  
1299 #region ACK Appending
1300  
1301 int dataLength = buffer.DataLength;
1302  
1303 // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here
1304 if (!isZerocoded)
1305 {
1306 // Keep appending ACKs until there is no room left in the buffer or there are
1307 // no more ACKs to append
1308 uint ackCount = 0;
1309 uint ack;
1310 while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(out ack))
1311 {
1312 Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
1313 dataLength += 4;
1314 ++ackCount;
1315 }
1316  
1317 if (ackCount > 0)
1318 {
1319 // Set the last byte of the packet equal to the number of appended ACKs
1320 buffer.Data[dataLength++] = (byte)ackCount;
1321 // Set the appended ACKs flag on this packet
1322 buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS);
1323 }
1324 }
1325  
1326 buffer.DataLength = dataLength;
1327  
1328 #endregion ACK Appending
1329  
1330 #region Sequence Number Assignment
1331  
1332 if (!isResend)
1333 {
1334 // Not a resend, assign a new sequence number
1335 uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence);
1336 Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
1337 outgoingPacket.SequenceNumber = sequenceNumber;
1338  
1339 if (isReliable)
1340 {
1341 // Add this packet to the list of ACK responses we are waiting on from the server
1342 udpClient.NeedAcks.Add(outgoingPacket);
1343 }
1344 }
1345 else
1346 {
1347 Interlocked.Increment(ref udpClient.PacketsResent);
1348  
1349 // We're not going to worry about interlock yet since its not currently critical that this total count
1350 // is 100% correct
1351 PacketsResentCount++;
1352 }
1353  
1354 #endregion Sequence Number Assignment
1355  
1356 // Stats tracking
1357 Interlocked.Increment(ref udpClient.PacketsSent);
1358  
1359 // We're not going to worry about interlock yet since its not currently critical that this total count
1360 // is 100% correct
1361 PacketsSentCount++;
1362  
1363 // Put the UDP payload on the wire
1364 AsyncBeginSend(buffer);
1365  
1366 // Keep track of when this packet was sent out (right now)
1367 outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue;
1368 }
1369  
1370 private void RecordMalformedInboundPacket(IPEndPoint endPoint)
1371 {
1372 // if (m_malformedCount < 100)
1373 // m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
1374  
1375 IncomingMalformedPacketCount++;
1376  
1377 if ((IncomingMalformedPacketCount % 10000) == 0)
1378 m_log.WarnFormat(
1379 "[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack. Last was from {1}",
1380 IncomingMalformedPacketCount, endPoint);
1381 }
1382  
1383 public override void PacketReceived(UDPPacketBuffer buffer)
1384 {
1385 // Debugging/Profiling
1386 //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; }
1387 //catch (Exception) { }
1388 // m_log.DebugFormat(
1389 // "[LLUDPSERVER]: Packet received from {0} in {1}", buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
1390  
1391 LLUDPClient udpClient = null;
1392 Packet packet = null;
1393 int packetEnd = buffer.DataLength - 1;
1394 IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
1395  
1396 #region Decoding
1397  
1398 if (buffer.DataLength < 7)
1399 {
1400 // m_log.WarnFormat(
1401 // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
1402 // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
1403  
1404 RecordMalformedInboundPacket(endPoint);
1405  
1406 return; // Drop undersized packet
1407 }
1408  
1409 int headerLen = 7;
1410 if (buffer.Data[6] == 0xFF)
1411 {
1412 if (buffer.Data[7] == 0xFF)
1413 headerLen = 10;
1414 else
1415 headerLen = 8;
1416 }
1417  
1418 if (buffer.DataLength < headerLen)
1419 {
1420 // m_log.WarnFormat(
1421 // "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}",
1422 // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
1423  
1424 RecordMalformedInboundPacket(endPoint);
1425  
1426 return; // Malformed header
1427 }
1428  
1429 try
1430 {
1431 // packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
1432 // // Only allocate a buffer for zerodecoding if the packet is zerocoded
1433 // ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
1434 // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
1435 // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
1436 // bytes are copied out).
1437 packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd,
1438 // Only allocate a buffer for zerodecoding if the packet is zerocoded
1439 ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
1440 }
1441 catch (Exception e)
1442 {
1443 if (IncomingMalformedPacketCount < 100)
1444 m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
1445 }
1446  
1447 // Fail-safe check
1448 if (packet == null)
1449 {
1450 if (IncomingMalformedPacketCount < 100)
1451 {
1452 m_log.WarnFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}, data {2}:",
1453 buffer.DataLength, buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
1454 }
1455  
1456 RecordMalformedInboundPacket(endPoint);
1457  
1458 return;
1459 }
1460  
1461 #endregion Decoding
1462  
1463 #region Packet to Client Mapping
1464  
1465 // UseCircuitCode handling
1466 if (packet.Type == PacketType.UseCircuitCode)
1467 {
1468 // We need to copy the endpoint so that it doesn't get changed when another thread reuses the
1469 // buffer.
1470 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
1471  
1472 Util.FireAndForget(HandleUseCircuitCode, array);
1473  
1474 return;
1475 }
1476 else if (packet.Type == PacketType.CompleteAgentMovement)
1477 {
1478 // Send ack straight away to let the viewer know that we got it.
1479 SendAckImmediate(endPoint, packet.Header.Sequence);
1480  
1481 // We need to copy the endpoint so that it doesn't get changed when another thread reuses the
1482 // buffer.
1483 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
1484  
1485 Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
1486  
1487 return;
1488 }
1489  
1490 // Determine which agent this packet came from
1491 IClientAPI client;
1492 if (!Scene.TryGetClient(endPoint, out client) || !(client is LLClientView))
1493 {
1494 //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
1495  
1496 IncomingOrphanedPacketCount++;
1497  
1498 if ((IncomingOrphanedPacketCount % 10000) == 0)
1499 m_log.WarnFormat(
1500 "[LLUDPSERVER]: Received {0} orphaned packets so far. Last was from {1}",
1501 IncomingOrphanedPacketCount, endPoint);
1502  
1503 return;
1504 }
1505  
1506 udpClient = ((LLClientView)client).UDPClient;
1507  
1508 if (!udpClient.IsConnected)
1509 return;
1510  
1511 #endregion Packet to Client Mapping
1512  
1513 // Stats tracking
1514 Interlocked.Increment(ref udpClient.PacketsReceived);
1515  
1516 int now = Environment.TickCount & Int32.MaxValue;
1517 udpClient.TickLastPacketReceived = now;
1518  
1519 #region ACK Receiving
1520  
1521 // Handle appended ACKs
1522 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
1523 {
1524 // m_log.DebugFormat(
1525 // "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
1526 // packet.Header.AckList.Length, client.Name, m_scene.Name);
1527  
1528 for (int i = 0; i < packet.Header.AckList.Length; i++)
1529 udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
1530 }
1531  
1532 // Handle PacketAck packets
1533 if (packet.Type == PacketType.PacketAck)
1534 {
1535 PacketAckPacket ackPacket = (PacketAckPacket)packet;
1536  
1537 // m_log.DebugFormat(
1538 // "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
1539 // ackPacket.Packets.Length, client.Name, m_scene.Name);
1540  
1541 for (int i = 0; i < ackPacket.Packets.Length; i++)
1542 udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
1543  
1544 // We don't need to do anything else with PacketAck packets
1545 return;
1546 }
1547  
1548 #endregion ACK Receiving
1549  
1550 #region ACK Sending
1551  
1552 if (packet.Header.Reliable)
1553 {
1554 // m_log.DebugFormat(
1555 // "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
1556 // packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
1557  
1558 udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
1559  
1560 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
1561 // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove
1562 // 2*MTU bytes from the value and send ACKs, and finally add the local value back to
1563 // client.BytesSinceLastACK. Lockless thread safety
1564 int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0);
1565 bytesSinceLastACK += buffer.DataLength;
1566 if (bytesSinceLastACK > LLUDPServer.MTU * 2)
1567 {
1568 bytesSinceLastACK -= LLUDPServer.MTU * 2;
1569 SendAcks(udpClient);
1570 }
1571 Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK);
1572 }
1573  
1574 #endregion ACK Sending
1575  
1576 #region Incoming Packet Accounting
1577  
1578 // We're not going to worry about interlock yet since its not currently critical that this total count
1579 // is 100% correct
1580 if (packet.Header.Resent)
1581 IncomingPacketsResentCount++;
1582  
1583 // Check the archive of received reliable packet IDs to see whether we already received this packet
1584 if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence))
1585 {
1586 if (packet.Header.Resent)
1587 m_log.DebugFormat(
1588 "[LLUDPSERVER]: Received a resend of already processed packet #{0}, type {1} from {2}",
1589 packet.Header.Sequence, packet.Type, client.Name);
1590 else
1591 m_log.WarnFormat(
1592 "[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #{0}, type {1} from {2}",
1593 packet.Header.Sequence, packet.Type, client.Name);
1594  
1595 // Avoid firing a callback twice for the same packet
1596 return;
1597 }
1598  
1599 #endregion Incoming Packet Accounting
1600  
1601 #region BinaryStats
1602 LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length);
1603 #endregion BinaryStats
1604  
1605 if (packet.Type == PacketType.AgentUpdate)
1606 {
1607 if (m_discardAgentUpdates)
1608 return;
1609  
1610 ((LLClientView)client).TotalAgentUpdates++;
1611  
1612 AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
1613  
1614 LLClientView llClient = client as LLClientView;
1615 if (agentUpdate.AgentData.SessionID != client.SessionId
1616 || agentUpdate.AgentData.AgentID != client.AgentId
1617 || !(llClient == null || llClient.CheckAgentUpdateSignificance(agentUpdate.AgentData)) )
1618 {
1619 PacketPool.Instance.ReturnPacket(packet);
1620 return;
1621 }
1622 }
1623  
1624 #region Ping Check Handling
1625  
1626 if (packet.Type == PacketType.StartPingCheck)
1627 {
1628 // m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
1629  
1630 // We don't need to do anything else with ping checks
1631 StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
1632 CompletePing(udpClient, startPing.PingID.PingID);
1633  
1634 if ((Environment.TickCount - m_elapsedMSSinceLastStatReport) >= 3000)
1635 {
1636 udpClient.SendPacketStats();
1637 m_elapsedMSSinceLastStatReport = Environment.TickCount;
1638 }
1639 return;
1640 }
1641 else if (packet.Type == PacketType.CompletePingCheck)
1642 {
1643 // We don't currently track client ping times
1644 return;
1645 }
1646  
1647 #endregion Ping Check Handling
1648  
1649 IncomingPacket incomingPacket;
1650  
1651 // Inbox insertion
1652 if (UsePools)
1653 {
1654 incomingPacket = m_incomingPacketPool.GetObject();
1655 incomingPacket.Client = (LLClientView)client;
1656 incomingPacket.Packet = packet;
1657 }
1658 else
1659 {
1660 incomingPacket = new IncomingPacket((LLClientView)client, packet);
1661 }
1662  
1663 packetInbox.Enqueue(incomingPacket);
1664 }
1665  
1666 #region BinaryStats
1667  
1668 public class PacketLogger
1669 {
1670 public DateTime StartTime;
1671 public string Path = null;
1672 public System.IO.BinaryWriter Log = null;
1673 }
1674  
1675 public static PacketLogger PacketLog;
1676  
1677 protected static bool m_shouldCollectStats = false;
1678 // Number of seconds to log for
1679 static TimeSpan binStatsMaxFilesize = TimeSpan.FromSeconds(300);
1680 static object binStatsLogLock = new object();
1681 static string binStatsDir = "";
1682  
1683 //for Aggregated In/Out BW logging
1684 static bool m_aggregatedBWStats = false;
1685 static long m_aggregatedBytesIn = 0;
1686 static long m_aggregatedByestOut = 0;
1687 static object aggBWStatsLock = new object();
1688  
1689 public static long AggregatedLLUDPBytesIn
1690 {
1691 get { return m_aggregatedBytesIn; }
1692 }
1693 public static long AggregatedLLUDPBytesOut
1694 {
1695 get {return m_aggregatedByestOut;}
1696 }
1697  
1698 public static void LogPacketHeader(bool incoming, uint circuit, byte flags, PacketType packetType, ushort size)
1699 {
1700 if (m_aggregatedBWStats)
1701 {
1702 lock (aggBWStatsLock)
1703 {
1704 if (incoming)
1705 m_aggregatedBytesIn += size;
1706 else
1707 m_aggregatedByestOut += size;
1708 }
1709 }
1710  
1711 if (!m_shouldCollectStats) return;
1712  
1713 // Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size
1714  
1715 // Put the incoming bit into the least significant bit of the flags byte
1716 if (incoming)
1717 flags |= 0x01;
1718 else
1719 flags &= 0xFE;
1720  
1721 // Put the flags byte into the most significant bits of the type integer
1722 uint type = (uint)packetType;
1723 type |= (uint)flags << 24;
1724  
1725 // m_log.Debug("1 LogPacketHeader(): Outside lock");
1726 lock (binStatsLogLock)
1727 {
1728 DateTime now = DateTime.Now;
1729  
1730 // m_log.Debug("2 LogPacketHeader(): Inside lock. now is " + now.Ticks);
1731 try
1732 {
1733 if (PacketLog == null || (now > PacketLog.StartTime + binStatsMaxFilesize))
1734 {
1735 if (PacketLog != null && PacketLog.Log != null)
1736 {
1737 PacketLog.Log.Close();
1738 }
1739  
1740 // First log file or time has expired, start writing to a new log file
1741 PacketLog = new PacketLogger();
1742 PacketLog.StartTime = now;
1743 PacketLog.Path = (binStatsDir.Length > 0 ? binStatsDir + System.IO.Path.DirectorySeparatorChar.ToString() : "")
1744 + String.Format("packets-{0}.log", now.ToString("yyyyMMddHHmmss"));
1745 PacketLog.Log = new BinaryWriter(File.Open(PacketLog.Path, FileMode.Append, FileAccess.Write));
1746 }
1747  
1748 // Serialize the data
1749 byte[] output = new byte[18];
1750 Buffer.BlockCopy(BitConverter.GetBytes(now.Ticks), 0, output, 0, 8);
1751 Buffer.BlockCopy(BitConverter.GetBytes(circuit), 0, output, 8, 4);
1752 Buffer.BlockCopy(BitConverter.GetBytes(type), 0, output, 12, 4);
1753 Buffer.BlockCopy(BitConverter.GetBytes(size), 0, output, 16, 2);
1754  
1755 // Write the serialized data to disk
1756 if (PacketLog != null && PacketLog.Log != null)
1757 PacketLog.Log.Write(output);
1758 }
1759 catch (Exception ex)
1760 {
1761 m_log.Error("Packet statistics gathering failed: " + ex.Message, ex);
1762 if (PacketLog.Log != null)
1763 {
1764 PacketLog.Log.Close();
1765 }
1766 PacketLog = null;
1767 }
1768 }
1769 }
1770  
1771 #endregion BinaryStats
1772  
1773 private void HandleUseCircuitCode(object o)
1774 {
1775 IPEndPoint endPoint = null;
1776 IClientAPI client = null;
1777  
1778 try
1779 {
1780 // DateTime startTime = DateTime.Now;
1781 object[] array = (object[])o;
1782 endPoint = (IPEndPoint)array[0];
1783 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
1784  
1785 m_log.DebugFormat(
1786 "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
1787 uccp.CircuitCode.Code, Scene.RegionInfo.RegionName, endPoint);
1788  
1789 AuthenticateResponse sessionInfo;
1790 if (IsClientAuthorized(uccp, out sessionInfo))
1791 {
1792 // Begin the process of adding the client to the simulator
1793 client
1794 = AddClient(
1795 uccp.CircuitCode.Code,
1796 uccp.CircuitCode.ID,
1797 uccp.CircuitCode.SessionID,
1798 endPoint,
1799 sessionInfo);
1800  
1801 // Send ack straight away to let the viewer know that the connection is active.
1802 // The client will be null if it already exists (e.g. if on a region crossing the client sends a use
1803 // circuit code to the existing child agent. This is not particularly obvious.
1804 SendAckImmediate(endPoint, uccp.Header.Sequence);
1805  
1806 // We only want to send initial data to new clients, not ones which are being converted from child to root.
1807 if (client != null)
1808 {
1809 AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(uccp.CircuitCode.Code);
1810 bool tp = (aCircuit.teleportFlags > 0);
1811 // Let's delay this for TP agents, otherwise the viewer doesn't know where to get resources from
1812 if (!tp && !client.SceneAgent.SentInitialDataToClient)
1813 client.SceneAgent.SendInitialDataToClient();
1814 }
1815 }
1816 else
1817 {
1818 // Don't create clients for unauthorized requesters.
1819 m_log.WarnFormat(
1820 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
1821 uccp.CircuitCode.ID, Scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint);
1822 }
1823  
1824 // m_log.DebugFormat(
1825 // "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms",
1826 // buffer.RemoteEndPoint, (DateTime.Now - startTime).Milliseconds);
1827  
1828 }
1829 catch (Exception e)
1830 {
1831 m_log.ErrorFormat(
1832 "[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
1833 endPoint != null ? endPoint.ToString() : "n/a",
1834 client != null ? client.Name : "unknown",
1835 client != null ? client.AgentId.ToString() : "unknown",
1836 e.Message,
1837 e.StackTrace);
1838 }
1839 }
1840  
1841 private void HandleCompleteMovementIntoRegion(object o)
1842 {
1843 IPEndPoint endPoint = null;
1844 IClientAPI client = null;
1845  
1846 try
1847 {
1848 object[] array = (object[])o;
1849 endPoint = (IPEndPoint)array[0];
1850 CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1];
1851  
1852 m_log.DebugFormat(
1853 "[LLUDPSERVER]: Handling CompleteAgentMovement request from {0} in {1}", endPoint, Scene.Name);
1854  
1855 // Determine which agent this packet came from
1856 // We need to wait here because in when using the OpenSimulator V2 teleport protocol to travel to a destination
1857 // simulator with no existing child presence, the viewer (at least LL 3.3.4) will send UseCircuitCode
1858 // and then CompleteAgentMovement immediately without waiting for an ack. As we are now handling these
1859 // packets asynchronously, we need to account for this thread proceeding more quickly than the
1860 // UseCircuitCode thread.
1861 int count = 40;
1862 while (count-- > 0)
1863 {
1864 if (Scene.TryGetClient(endPoint, out client))
1865 {
1866 if (!client.IsActive)
1867 {
1868 // This check exists to catch a condition where the client has been closed by another thread
1869 // but has not yet been removed from the client manager (and possibly a new connection has
1870 // not yet been established).
1871 m_log.DebugFormat(
1872 "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active yet. Waiting.",
1873 endPoint, client.Name, Scene.Name);
1874 }
1875 else if (client.SceneAgent == null)
1876 {
1877 // This check exists to catch a condition where the new client has been added to the client
1878 // manager but the SceneAgent has not yet been set in Scene.AddNewAgent(). If we are too
1879 // eager, then the new ScenePresence may not have registered a listener for this messsage
1880 // before we try to process it.
1881 // XXX: A better long term fix may be to add the SceneAgent before the client is added to
1882 // the client manager
1883 m_log.DebugFormat(
1884 "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client SceneAgent not set yet. Waiting.",
1885 endPoint, client.Name, Scene.Name);
1886 }
1887 else
1888 {
1889 break;
1890 }
1891 }
1892 else
1893 {
1894 m_log.DebugFormat(
1895 "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} in {1} but no client exists yet. Waiting.",
1896 endPoint, Scene.Name);
1897 }
1898  
1899 Thread.Sleep(200);
1900 }
1901  
1902 if (client == null)
1903 {
1904 m_log.DebugFormat(
1905 "[LLUDPSERVER]: No client found for CompleteAgentMovement from {0} in {1} after wait. Dropping.",
1906 endPoint, Scene.Name);
1907  
1908 return;
1909 }
1910 else if (!client.IsActive || client.SceneAgent == null)
1911 {
1912 // This check exists to catch a condition where the client has been closed by another thread
1913 // but has not yet been removed from the client manager.
1914 // The packet could be simply ignored but it is useful to know if this condition occurred for other debugging
1915 // purposes.
1916 m_log.DebugFormat(
1917 "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active after wait. Dropping.",
1918 endPoint, client.Name, Scene.Name);
1919  
1920 return;
1921 }
1922  
1923 IncomingPacket incomingPacket1;
1924  
1925 // Inbox insertion
1926 if (UsePools)
1927 {
1928 incomingPacket1 = m_incomingPacketPool.GetObject();
1929 incomingPacket1.Client = (LLClientView)client;
1930 incomingPacket1.Packet = packet;
1931 }
1932 else
1933 {
1934 incomingPacket1 = new IncomingPacket((LLClientView)client, packet);
1935 }
1936  
1937 packetInbox.Enqueue(incomingPacket1);
1938 }
1939 catch (Exception e)
1940 {
1941 m_log.ErrorFormat(
1942 "[LLUDPSERVER]: CompleteAgentMovement handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
1943 endPoint != null ? endPoint.ToString() : "n/a",
1944 client != null ? client.Name : "unknown",
1945 client != null ? client.AgentId.ToString() : "unknown",
1946 e.Message,
1947 e.StackTrace);
1948 }
1949 }
1950  
1951 /// <summary>
1952 /// Send an ack immediately to the given endpoint.
1953 /// </summary>
1954 /// <remarks>
1955 /// FIXME: Might be possible to use SendPacketData() like everything else, but this will require refactoring so
1956 /// that we can obtain the UDPClient easily at this point.
1957 /// </remarks>
1958 /// <param name="remoteEndpoint"></param>
1959 /// <param name="sequenceNumber"></param>
1960 private void SendAckImmediate(IPEndPoint remoteEndpoint, uint sequenceNumber)
1961 {
1962 PacketAckPacket ack = new PacketAckPacket();
1963 ack.Header.Reliable = false;
1964 ack.Packets = new PacketAckPacket.PacketsBlock[1];
1965 ack.Packets[0] = new PacketAckPacket.PacketsBlock();
1966 ack.Packets[0].ID = sequenceNumber;
1967  
1968 SendAckImmediate(remoteEndpoint, ack);
1969 }
1970  
1971 public virtual void SendAckImmediate(IPEndPoint remoteEndpoint, PacketAckPacket ack)
1972 {
1973 byte[] packetData = ack.ToBytes();
1974 int length = packetData.Length;
1975  
1976 UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length);
1977 buffer.DataLength = length;
1978  
1979 Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length);
1980  
1981 AsyncBeginSend(buffer);
1982 }
1983  
1984 private bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo)
1985 {
1986 UUID agentID = useCircuitCode.CircuitCode.ID;
1987 UUID sessionID = useCircuitCode.CircuitCode.SessionID;
1988 uint circuitCode = useCircuitCode.CircuitCode.Code;
1989  
1990 sessionInfo = m_circuitManager.AuthenticateSession(sessionID, agentID, circuitCode);
1991 return sessionInfo.Authorised;
1992 }
1993  
1994 /// <summary>
1995 /// Add a client.
1996 /// </summary>
1997 /// <param name="circuitCode"></param>
1998 /// <param name="agentID"></param>
1999 /// <param name="sessionID"></param>
2000 /// <param name="remoteEndPoint"></param>
2001 /// <param name="sessionInfo"></param>
2002 /// <returns>The client if it was added. Null if the client already existed.</returns>
2003 protected virtual IClientAPI AddClient(
2004 uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo)
2005 {
2006 IClientAPI client = null;
2007  
2008 // We currently synchronize this code across the whole scene to avoid issues such as
2009 // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
2010 // consistently, this lock could probably be removed.
2011 lock (this)
2012 {
2013 if (!Scene.TryGetClient(agentID, out client))
2014 {
2015 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
2016  
2017 client = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
2018 client.OnLogout += LogoutHandler;
2019 client.DebugPacketLevel = DefaultClientPacketDebugLevel;
2020  
2021 ((LLClientView)client).DisableFacelights = m_disableFacelights;
2022  
2023 client.Start();
2024 }
2025 }
2026  
2027 return client;
2028 }
2029  
2030 /// <summary>
2031 /// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds).
2032 /// </summary>
2033 /// <remarks>
2034 /// If a connection is active then we will always receive packets even if nothing else is happening, due to
2035 /// regular client pings.
2036 /// </remarks>
2037 /// <param name='client'></param>
2038 /// <param name='timeoutTicks'></param>
2039 private void DeactivateClientDueToTimeout(LLClientView client, int timeoutTicks)
2040 {
2041 lock (client.CloseSyncLock)
2042 {
2043 ClientLogoutsDueToNoReceives++;
2044  
2045 m_log.WarnFormat(
2046 "[LLUDPSERVER]: No packets received from {0} agent of {1} for {2}ms in {3}. Disconnecting.",
2047 client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, timeoutTicks, Scene.Name);
2048  
2049 if (!client.SceneAgent.IsChildAgent)
2050 client.Kick("Simulator logged you out due to connection timeout.");
2051 }
2052  
2053 Scene.CloseAgent(client.AgentId, true);
2054 }
2055  
2056 private void IncomingPacketHandler()
2057 {
2058 // Set this culture for the thread that incoming packets are received
2059 // on to en-US to avoid number parsing issues
2060 Culture.SetCurrentCulture();
2061  
2062 while (IsRunningInbound)
2063 {
2064 try
2065 {
2066 IncomingPacket incomingPacket = null;
2067  
2068 /*
2069 // HACK: This is a test to try and rate limit packet handling on Mono.
2070 // If it works, a more elegant solution can be devised
2071 if (Util.FireAndForgetCount() < 2)
2072 {
2073 //m_log.Debug("[LLUDPSERVER]: Incoming packet handler is sleeping");
2074 Thread.Sleep(30);
2075 }
2076 */
2077  
2078 if (packetInbox.Dequeue(100, ref incomingPacket))
2079 {
2080 ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
2081  
2082 if (UsePools)
2083 m_incomingPacketPool.ReturnObject(incomingPacket);
2084 }
2085 }
2086 catch (Exception ex)
2087 {
2088 m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex);
2089 }
2090  
2091 Watchdog.UpdateThread();
2092 }
2093  
2094 if (packetInbox.Count > 0)
2095 m_log.Warn("[LLUDPSERVER]: IncomingPacketHandler is shutting down, dropping " + packetInbox.Count + " packets");
2096 packetInbox.Clear();
2097  
2098 Watchdog.RemoveThread();
2099 }
2100  
2101 private void OutgoingPacketHandler()
2102 {
2103 // Set this culture for the thread that outgoing packets are sent
2104 // on to en-US to avoid number parsing issues
2105 Culture.SetCurrentCulture();
2106  
2107 // Typecast the function to an Action<IClientAPI> once here to avoid allocating a new
2108 // Action generic every round
2109 Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler;
2110  
2111 while (base.IsRunningOutbound)
2112 {
2113 try
2114 {
2115 m_packetSent = false;
2116  
2117 #region Update Timers
2118  
2119 m_resendUnacked = false;
2120 m_sendAcks = false;
2121 m_sendPing = false;
2122  
2123 // Update elapsed time
2124 int thisTick = Environment.TickCount & Int32.MaxValue;
2125 if (m_tickLastOutgoingPacketHandler > thisTick)
2126 m_elapsedMSOutgoingPacketHandler += ((Int32.MaxValue - m_tickLastOutgoingPacketHandler) + thisTick);
2127 else
2128 m_elapsedMSOutgoingPacketHandler += (thisTick - m_tickLastOutgoingPacketHandler);
2129  
2130 m_tickLastOutgoingPacketHandler = thisTick;
2131  
2132 // Check for pending outgoing resends every 100ms
2133 if (m_elapsedMSOutgoingPacketHandler >= 100)
2134 {
2135 m_resendUnacked = true;
2136 m_elapsedMSOutgoingPacketHandler = 0;
2137 m_elapsed100MSOutgoingPacketHandler += 1;
2138 }
2139  
2140 // Check for pending outgoing ACKs every 500ms
2141 if (m_elapsed100MSOutgoingPacketHandler >= 5)
2142 {
2143 m_sendAcks = true;
2144 m_elapsed100MSOutgoingPacketHandler = 0;
2145 m_elapsed500MSOutgoingPacketHandler += 1;
2146 }
2147  
2148 // Send pings to clients every 5000ms
2149 if (m_elapsed500MSOutgoingPacketHandler >= 10)
2150 {
2151 m_sendPing = true;
2152 m_elapsed500MSOutgoingPacketHandler = 0;
2153 }
2154  
2155 #endregion Update Timers
2156  
2157 // Use this for emergency monitoring -- bug hunting
2158 //if (m_scene.EmergencyMonitoring)
2159 // clientPacketHandler = MonitoredClientOutgoingPacketHandler;
2160 //else
2161 // clientPacketHandler = ClientOutgoingPacketHandler;
2162  
2163 // Handle outgoing packets, resends, acknowledgements, and pings for each
2164 // client. m_packetSent will be set to true if a packet is sent
2165 Scene.ForEachClient(clientPacketHandler);
2166  
2167 m_currentOutgoingClient = null;
2168  
2169 // If nothing was sent, sleep for the minimum amount of time before a
2170 // token bucket could get more tokens
2171 //if (!m_packetSent)
2172 // Thread.Sleep((int)TickCountResolution);
2173 //
2174 // Instead, now wait for data present to be explicitly signalled. Evidence so far is that with
2175 // modern mono it reduces CPU base load since there is no more continuous polling.
2176 if (!m_packetSent)
2177 m_dataPresentEvent.WaitOne(100);
2178  
2179 Watchdog.UpdateThread();
2180 }
2181 catch (Exception ex)
2182 {
2183 m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler loop threw an exception: " + ex.Message, ex);
2184 }
2185 }
2186  
2187 Watchdog.RemoveThread();
2188 }
2189  
2190 protected void ClientOutgoingPacketHandler(IClientAPI client)
2191 {
2192 m_currentOutgoingClient = client;
2193  
2194 try
2195 {
2196 if (client is LLClientView)
2197 {
2198 LLClientView llClient = (LLClientView)client;
2199 LLUDPClient udpClient = llClient.UDPClient;
2200  
2201 if (udpClient.IsConnected)
2202 {
2203 if (m_resendUnacked)
2204 HandleUnacked(llClient);
2205  
2206 if (m_sendAcks)
2207 SendAcks(udpClient);
2208  
2209 if (m_sendPing)
2210 SendPing(udpClient);
2211  
2212 // Dequeue any outgoing packets that are within the throttle limits
2213 if (udpClient.DequeueOutgoing())
2214 m_packetSent = true;
2215 }
2216 }
2217 }
2218 catch (Exception ex)
2219 {
2220 m_log.Error(
2221 string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex);
2222 }
2223 }
2224  
2225 #region Emergency Monitoring
2226 // Alternative packet handler fuull of instrumentation
2227 // Handy for hunting bugs
2228 private Stopwatch watch1 = new Stopwatch();
2229 private Stopwatch watch2 = new Stopwatch();
2230  
2231 private float avgProcessingTicks = 0;
2232 private float avgResendUnackedTicks = 0;
2233 private float avgSendAcksTicks = 0;
2234 private float avgSendPingTicks = 0;
2235 private float avgDequeueTicks = 0;
2236 private long nticks = 0;
2237 private long nticksUnack = 0;
2238 private long nticksAck = 0;
2239 private long nticksPing = 0;
2240 private int npacksSent = 0;
2241 private int npackNotSent = 0;
2242  
2243 /// <summary>
2244 /// Number of inbound packets processed since startup.
2245 /// </summary>
2246 public long IncomingPacketsProcessed { get; private set; }
2247  
2248 private void MonitoredClientOutgoingPacketHandler(IClientAPI client)
2249 {
2250 nticks++;
2251 watch1.Start();
2252 m_currentOutgoingClient = client;
2253  
2254 try
2255 {
2256 if (client is LLClientView)
2257 {
2258 LLClientView llClient = (LLClientView)client;
2259 LLUDPClient udpClient = llClient.UDPClient;
2260  
2261 if (udpClient.IsConnected)
2262 {
2263 if (m_resendUnacked)
2264 {
2265 nticksUnack++;
2266 watch2.Start();
2267  
2268 HandleUnacked(llClient);
2269  
2270 watch2.Stop();
2271 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);
2272 watch2.Reset();
2273 }
2274  
2275 if (m_sendAcks)
2276 {
2277 nticksAck++;
2278 watch2.Start();
2279  
2280 SendAcks(udpClient);
2281  
2282 watch2.Stop();
2283 avgSendAcksTicks = (nticksAck - 1) / (float)nticksAck * avgSendAcksTicks + (watch2.ElapsedTicks / (float)nticksAck);
2284 watch2.Reset();
2285 }
2286  
2287 if (m_sendPing)
2288 {
2289 nticksPing++;
2290 watch2.Start();
2291  
2292 SendPing(udpClient);
2293  
2294 watch2.Stop();
2295 avgSendPingTicks = (nticksPing - 1) / (float)nticksPing * avgSendPingTicks + (watch2.ElapsedTicks / (float)nticksPing);
2296 watch2.Reset();
2297 }
2298  
2299 watch2.Start();
2300 // Dequeue any outgoing packets that are within the throttle limits
2301 if (udpClient.DequeueOutgoing())
2302 {
2303 m_packetSent = true;
2304 npacksSent++;
2305 }
2306 else
2307 {
2308 npackNotSent++;
2309 }
2310  
2311 watch2.Stop();
2312 avgDequeueTicks = (nticks - 1) / (float)nticks * avgDequeueTicks + (watch2.ElapsedTicks / (float)nticks);
2313 watch2.Reset();
2314  
2315 }
2316 else
2317 {
2318 m_log.WarnFormat("[LLUDPSERVER]: Client is not connected");
2319 }
2320 }
2321 }
2322 catch (Exception ex)
2323 {
2324 m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name +
2325 " threw an exception: " + ex.Message, ex);
2326 }
2327 watch1.Stop();
2328 avgProcessingTicks = (nticks - 1) / (float)nticks * avgProcessingTicks + (watch1.ElapsedTicks / (float)nticks);
2329 watch1.Reset();
2330  
2331 // reuse this -- it's every ~100ms
2332 if (Scene.EmergencyMonitoring && nticks % 100 == 0)
2333 {
2334 m_log.InfoFormat("[LLUDPSERVER]: avg processing ticks: {0} avg unacked: {1} avg acks: {2} avg ping: {3} avg dequeue: {4} (TickCountRes: {5} sent: {6} notsent: {7})",
2335 avgProcessingTicks, avgResendUnackedTicks, avgSendAcksTicks, avgSendPingTicks, avgDequeueTicks, TickCountResolution, npacksSent, npackNotSent);
2336 npackNotSent = npacksSent = 0;
2337 }
2338  
2339 }
2340  
2341 #endregion
2342  
2343 private void ProcessInPacket(IncomingPacket incomingPacket)
2344 {
2345 Packet packet = incomingPacket.Packet;
2346 LLClientView client = incomingPacket.Client;
2347  
2348 if (client.IsActive)
2349 {
2350 m_currentIncomingClient = client;
2351  
2352 try
2353 {
2354 // Process this packet
2355 client.ProcessInPacket(packet);
2356 }
2357 catch (ThreadAbortException)
2358 {
2359 // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
2360 m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server");
2361 Stop();
2362 }
2363 catch (Exception e)
2364 {
2365 // Don't let a failure in an individual client thread crash the whole sim.
2366 m_log.Error(
2367 string.Format(
2368 "[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw ",
2369 client.Name, packet.Type),
2370 e);
2371 }
2372 finally
2373 {
2374 m_currentIncomingClient = null;
2375 }
2376 }
2377 else
2378 {
2379 m_log.DebugFormat(
2380 "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}",
2381 packet.Type, client.Name, Scene.RegionInfo.RegionName);
2382 }
2383  
2384 IncomingPacketsProcessed++;
2385 }
2386  
2387 protected void LogoutHandler(IClientAPI client)
2388 {
2389 client.SendLogoutPacket();
2390  
2391 if (!client.IsLoggingOut)
2392 {
2393 client.IsLoggingOut = true;
2394 Scene.CloseAgent(client.AgentId, false);
2395 }
2396 }
2397 }
2398 }