From 6f58064f104ef231faed3c2259885065c4242eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 7 Feb 2008 15:09:19 +0000 Subject: [PATCH] * Added flags field in net_protocol_module_info; there is currently a single defined flag: NET_PROTOCOL_ATOMIC_MESSAGES. * socket_send() now honours NET_PROTOCOL_ATOMIC_MESSAGES and returns either EMSGSIZE if the data to be send is larger than net_socket::send::buffer_size, or divides the data in appropriately sized chunks. * This fixes sending >=64K over a TCP socket at once (TCP would just have returned an error in that case). * TCP now overrides the default send buffer size (to 32768 for now). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23915 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/net/net_protocol.h | 6 +- .../kernel/network/protocols/icmp/icmp.cpp | 4 +- .../kernel/network/protocols/ipv4/ipv4.cpp | 4 +- .../network/protocols/tcp/TCPEndpoint.cpp | 4 +- .../kernel/network/protocols/tcp/tcp.cpp | 7 +- .../kernel/network/protocols/udp/udp.cpp | 4 +- src/add-ons/kernel/network/stack/link.cpp | 4 +- .../kernel/network/stack/net_socket.cpp | 135 +++++++++++++----- 8 files changed, 121 insertions(+), 47 deletions(-) diff --git a/headers/private/net/net_protocol.h b/headers/private/net/net_protocol.h index 491b9799e1..3974a979cd 100644 --- a/headers/private/net/net_protocol.h +++ b/headers/private/net/net_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef NET_PROTOCOL_H @@ -22,8 +22,12 @@ typedef struct net_protocol { net_socket *socket; } net_protocol; +// net_protocol_module_info::flags field +#define NET_PROTOCOL_ATOMIC_MESSAGES 0x01 + struct net_protocol_module_info { module_info info; + uint32 flags; net_protocol *(*init_protocol)(net_socket *socket); status_t (*uninit_protocol)(net_protocol *self); diff --git a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp index 10ff9ee7cc..baeffe4749 100644 --- a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp +++ b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -338,6 +338,8 @@ net_protocol_module_info sICMPModule = { 0, icmp_std_ops }, + NET_PROTOCOL_ATOMIC_MESSAGES, + icmp_init_protocol, icmp_uninit_protocol, icmp_open, diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index a5f2f1f5a5..cd4bc42864 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -1698,6 +1698,8 @@ net_protocol_module_info gIPv4Module = { 0, ipv4_std_ops }, + NET_PROTOCOL_ATOMIC_MESSAGES, + ipv4_init_protocol, ipv4_uninit_protocol, ipv4_open, diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index ebf9ad2545..aea50eb0d0 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -564,7 +564,7 @@ TCPEndpoint::SendData(net_buffer *buffer) if (buffer->size > 0) { if (buffer->size > fSendQueue.Size()) - return EMSGSIZE; + return ENOBUFS; bigtime_t timeout = absolute_timeout(socket->send.timeout); diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 19d5f1b601..1ac0621e1d 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -421,6 +421,9 @@ dump_endpoint(int argc, char *argv[]) net_protocol * tcp_init_protocol(net_socket *socket) { + socket->send.buffer_size = 32768; + // override net_socket default + TCPEndpoint *protocol = new (std::nothrow) TCPEndpoint(socket); if (protocol == NULL) return NULL; @@ -775,6 +778,8 @@ net_protocol_module_info sTCPModule = { 0, tcp_std_ops }, + 0, + tcp_init_protocol, tcp_uninit_protocol, tcp_open, diff --git a/src/add-ons/kernel/network/protocols/udp/udp.cpp b/src/add-ons/kernel/network/protocols/udp/udp.cpp index 7510aa3a33..ce9d864836 100644 --- a/src/add-ons/kernel/network/protocols/udp/udp.cpp +++ b/src/add-ons/kernel/network/protocols/udp/udp.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -1230,6 +1230,8 @@ net_protocol_module_info sUDPModule = { 0, udp_std_ops }, + NET_PROTOCOL_ATOMIC_MESSAGES, + udp_init_protocol, udp_uninit_protocol, udp_open, diff --git a/src/add-ons/kernel/network/stack/link.cpp b/src/add-ons/kernel/network/stack/link.cpp index 21a30b2b6a..85096cba0f 100644 --- a/src/add-ons/kernel/network/stack/link.cpp +++ b/src/add-ons/kernel/network/stack/link.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -477,6 +477,8 @@ net_protocol_module_info gLinkModule = { 0, link_std_ops }, + NET_PROTOCOL_ATOMIC_MESSAGES, + link_init_protocol, link_uninit_protocol, link_open, diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index 98b71e8136..34b4722fde 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -48,6 +48,23 @@ struct list sSocketList; benaphore sSocketLock; +static size_t +compute_user_iovec_length(iovec *userVec, uint32 count) +{ + size_t length = 0; + + for (uint32 i = 0; i < count; i++) { + iovec vec; + if (user_memcpy(&vec, userVec + i, sizeof(iovec)) < B_OK) + return 0; + + length += vec.iov_len; + } + + return length; +} + + static status_t create_socket(int family, int type, int protocol, net_socket_private **_socket) { @@ -883,11 +900,15 @@ socket_receive(net_socket *socket, msghdr *header, void *data, size_t length, ssize_t -socket_send(net_socket *socket, msghdr *header, const void *data, - size_t length, int flags) +socket_send(net_socket *socket, msghdr *header, const void *data, size_t length, + int flags) { const sockaddr *address = NULL; socklen_t addressLength = 0; + size_t bytesLeft = length; + + if (length > SSIZE_MAX) + return B_BAD_VALUE; // the convention to this function is that have header been // present, { data, length } would have been iovec[0] and is @@ -899,6 +920,10 @@ socket_send(net_socket *socket, msghdr *header, const void *data, if (header->msg_iovlen <= 1) header = NULL; + else { + bytesLeft += compute_user_iovec_length(header->msg_iov + 1, + header->msg_iovlen - 1); + } } if (addressLength == 0) @@ -920,6 +945,10 @@ socket_send(net_socket *socket, msghdr *header, const void *data, return EDESTADDRREQ; } + if ((socket->first_info->flags & NET_PROTOCOL_ATOMIC_MESSAGES) != 0 + && bytesLeft > socket->send.buffer_size) + return EMSGSIZE; + if (socket->address.ss_len == 0) { // try to bind first status_t status = socket_bind(socket, NULL, 0); @@ -927,51 +956,79 @@ socket_send(net_socket *socket, msghdr *header, const void *data, return status; } - // TODO: useful, maybe even computed header space! - net_buffer *buffer = gNetBufferModule.create(256); - if (buffer == NULL) - return ENOBUFS; + ssize_t bytesSent = 0; + size_t vecOffset = 0; + uint32 vecIndex = 0; - if (gNetBufferModule.append(buffer, data, length) < B_OK) { - gNetBufferModule.free(buffer); - return ENOBUFS; - } + while (bytesLeft > 0) { + // TODO: useful, maybe even computed header space! + net_buffer *buffer = gNetBufferModule.create(256); + if (buffer == NULL) + return ENOBUFS; - if (header) { - // copy additional data into buffer - for (int i = 1; i < header->msg_iovlen; i++) { - iovec vec; - if (user_memcpy(&vec, header->msg_iov + i, sizeof(iovec)) < B_OK) - return B_BAD_ADDRESS; - if (gNetBufferModule.append(buffer, vec.iov_base, - vec.iov_len) < B_OK) { + while (buffer->size < socket->send.buffer_size + && buffer->size < bytesLeft) { + if (vecIndex > 0 && vecOffset == 0) { + // retrieve next iovec buffer from header + iovec vec; + if (user_memcpy(&vec, header->msg_iov + vecIndex, sizeof(iovec)) + < B_OK) { + gNetBufferModule.free(buffer); + return B_BAD_ADDRESS; + } + + data = vec.iov_base; + length = vec.iov_len; + } + + size_t bytes = length; + if (buffer->size + bytes > socket->send.buffer_size) + bytes = socket->send.buffer_size - buffer->size; + + if (gNetBufferModule.append(buffer, data, bytes) < B_OK) { gNetBufferModule.free(buffer); return ENOBUFS; } - length += vec.iov_len; + if (bytes != length) { + // partial send + vecOffset = bytes; + length -= vecOffset; + data = (uint8 *)data + vecOffset; + } else if (header != NULL) { + // proceed with next buffer, if any + vecOffset = 0; + vecIndex++; + + if (vecIndex >= (uint32)header->msg_iovlen) + break; + } } + + size_t bufferSize = buffer->size; + buffer->flags = flags; + memcpy(buffer->source, &socket->address, socket->address.ss_len); + memcpy(buffer->destination, address, addressLength); + + status_t status = socket->first_info->send_data(socket->first_protocol, + buffer); + if (status < B_OK) { + size_t sizeAfterSend = buffer->size; + gNetBufferModule.free(buffer); + + if (sizeAfterSend != bufferSize + && (status == B_INTERRUPTED || status == B_WOULD_BLOCK)) { + // this appears to be a partial write + return bytesSent + (bufferSize - sizeAfterSend); + } + return status; + } + + bytesLeft -= bufferSize; + bytesSent += bufferSize; } - buffer->flags = flags; - memcpy(buffer->source, &socket->address, socket->address.ss_len); - memcpy(buffer->destination, address, addressLength); - - status_t status = socket->first_info->send_data(socket->first_protocol, - buffer); - if (status < B_OK) { - size_t size = buffer->size; - gNetBufferModule.free(buffer); - - if (size != length - && (status == B_INTERRUPTED || status == B_WOULD_BLOCK)) { - // this appears to be a partial write - return length - size; - } - return status; - } - - return length; + return bytesSent; }