* Implemented select support for sockets and notifications, not yet tested, though;
this closes ticket #811. * Added notification support to IPv4 and UDP. * Implemented reading out SO_ERROR. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19017 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -9,6 +9,9 @@
|
|||||||
#include <net_buffer.h>
|
#include <net_buffer.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <Select.h>
|
||||||
|
#include <lock.h>
|
||||||
|
|
||||||
|
|
||||||
#define NET_SOCKET_MODULE_NAME "network/stack/socket/v1"
|
#define NET_SOCKET_MODULE_NAME "network/stack/socket/v1"
|
||||||
|
|
||||||
@@ -31,6 +34,11 @@ typedef struct net_socket {
|
|||||||
uint32 low_water_mark;
|
uint32 low_water_mark;
|
||||||
bigtime_t timeout;
|
bigtime_t timeout;
|
||||||
} send, receive;
|
} send, receive;
|
||||||
|
|
||||||
|
// TODO: could be moved into a private structure
|
||||||
|
status_t error;
|
||||||
|
struct select_sync_pool *select_pool;
|
||||||
|
benaphore lock;
|
||||||
} net_socket;
|
} net_socket;
|
||||||
|
|
||||||
struct net_socket_module_info {
|
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,
|
status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags,
|
||||||
net_buffer **_buffer);
|
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
|
// standard socket API
|
||||||
int (*accept)(net_socket *socket, struct sockaddr *address,
|
int (*accept)(net_socket *socket, struct sockaddr *address,
|
||||||
socklen_t *_addressLength, net_socket **_acceptedSocket);
|
socklen_t *_addressLength, net_socket **_acceptedSocket);
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ struct net_stack_module_info {
|
|||||||
|
|
||||||
// Utility Functions
|
// Utility Functions
|
||||||
|
|
||||||
|
// notification
|
||||||
|
status_t (*notify_socket)(struct net_socket *socket, uint8 event, int32 value);
|
||||||
|
|
||||||
// checksum
|
// checksum
|
||||||
uint16 (*checksum)(uint8 *buffer, size_t length);
|
uint16 (*checksum)(uint8 *buffer, size_t length);
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ typedef DoublyLinkedList<class RawSocket> RawSocketList;
|
|||||||
|
|
||||||
class RawSocket : public DoublyLinkedListLinkImpl<RawSocket> {
|
class RawSocket : public DoublyLinkedListLinkImpl<RawSocket> {
|
||||||
public:
|
public:
|
||||||
RawSocket();
|
RawSocket(net_socket *socket);
|
||||||
~RawSocket();
|
~RawSocket();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
@@ -127,6 +127,7 @@ class RawSocket : public DoublyLinkedListLinkImpl<RawSocket> {
|
|||||||
status_t Write(net_buffer *buffer);
|
status_t Write(net_buffer *buffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
net_socket *fSocket;
|
||||||
net_fifo fFifo;
|
net_fifo fFifo;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -153,7 +154,9 @@ static benaphore sFragmentLock;
|
|||||||
static hash_table *sFragmentHash;
|
static hash_table *sFragmentHash;
|
||||||
|
|
||||||
|
|
||||||
RawSocket::RawSocket()
|
RawSocket::RawSocket(net_socket *socket)
|
||||||
|
:
|
||||||
|
fSocket(socket)
|
||||||
{
|
{
|
||||||
status_t status = sStackModule->init_fifo(&fFifo, "ipv4 raw socket", 65536);
|
status_t status = sStackModule->init_fifo(&fFifo, "ipv4 raw socket", 65536);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
@@ -211,7 +214,13 @@ RawSocket::Write(net_buffer *source)
|
|||||||
if (buffer == NULL)
|
if (buffer == NULL)
|
||||||
return B_NO_MEMORY;
|
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;
|
ipv4_protocol *protocol = (ipv4_protocol *)_protocol;
|
||||||
|
|
||||||
RawSocket *raw = new (std::nothrow) RawSocket;
|
RawSocket *raw = new (std::nothrow) RawSocket(protocol->socket);
|
||||||
if (raw == NULL)
|
if (raw == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
|||||||
@@ -871,7 +871,9 @@ UdpEndpoint::StoreData(net_buffer *_buffer)
|
|||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
status_t status = sStackModule->fifo_enqueue_buffer(&fFifo, buffer);
|
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);
|
sBufferModule->free(buffer);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
#include <util/list.h>
|
#include <util/list.h>
|
||||||
|
#include <fs/select_sync_pool.h>
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -32,6 +33,10 @@ create_socket(int family, int type, int protocol, net_socket **_socket)
|
|||||||
socket->type = type;
|
socket->type = type;
|
||||||
socket->protocol = protocol;
|
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)
|
// set defaults (may be overridden by the protocols)
|
||||||
socket->send.buffer_size = 65536;
|
socket->send.buffer_size = 65536;
|
||||||
socket->send.low_water_mark = 1;
|
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.low_water_mark = 1;
|
||||||
socket->receive.timeout = B_INFINITE_TIMEOUT;
|
socket->receive.timeout = B_INFINITE_TIMEOUT;
|
||||||
|
|
||||||
status_t status = get_domain_protocols(socket);
|
socket->select_pool = NULL;
|
||||||
if (status < B_OK) {
|
|
||||||
delete socket;
|
status = get_domain_protocols(socket);
|
||||||
return status;
|
if (status < B_OK)
|
||||||
}
|
goto err2;
|
||||||
|
|
||||||
status = socket->first_info->open(socket->first_protocol);
|
status = socket->first_info->open(socket->first_protocol);
|
||||||
if (status < B_OK) {
|
if (status < B_OK)
|
||||||
put_domain_protocols(socket);
|
goto err3;
|
||||||
delete socket;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*_socket = socket;
|
*_socket = socket;
|
||||||
return B_OK;
|
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);
|
status_t status = socket->first_info->free(socket->first_protocol);
|
||||||
|
|
||||||
put_domain_protocols(socket);
|
put_domain_protocols(socket);
|
||||||
|
benaphore_destroy(&socket->lock);
|
||||||
|
delete_select_sync_pool(socket->select_pool);
|
||||||
delete socket;
|
delete socket;
|
||||||
|
|
||||||
return status;
|
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
|
// #pragma mark - standard socket API
|
||||||
|
|
||||||
|
|
||||||
@@ -306,6 +408,17 @@ socket_getsockopt(net_socket *socket, int level, int option, void *value,
|
|||||||
return B_OK;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -572,6 +685,11 @@ net_socket_module_info gNetSocketModule = {
|
|||||||
socket_send_data,
|
socket_send_data,
|
||||||
socket_receive_data,
|
socket_receive_data,
|
||||||
|
|
||||||
|
// notifications
|
||||||
|
socket_request_notification,
|
||||||
|
socket_cancel_notification,
|
||||||
|
socket_notify,
|
||||||
|
|
||||||
// standard socket API
|
// standard socket API
|
||||||
socket_accept,
|
socket_accept,
|
||||||
socket_bind,
|
socket_bind,
|
||||||
|
|||||||
@@ -910,6 +910,8 @@ static net_stack_module_info sNetStackModule = {
|
|||||||
unregister_device_monitor,
|
unregister_device_monitor,
|
||||||
device_removed,
|
device_removed,
|
||||||
|
|
||||||
|
notify_socket,
|
||||||
|
|
||||||
checksum,
|
checksum,
|
||||||
|
|
||||||
init_fifo,
|
init_fifo,
|
||||||
|
|||||||
@@ -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
|
// #pragma mark - FIFOs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
uint16 compute_checksum(uint8 *_buffer, size_t length);
|
uint16 compute_checksum(uint8 *_buffer, size_t length);
|
||||||
uint16 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
|
// fifos
|
||||||
status_t init_fifo(net_fifo *fifo, const char *name, size_t maxBytes);
|
status_t init_fifo(net_fifo *fifo, const char *name, size_t maxBytes);
|
||||||
void uninit_fifo(net_fifo *fifo);
|
void uninit_fifo(net_fifo *fifo);
|
||||||
|
|||||||
Reference in New Issue
Block a user