gboolean socket_addToOutputBuffer(Socket* socket, Packet* packet) { MAGIC_ASSERT(socket); /* check if the packet fits */ guint length = packet_getPayloadLength(packet); if(length > socket_getOutputBufferSpace(socket)) { return FALSE; } /* add to our queue */ g_queue_push_tail(socket->outputBuffer, packet); socket->outputBufferLength += length; packet_addDeliveryStatus(packet, PDS_SND_SOCKET_BUFFERED); /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we just added a packet, we are no longer writable if full */ if(socket_getOutputBufferSpace(socket) <= 0) { descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, FALSE); } /* tell the interface to include us when sending out to the network */ in_addr_t ip = packet_getSourceIP(packet); NetworkInterface* interface = host_lookupInterface(worker_getCurrentHost(), ip); networkinterface_wantsSend(interface, socket); return TRUE; }
Packet* socket_removeFromOutputBuffer(Socket* socket) { MAGIC_ASSERT(socket); /* see if we have any packets */ Packet* packet = g_queue_pop_head(socket->outputBuffer); if(packet) { /* just removed a packet */ guint length = packet_getPayloadLength(packet); socket->outputBufferLength -= length; /* check if we need to reduce the buffer size */ if(socket->outputBufferSizePending > 0) { socket_setOutputBufferSize(socket, socket->outputBufferSizePending); } /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we are writable if we now have space */ if(socket_getOutputBufferSpace(socket) > 0) { descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, TRUE); } } return packet; }
static gsize _tcp_getBufferSpaceOut(TCP* tcp) { MAGIC_ASSERT(tcp); /* account for throttled and retransmission buffer */ gssize s = (gssize)(socket_getOutputBufferSpace(&(tcp->super)) - tcp->throttledOutputLength - tcp->retransmissionLength); gsize space = MAX(0, s); if(space == 0) { descriptor_adjustStatus((Descriptor*)tcp, DS_WRITABLE, FALSE); } return space; }
static void _tcp_updateReceiveWindow(TCP* tcp) { MAGIC_ASSERT(tcp); gsize space = socket_getOutputBufferSpace(&(tcp->super)); gsize nPackets = space / (CONFIG_MTU - CONFIG_HEADER_SIZE_TCPIPETH); tcp->receive.window = nPackets; if(tcp->receive.window < 1) { tcp->receive.window = 1; } }
/* * this function builds a UDP packet and sends to the virtual node given by the * ip and port parameters. this function assumes that the socket is already * bound to a local port, no matter if that happened explicitly or implicitly. */ gssize udp_sendUserData(UDP* udp, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { MAGIC_ASSERT(udp); gsize space = socket_getOutputBufferSpace(&(udp->super)); if(space < nBytes) { /* not enough space to buffer the data */ return -1; } /* break data into segments and send each in a packet */ gsize maxPacketLength = CONFIG_DATAGRAM_MAX_SIZE; gsize remaining = nBytes; gsize offset = 0; /* create as many packets as needed */ while(remaining > 0) { gsize copyLength = MIN(maxPacketLength, remaining); /* use default destination if none was specified */ in_addr_t destinationIP = (ip != 0) ? ip : udp->super.peerIP; in_port_t destinationPort = (port != 0) ? port : udp->super.peerPort; /* create the UDP packet */ Packet* packet = packet_new(buffer + offset, copyLength); packet_setUDP(packet, PUDP_NONE, socket_getBinding(&(udp->super)), udp->super.boundPort, destinationIP, destinationPort); /* buffer it in the transport layer, to be sent out when possible */ gboolean success = socket_addToOutputBuffer((Socket*) udp, packet); /* counter maintenance */ if(success) { remaining -= copyLength; offset += copyLength; } else { warning("unable to send UDP packet"); break; } } /* update the tracker output buffer stats */ Tracker* tracker = node_getTracker(worker_getPrivate()->cached_node); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("buffered %"G_GSIZE_FORMAT" outbound UDP bytes from user", offset); return (gssize) offset; }
static void _tcp_flush(TCP* tcp) { MAGIC_ASSERT(tcp); /* make sure our information is up to date */ _tcp_updateReceiveWindow(tcp); _tcp_updateSendWindow(tcp); /* flush packets that can now be sent to socket */ while(g_queue_get_length(tcp->throttledOutput) > 0) { /* get the next throttled packet, in sequence order */ Packet* packet = g_queue_pop_head(tcp->throttledOutput); /* break out if we have no packets left */ if(!packet) { break; } guint length = packet_getPayloadLength(packet); if(length > 0) { PacketTCPHeader header; packet_getTCPHeader(packet, &header); /* we cant send it if our window is too small */ gboolean fitsInWindow = (header.sequence < (tcp->send.unacked + tcp->send.window)) ? TRUE : FALSE; /* we cant send it if we dont have enough space */ gboolean fitsInBuffer = (length <= socket_getOutputBufferSpace(&(tcp->super))) ? TRUE : FALSE; if(!fitsInBuffer || !fitsInWindow) { /* we cant send the packet yet */ g_queue_push_head(tcp->throttledOutput, packet); break; } else { /* we will send: store length in virtual retransmission buffer * so we can reduce buffer space consumed when we receive the ack */ _tcp_addRetransmit(tcp, header.sequence, length); } } /* packet is sendable, we removed it from out buffer */ tcp->throttledOutputLength -= length; /* update TCP header to our current advertised window and acknowledgement */ packet_updateTCP(packet, tcp->receive.next, tcp->receive.window); /* keep track of the last things we sent them */ tcp->send.lastAcknowledgement = tcp->receive.next; tcp->send.lastWindow = tcp->receive.window; /* socket will queue it ASAP */ gboolean success = socket_addToOutputBuffer(&(tcp->super), packet); /* we already checked for space, so this should always succeed */ g_assert(success); } /* any packets now in order can be pushed to our user input buffer */ while(g_queue_get_length(tcp->unorderedInput) > 0) { Packet* packet = g_queue_pop_head(tcp->unorderedInput); PacketTCPHeader header; packet_getTCPHeader(packet, &header); if(header.sequence == tcp->receive.next) { /* move from the unordered buffer to user input buffer */ gboolean fitInBuffer = socket_addToInputBuffer(&(tcp->super), packet); if(fitInBuffer) { tcp->unorderedInputLength -= packet_getPayloadLength(packet); (tcp->receive.next)++; continue; } } /* we could not buffer it because its out of order or we have no space */ g_queue_push_head(tcp->unorderedInput, packet); break; } /* check if user needs an EOF signal */ gboolean wantsEOF = ((tcp->flags & TCPF_LOCAL_CLOSED) || (tcp->flags & TCPF_REMOTE_CLOSED)) ? TRUE : FALSE; if(wantsEOF) { /* if anyone closed, can't send anymore */ tcp->error |= TCPE_SEND_EOF; if((tcp->receive.next >= tcp->receive.end) && !(tcp->flags & TCPF_EOF_SIGNALED)) { /* user needs to read a 0 so it knows we closed */ tcp->error |= TCPE_RECEIVE_EOF; descriptor_adjustStatus((Descriptor*)tcp, DS_READABLE, TRUE); } } }
/* * this function builds a UDP packet and sends to the virtual node given by the * ip and port parameters. this function assumes that the socket is already * bound to a local port, no matter if that happened explicitly or implicitly. */ gssize udp_sendUserData(UDP* udp, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { MAGIC_ASSERT(udp); gsize space = socket_getOutputBufferSpace(&(udp->super)); if(space < nBytes) { /* not enough space to buffer the data */ return -1; } /* break data into segments and send each in a packet */ gsize maxPacketLength = CONFIG_DATAGRAM_MAX_SIZE; gsize remaining = nBytes; gsize offset = 0; /* create as many packets as needed */ while(remaining > 0) { gsize copyLength = MIN(maxPacketLength, remaining); /* use default destination if none was specified */ in_addr_t destinationIP = (ip != 0) ? ip : udp->super.peerIP; in_port_t destinationPort = (port != 0) ? port : udp->super.peerPort; in_addr_t sourceIP = 0; in_port_t sourcePort = 0; socket_getSocketName(&(udp->super), &sourceIP, &sourcePort); if(sourceIP == htonl(INADDR_ANY)) { /* source interface depends on destination */ if(destinationIP == htonl(INADDR_LOOPBACK)) { sourceIP = htonl(INADDR_LOOPBACK); } else { sourceIP = host_getDefaultIP(worker_getCurrentHost()); } } utility_assert(sourceIP && sourcePort && destinationIP && destinationPort); /* create the UDP packet */ Packet* packet = packet_new(buffer + offset, copyLength); packet_setUDP(packet, PUDP_NONE, sourceIP, sourcePort, destinationIP, destinationPort); packet_addDeliveryStatus(packet, PDS_SND_CREATED); /* buffer it in the transport layer, to be sent out when possible */ gboolean success = socket_addToOutputBuffer((Socket*) udp, packet); /* counter maintenance */ if(success) { remaining -= copyLength; offset += copyLength; } else { warning("unable to send UDP packet"); break; } } /* update the tracker output buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("buffered %"G_GSIZE_FORMAT" outbound UDP bytes from user", offset); return (gssize) offset; }