diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index 7eb38827b3..b8e72f8c3a 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -9,6 +9,9 @@ #include #include +#include +#include + #define NET_SOCKET_MODULE_NAME "network/stack/socket/v1" @@ -31,6 +34,11 @@ typedef struct net_socket { uint32 low_water_mark; bigtime_t timeout; } send, receive; + + // TODO: could be moved into a private structure + status_t error; + struct select_sync_pool *select_pool; + benaphore lock; } net_socket; struct net_socket_module_info { @@ -53,6 +61,13 @@ struct net_socket_module_info { status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags, net_buffer **_buffer); + // notifications + status_t (*request_notification)(net_socket *socket, uint8 event, uint32 ref, + selectsync *sync); + status_t (*cancel_notification)(net_socket *socket, uint8 event, + selectsync *sync); + status_t (*notify)(net_socket *socket, uint8 event, int32 value); + // standard socket API int (*accept)(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength, net_socket **_acceptedSocket); diff --git a/headers/private/net/net_stack.h b/headers/private/net/net_stack.h index 9f97fcd702..a3868f4bfc 100644 --- a/headers/private/net/net_stack.h +++ b/headers/private/net/net_stack.h @@ -76,6 +76,9 @@ struct net_stack_module_info { // Utility Functions + // notification + status_t (*notify_socket)(struct net_socket *socket, uint8 event, int32 value); + // checksum uint16 (*checksum)(uint8 *buffer, size_t length); diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index 9e1741bbc8..7cc4550d53 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -115,7 +115,7 @@ typedef DoublyLinkedList RawSocketList; class RawSocket : public DoublyLinkedListLinkImpl { public: - RawSocket(); + RawSocket(net_socket *socket); ~RawSocket(); status_t InitCheck(); @@ -127,6 +127,7 @@ class RawSocket : public DoublyLinkedListLinkImpl { status_t Write(net_buffer *buffer); private: + net_socket *fSocket; net_fifo fFifo; }; @@ -153,7 +154,9 @@ static benaphore sFragmentLock; static hash_table *sFragmentHash; -RawSocket::RawSocket() +RawSocket::RawSocket(net_socket *socket) + : + fSocket(socket) { status_t status = sStackModule->init_fifo(&fFifo, "ipv4 raw socket", 65536); if (status < B_OK) @@ -211,7 +214,13 @@ RawSocket::Write(net_buffer *source) if (buffer == NULL) return B_NO_MEMORY; - return sStackModule->fifo_enqueue_buffer(&fFifo, buffer); + status_t status = sStackModule->fifo_enqueue_buffer(&fFifo, buffer); + if (status >= B_OK) + sStackModule->notify_socket(fSocket, B_SELECT_READ, BytesAvailable()); + else + sBufferModule->free(buffer); + + return status; } @@ -597,7 +606,7 @@ ipv4_open(net_protocol *_protocol) { ipv4_protocol *protocol = (ipv4_protocol *)_protocol; - RawSocket *raw = new (std::nothrow) RawSocket; + RawSocket *raw = new (std::nothrow) RawSocket(protocol->socket); if (raw == NULL) return B_NO_MEMORY; diff --git a/src/add-ons/kernel/network/protocols/udp/udp.cpp b/src/add-ons/kernel/network/protocols/udp/udp.cpp index 20ed5fc74f..fc24af8e38 100644 --- a/src/add-ons/kernel/network/protocols/udp/udp.cpp +++ b/src/add-ons/kernel/network/protocols/udp/udp.cpp @@ -871,7 +871,9 @@ UdpEndpoint::StoreData(net_buffer *_buffer) return B_NO_MEMORY; status_t status = sStackModule->fifo_enqueue_buffer(&fFifo, buffer); - if (status < B_OK) + if (status >= B_OK) + sStackModule->notify_socket(socket, B_SELECT_READ, BytesAvailable()); + else sBufferModule->free(buffer); return status; diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index f9d456c7a1..8876080554 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -32,6 +33,10 @@ create_socket(int family, int type, int protocol, net_socket **_socket) socket->type = type; socket->protocol = protocol; + status_t status = benaphore_init(&socket->lock, "socket"); + if (status < B_OK) + goto err1; + // set defaults (may be overridden by the protocols) socket->send.buffer_size = 65536; socket->send.low_water_mark = 1; @@ -40,21 +45,26 @@ create_socket(int family, int type, int protocol, net_socket **_socket) socket->receive.low_water_mark = 1; socket->receive.timeout = B_INFINITE_TIMEOUT; - status_t status = get_domain_protocols(socket); - if (status < B_OK) { - delete socket; - return status; - } + socket->select_pool = NULL; + + status = get_domain_protocols(socket); + if (status < B_OK) + goto err2; status = socket->first_info->open(socket->first_protocol); - if (status < B_OK) { - put_domain_protocols(socket); - delete socket; - return status; - } + if (status < B_OK) + goto err3; *_socket = socket; return B_OK; + +err3: + put_domain_protocols(socket); +err2: + benaphore_destroy(&socket->lock); +err1: + delete socket; + return status; } @@ -71,6 +81,8 @@ socket_free(net_socket *socket) status_t status = socket->first_info->free(socket->first_protocol); put_domain_protocols(socket); + benaphore_destroy(&socket->lock); + delete_select_sync_pool(socket->select_pool); delete socket; return status; @@ -166,6 +178,96 @@ socket_receive_data(net_socket *socket, size_t length, uint32 flags, } +// #pragma mark - notifications + + +status_t +socket_request_notification(net_socket *socket, uint8 event, uint32 ref, + selectsync *sync) +{ + benaphore_lock(&socket->lock); + + status_t status = add_select_sync_pool_entry(&socket->select_pool, sync, + ref, event); + if (status < B_OK) { + benaphore_unlock(&socket->lock); + return status; + } + + // check if the event is already present + // TODO: add support for poll() types + + switch (event) { + case B_SELECT_READ: + { + ssize_t available = socket_read_avail(socket); + if ((ssize_t)socket->receive.low_water_mark <= available || available < B_OK) + notify_select_event(sync, ref, event); + break; + } + case B_SELECT_WRITE: + { + ssize_t available = socket_send_avail(socket); + if ((ssize_t)socket->send.low_water_mark <= available || available < B_OK) + notify_select_event(sync, ref, event); + break; + } + case B_SELECT_ERROR: + // TODO: B_SELECT_ERROR condition! + break; + } + + benaphore_unlock(&socket->lock); + return B_OK; +} + + +status_t +socket_cancel_notification(net_socket *socket, uint8 event, selectsync *sync) +{ + benaphore_lock(&socket->lock); + + status_t status = remove_select_sync_pool_entry(&socket->select_pool, + sync, event); + + benaphore_unlock(&socket->lock); + return status; +} + + +status_t +socket_notify(net_socket *socket, uint8 event, int32 value) +{ + benaphore_lock(&socket->lock); + + bool notify = true; + + switch (event) { + case B_SELECT_READ: + { + if ((ssize_t)socket->receive.low_water_mark > value && value >= B_OK) + notify = false; + break; + } + case B_SELECT_WRITE: + { + if ((ssize_t)socket->send.low_water_mark > value && value >= B_OK) + notify = false; + break; + } + case B_SELECT_ERROR: + socket->error = value; + break; + } + + if (notify && socket->select_pool) + notify_select_event_pool(socket->select_pool, event); + + benaphore_unlock(&socket->lock); + return B_OK; +} + + // #pragma mark - standard socket API @@ -306,6 +408,17 @@ socket_getsockopt(net_socket *socket, int level, int option, void *value, return B_OK; } + case SO_ERROR: + { + int32 *_set = (int32 *)value; + *_set = socket->error; + *_length = sizeof(int32); + + socket->error = B_OK; + // clear error upon retrieval + return B_OK; + } + default: break; } @@ -572,6 +685,11 @@ net_socket_module_info gNetSocketModule = { socket_send_data, socket_receive_data, + // notifications + socket_request_notification, + socket_cancel_notification, + socket_notify, + // standard socket API socket_accept, socket_bind, diff --git a/src/add-ons/kernel/network/stack/stack.cpp b/src/add-ons/kernel/network/stack/stack.cpp index 3653dc88e1..e3c13c674c 100644 --- a/src/add-ons/kernel/network/stack/stack.cpp +++ b/src/add-ons/kernel/network/stack/stack.cpp @@ -910,6 +910,8 @@ static net_stack_module_info sNetStackModule = { unregister_device_monitor, device_removed, + notify_socket, + checksum, init_fifo, diff --git a/src/add-ons/kernel/network/stack/utility.cpp b/src/add-ons/kernel/network/stack/utility.cpp index e769d85abb..75c32368fb 100644 --- a/src/add-ons/kernel/network/stack/utility.cpp +++ b/src/add-ons/kernel/network/stack/utility.cpp @@ -63,6 +63,16 @@ checksum(uint8 *buffer, size_t length) } +// #pragma mark - Notifications + + +status_t +notify_socket(net_socket *socket, uint8 event, int32 value) +{ + return gNetSocketModule.notify(socket, event, value); +} + + // #pragma mark - FIFOs diff --git a/src/add-ons/kernel/network/stack/utility.h b/src/add-ons/kernel/network/stack/utility.h index 003a40e043..1044852dad 100644 --- a/src/add-ons/kernel/network/stack/utility.h +++ b/src/add-ons/kernel/network/stack/utility.h @@ -16,6 +16,9 @@ uint16 compute_checksum(uint8 *_buffer, size_t length); uint16 checksum(uint8 *buffer, size_t length); +// notifications +status_t notify_socket(net_socket *socket, uint8 event, int32 value); + // fifos status_t init_fifo(net_fifo *fifo, const char *name, size_t maxBytes); void uninit_fifo(net_fifo *fifo);