aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs199
1 files changed, 149 insertions, 50 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index 0831946..6e7a6a8 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -147,23 +147,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
147 private int m_elapsed500MSOutgoingPacketHandler; 147 private int m_elapsed500MSOutgoingPacketHandler;
148 148
149 /// <summary>Flag to signal when clients should check for resends</summary> 149 /// <summary>Flag to signal when clients should check for resends</summary>
150 private bool m_resendUnacked; 150 protected bool m_resendUnacked;
151
151 /// <summary>Flag to signal when clients should send ACKs</summary> 152 /// <summary>Flag to signal when clients should send ACKs</summary>
152 private bool m_sendAcks; 153 protected bool m_sendAcks;
154
153 /// <summary>Flag to signal when clients should send pings</summary> 155 /// <summary>Flag to signal when clients should send pings</summary>
154 private bool m_sendPing; 156 protected bool m_sendPing;
155 157
156 private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); 158 private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>();
157 159
158 private int m_defaultRTO = 0; 160 private int m_defaultRTO = 0;
159 private int m_maxRTO = 0; 161 private int m_maxRTO = 0;
160 162 private int m_ackTimeout = 0;
163 private int m_pausedAckTimeout = 0;
161 private bool m_disableFacelights = false; 164 private bool m_disableFacelights = false;
162 165
163 public Socket Server { get { return null; } } 166 public Socket Server { get { return null; } }
164 167
165 private int m_malformedCount = 0; // Guard against a spamming attack 168 private int m_malformedCount = 0; // Guard against a spamming attack
166 169
170 /// <summary>
171 /// Record current outgoing client for monitoring purposes.
172 /// </summary>
173 private IClientAPI m_currentOutgoingClient;
174
175 /// <summary>
176 /// Recording current incoming client for monitoring purposes.
177 /// </summary>
178 private IClientAPI m_currentIncomingClient;
179
167 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) 180 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
168 : base(listenIP, (int)port) 181 : base(listenIP, (int)port)
169 { 182 {
@@ -200,11 +213,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
200 m_defaultRTO = config.GetInt("DefaultRTO", 0); 213 m_defaultRTO = config.GetInt("DefaultRTO", 0);
201 m_maxRTO = config.GetInt("MaxRTO", 0); 214 m_maxRTO = config.GetInt("MaxRTO", 0);
202 m_disableFacelights = config.GetBoolean("DisableFacelights", false); 215 m_disableFacelights = config.GetBoolean("DisableFacelights", false);
216 m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60);
217 m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300);
203 } 218 }
204 else 219 else
205 { 220 {
206 PrimUpdatesPerCallback = 100; 221 PrimUpdatesPerCallback = 100;
207 TextureSendLimit = 20; 222 TextureSendLimit = 20;
223 m_ackTimeout = 1000 * 60; // 1 minute
224 m_pausedAckTimeout = 1000 * 300; // 5 minutes
208 } 225 }
209 226
210 #region BinaryStats 227 #region BinaryStats
@@ -241,19 +258,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP
241 if (m_scene == null) 258 if (m_scene == null)
242 throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); 259 throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
243 260
244 m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode"); 261 m_log.InfoFormat(
262 "[LLUDPSERVER]: Starting the LLUDP server in {0} mode",
263 m_asyncPacketHandling ? "asynchronous" : "synchronous");
245 264
246 base.Start(m_recvBufferSize, m_asyncPacketHandling); 265 base.Start(m_recvBufferSize, m_asyncPacketHandling);
247 266
248 // Start the packet processing threads 267 // Start the packet processing threads
249 Watchdog.StartThread( 268 Watchdog.StartThread(
250 IncomingPacketHandler, "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); 269 IncomingPacketHandler,
270 string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
271 ThreadPriority.Normal,
272 false,
273 true,
274 GetWatchdogIncomingAlarmData,
275 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
276
251 Watchdog.StartThread( 277 Watchdog.StartThread(
252 OutgoingPacketHandler, "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); 278 OutgoingPacketHandler,
279 string.Format("Outgoing Packets ({0})", m_scene.RegionInfo.RegionName),
280 ThreadPriority.Normal,
281 false,
282 true,
283 GetWatchdogOutgoingAlarmData,
284 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
253 285
254 m_elapsedMSSinceLastStatReport = Environment.TickCount; 286 m_elapsedMSSinceLastStatReport = Environment.TickCount;
255 } 287 }
256 288
289 /// <summary>
290 /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
291 /// </summary>
292 /// <returns></returns>
293 private string GetWatchdogIncomingAlarmData()
294 {
295 return string.Format(
296 "Client is {0}",
297 m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none");
298 }
299
300 /// <summary>
301 /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
302 /// </summary>
303 /// <returns></returns>
304 private string GetWatchdogOutgoingAlarmData()
305 {
306 return string.Format(
307 "Client is {0}",
308 m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
309 }
310
257 public new void Stop() 311 public new void Stop()
258 { 312 {
259 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); 313 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
@@ -487,19 +541,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
487 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null); 541 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
488 } 542 }
489 543
490 public void HandleUnacked(LLUDPClient udpClient) 544 public void HandleUnacked(LLClientView client)
491 { 545 {
546 LLUDPClient udpClient = client.UDPClient;
547
492 if (!udpClient.IsConnected) 548 if (!udpClient.IsConnected)
493 return; 549 return;
494 550
495 // Disconnect an agent if no packets are received for some time 551 // Disconnect an agent if no packets are received for some time
496 //FIXME: Make 60 an .ini setting 552 int timeoutTicks = m_ackTimeout;
497 if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > 1000 * 60) 553
554 // Allow more slack if the client is "paused" eg file upload dialogue is open
555 // Some sort of limit is needed in case the client crashes, loses its network connection
556 // or some other disaster prevents it from sendung the AgentResume
557 if (udpClient.IsPaused)
558 timeoutTicks = m_pausedAckTimeout;
559
560 if (client.IsActive &&
561 (Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks)
498 { 562 {
499 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); 563 // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
500 StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); 564 // though it's set later on by LLClientView.Close()
565 client.IsActive = false;
566
567 // Fire this out on a different thread so that we don't hold up outgoing packet processing for
568 // everybody else if this is being called due to an ack timeout.
569 // This is the same as processing as the async process of a logout request.
570 Util.FireAndForget(o => DeactivateClientDueToTimeout(client));
501 571
502 RemoveClient(udpClient);
503 return; 572 return;
504 } 573 }
505 574
@@ -850,7 +919,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
850 #endregion Ping Check Handling 919 #endregion Ping Check Handling
851 920
852 // Inbox insertion 921 // Inbox insertion
853 packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); 922 packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet));
854 } 923 }
855 924
856 #region BinaryStats 925 #region BinaryStats
@@ -946,7 +1015,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
946 UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; 1015 UDPPacketBuffer buffer = (UDPPacketBuffer)array[0];
947 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; 1016 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
948 1017
949 m_log.DebugFormat("[LLUDPSERVER]: Handling UseCircuitCode request from {0}", buffer.RemoteEndPoint); 1018 m_log.DebugFormat(
1019 "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
1020 uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint);
950 1021
951 remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; 1022 remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
952 1023
@@ -1001,8 +1072,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1001 { 1072 {
1002 // Don't create clients for unauthorized requesters. 1073 // Don't create clients for unauthorized requesters.
1003 m_log.WarnFormat( 1074 m_log.WarnFormat(
1004 "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", 1075 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
1005 uccp.CircuitCode.ID, uccp.CircuitCode.Code, remoteEndPoint); 1076 uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint);
1006 lock (m_pendingCache) 1077 lock (m_pendingCache)
1007 m_pendingCache.Remove(remoteEndPoint); 1078 m_pendingCache.Remove(remoteEndPoint);
1008 } 1079 }
@@ -1090,7 +1161,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1090 { 1161 {
1091 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); 1162 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
1092 1163
1093 client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); 1164 client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
1094 client.OnLogout += LogoutHandler; 1165 client.OnLogout += LogoutHandler;
1095 1166
1096 ((LLClientView)client).DisableFacelights = m_disableFacelights; 1167 ((LLClientView)client).DisableFacelights = m_disableFacelights;
@@ -1102,15 +1173,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1102 return client; 1173 return client;
1103 } 1174 }
1104 1175
1105 private void RemoveClient(LLUDPClient udpClient) 1176 /// <summary>
1177 /// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds).
1178 /// </summary>
1179 /// <remarks>
1180 /// If a connection is active then we will always receive packets even if nothing else is happening, due to
1181 /// regular client pings.
1182 /// </remarks>
1183 /// <param name='client'></param>
1184 private void DeactivateClientDueToTimeout(IClientAPI client)
1106 { 1185 {
1107 // Remove this client from the scene 1186 // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
1108 IClientAPI client; 1187 // though it's set later on by LLClientView.Close()
1109 if (m_scene.TryGetClient(udpClient.AgentID, out client)) 1188 client.IsActive = false;
1110 { 1189
1111 client.IsLoggingOut = true; 1190 m_log.WarnFormat(
1112 client.Close(false); 1191 "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}",
1113 } 1192 client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName);
1193
1194 StatsManager.SimExtraStats.AddAbnormalClientThreadTermination();
1195
1196 if (!client.SceneAgent.IsChildAgent)
1197 client.Kick("Simulator logged you out due to connection timeout");
1198
1199 Util.FireAndForget(o => client.Close());
1114 } 1200 }
1115 1201
1116 private void IncomingPacketHandler() 1202 private void IncomingPacketHandler()
@@ -1219,6 +1305,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1219 // client. m_packetSent will be set to true if a packet is sent 1305 // client. m_packetSent will be set to true if a packet is sent
1220 m_scene.ForEachClient(clientPacketHandler); 1306 m_scene.ForEachClient(clientPacketHandler);
1221 1307
1308 m_currentOutgoingClient = null;
1309
1222 // If nothing was sent, sleep for the minimum amount of time before a 1310 // If nothing was sent, sleep for the minimum amount of time before a
1223 // token bucket could get more tokens 1311 // token bucket could get more tokens
1224 if (!m_packetSent) 1312 if (!m_packetSent)
@@ -1235,18 +1323,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1235 Watchdog.RemoveThread(); 1323 Watchdog.RemoveThread();
1236 } 1324 }
1237 1325
1238 private void ClientOutgoingPacketHandler(IClientAPI client) 1326 protected void ClientOutgoingPacketHandler(IClientAPI client)
1239 { 1327 {
1328 m_currentOutgoingClient = client;
1329
1240 try 1330 try
1241 { 1331 {
1242 if (client is LLClientView) 1332 if (client is LLClientView)
1243 { 1333 {
1244 LLUDPClient udpClient = ((LLClientView)client).UDPClient; 1334 LLClientView llClient = (LLClientView)client;
1335 LLUDPClient udpClient = llClient.UDPClient;
1245 1336
1246 if (udpClient.IsConnected) 1337 if (udpClient.IsConnected)
1247 { 1338 {
1248 if (m_resendUnacked) 1339 if (m_resendUnacked)
1249 HandleUnacked(udpClient); 1340 HandleUnacked(llClient);
1250 1341
1251 if (m_sendAcks) 1342 if (m_sendAcks)
1252 SendAcks(udpClient); 1343 SendAcks(udpClient);
@@ -1262,8 +1353,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1262 } 1353 }
1263 catch (Exception ex) 1354 catch (Exception ex)
1264 { 1355 {
1265 m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + 1356 m_log.Error(
1266 " threw an exception: " + ex.Message, ex); 1357 string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex);
1267 } 1358 }
1268 } 1359 }
1269 1360
@@ -1289,11 +1380,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1289 { 1380 {
1290 nticks++; 1381 nticks++;
1291 watch1.Start(); 1382 watch1.Start();
1383 m_currentOutgoingClient = client;
1384
1292 try 1385 try
1293 { 1386 {
1294 if (client is LLClientView) 1387 if (client is LLClientView)
1295 { 1388 {
1296 LLUDPClient udpClient = ((LLClientView)client).UDPClient; 1389 LLClientView llClient = (LLClientView)client;
1390 LLUDPClient udpClient = llClient.UDPClient;
1297 1391
1298 if (udpClient.IsConnected) 1392 if (udpClient.IsConnected)
1299 { 1393 {
@@ -1302,7 +1396,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1302 nticksUnack++; 1396 nticksUnack++;
1303 watch2.Start(); 1397 watch2.Start();
1304 1398
1305 HandleUnacked(udpClient); 1399 HandleUnacked(llClient);
1306 1400
1307 watch2.Stop(); 1401 watch2.Stop();
1308 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); 1402 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);
@@ -1373,23 +1467,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1373 1467
1374 #endregion 1468 #endregion
1375 1469
1376 private void ProcessInPacket(object state) 1470 private void ProcessInPacket(IncomingPacket incomingPacket)
1377 { 1471 {
1378 IncomingPacket incomingPacket = (IncomingPacket)state;
1379 Packet packet = incomingPacket.Packet; 1472 Packet packet = incomingPacket.Packet;
1380 LLUDPClient udpClient = incomingPacket.Client; 1473 LLClientView client = incomingPacket.Client;
1381 IClientAPI client;
1382 1474
1383 // Sanity check 1475 if (client.IsActive)
1384 if (packet == null || udpClient == null)
1385 { 1476 {
1386 m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"", 1477 m_currentIncomingClient = client;
1387 packet, udpClient);
1388 }
1389 1478
1390 // Make sure this client is still alive
1391 if (m_scene.TryGetClient(udpClient.AgentID, out client))
1392 {
1393 try 1479 try
1394 { 1480 {
1395 // Process this packet 1481 // Process this packet
@@ -1404,21 +1490,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1404 catch (Exception e) 1490 catch (Exception e)
1405 { 1491 {
1406 // Don't let a failure in an individual client thread crash the whole sim. 1492 // Don't let a failure in an individual client thread crash the whole sim.
1407 m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); 1493 m_log.Error(
1408 m_log.Error(e.Message, e); 1494 string.Format(
1495 "[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw ",
1496 client.Name, packet.Type),
1497 e);
1498 }
1499 finally
1500 {
1501 m_currentIncomingClient = null;
1409 } 1502 }
1410 } 1503 }
1411 else 1504 else
1412 { 1505 {
1413 m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID); 1506 m_log.DebugFormat(
1507 "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}",
1508 packet.Type, client.Name, m_scene.RegionInfo.RegionName);
1414 } 1509 }
1415 } 1510 }
1416 1511
1417 protected void LogoutHandler(IClientAPI client) 1512 protected void LogoutHandler(IClientAPI client)
1418 { 1513 {
1419 client.SendLogoutPacket(); 1514 client.SendLogoutPacket();
1420 if (client.IsActive) 1515
1421 RemoveClient(((LLClientView)client).UDPClient); 1516 if (!client.IsLoggingOut)
1517 {
1518 client.IsLoggingOut = true;
1519 client.Close();
1520 }
1422 } 1521 }
1423 } 1522 }
1424} 1523}