/** * @file llpacketring.cpp * @brief implementation of LLPacketRing class for a packet. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llpacketring.h" #if LL_WINDOWS #include <winsock2.h> #else #include <sys/socket.h> #include <netinet/in.h> #endif // linden library includes #include "llerror.h" #include "lltimer.h" #include "llproxy.h" #include "llrand.h" #include "message.h" #include "u64.h" /////////////////////////////////////////////////////////// LLPacketRing::LLPacketRing () : mUseInThrottle(FALSE), mUseOutThrottle(FALSE), mInThrottle(256000.f), mOutThrottle(64000.f), mActualBitsIn(0), mActualBitsOut(0), mMaxBufferLength(64000), mInBufferLength(0), mOutBufferLength(0), mDropPercentage(0.0f), mPacketsToDrop(0x0) { } /////////////////////////////////////////////////////////// LLPacketRing::~LLPacketRing () { cleanup(); } /////////////////////////////////////////////////////////// void LLPacketRing::cleanup () { LLPacketBuffer *packetp; while (!mReceiveQueue.empty()) { packetp = mReceiveQueue.front(); delete packetp; mReceiveQueue.pop(); } while (!mSendQueue.empty()) { packetp = mSendQueue.front(); delete packetp; mSendQueue.pop(); } } /////////////////////////////////////////////////////////// void LLPacketRing::dropPackets (U32 num_to_drop) { mPacketsToDrop += num_to_drop; } /////////////////////////////////////////////////////////// void LLPacketRing::setDropPercentage (F32 percent_to_drop) { mDropPercentage = percent_to_drop; } void LLPacketRing::setUseInThrottle(const BOOL use_throttle) { mUseInThrottle = use_throttle; } void LLPacketRing::setUseOutThrottle(const BOOL use_throttle) { mUseOutThrottle = use_throttle; } void LLPacketRing::setInBandwidth(const F32 bps) { mInThrottle.setRate(bps); } void LLPacketRing::setOutBandwidth(const F32 bps) { mOutThrottle.setRate(bps); } /////////////////////////////////////////////////////////// S32 LLPacketRing::receiveFromRing (S32 socket, char *datap) { if (mInThrottle.checkOverflow(0)) { // We don't have enough bandwidth, don't give them a packet. return 0; } LLPacketBuffer *packetp = NULL; if (mReceiveQueue.empty()) { // No packets on the queue, don't give them any. return 0; } S32 packet_size = 0; packetp = mReceiveQueue.front(); mReceiveQueue.pop(); packet_size = packetp->getSize(); if (packetp->getData() != NULL) { memcpy(datap, packetp->getData(), packet_size); /*Flawfinder: ignore*/ } // need to set sender IP/port!! mLastSender = packetp->getHost(); mLastReceivingIF = packetp->getReceivingInterface(); delete packetp; this->mInBufferLength -= packet_size; // Adjust the throttle mInThrottle.throttleOverflow(packet_size * 8.f); return packet_size; } /////////////////////////////////////////////////////////// S32 LLPacketRing::receivePacket (S32 socket, char *datap) { S32 packet_size = 0; // If using the throttle, simulate a limited size input buffer. if (mUseInThrottle) { BOOL done = FALSE; // push any current net packet (if any) onto delay ring while (!done) { LLPacketBuffer *packetp; packetp = new LLPacketBuffer(socket); if (packetp->getSize()) { mActualBitsIn += packetp->getSize() * 8; // Fake packet loss if (mDropPercentage && (ll_frand(100.f) < mDropPercentage)) { mPacketsToDrop++; } if (mPacketsToDrop) { delete packetp; packetp = NULL; packet_size = 0; mPacketsToDrop--; } } // If we faked packet loss, then we don't have a packet // to use for buffer overflow testing if (packetp) { if (mInBufferLength + packetp->getSize() > mMaxBufferLength) { // Toss it. LL_WARNS() << "Throwing away packet, overflowing buffer" << LL_ENDL; delete packetp; packetp = NULL; } else if (packetp->getSize()) { mReceiveQueue.push(packetp); mInBufferLength += packetp->getSize(); } else { delete packetp; packetp = NULL; done = true; } } else { // No packetp, keep going? - no packetp == faked packet loss } } // Now, grab data off of the receive queue according to our // throttled bandwidth settings. packet_size = receiveFromRing(socket, datap); } else { // no delay, pull straight from net if (LLProxy::isSOCKSProxyEnabled()) { U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer))); if (packet_size > SOCKS_HEADER_SIZE) { // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6) memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size - SOCKS_HEADER_SIZE); proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer)); mLastSender.setAddress(header->addr); mLastSender.setPort(ntohs(header->port)); packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size } else { packet_size = 0; } } else { packet_size = receive_packet(socket, datap); mLastSender = ::get_sender(); } mLastReceivingIF = ::get_receiving_interface(); if (packet_size) // did we actually get a packet? { if (mDropPercentage && (ll_frand(100.f) < mDropPercentage)) { mPacketsToDrop++; } if (mPacketsToDrop) { packet_size = 0; mPacketsToDrop--; } } } return packet_size; } BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host) { BOOL status = TRUE; if (!mUseOutThrottle) { return sendPacketImpl(h_socket, send_buffer, buf_size, host ); } else { mActualBitsOut += buf_size * 8; LLPacketBuffer *packetp = NULL; // See if we've got enough throttle to send a packet. while (!mOutThrottle.checkOverflow(0.f)) { // While we have enough bandwidth, send a packet from the queue or the current packet S32 packet_size = 0; if (!mSendQueue.empty()) { // Send a packet off of the queue LLPacketBuffer *packetp = mSendQueue.front(); mSendQueue.pop(); mOutBufferLength -= packetp->getSize(); packet_size = packetp->getSize(); status = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost()); delete packetp; // Update the throttle mOutThrottle.throttleOverflow(packet_size * 8.f); } else { // If the queue's empty, we can just send this packet right away. status = sendPacketImpl(h_socket, send_buffer, buf_size, host ); packet_size = buf_size; // Update the throttle mOutThrottle.throttleOverflow(packet_size * 8.f); // This was the packet we're sending now, there are no other packets // that we need to send return status; } } // We haven't sent the incoming packet, add it to the queue if (mOutBufferLength + buf_size > mMaxBufferLength) { // Nuke this packet, we overflowed the buffer. // Toss it. LL_WARNS() << "Throwing away outbound packet, overflowing buffer" << LL_ENDL; } else { static LLTimer queue_timer; if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f) { // Add it to the queue LL_INFOS() << "Outbound packet queue " << mOutBufferLength << " bytes" << LL_ENDL; queue_timer.reset(); } packetp = new LLPacketBuffer(host, send_buffer, buf_size); mOutBufferLength += packetp->getSize(); mSendQueue.push(packetp); } } return status; } BOOL LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host) { if (!LLProxy::isSOCKSProxyEnabled()) { return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort()); } char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer)); socks_header->rsv = 0; socks_header->addr = host.getAddress(); socks_header->port = htons(host.getPort()); socks_header->atype = ADDRESS_IPV4; socks_header->frag = 0; memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size); return send_packet( h_socket, headered_send_buffer, buf_size + SOCKS_HEADER_SIZE, LLProxy::getInstance()->getUDPProxy().getAddress(), LLProxy::getInstance()->getUDPProxy().getPort()); }