diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index ad08ab2b56..49004e3394 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -125,7 +125,7 @@ BEOS_ADD_ONS_DRIVERS_GRAPHICS = $(X86_ONLY)radeon $(X86_ONLY)nvidia ; BEOS_ADD_ONS_DRIVERS_MIDI = emuxki ; BEOS_ADD_ONS_DRIVERS_NET = $(X86_ONLY)3com etherpci $(X86_ONLY)ipro1000 - $(X86_ONLY)rtl8139 rtl8169 sis900 $(X86_ONLY)via_rhine wb840 net_stack + $(X86_ONLY)rtl8139 rtl8169 sis900 $(X86_ONLY)via_rhine wb840 $(X86_ONLY)ipro100 $(X86_ONLY)nforce #vlance $(X86_ONLY)marvell_yukon $(X86_ONLY)syskonnect $(GPL_ONLY)bcm440x $(GPL_ONLY)bcm570x diff --git a/headers/private/kernel/syscalls.h b/headers/private/kernel/syscalls.h index aeabb38eed..af1f0be8e2 100644 --- a/headers/private/kernel/syscalls.h +++ b/headers/private/kernel/syscalls.h @@ -12,6 +12,7 @@ #include #include +#include #ifdef __cplusplus @@ -23,6 +24,7 @@ struct fd_info; struct fd_set; struct fs_info; struct iovec; +struct net_stat; struct pollfd; struct rlimit; struct sigaction; @@ -222,6 +224,44 @@ extern status_t _kern_unlock_node(int fd); extern status_t _kern_get_next_fd_info(team_id team, uint32 *_cookie, struct fd_info *info, size_t infoSize); +// socket functions +extern int _kern_socket(int family, int type, int protocol); +extern status_t _kern_bind(int socket, const struct sockaddr *address, + socklen_t addressLength); +extern status_t _kern_shutdown_socket(int socket, int how); +extern status_t _kern_connect(int socket, const struct sockaddr *address, + socklen_t addressLength); +extern status_t _kern_listen(int socket, int backlog); +extern int _kern_accept(int socket, struct sockaddr *address, + socklen_t *_addressLength); +extern ssize_t _kern_recv(int socket, void *data, size_t length, + int flags); +extern ssize_t _kern_recvfrom(int socket, void *data, size_t length, + int flags, struct sockaddr *address, + socklen_t *_addressLength); +extern ssize_t _kern_recvmsg(int socket, struct msghdr *message, + int flags); +extern ssize_t _kern_send(int socket, const void *data, size_t length, + int flags); +extern ssize_t _kern_sendto(int socket, const void *data, size_t length, + int flags, const struct sockaddr *address, + socklen_t addressLength); +extern ssize_t _kern_sendmsg(int socket, const struct msghdr *message, + int flags); +extern status_t _kern_getsockopt(int socket, int level, int option, + void *value, socklen_t *_length); +extern status_t _kern_setsockopt(int socket, int level, int option, + const void *value, socklen_t length); +extern status_t _kern_getpeername(int socket, struct sockaddr *address, + socklen_t *_addressLength); +extern status_t _kern_getsockname(int socket, struct sockaddr *address, + socklen_t *_addressLength); +extern int _kern_sockatmark(int socket); +extern status_t _kern_socketpair(int family, int type, int protocol, + int *socketVector); +extern status_t _kern_get_next_socket_stat(int family, uint32 *cookie, + struct net_stat *stat); + // node monitor functions extern status_t _kern_stop_notifying(port_id port, uint32 token); extern status_t _kern_start_watching(dev_t device, ino_t node, uint32 flags, diff --git a/headers/private/kernel/vfs.h b/headers/private/kernel/vfs.h index a129773f83..baba646522 100644 --- a/headers/private/kernel/vfs.h +++ b/headers/private/kernel/vfs.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -28,12 +29,13 @@ #define B_UNMOUNT_BUSY_PARTITION 0x80000000 -struct kernel_args; -struct vm_cache; struct file_descriptor; +struct kernel_args; +struct net_stat; +struct pollfd; struct selectsync; struct select_info; -struct pollfd; +struct vm_cache; struct vnode; @@ -197,7 +199,7 @@ status_t _user_change_root(const char *path); int _user_open_query(dev_t device, const char *query, size_t queryLength, uint32 flags, port_id port, int32 token); -/* fd user prototypes (implementation located in fd.c) */ +/* fd user prototypes (implementation located in fd.cpp) */ extern ssize_t _user_read(int fd, off_t pos, void *buffer, size_t bufferSize); extern ssize_t _user_readv(int fd, off_t pos, const iovec *vecs, size_t count); extern ssize_t _user_write(int fd, off_t pos, const void *buffer, size_t bufferSize); @@ -211,6 +213,44 @@ extern int _user_dup2(int ofd, int nfd); extern status_t _user_lock_node(int fd); extern status_t _user_unlock_node(int fd); +/* socket user prototypes (implementation in socket.cpp) */ +extern int _user_socket(int family, int type, int protocol); +extern status_t _user_bind(int socket, const struct sockaddr *address, + socklen_t addressLength); +extern status_t _user_shutdown_socket(int socket, int how); +extern status_t _user_connect(int socket, const struct sockaddr *address, + socklen_t addressLength); +extern status_t _user_listen(int socket, int backlog); +extern int _user_accept(int socket, struct sockaddr *address, + socklen_t *_addressLength); +extern ssize_t _user_recv(int socket, void *data, size_t length, + int flags); +extern ssize_t _user_recvfrom(int socket, void *data, size_t length, + int flags, struct sockaddr *address, + socklen_t *_addressLength); +extern ssize_t _user_recvmsg(int socket, struct msghdr *message, + int flags); +extern ssize_t _user_send(int socket, const void *data, size_t length, + int flags); +extern ssize_t _user_sendto(int socket, const void *data, size_t length, + int flags, const struct sockaddr *address, + socklen_t addressLength); +extern ssize_t _user_sendmsg(int socket, const struct msghdr *message, + int flags); +extern status_t _user_getsockopt(int socket, int level, int option, + void *value, socklen_t *_length); +extern status_t _user_setsockopt(int socket, int level, int option, + const void *value, socklen_t length); +extern status_t _user_getpeername(int socket, struct sockaddr *address, + socklen_t *_addressLength); +extern status_t _user_getsockname(int socket, struct sockaddr *address, + socklen_t *_addressLength); +extern int _user_sockatmark(int socket); +extern status_t _user_socketpair(int family, int type, int protocol, + int *socketVector); +extern status_t _user_get_next_socket_stat(int family, uint32 *cookie, + struct net_stat *stat); + /* vfs entry points... */ #ifdef __cplusplus diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index 91d9203509..7231bcd900 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -53,7 +53,7 @@ struct net_socket_module_info { status_t (*writev)(net_socket *socket, const iovec *vecs, size_t vecCount, size_t *_length); status_t (*control)(net_socket *socket, int32 op, void *data, - size_t length); + size_t length, bool kernel); ssize_t (*read_avail)(net_socket *socket); ssize_t (*send_avail)(net_socket *socket); @@ -81,7 +81,7 @@ struct net_socket_module_info { // notifications status_t (*request_notification)(net_socket *socket, uint8 event, - uint32 ref, struct selectsync *sync); + struct selectsync *sync); status_t (*cancel_notification)(net_socket *socket, uint8 event, struct selectsync *sync); status_t (*notify)(net_socket *socket, uint8 event, int32 value); @@ -107,6 +107,8 @@ struct net_socket_module_info { int (*setsockopt)(net_socket *socket, int level, int option, const void *optionValue, int optionLength); int (*shutdown)(net_socket *socket, int direction); + status_t (*socketpair)(int family, int type, int protocol, + net_socket* _sockets[2]); }; #endif // NET_SOCKET_H diff --git a/headers/private/net/net_stack_interface.h b/headers/private/net/net_stack_interface.h new file mode 100644 index 0000000000..34f3f7b32e --- /dev/null +++ b/headers/private/net/net_stack_interface.h @@ -0,0 +1,83 @@ +/* + * Copyright 2008, Haiku, Inc. All Rights Reserved. + * This file may be used under the terms of the MIT License. + */ +#ifndef NET_STACK_INTERFACE_H +#define NET_STACK_INTERFACE_H + + +#include + + +// name of the kernel stack interface +#define NET_STACK_INTERFACE_MODULE_NAME "network/stack/kernel_interface/v1" + +// name of the userland stack interface +#define NET_STACK_USERLAND_INTERFACE_MODULE_NAME \ + "network/stack/userland_interface/v1" + + +struct net_socket; +struct net_stat; + + +struct net_stack_interface_module_info { + module_info info; + + status_t (*open)(int family, int type, int protocol, net_socket** _socket); + status_t (*close)(net_socket* socket); + status_t (*free)(net_socket* socket); + + status_t (*bind)(net_socket* socket, const struct sockaddr* address, + socklen_t addressLength, bool kernel); + status_t (*shutdown)(net_socket* socket, int how); + status_t (*connect)(net_socket* socket, const struct sockaddr* address, + socklen_t addressLength, bool kernel); + status_t (*listen)(net_socket* socket, int backlog); + status_t (*accept)(net_socket* socket, struct sockaddr* address, + socklen_t* _addressLength, net_socket** _acceptedSocket); + + ssize_t (*recv)(net_socket* socket, void* data, size_t length, int flags, + bool kernel); + ssize_t (*recvfrom)(net_socket* socket, void* data, size_t length, + int flags, struct sockaddr* address, + socklen_t* _addressLength, bool kernel); + ssize_t (*recvmsg)(net_socket* socket, struct msghdr* message, int flags, + bool kernel); + + ssize_t (*send)(net_socket* socket, const void* data, size_t length, + int flags, bool kernel); + ssize_t (*sendto)(net_socket* socket, const void* data, size_t length, + int flags, const struct sockaddr* address, + socklen_t addressLength, bool kernel); + ssize_t (*sendmsg)(net_socket* socket, const struct msghdr* message, + int flags, bool kernel); + + status_t (*getsockopt)(net_socket* socket, int level, int option, + void* value, socklen_t* _length); + status_t (*setsockopt)(net_socket* socket, int level, int option, + const void* value, socklen_t length); + + status_t (*getpeername)(net_socket* socket, struct sockaddr* address, + socklen_t* _addressLength); + status_t (*getsockname)(net_socket* socket, struct sockaddr* address, + socklen_t* _addressLength); + + int (*sockatmark)(net_socket* socket); + + status_t (*socketpair)(int family, int type, int protocol, + net_socket* _sockets[2]); + + status_t (*ioctl)(net_socket* socket, uint32 op, void *buffer, + size_t length, bool kernel); + status_t (*select)(net_socket* socket, uint8 event, + struct selectsync *sync); + status_t (*deselect)(net_socket* socket, uint8 event, + struct selectsync *sync); + + status_t (*get_next_socket_stat)(int family, uint32 *cookie, + struct net_stat *stat); +}; + + +#endif // NET_STACK_INTERFACE_H diff --git a/src/add-ons/kernel/network/stack/Jamfile b/src/add-ons/kernel/network/stack/Jamfile index 7b2bac5e35..ed6dc0690c 100644 --- a/src/add-ons/kernel/network/stack/Jamfile +++ b/src/add-ons/kernel/network/stack/Jamfile @@ -24,6 +24,7 @@ KernelAddon stack : radix.c routes.cpp stack.cpp + stack_interface.cpp utility.cpp ; diff --git a/src/add-ons/kernel/network/stack/interfaces.cpp b/src/add-ons/kernel/network/stack/interfaces.cpp index 284a0cc657..56a5c43066 100644 --- a/src/add-ons/kernel/network/stack/interfaces.cpp +++ b/src/add-ons/kernel/network/stack/interfaces.cpp @@ -270,7 +270,7 @@ create_interface(net_domain *domain, const char *name, const char *baseName, // Grab a reference to the networking stack, to make sure it won't be // unloaded as long as an interface exists module_info *module; - get_module(NET_STARTER_MODULE_NAME, &module); + get_module(gNetStackInterfaceModule.info.name, &module); *_interface = interface; return B_OK; @@ -320,7 +320,7 @@ delete_interface(net_interface_private *interface) // Release reference of the stack - at this point, our stack may be unloaded // if no other interfaces or sockets are left - put_module(NET_STARTER_MODULE_NAME); + put_module(gNetStackInterfaceModule.info.name); } diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index 4640e7cf75..c838d68539 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -9,21 +9,26 @@ #include "stack_private.h" -#include -#include -#include +#include +#include +#include +#include +#include + +#include #include #include #include #include #include -#include -#include -#include -#include -#include +#include +#include + +#include +#include +#include struct net_socket_private : net_socket { @@ -42,6 +47,8 @@ struct net_socket_private : net_socket { void socket_delete(net_socket *socket); int socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength); +int socket_setsockopt(net_socket *socket, int level, int option, + const void *value, int length); struct list sSocketList; @@ -236,8 +243,37 @@ socket_writev(net_socket *socket, const iovec *vecs, size_t vecCount, status_t -socket_control(net_socket *socket, int32 op, void *data, size_t length) +socket_control(net_socket *socket, int32 op, void *data, size_t length, + bool kernel) { + switch (op) { + case FIONBIO: + { + if (data == NULL) + return B_BAD_VALUE; + + int value; + if (kernel) { + value = *(int*)data; + } else { + if (!IS_USER_ADDRESS(data) + || user_memcpy(&value, data, sizeof(int)) != B_OK) { + return B_BAD_ADDRESS; + } + } + return socket_setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &value, + sizeof(int)); + } + + case B_SET_BLOCKING_IO: + case B_SET_NONBLOCKING_IO: + { + int value = op == B_SET_NONBLOCKING_IO; + return socket_setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &value, + sizeof(int)); + } + } + return socket->first_info->control(socket->first_protocol, LEVEL_DRIVER_IOCTL, op, data, &length); } @@ -499,8 +535,7 @@ socket_connected(net_socket *socket) status_t -socket_request_notification(net_socket *_socket, uint8 event, uint32 ref, - selectsync *sync) +socket_request_notification(net_socket *_socket, uint8 event, selectsync *sync) { net_socket_private *socket = (net_socket_private *)_socket; @@ -1177,6 +1212,59 @@ socket_shutdown(net_socket *socket, int direction) } +status_t +socket_socketpair(int family, int type, int protocol, net_socket* sockets[2]) +{ + sockets[0] = NULL; + sockets[1] = NULL; + + // create sockets + status_t error = socket_open(family, type, protocol, &sockets[0]); + if (error != B_OK) + return error; + + if (error == B_OK) + error = socket_open(family, type, protocol, &sockets[1]); + + // bind one + if (error == B_OK) + error = socket_bind(sockets[0], NULL, 0); + + // start listening + if (error == B_OK) + error = socket_listen(sockets[0], 1); + + // connect them + if (error == B_OK) { + error = socket_connect(sockets[1], (sockaddr*)&sockets[0]->address, + sockets[0]->address.ss_len); + } + + // accept a socket + net_socket* acceptedSocket = NULL; + if (error == B_OK) + error = socket_accept(sockets[0], NULL, NULL, &acceptedSocket); + + if (error == B_OK) { + // everything worked: close the listener socket + socket_close(sockets[0]); + socket_free(sockets[0]); + sockets[0] = acceptedSocket; + } else { + // close sockets on error + for (int i = 0; i < 2; i++) { + if (sockets[i] != NULL) { + socket_close(sockets[i]); + socket_free(sockets[i]); + sockets[i] = NULL; + } + } + } + + return error; +} + + // #pragma mark - @@ -1254,5 +1342,6 @@ net_socket_module_info gNetSocketModule = { socket_send, socket_setsockopt, socket_shutdown, + socket_socketpair }; diff --git a/src/add-ons/kernel/network/stack/stack.cpp b/src/add-ons/kernel/network/stack/stack.cpp index 2725a4169b..1363f02183 100644 --- a/src/add-ons/kernel/network/stack/stack.cpp +++ b/src/add-ons/kernel/network/stack/stack.cpp @@ -737,7 +737,7 @@ scan_modules(const char *path) } -static status_t +status_t init_stack() { status_t status = init_domains(); @@ -858,21 +858,6 @@ uninit_stack() } -static status_t -starter_std_ops(int32 op, ...) -{ - switch (op) { - case B_MODULE_INIT: - return init_stack(); - case B_MODULE_UNINIT: - return uninit_stack(); - - default: - return B_ERROR; - } -} - - static status_t stack_std_ops(int32 op, ...) { @@ -938,18 +923,12 @@ net_stack_module_info gNetStackModule = { restore_syscall_restart_timeout, }; -static module_info sNetStarterModule = { - NET_STARTER_MODULE_NAME, - 0, - starter_std_ops -}; - module_info *modules[] = { (module_info *)&gNetStackModule, - (module_info *)&sNetStarterModule, (module_info *)&gNetBufferModule, (module_info *)&gNetSocketModule, (module_info *)&gNetDatalinkModule, (module_info *)&gLinkModule, + (module_info *)&gNetStackInterfaceModule, NULL }; diff --git a/src/add-ons/kernel/network/stack/stack_interface.cpp b/src/add-ons/kernel/network/stack/stack_interface.cpp new file mode 100644 index 0000000000..4d62487ad6 --- /dev/null +++ b/src/add-ons/kernel/network/stack/stack_interface.cpp @@ -0,0 +1,306 @@ + + + +#include "stack_private.h" + + +/* Interface module providing networking to the kernel. +*/ + + +static status_t +stack_interface_open(int family, int type, int protocol, net_socket** _socket) +{ + return gNetSocketModule.open_socket(family, type, protocol, _socket); +} + + +static status_t +stack_interface_close(net_socket* socket) +{ + return gNetSocketModule.close(socket); +} + + +static status_t +stack_interface_free(net_socket* socket) +{ + return gNetSocketModule.free(socket); +} + + +static status_t +stack_interface_bind(net_socket* socket, const struct sockaddr* address, + socklen_t addressLength, bool kernel) +{ +// TODO: Pass kernel flag! + return gNetSocketModule.bind(socket, address, addressLength); +} + + +static status_t +stack_interface_shutdown(net_socket* socket, int how) +{ + return gNetSocketModule.shutdown(socket, how); +} + + +static status_t +stack_interface_connect(net_socket* socket, const struct sockaddr* address, + socklen_t addressLength, bool kernel) +{ +// TODO: Pass kernel flag! + return gNetSocketModule.connect(socket, address, addressLength); +} + + +static status_t +stack_interface_listen(net_socket* socket, int backlog) +{ + return gNetSocketModule.listen(socket, backlog); +} + + +static status_t +stack_interface_accept(net_socket* socket, struct sockaddr* address, + socklen_t* _addressLength, net_socket** _acceptedSocket) +{ + return gNetSocketModule.accept(socket, address, _addressLength, + _acceptedSocket); +} + + +static ssize_t +stack_interface_recv(net_socket* socket, void* data, size_t length, int flags, + bool kernel) +{ +// TODO: Pass kernel flag! + return gNetSocketModule.receive(socket, NULL, data, length, flags); +} + + +static ssize_t +stack_interface_recvfrom(net_socket* socket, void* data, size_t length, + int flags, struct sockaddr* address, socklen_t* _addressLength, bool kernel) +{ + msghdr message; + iovec vecs[1] = { { data, length } }; + message.msg_name = address; + message.msg_namelen = *_addressLength; + message.msg_iov = vecs; + message.msg_iovlen = 1; + message.msg_control = NULL; + message.msg_controllen = 0; + message.msg_flags = 0; + +// TODO: Pass kernel flag! + status_t error = gNetSocketModule.receive(socket, &message, data, length, + flags); + if (error != B_OK) + return error; + + *_addressLength = message.msg_namelen; + return B_OK; +} + + +static ssize_t +stack_interface_recvmsg(net_socket* socket, struct msghdr* message, int flags, + bool kernel) +{ + void* buffer = NULL; + size_t len = 0; + if (message->msg_iovlen > 0) { + buffer = message->msg_iov[0].iov_base; + len = message->msg_iov[0].iov_len; + } + +// TODO: Pass kernel flag! + return gNetSocketModule.receive(socket, message, buffer, len, flags); +} + + +static ssize_t +stack_interface_send(net_socket* socket, const void* data, size_t length, + int flags, bool kernel) +{ +// TODO: Pass kernel flag! + return gNetSocketModule.send(socket, NULL, data, length, flags); +} + + +static ssize_t +stack_interface_sendto(net_socket* socket, const void* data, size_t length, + int flags, const struct sockaddr* address, socklen_t addressLength, + bool kernel) +{ + msghdr message; + iovec vecs[1] = { { (void*)data, length } }; + message.msg_name = (void*)address; + message.msg_namelen = addressLength; + message.msg_iov = vecs; + message.msg_iovlen = 1; + message.msg_control = NULL; + message.msg_controllen = 0; + message.msg_flags = 0; + +// TODO: Pass kernel flag! + return gNetSocketModule.send(socket, &message, data, length, flags); +} + + +static ssize_t +stack_interface_sendmsg(net_socket* socket, const struct msghdr* message, + int flags, bool kernel) +{ + void* buffer = NULL; + size_t len = 0; + if (message->msg_iovlen > 0) { + buffer = message->msg_iov[0].iov_base; + len = message->msg_iov[0].iov_len; + } + +// TODO: Pass kernel flag! + return gNetSocketModule.send(socket, (msghdr*)message, buffer, len, flags); +} + + +static status_t +stack_interface_getsockopt(net_socket* socket, int level, int option, + void* value, socklen_t* _length) +{ + int length = *_length; + status_t error = gNetSocketModule.getsockopt(socket, level, option, value, + &length); + *_length = length; + return error; +} + + +static status_t +stack_interface_setsockopt(net_socket* socket, int level, int option, + const void* value, socklen_t length) +{ + return gNetSocketModule.setsockopt(socket, level, option, value, length); +} + + +static status_t +stack_interface_getpeername(net_socket* socket, struct sockaddr* address, + socklen_t* _addressLength) +{ + return gNetSocketModule.getpeername(socket, address, _addressLength); +} + + +static status_t +stack_interface_getsockname(net_socket* socket, struct sockaddr* address, + socklen_t* _addressLength) +{ + return gNetSocketModule.getsockname(socket, address, _addressLength); +} + + +static int +stack_interface_sockatmark(net_socket* socket) +{ +// TODO:... + return B_UNSUPPORTED; +} + + +static status_t +stack_interface_socketpair(int family, int type, int protocol, + net_socket* _sockets[2]) +{ + return gNetSocketModule.socketpair(family, type, protocol, _sockets); +} + + +static status_t +stack_interface_ioctl(net_socket* socket, uint32 op, void *buffer, + size_t length, bool kernel) +{ + return gNetSocketModule.control(socket, op, buffer, length, kernel); +} + + +static status_t +stack_interface_select(net_socket* socket, uint8 event, struct selectsync *sync) +{ + return gNetSocketModule.request_notification(socket, event, sync); +} + + +static status_t +stack_interface_deselect(net_socket* socket, uint8 event, + struct selectsync *sync) +{ + return gNetSocketModule.cancel_notification(socket, event, sync); +} + + +status_t +stack_interface_get_next_socket_stat(int family, uint32 *cookie, + struct net_stat *stat) +{ + return gNetSocketModule.get_next_stat(cookie, family, stat); +} + + +static status_t +stack_interface_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return init_stack(); + case B_MODULE_UNINIT: + return uninit_stack(); + + default: + return B_ERROR; + } +} + + +net_stack_interface_module_info gNetStackInterfaceModule = { + { + NET_STACK_INTERFACE_MODULE_NAME, + 0, + stack_interface_std_ops + }, + + &stack_interface_open, + &stack_interface_close, + &stack_interface_free, + + &stack_interface_bind, + &stack_interface_shutdown, + &stack_interface_connect, + &stack_interface_listen, + &stack_interface_accept, + + &stack_interface_recv, + &stack_interface_recvfrom, + &stack_interface_recvmsg, + + &stack_interface_send, + &stack_interface_sendto, + &stack_interface_sendmsg, + + &stack_interface_getsockopt, + &stack_interface_setsockopt, + + &stack_interface_getpeername, + &stack_interface_getsockname, + + &stack_interface_sockatmark, + + &stack_interface_socketpair, + + &stack_interface_ioctl, + &stack_interface_select, + &stack_interface_deselect, + + &stack_interface_get_next_socket_stat +}; diff --git a/src/add-ons/kernel/network/stack/stack_private.h b/src/add-ons/kernel/network/stack/stack_private.h index 9c047c43a4..f877b3e6d8 100644 --- a/src/add-ons/kernel/network/stack/stack_private.h +++ b/src/add-ons/kernel/network/stack/stack_private.h @@ -15,15 +15,15 @@ #include #include #include +#include -#define NET_STARTER_MODULE_NAME "network/stack/starter/v1" - extern net_stack_module_info gNetStackModule; extern net_buffer_module_info gNetBufferModule; extern net_socket_module_info gNetSocketModule; extern net_datalink_module_info gNetDatalinkModule; extern net_datalink_protocol_module_info gDatalinkInterfaceProtocolModule; +extern net_stack_interface_module_info gNetStackInterfaceModule; // stack.cpp status_t register_domain_datalink_protocols(int family, int type, ...); @@ -33,4 +33,7 @@ status_t put_domain_protocols(net_socket *socket); status_t get_domain_datalink_protocols(net_interface *interface); status_t put_domain_datalink_protocols(net_interface *interface); +status_t init_stack(); +status_t uninit_stack(); + #endif // STACK_PRIVATE_H diff --git a/src/bin/network/netstat/Jamfile b/src/bin/network/netstat/Jamfile index 75690893a6..d226c150e7 100644 --- a/src/bin/network/netstat/Jamfile +++ b/src/bin/network/netstat/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src bin network netstat ; UsePrivateHeaders net ; +UsePrivateKernelHeaders ; # for syscalls.h BinCommand netstat : netstat.cpp diff --git a/src/bin/network/netstat/netstat.cpp b/src/bin/network/netstat/netstat.cpp index d6306996b1..b7dfdb5852 100644 --- a/src/bin/network/netstat/netstat.cpp +++ b/src/bin/network/netstat/netstat.cpp @@ -7,23 +7,21 @@ * James Woodcock */ - -#include - -#include -#include - #include -#include -#include -#include - #include +#include +#include +#include +#include #include #include #include #include -#include + +#include + +#include +#include extern const char* __progname; @@ -110,22 +108,6 @@ usage(int status) } -status_t -get_next_stat(int stack, uint32& cookie, int family, net_stat& stat) -{ - get_next_stat_args args; - args.cookie = cookie; - args.family = family; - - if (ioctl(stack, NET_STACK_GET_NEXT_STAT, &args, sizeof(args)) < 0) - return errno; - - cookie = args.cookie; - memcpy(&stat, &args.stat, sizeof(net_stat)); - return B_OK; -} - - bool get_address_family(const char* argument, int32& familyIndex) { @@ -177,13 +159,6 @@ main(int argc, char** argv) } } while (opt != -1); - int stack = open(NET_STACK_DRIVER_PATH, O_RDWR); - if (stack < 0) { - fprintf(stderr, "%s: The networking stack doesn't seem to be " - "available.\n", kProgramName); - return -1; - } - bool printProgram = true; // TODO: add some more program options... :-) @@ -193,7 +168,7 @@ main(int argc, char** argv) uint32 cookie = 0; int family = -1; net_stat stat; - while (get_next_stat(stack, cookie, family, stat) == B_OK) { + while (_kern_get_next_socket_stat(family, &cookie, &stat) == B_OK) { protoent* proto = getprotobynumber(stat.protocol); if (proto != NULL) printf("%-6s ", proto->p_name); @@ -226,7 +201,6 @@ main(int argc, char** argv) printf("%ld\n", stat.owner); } - close(stack); return 0; } diff --git a/src/kits/network/Jamfile b/src/kits/network/Jamfile index 1a203e08c8..d4388dad29 100644 --- a/src/kits/network/Jamfile +++ b/src/kits/network/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src kits network ; -UsePrivateHeaders libroot net ; +UsePrivateHeaders libroot net shared ; +UsePrivateKernelHeaders ; local services = [ FGristFiles services ] ; SEARCH on $(services) = [ FDirName $(SUBDIR) dns defaults ] ; diff --git a/src/kits/network/socket.cpp b/src/kits/network/socket.cpp index 60925068f7..5ae9d3f76c 100644 --- a/src/kits/network/socket.cpp +++ b/src/kits/network/socket.cpp @@ -18,19 +18,9 @@ #include #include +#include -// TODO: this is meant for debugging purposes only, and should be disabled later -static char * -stack_driver_path(void) -{ - // user-defined stack driver path? - char *path = getenv("NET_STACK_DRIVER_PATH"); - if (path != NULL) - return path; - - // use the default stack driver path - return NET_STACK_DRIVER_PATH; -} +#include static inline bool @@ -171,24 +161,10 @@ convert_from_r5_sockopt(int& level, int& option) extern "C" int socket(int family, int type, int protocol) { - int socket = open(stack_driver_path(), O_RDWR); - if (socket < 0) - return -1; - if (check_r5_compatibility()) convert_from_r5_socket(family, type, protocol); - socket_args args; - args.family = family; - args.type = type; - args.protocol = protocol; - - if (ioctl(socket, NET_STACK_SOCKET, &args, sizeof(args)) < 0) { - close(socket); - return -1; - } - - return socket; + RETURN_AND_SET_ERRNO(_kern_socket(family, type, protocol)); } @@ -203,18 +179,14 @@ bind(int socket, const struct sockaddr *address, socklen_t addressLength) addressLength = sizeof(struct sockaddr_in); } - sockaddr_args args; - args.address = const_cast(address); - args.address_length = addressLength; - - return ioctl(socket, NET_STACK_BIND, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_bind(socket, address, addressLength)); } extern "C" int shutdown(int socket, int how) { - return ioctl(socket, NET_STACK_SHUTDOWN, (void *)how, 0); + RETURN_AND_SET_ERRNO(_kern_shutdown_socket(socket, how)); } @@ -229,53 +201,46 @@ connect(int socket, const struct sockaddr *address, socklen_t addressLength) addressLength = sizeof(struct sockaddr_in); } - sockaddr_args args; - args.address = const_cast(address); - args.address_length = addressLength; - - return ioctl(socket, NET_STACK_CONNECT, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_connect(socket, address, addressLength)); } extern "C" int listen(int socket, int backlog) { - return ioctl(socket, NET_STACK_LISTEN, (void *)backlog, 0); + RETURN_AND_SET_ERRNO(_kern_listen(socket, backlog)); } extern "C" int -accept(int socket, struct sockaddr *address, socklen_t *_addressLength) +accept(int socket, struct sockaddr *_address, socklen_t *_addressLength) { - int acceptSocket = open(stack_driver_path(), O_RDWR); - if (acceptSocket < 0) - return -1; - bool r5compatible = check_r5_compatibility(); struct sockaddr r5addr; - accept_args args; - args.accept_socket = acceptSocket; + sockaddr* address; + socklen_t addressLength; - if (r5compatible && address != NULL) { - args.address = &r5addr; - args.address_length = sizeof(r5addr); + if (r5compatible && _address != NULL) { + address = &r5addr; + addressLength = sizeof(r5addr); } else { - args.address = address; - args.address_length = _addressLength ? *_addressLength : 0; + address = _address; + addressLength = _addressLength ? *_addressLength : 0; } - if (ioctl(socket, NET_STACK_ACCEPT, &args, sizeof(args)) < 0) { - close(acceptSocket); + int acceptSocket = _kern_accept(socket, address, &addressLength); + if (acceptSocket < 0) { + errno = acceptSocket; return -1; } - if (r5compatible && address != NULL) { - convert_to_r5_sockaddr(address, &r5addr); + if (r5compatible && _address != NULL) { + convert_to_r5_sockaddr(_address, &r5addr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) - *_addressLength = args.address_length; + *_addressLength = addressLength; return acceptSocket; } @@ -284,50 +249,41 @@ accept(int socket, struct sockaddr *address, socklen_t *_addressLength) extern "C" ssize_t recv(int socket, void *data, size_t length, int flags) { - message_args args; - args.data = data; - args.length = length; - args.flags = flags; - args.header = NULL; - - return ioctl(socket, NET_STACK_RECEIVE, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_recv(socket, data, length, flags)); } extern "C" ssize_t recvfrom(int socket, void *data, size_t length, int flags, - struct sockaddr *address, socklen_t *_addressLength) + struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr r5addr; - message_args args; - args.data = data; - args.length = length; - args.flags = flags; + sockaddr* address; + socklen_t addressLength; - msghdr header; - memset(&header, 0, sizeof(header)); - args.header = &header; - - if (r5compatible) { - header.msg_name = (char *)&r5addr; - header.msg_namelen = sizeof(r5addr); + if (r5compatible && _address != NULL) { + address = &r5addr; + addressLength = sizeof(r5addr); } else { - header.msg_name = (char *)address; - header.msg_namelen = _addressLength ? *_addressLength : 0; + address = _address; + addressLength = _addressLength ? *_addressLength : 0; } - ssize_t bytesReceived = ioctl(socket, NET_STACK_RECEIVE, &args, sizeof(args)); - if (bytesReceived < 0) + ssize_t bytesReceived = _kern_recvfrom(socket, data, length, flags, + address, &addressLength); + if (bytesReceived < 0) { + errno = bytesReceived; return -1; + } if (r5compatible) { - convert_to_r5_sockaddr(address, &r5addr); + convert_to_r5_sockaddr(_address, &r5addr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) - *_addressLength = header.msg_namelen; + *_addressLength = addressLength; return bytesReceived; } @@ -336,36 +292,14 @@ recvfrom(int socket, void *data, size_t length, int flags, extern "C" ssize_t recvmsg(int socket, struct msghdr *message, int flags) { - message_args args; - - if (message == NULL || (message->msg_iovlen > 0 && message->msg_iov == NULL)) - return B_BAD_VALUE; - - args.header = message; - args.flags = flags; - - if (message->msg_iovlen > 0) { - args.data = message->msg_iov[0].iov_base; - args.length = message->msg_iov[0].iov_len; - } else { - args.data = NULL; - args.length = 0; - } - - return ioctl(socket, NET_STACK_RECEIVE, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_recvmsg(socket, message, flags)); } extern "C" ssize_t send(int socket, const void *data, size_t length, int flags) { - message_args args; - args.data = const_cast(data); - args.length = length; - args.flags = flags; - args.header = NULL; - - return ioctl(socket, NET_STACK_SEND, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_send(socket, data, length, flags)); } @@ -381,41 +315,15 @@ sendto(int socket, const void *data, size_t length, int flags, addressLength = sizeof(struct sockaddr_in); } - message_args args; - msghdr header; - memset(&header, 0, sizeof(header)); - - args.header = &header; - args.data = const_cast(data); - args.length = length; - args.flags = flags; - header.msg_name = (char *)const_cast(address); - header.msg_namelen = addressLength; - - return ioctl(socket, NET_STACK_SEND, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_sendto(socket, data, length, flags, address, + addressLength)); } extern "C" ssize_t sendmsg(int socket, const struct msghdr *message, int flags) { - message_args args; - - if (message == NULL || (message->msg_iovlen > 0 && message->msg_iov == NULL)) - return B_BAD_VALUE; - - args.header = const_cast(message); - args.flags = flags; - - if (message->msg_iovlen > 0) { - args.data = message->msg_iov[0].iov_base; - args.length = message->msg_iov[0].iov_len; - } else { - args.data = NULL; - args.length = 0; - } - - return ioctl(socket, NET_STACK_SEND, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_sendmsg(socket, message, flags)); } @@ -432,19 +340,8 @@ getsockopt(int socket, int level, int option, void *value, socklen_t *_length) convert_from_r5_sockopt(level, option); } - sockopt_args args; - args.level = level; - args.option = option; - args.value = value; - args.length = _length ? *_length : 0; - - if (ioctl(socket, NET_STACK_GETSOCKOPT, &args, sizeof(args)) < 0) - return -1; - - if (_length) - *_length = args.length; - - return 0; + RETURN_AND_SET_ERRNO(_kern_getsockopt(socket, level, option, value, + _length)); } @@ -455,69 +352,74 @@ setsockopt(int socket, int level, int option, const void *value, if (check_r5_compatibility()) convert_from_r5_sockopt(level, option); - sockopt_args args; - args.level = level; - args.option = option; - args.value = const_cast(value); - args.length = length; - - return ioctl(socket, NET_STACK_SETSOCKOPT, &args, sizeof(args)); + RETURN_AND_SET_ERRNO(_kern_setsockopt(socket, level, option, value, + length)); } extern "C" int -getpeername(int socket, struct sockaddr *address, socklen_t *_addressLength) +getpeername(int socket, struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr r5addr; - sockaddr_args args; - if (r5compatible) { - args.address = &r5addr; - args.address_length = sizeof(r5addr); + sockaddr* address; + socklen_t addressLength; + + if (r5compatible && _address != NULL) { + address = &r5addr; + addressLength = sizeof(r5addr); } else { - args.address = address; - args.address_length = _addressLength ? *_addressLength : 0; + address = _address; + addressLength = _addressLength ? *_addressLength : 0; } - if (ioctl(socket, NET_STACK_GETPEERNAME, &args, sizeof(args)) < 0) + status_t error = _kern_getpeername(socket, address, &addressLength); + if (error != B_OK) { + errno = error; return -1; + } if (r5compatible) { - convert_to_r5_sockaddr(address, &r5addr); + convert_to_r5_sockaddr(_address, &r5addr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) - *_addressLength = args.address_length; + *_addressLength = addressLength; return 0; } extern "C" int -getsockname(int socket, struct sockaddr *address, socklen_t *_addressLength) +getsockname(int socket, struct sockaddr *_address, socklen_t *_addressLength) { bool r5compatible = check_r5_compatibility(); struct sockaddr r5addr; - sockaddr_args args; - if (r5compatible) { - args.address = &r5addr; - args.address_length = sizeof(r5addr); + sockaddr* address; + socklen_t addressLength; + + if (r5compatible && _address != NULL) { + address = &r5addr; + addressLength = sizeof(r5addr); } else { - args.address = address; - args.address_length = _addressLength ? *_addressLength : 0; + address = _address; + addressLength = _addressLength ? *_addressLength : 0; } - if (ioctl(socket, NET_STACK_GETSOCKNAME, &args, sizeof(args)) < 0) + status_t error = _kern_getsockname(socket, address, &addressLength); + if (error != B_OK) { + errno = error; return -1; + } if (r5compatible) { - convert_to_r5_sockaddr(address, &r5addr); + convert_to_r5_sockaddr(_address, &r5addr); if (_addressLength != NULL) *_addressLength = sizeof(struct r5_sockaddr_in); } else if (_addressLength != NULL) - *_addressLength = args.address_length; + *_addressLength = addressLength; return 0; } @@ -526,33 +428,13 @@ getsockname(int socket, struct sockaddr *address, socklen_t *_addressLength) extern "C" int sockatmark(int socket) { - // TODO: implement me! - return -1; + RETURN_AND_SET_ERRNO(_kern_sockatmark(socket)); } extern "C" int socketpair(int family, int type, int protocol, int socketVector[2]) { - socketVector[0] = socket(family, type, protocol); - if (socketVector[0] < 0) - return -1; - - socketVector[1] = socket(family, type, protocol); - if (socketVector[1] < 0) - goto err1; - - socketpair_args args; - args.second_socket = socketVector[1]; - - if (ioctl(socketVector[0], NET_STACK_SOCKETPAIR, &args, sizeof(args)) < 0) - goto err2; - - return 0; - -err2: - close(socketVector[1]); -err1: - close(socketVector[0]); - return -1; + RETURN_AND_SET_ERRNO(_kern_socketpair(family, type, protocol, + socketVector)); } diff --git a/src/system/kernel/fs/Jamfile b/src/system/kernel/fs/Jamfile index 2d06433e88..ae16b005b1 100644 --- a/src/system/kernel/fs/Jamfile +++ b/src/system/kernel/fs/Jamfile @@ -3,7 +3,7 @@ SubDir HAIKU_TOP src system kernel fs ; UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; UsePrivateHeaders [ FDirName kernel fs ] ; UsePrivateHeaders [ FDirName kernel util ] ; -UsePrivateHeaders shared storage ; +UsePrivateHeaders net shared storage ; KernelMergeObject kernel_fs.o : devfs.cpp @@ -13,6 +13,7 @@ KernelMergeObject kernel_fs.o : KPath.cpp node_monitor.cpp rootfs.cpp + socket.cpp vfs.cpp vfs_boot.cpp vfs_net_boot.cpp diff --git a/src/system/kernel/fs/socket.cpp b/src/system/kernel/fs/socket.cpp new file mode 100644 index 0000000000..ae6e8ebd5d --- /dev/null +++ b/src/system/kernel/fs/socket.cpp @@ -0,0 +1,1149 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + + +#define MAX_SOCKET_ADDRESS_LEN (sizeof(sockaddr_storage)) +#define MAX_SOCKET_OPTION_LEN 128 +#define MAX_IO_VEC_COUNT 128 + + +static net_stack_interface_module_info* sStackInterface = NULL; +static vint32 sStackInterfaceInitialized = 0; + + +struct FDPutter { + FDPutter(file_descriptor* descriptor) + : descriptor(descriptor) + { + } + + ~FDPutter() + { + if (descriptor != NULL) + put_fd(descriptor); + } + + + file_descriptor* descriptor; +}; + + +static net_stack_interface_module_info* +init_stack_interface_module() +{ + // TODO: Add driver settings option to load the userland net stack. + + // load module + net_stack_interface_module_info* module; + status_t error = get_module(NET_STACK_INTERFACE_MODULE_NAME, + (module_info**)&module); + if (error != B_OK) + return NULL; + + sStackInterface = module; // assumed to be atomic + + // If someone else was faster getting the module, we put our reference. + if (atomic_test_and_set(&sStackInterfaceInitialized, 1, 0) != 0) + put_module(module->info.name); + + return module; +} + + +static inline net_stack_interface_module_info* +get_stack_interface_module() +{ + if (sStackInterface) + return sStackInterface; + + return init_stack_interface_module(); +} + + +static status_t +prepare_userland_address_result(struct sockaddr* userAddress, + socklen_t* _addressLength, socklen_t& addressLength, bool addressRequired) +{ + // check parameters + if (_addressLength == NULL) + return B_BAD_VALUE; + if (userAddress == NULL) { + if (addressRequired) + return B_BAD_VALUE; + } else if (!IS_USER_ADDRESS(userAddress) + || !IS_USER_ADDRESS(_addressLength)) { + return B_BAD_ADDRESS; + } + + // copy the buffer size from userland + addressLength = 0; + if (userAddress != NULL + && user_memcpy(&addressLength, _addressLength, sizeof(socklen_t)) + != B_OK) { + return B_BAD_ADDRESS; + } + + if (addressLength > MAX_SOCKET_ADDRESS_LEN) + addressLength = MAX_SOCKET_ADDRESS_LEN; + + return B_OK; +} + + +static status_t +prepare_userland_msghdr(const msghdr* userMessage, msghdr& message, + iovec*& userVecs, iovec* vecs, void*& userAddress, char* address) +{ + if (userMessage == NULL) + return B_BAD_VALUE; + + // copy message from userland + if (!IS_USER_ADDRESS(userMessage) + || user_memcpy(&message, userMessage, sizeof(msghdr)) != B_OK) { + return B_BAD_ADDRESS; + } + + // copy iovecs from userland + if (userVecs != NULL && message.msg_iovlen > 0) { + if (message.msg_iovlen > MAX_IO_VEC_COUNT || message.msg_iov == NULL) + return B_BAD_VALUE; + if (!IS_USER_ADDRESS(message.msg_iov) + || user_memcpy(vecs, message.msg_iov, + message.msg_iovlen * sizeof(iovec)) != B_OK) { + return B_BAD_ADDRESS; + } + + message.msg_iov = vecs; + } else { + message.msg_iov = NULL; + message.msg_iovlen = 0; + } + + // prepare the address field + userAddress = message.msg_name; + if (userAddress != NULL) { + if (!IS_USER_ADDRESS(message.msg_name)) + return B_BAD_ADDRESS; + if (message.msg_namelen > MAX_SOCKET_ADDRESS_LEN) + message.msg_namelen = MAX_SOCKET_ADDRESS_LEN; + + message.msg_name = address; + } + + return B_OK; +} + + +static status_t +get_socket_descriptor(int fd, bool kernel, file_descriptor*& descriptor) +{ + if (fd < 0) + return EBADF; + + descriptor = get_fd(get_current_io_context(kernel), fd); + if (descriptor == NULL) + return EBADF; + + if (descriptor->type != FDTYPE_SOCKET) { + put_fd(descriptor); + return ENOTSOCK; + } + + return B_OK; +} + + +#define GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor) \ + do { \ + status_t gsfdorError = get_socket_descriptor(fd, kernel, descriptor); \ + if (gsfdorError != B_OK) \ + return gsfdorError; \ + } while (false) + + +// #pragma mark - socket file descriptor + + +static status_t +socket_read(struct file_descriptor *descriptor, off_t pos, void *buffer, + size_t *_length) +{ + ssize_t bytesRead = sStackInterface->recv(descriptor->u.socket, buffer, + *_length, 0, !IS_USER_ADDRESS(buffer)); + *_length = bytesRead >= 0 ? bytesRead : 0; + return bytesRead >= 0 ? B_OK : bytesRead; +} + + +static status_t +socket_write(struct file_descriptor *descriptor, off_t pos, const void *buffer, + size_t *_length) +{ + ssize_t bytesWritten = sStackInterface->send(descriptor->u.socket, buffer, + *_length, 0, !IS_USER_ADDRESS(buffer)); + *_length = bytesWritten >= 0 ? bytesWritten : 0; + return bytesWritten >= 0 ? B_OK : bytesWritten; +} + + +static status_t +socket_ioctl(struct file_descriptor *descriptor, ulong op, void *buffer, + size_t length) +{ + return sStackInterface->ioctl(descriptor->u.socket, op, buffer, + length, !IS_USER_ADDRESS(buffer)); +} + + +static status_t +socket_select(struct file_descriptor *descriptor, uint8 event, + struct selectsync *sync) +{ + return sStackInterface->select(descriptor->u.socket, event, sync); +} + + +static status_t +socket_deselect(struct file_descriptor *descriptor, uint8 event, + struct selectsync *sync) +{ + return sStackInterface->deselect(descriptor->u.socket, event, sync); +} + + +static status_t +socket_read_stat(struct file_descriptor *descriptor, struct stat *st) +{ + st->st_dev = 0; + st->st_ino = (addr_t)descriptor->u.socket; + st->st_mode = S_IFSOCK | 0666; + st->st_nlink = 1; + st->st_uid = 0; + st->st_gid = 0; + st->st_size = 0; + st->st_rdev = 0; + st->st_blksize = 1024; // use MTU for datagram sockets? + time_t now = time(NULL); + st->st_atime = now; + st->st_mtime = now; + st->st_ctime = now; + st->st_crtime = now; + st->st_type = 0; + + return B_OK; +} + + +static status_t +socket_close(struct file_descriptor *descriptor) +{ + return sStackInterface->close(descriptor->u.socket); +} + + +static void +socket_free(struct file_descriptor *descriptor) +{ + sStackInterface->free(descriptor->u.socket); +} + + +static struct fd_ops sSocketFDOps = { + &socket_read, + &socket_write, + NULL, // fd_seek + &socket_ioctl, + &socket_select, + &socket_deselect, + NULL, // fd_read_dir + NULL, // fd_rewind_dir + &socket_read_stat, + NULL, // fd_write_stat + &socket_close, + &socket_free +}; + + +static int +create_socket_fd(net_socket* socket, bool kernel) +{ + // allocate a file descriptor + file_descriptor* descriptor = alloc_fd(); + if (descriptor == NULL) + return B_NO_MEMORY; + + // init it + descriptor->type = FDTYPE_SOCKET; + descriptor->ops = &sSocketFDOps; + descriptor->u.socket = socket; + descriptor->open_mode = O_RDWR; + + // publish it + int fd = new_fd(get_current_io_context(kernel), descriptor); + if (fd < 0) + free(descriptor); + + return fd; +} + + +// #pragma mark - common sockets API implementation + + +static int +common_socket(int family, int type, int protocol, bool kernel) +{ + if (!get_stack_interface_module()) + return B_UNSUPPORTED; + + // create the socket + net_socket* socket; + status_t error = sStackInterface->open(family, type, protocol, &socket); + if (error != B_OK) + return error; + + // allocate the FD + int fd = create_socket_fd(socket, kernel); + if (fd < 0) { + sStackInterface->close(socket); + sStackInterface->free(socket); + } + + return fd; +} + + +static status_t +common_bind(int fd, const struct sockaddr *address, socklen_t addressLength, + bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->bind(descriptor->u.socket, address, addressLength, + kernel); +} + + +static status_t +common_shutdown(int fd, int how, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->shutdown(descriptor->u.socket, how); +} + + +static status_t +common_connect(int fd, const struct sockaddr *address, + socklen_t addressLength, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->connect(descriptor->u.socket, address, + addressLength, kernel); +} + + +static status_t +common_listen(int fd, int backlog, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->listen(descriptor->u.socket, backlog); +} + + +static int +common_accept(int fd, struct sockaddr *address, socklen_t *_addressLength, + bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + net_socket* acceptedSocket; + status_t error = sStackInterface->accept(descriptor->u.socket, address, + _addressLength, &acceptedSocket); + if (error != B_OK) + return error; + + // allocate the FD + int acceptedFD = create_socket_fd(acceptedSocket, kernel); + if (acceptedFD < 0) { + sStackInterface->close(acceptedSocket); + sStackInterface->free(acceptedSocket); + } + + return acceptedFD; +} + + +static ssize_t +common_recv(int fd, void *data, size_t length, int flags, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->recv(descriptor->u.socket, data, length, flags, + kernel); +} + + +static ssize_t +common_recvfrom(int fd, void *data, size_t length, int flags, + struct sockaddr *address, socklen_t *_addressLength, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->recvfrom(descriptor->u.socket, data, length, + flags, address, _addressLength, kernel); +} + + +static ssize_t +common_recvmsg(int fd, struct msghdr *message, int flags, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->recvmsg(descriptor->u.socket, message, flags, + kernel); +} + + +static ssize_t +common_send(int fd, const void *data, size_t length, int flags, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->send(descriptor->u.socket, data, length, flags, + kernel); +} + + +static ssize_t +common_sendto(int fd, const void *data, size_t length, int flags, + const struct sockaddr *address, socklen_t addressLength, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->sendto(descriptor->u.socket, data, length, flags, + address, addressLength, kernel); +} + + +static ssize_t +common_sendmsg(int fd, const struct msghdr *message, int flags, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->sendmsg(descriptor->u.socket, message, flags, + kernel); +} + + +static status_t +common_getsockopt(int fd, int level, int option, void *value, + socklen_t *_length, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->getsockopt(descriptor->u.socket, level, option, + value, _length); +} + + +static status_t +common_setsockopt(int fd, int level, int option, const void *value, + socklen_t length, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->setsockopt(descriptor->u.socket, level, option, + value, length); +} + + +static status_t +common_getpeername(int fd, struct sockaddr *address, + socklen_t *_addressLength, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->getpeername(descriptor->u.socket, address, + _addressLength); +} + + +static status_t +common_getsockname(int fd, struct sockaddr *address, + socklen_t *_addressLength, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->getsockname(descriptor->u.socket, address, + _addressLength); +} + + +static int +common_sockatmark(int fd, bool kernel) +{ + file_descriptor* descriptor; + GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); + FDPutter _(descriptor); + + return sStackInterface->sockatmark(descriptor->u.socket); +} + + +static status_t +common_socketpair(int family, int type, int protocol, int fds[2], bool kernel) +{ + if (!get_stack_interface_module()) + return B_UNSUPPORTED; + + net_socket* sockets[2]; + status_t error = sStackInterface->socketpair(family, type, protocol, + sockets); + if (error != B_OK) + return error; + + // allocate the FDs + for (int i = 0; i < 2; i++) { + fds[i] = create_socket_fd(sockets[i], kernel); + if (fds[i] < 0) { + sStackInterface->close(sockets[i]); + sStackInterface->free(sockets[i]); + return fds[i]; + } + } + + return B_OK; +} + + +static status_t +common_get_next_socket_stat(int family, uint32 *cookie, struct net_stat *stat) +{ + if (!get_stack_interface_module()) + return B_UNSUPPORTED; + + return sStackInterface->get_next_socket_stat(family, cookie, stat); +} + + +// #pragma mark - kernel sockets API + + +int +socket(int family, int type, int protocol) +{ + RETURN_AND_SET_ERRNO(common_socket(family, type, protocol, true)); +} + + +int +bind(int socket, const struct sockaddr *address, socklen_t addressLength) +{ + RETURN_AND_SET_ERRNO(common_bind(socket, address, addressLength, true)); +} + + +int +shutdown(int socket, int how) +{ + RETURN_AND_SET_ERRNO(common_shutdown(socket, how, true)); +} + + +int +connect(int socket, const struct sockaddr *address, socklen_t addressLength) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_connect(socket, address, addressLength, true)); +} + + +int +listen(int socket, int backlog) +{ + RETURN_AND_SET_ERRNO(common_listen(socket, backlog, true)); +} + + +int +accept(int socket, struct sockaddr *address, socklen_t *_addressLength) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_accept(socket, address, _addressLength, true)); +} + + +ssize_t +recv(int socket, void *data, size_t length, int flags) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_recv(socket, data, length, flags, true)); +} + + +ssize_t +recvfrom(int socket, void *data, size_t length, int flags, + struct sockaddr *address, socklen_t *_addressLength) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_recvfrom(socket, data, length, flags, address, + _addressLength, true)); +} + + +ssize_t +recvmsg(int socket, struct msghdr *message, int flags) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_recvmsg(socket, message, flags, true)); +} + + +ssize_t +send(int socket, const void *data, size_t length, int flags) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_send(socket, data, length, flags, true)); +} + + +ssize_t +sendto(int socket, const void *data, size_t length, int flags, + const struct sockaddr *address, socklen_t addressLength) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_sendto(socket, data, length, flags, address, + addressLength, true)); +} + + +ssize_t +sendmsg(int socket, const struct msghdr *message, int flags) +{ + IoctlSyscallFlagUnsetter _; + RETURN_AND_SET_ERRNO(common_sendmsg(socket, message, flags, true)); +} + + +int +getsockopt(int socket, int level, int option, void *value, socklen_t *_length) +{ + RETURN_AND_SET_ERRNO(common_getsockopt(socket, level, option, value, + _length, true)); +} + + +int +setsockopt(int socket, int level, int option, const void *value, + socklen_t length) +{ + RETURN_AND_SET_ERRNO(common_setsockopt(socket, level, option, value, + length, true)); +} + + +int +getpeername(int socket, struct sockaddr *address, socklen_t *_addressLength) +{ + RETURN_AND_SET_ERRNO(common_getpeername(socket, address, _addressLength, + true)); +} + + +int +getsockname(int socket, struct sockaddr *address, socklen_t *_addressLength) +{ + RETURN_AND_SET_ERRNO(common_getsockname(socket, address, _addressLength, + true)); +} + + +int +sockatmark(int socket) +{ + RETURN_AND_SET_ERRNO(common_sockatmark(socket, true)); +} + + +int +socketpair(int family, int type, int protocol, int socketVector[2]) +{ + RETURN_AND_SET_ERRNO(common_socketpair(family, type, protocol, + socketVector, true)); +} + + +// #pragma mark - syscalls + + +int +_user_socket(int family, int type, int protocol) +{ + return common_socket(family, type, protocol, false); +} + + +status_t +_user_bind(int socket, const struct sockaddr *userAddress, + socklen_t addressLength) +{ + // check parameters and copy address from userland + if (userAddress == NULL || addressLength > MAX_SOCKET_ADDRESS_LEN) + return B_BAD_VALUE; + + char address[MAX_SOCKET_ADDRESS_LEN]; + if (!IS_USER_ADDRESS(userAddress) + || user_memcpy(address, userAddress, addressLength) != B_OK) { + return B_BAD_ADDRESS; + } + + return common_bind(socket, (sockaddr*)address, addressLength, false); +} + + +status_t +_user_shutdown_socket(int socket, int how) +{ + return common_shutdown(socket, how, false); +} + + +status_t +_user_connect(int socket, const struct sockaddr *userAddress, + socklen_t addressLength) +{ + // check parameters and copy address from userland + if (userAddress == NULL || addressLength > MAX_SOCKET_ADDRESS_LEN) + return B_BAD_VALUE; + + char address[MAX_SOCKET_ADDRESS_LEN]; + if (!IS_USER_ADDRESS(userAddress) + || user_memcpy(address, userAddress, addressLength) != B_OK) { + return B_BAD_ADDRESS; + } + + status_t error; + IoctlSyscallRestartWrapper restartWrapper(error); + + error = common_connect(socket, (sockaddr*)address, addressLength, false); + + return error; +} + + +status_t +_user_listen(int socket, int backlog) +{ + return common_listen(socket, backlog, false); +} + + +int +_user_accept(int socket, struct sockaddr *userAddress, + socklen_t *_addressLength) +{ + // check parameters + socklen_t addressLength = 0; + status_t error = prepare_userland_address_result(userAddress, + _addressLength, addressLength, false); + if (error != B_OK) + return error; + + // accept() + int result; + IoctlSyscallRestartWrapper restartWrapper(result); + + char address[MAX_SOCKET_ADDRESS_LEN]; + result = common_accept(socket, + userAddress != NULL ? (sockaddr*)address : NULL, &addressLength, false); + + // copy address size and address back to userland + if (user_memcpy(_addressLength, &addressLength, + sizeof(socklen_t)) != B_OK + || userAddress != NULL + && user_memcpy(userAddress, address, addressLength) != B_OK) { + _user_close(result); + return B_BAD_ADDRESS; + } + + return result; +} + + +ssize_t +_user_recv(int socket, void *data, size_t length, int flags) +{ + ssize_t result; + IoctlSyscallRestartWrapper restartWrapper(result); + + result = common_recv(socket, data, length, flags, false); + + return result; +} + + +ssize_t +_user_recvfrom(int socket, void *data, size_t length, int flags, + struct sockaddr *userAddress, socklen_t *_addressLength) +{ + // check parameters + socklen_t addressLength = 0; + status_t error = prepare_userland_address_result(userAddress, + _addressLength, addressLength, false); + if (error != B_OK) + return error; + + // recvfrom() + ssize_t result; + IoctlSyscallRestartWrapper restartWrapper(result); + + char address[MAX_SOCKET_ADDRESS_LEN]; + result = common_recvfrom(socket, data, length, flags, + userAddress != NULL ? (sockaddr*)address : NULL, &addressLength, false); + if (result < 0) + return result; + + // copy address size and address back to userland + if (user_memcpy(_addressLength, &addressLength, + sizeof(socklen_t)) != B_OK + || userAddress != NULL + && user_memcpy(userAddress, address, addressLength) != B_OK) { + return B_BAD_ADDRESS; + } + + return result; +} + + +ssize_t +_user_recvmsg(int socket, struct msghdr *userMessage, int flags) +{ + // copy message from userland + msghdr message; + iovec* userVecs = message.msg_iov; + iovec vecs[MAX_IO_VEC_COUNT]; + void* userAddress = message.msg_name; + char address[MAX_SOCKET_ADDRESS_LEN]; + + status_t error = prepare_userland_msghdr(userMessage, message, userVecs, + vecs, userAddress, address); + if (error != B_OK) + return error; + + // recvmsg() + ssize_t result; + IoctlSyscallRestartWrapper restartWrapper(result); + + result = common_recvmsg(socket, &message, flags, false); + if (result < 0) + return result; + + // copy the address and address length back to userland + if (userAddress != NULL && user_memcpy(userAddress, address, + message.msg_namelen) != B_OK + || user_memcpy(&userMessage->msg_namelen, &message.msg_namelen, + sizeof(message.msg_namelen)) != B_OK) { + return B_BAD_ADDRESS; + } + + return result; +} + + +ssize_t +_user_send(int socket, const void *data, size_t length, int flags) +{ + ssize_t result; + IoctlSyscallRestartWrapper restartWrapper(result); + + result = common_send(socket, data, length, flags, false); + + return result; +} + + +ssize_t +_user_sendto(int socket, const void *data, size_t length, int flags, + const struct sockaddr *userAddress, socklen_t addressLength) +{ +// TODO: If this is a connection-mode socket, the address parameter is +// supposed to be ignored. + if (userAddress == NULL || addressLength <= 0 + || addressLength > MAX_SOCKET_ADDRESS_LEN) { + return B_BAD_VALUE; + } + + // copy address from userland + char address[MAX_SOCKET_ADDRESS_LEN]; + if (!IS_USER_ADDRESS(userAddress) + || user_memcpy(address, userAddress, addressLength) != B_OK) { + return B_BAD_ADDRESS; + } + + // sendto() + ssize_t result; + IoctlSyscallRestartWrapper restartWrapper(result); + + result = common_sendto(socket, data, length, flags, (sockaddr*)address, + addressLength, false); + return result; +} + + +ssize_t +_user_sendmsg(int socket, const struct msghdr *userMessage, int flags) +{ + // copy message from userland + msghdr message; + iovec* userVecs = message.msg_iov; + iovec vecs[MAX_IO_VEC_COUNT]; + void* userAddress = message.msg_name; + char address[MAX_SOCKET_ADDRESS_LEN]; + + status_t error = prepare_userland_msghdr(userMessage, message, userVecs, + vecs, userAddress, address); + if (error != B_OK) + return error; + + // copy the address from userland + if (userAddress != NULL + && user_memcpy(address, userAddress, message.msg_namelen) != B_OK) { + return B_BAD_ADDRESS; + } + + // sendmsg() + ssize_t result; + IoctlSyscallRestartWrapper restartWrapper(result); + + result = common_sendmsg(socket, &message, flags, false); + return result; +} + + +status_t +_user_getsockopt(int socket, int level, int option, void *userValue, + socklen_t *_length) +{ + // check params + if (userValue == NULL || _length == NULL) + return B_BAD_VALUE; + if (!IS_USER_ADDRESS(userValue) || !IS_USER_ADDRESS(_length)) + return B_BAD_ADDRESS; + + // copy length from userland + socklen_t length; + if (user_memcpy(&length, _length, sizeof(socklen_t)) != B_OK) + return B_BAD_ADDRESS; + + if (length > MAX_SOCKET_OPTION_LEN) + return B_BAD_VALUE; + + // getsockopt() + char value[MAX_SOCKET_OPTION_LEN]; + status_t error = common_getsockopt(socket, level, option, value, &length, + false); + if (error != B_OK) + return error; + + // copy value back to userland + if (user_memcpy(userValue, value, length) != B_OK) + return B_BAD_ADDRESS; + + return B_OK; +} + + +status_t +_user_setsockopt(int socket, int level, int option, const void *userValue, + socklen_t length) +{ + // check params + if (userValue == NULL || length > MAX_SOCKET_OPTION_LEN) + return B_BAD_VALUE; + + // copy value from userland + char value[MAX_SOCKET_OPTION_LEN]; + if (!IS_USER_ADDRESS(userValue) + || user_memcpy(value, userValue, length) != B_OK) { + return B_BAD_ADDRESS; + } + + // setsockopt(); + return common_setsockopt(socket, level, option, value, length, false); +} + + +status_t +_user_getpeername(int socket, struct sockaddr *userAddress, + socklen_t *_addressLength) +{ + // check parameters + socklen_t addressLength = 0; + status_t error = prepare_userland_address_result(userAddress, + _addressLength, addressLength, true); + if (error != B_OK) + return error; + + // getpeername() + char address[MAX_SOCKET_ADDRESS_LEN]; + error = common_getpeername(socket, (sockaddr*)address, &addressLength, + false); + if (error != B_OK) + return error; + + // copy address size and address back to userland + if (user_memcpy(_addressLength, &addressLength, + sizeof(socklen_t)) != B_OK + || userAddress != NULL + && user_memcpy(userAddress, address, addressLength) != B_OK) { + return B_BAD_ADDRESS; + } + + return B_OK; +} + + +status_t +_user_getsockname(int socket, struct sockaddr *userAddress, + socklen_t *_addressLength) +{ + // check parameters + socklen_t addressLength = 0; + status_t error = prepare_userland_address_result(userAddress, + _addressLength, addressLength, true); + if (error != B_OK) + return error; + + // getsocknam() + char address[MAX_SOCKET_ADDRESS_LEN]; + error = common_getsockname(socket, (sockaddr*)address, &addressLength, + false); + if (error != B_OK) + return error; + + // copy address size and address back to userland + if (user_memcpy(_addressLength, &addressLength, + sizeof(socklen_t)) != B_OK + || userAddress != NULL + && user_memcpy(userAddress, address, addressLength) != B_OK) { + return B_BAD_ADDRESS; + } + + return B_OK; +} + + +int +_user_sockatmark(int socket) +{ + return common_sockatmark(socket, false); +} + + +status_t +_user_socketpair(int family, int type, int protocol, int *userSocketVector) +{ + // check parameters + if (userSocketVector == NULL) + return B_BAD_VALUE; + if (!IS_USER_ADDRESS(userSocketVector)) + return B_BAD_ADDRESS; + + // socketpair() + int socketVector[2]; + status_t error = common_socketpair(family, type, protocol, socketVector, + false); + if (error != B_OK) + return error; + + // copy FDs back to userland + if (user_memcpy(userSocketVector, socketVector, + sizeof(socketVector)) != B_OK) { + _user_close(socketVector[0]); + _user_close(socketVector[1]); + return B_BAD_ADDRESS; + } + + return B_OK; +} + + +status_t +_user_get_next_socket_stat(int family, uint32 *_cookie, struct net_stat *_stat) +{ + // check parameters and copy cookie from userland + if (_cookie == NULL || _stat == NULL) + return B_BAD_VALUE; + + uint32 cookie; + if (!IS_USER_ADDRESS(_stat) || !IS_USER_ADDRESS(_cookie) + || user_memcpy(&cookie, _cookie, sizeof(cookie)) != B_OK) { + return B_BAD_ADDRESS; + } + + net_stat stat; + status_t error = common_get_next_socket_stat(family, &cookie, &stat); + if (error != B_OK) + return error; + + // copy cookie and data back to userland + if (user_memcpy(_cookie, &cookie, sizeof(cookie)) != B_OK + || user_memcpy(_stat, &stat, sizeof(net_stat)) != B_OK) { + return B_BAD_ADDRESS; + } + + return B_OK; +}