diff --git a/headers/private/net/net_protocol.h b/headers/private/net/net_protocol.h index e4014ccb86..e51f06e16a 100644 --- a/headers/private/net/net_protocol.h +++ b/headers/private/net/net_protocol.h @@ -36,6 +36,10 @@ struct net_protocol_module_info { status_t (*accept)(net_protocol *self, struct net_socket **_acceptedSocket); status_t (*control)(net_protocol *self, int level, int option, void *value, size_t *_length); + status_t (*getsockopt)(net_protocol *self, int level, int option, + void *value, int *_length); + status_t (*setsockopt)(net_protocol *self, int level, int option, + const void *value, int length); status_t (*bind)(net_protocol *self, struct sockaddr *address); status_t (*unbind)(net_protocol *self, struct sockaddr *address); diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index a9efc8ca4a..07d50e71f0 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -59,6 +59,11 @@ struct net_socket_module_info { status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags, net_buffer **_buffer); + status_t (*get_option)(net_socket *socket, int option, void *value, + int *_length); + status_t (*set_option)(net_socket *socket, int option, + const void *value, int length); + status_t (*get_next_stat)(uint32 *cookie, int family, struct net_stat *stat); // connections diff --git a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp index 2c591db367..e02cf29688 100644 --- a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp +++ b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp @@ -330,6 +330,8 @@ net_protocol_module_info sICMPModule = { icmp_connect, icmp_accept, icmp_control, + NULL, // getsockopt + NULL, // setsockopt icmp_bind, icmp_unbind, icmp_listen, diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index fae5d02148..97e9e53807 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -181,6 +181,7 @@ net_buffer_module_info *gBufferModule; static struct net_domain *sDomain; static net_datalink_module_info *sDatalinkModule; +static net_socket_module_info *sSocketModule; static int32 sPacketID; static RawSocketList sRawSockets; static benaphore sRawSocketsLock; @@ -1049,7 +1050,7 @@ get_int_option(void *target, size_t length, int value) template static status_t -set_int_option(Type &target, void *_value, size_t length) +set_int_option(Type &target, const void *_value, size_t length) { int value; @@ -1071,56 +1072,80 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, if ((level & LEVEL_MASK) != IPPROTO_IP) return sDatalinkModule->control(sDomain, option, value, _length); + return B_BAD_VALUE; +} + + +status_t +ipv4_getsockopt(net_protocol *_protocol, int level, int option, void *value, + int *_length) +{ ipv4_protocol *protocol = (ipv4_protocol *)_protocol; - if (level & LEVEL_GET_OPTION) { - // get options + // as we are the last protocol in the chain (i.e. no socket protocol + // below) we must call into the socket module directly. + if (level == SOL_SOCKET) + return sSocketModule->get_option(protocol->socket, option, value, + _length); + else if (level != IPPROTO_IP) + return B_BAD_VALUE; - switch (option) { - case IP_HDRINCL: - return get_int_option(value, *_length, - (protocol->flags & IP_FLAG_HEADER_INCLUDED) != 0); + switch (option) { + case IP_HDRINCL: + return get_int_option(value, *_length, + (protocol->flags & IP_FLAG_HEADER_INCLUDED) != 0); - case IP_TTL: - return get_int_option(value, *_length, protocol->time_to_live); + case IP_TTL: + return get_int_option(value, *_length, protocol->time_to_live); - case IP_TOS: - return get_int_option(value, *_length, protocol->service_type); + case IP_TOS: + return get_int_option(value, *_length, protocol->service_type); - case IP_MULTICAST_TTL: - return get_int_option(value, *_length, - protocol->multicast_time_to_live); + case IP_MULTICAST_TTL: + return get_int_option(value, *_length, + protocol->multicast_time_to_live); - case IP_ADD_MEMBERSHIP: - case IP_DROP_MEMBERSHIP: - case IP_BLOCK_SOURCE: - case IP_UNBLOCK_SOURCE: - case IP_ADD_SOURCE_MEMBERSHIP: - case IP_DROP_SOURCE_MEMBERSHIP: - case MCAST_JOIN_GROUP: - case MCAST_LEAVE_GROUP: - case MCAST_BLOCK_SOURCE: - case MCAST_UNBLOCK_SOURCE: - case MCAST_JOIN_SOURCE_GROUP: - case MCAST_LEAVE_SOURCE_GROUP: - // RFC 3678, Section 4.1: - // ``An error of EOPNOTSUPP is returned if these options are - // used with getsockopt().'' - return EOPNOTSUPP; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + case IP_BLOCK_SOURCE: + case IP_UNBLOCK_SOURCE: + case IP_ADD_SOURCE_MEMBERSHIP: + case IP_DROP_SOURCE_MEMBERSHIP: + case MCAST_JOIN_GROUP: + case MCAST_LEAVE_GROUP: + case MCAST_BLOCK_SOURCE: + case MCAST_UNBLOCK_SOURCE: + case MCAST_JOIN_SOURCE_GROUP: + case MCAST_LEAVE_SOURCE_GROUP: + // RFC 3678, Section 4.1: + // ``An error of EOPNOTSUPP is returned if these options are + // used with getsockopt().'' + return EOPNOTSUPP; - default: - dprintf("IPv4::control(): get unknown option: %d\n", option); - return ENOPROTOOPT; - } + default: + dprintf("IPv4::getsockopt(): get unknown option: %d\n", option); + return ENOPROTOOPT; } +} - // set options + +status_t +ipv4_setsockopt(net_protocol *_protocol, int level, int option, + const void *value, int length) +{ + ipv4_protocol *protocol = (ipv4_protocol *)_protocol; + + if (level == SOL_SOCKET) + return sSocketModule->set_option(protocol->socket, option, value, + length); + else if (level != IPPROTO_IP) + return B_BAD_VALUE; switch (option) { case IP_HDRINCL: { int headerIncluded; - if (*_length != sizeof(int)) + if (length != sizeof(int)) return B_BAD_VALUE; if (user_memcpy(&headerIncluded, value, sizeof(headerIncluded)) < B_OK) return B_BAD_ADDRESS; @@ -1133,20 +1158,20 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, } case IP_TTL: - return set_int_option(protocol->time_to_live, value, *_length); + return set_int_option(protocol->time_to_live, value, length); case IP_TOS: - return set_int_option(protocol->service_type, value, *_length); + return set_int_option(protocol->service_type, value, length); case IP_MULTICAST_TTL: return set_int_option(protocol->multicast_time_to_live, value, - *_length); + length); case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: { ip_mreq mreq; - if (*_length != sizeof(ip_mreq)) + if (length != sizeof(ip_mreq)) return B_BAD_VALUE; if (user_memcpy(&mreq, value, sizeof(ip_mreq)) < B_OK) return B_BAD_ADDRESS; @@ -1161,7 +1186,7 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, case IP_DROP_SOURCE_MEMBERSHIP: { ip_mreq_source mreq; - if (*_length != sizeof(ip_mreq_source)) + if (length != sizeof(ip_mreq_source)) return B_BAD_VALUE; if (user_memcpy(&mreq, value, sizeof(ip_mreq_source)) < B_OK) return B_BAD_ADDRESS; @@ -1174,7 +1199,7 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, case MCAST_LEAVE_GROUP: { group_req greq; - if (*_length != sizeof(group_req)) + if (length != sizeof(group_req)) return B_BAD_VALUE; if (user_memcpy(&greq, value, sizeof(group_req)) < B_OK) return B_BAD_ADDRESS; @@ -1189,7 +1214,7 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, case MCAST_LEAVE_SOURCE_GROUP: { group_source_req greq; - if (*_length != sizeof(group_source_req)) + if (length != sizeof(group_source_req)) return B_BAD_VALUE; if (user_memcpy(&greq, value, sizeof(group_source_req)) < B_OK) return B_BAD_ADDRESS; @@ -1199,12 +1224,9 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, } default: - dprintf("IPv4::control(): set unknown option: %d\n", option); + dprintf("IPv4::setsockopt(): set unknown option: %d\n", option); return ENOPROTOOPT; } - - // never gets here - return B_BAD_VALUE; } @@ -1689,6 +1711,8 @@ net_protocol_module_info gIPv4Module = { ipv4_connect, ipv4_accept, ipv4_control, + ipv4_getsockopt, + ipv4_setsockopt, ipv4_bind, ipv4_unbind, ipv4_listen, @@ -1710,6 +1734,7 @@ module_dependency module_dependencies[] = { {NET_STACK_MODULE_NAME, (module_info **)&gStackModule}, {NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule}, {NET_DATALINK_MODULE_NAME, (module_info **)&sDatalinkModule}, + {NET_SOCKET_MODULE_NAME, (module_info **)&sSocketModule}, {} }; diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index 6b7fb2f012..16835ef3a2 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -385,9 +385,6 @@ TCPEndpoint::Connect(const sockaddr *address) } else if (fState != CLOSED) return EISCONN; - fSendQueue.SetMaxBytes(socket->send.buffer_size); - fReceiveQueue.SetMaxBytes(socket->receive.buffer_size); - status_t status = _PrepareSendPath(address); if (status < B_OK) return status; @@ -689,6 +686,24 @@ TCPEndpoint::ReadAvailable() } +status_t +TCPEndpoint::SetSendBufferSize(size_t length) +{ + RecursiveLocker _(fLock); + fSendQueue.SetMaxBytes(length); + return B_OK; +} + + +status_t +TCPEndpoint::SetReceiveBufferSize(size_t length) +{ + RecursiveLocker _(fLock); + fReceiveQueue.SetMaxBytes(length); + return B_OK; +} + + // #pragma mark - misc diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h index 4efcc06708..67d88ac07f 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h @@ -59,6 +59,9 @@ class TCPEndpoint : public net_protocol { status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer); ssize_t ReadAvailable(); + status_t SetSendBufferSize(size_t length); + status_t SetReceiveBufferSize(size_t length); + tcp_state State() const { return fState; } bool IsBound() const; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index db401d0f7b..675fa3f15a 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -410,24 +410,46 @@ tcp_control(net_protocol *_protocol, int level, int option, void *value, { TCPEndpoint *protocol = (TCPEndpoint *)_protocol; - switch (level & LEVEL_MASK) { - case IPPROTO_TCP: - if (option == NET_STAT_SOCKET) { - net_stat *stat = (net_stat *)value; - strlcpy(stat->state, name_for_state(protocol->State()), - sizeof(stat->state)); - return B_OK; - } - break; - case SOL_SOCKET: - break; - - default: - return protocol->next->module->control(protocol->next, level, option, - value, _length); + if ((level & LEVEL_MASK) == IPPROTO_TCP) { + if (option == NET_STAT_SOCKET) { + net_stat *stat = (net_stat *)value; + strlcpy(stat->state, name_for_state(protocol->State()), + sizeof(stat->state)); + return B_OK; + } } - return B_BAD_VALUE; + return protocol->next->module->control(protocol->next, level, option, + value, _length); +} + + +status_t +tcp_setsockopt(net_protocol *_protocol, int level, int option, + const void *_value, int length) +{ + TCPEndpoint *protocol = (TCPEndpoint *)_protocol; + + if (level == SOL_SOCKET) { + if (option == SO_SNDBUF || option == SO_RCVBUF) { + if (length != sizeof(int)) + return B_BAD_VALUE; + + status_t status; + const int *value = (const int *)_value; + + if (option == SO_SNDBUF) + status = protocol->SetSendBufferSize(*value); + else + status = protocol->SetReceiveBufferSize(*value); + + if (status < B_OK) + return status; + } + } + + return protocol->next->module->setsockopt(protocol, level, option, _value, + length); } @@ -671,6 +693,8 @@ net_protocol_module_info sTCPModule = { tcp_connect, tcp_accept, tcp_control, + NULL, // getsockopt + tcp_setsockopt, tcp_bind, tcp_unbind, tcp_listen, diff --git a/src/add-ons/kernel/network/protocols/udp/udp.cpp b/src/add-ons/kernel/network/protocols/udp/udp.cpp index de0b15d46b..07ba62ba76 100644 --- a/src/add-ons/kernel/network/protocols/udp/udp.cpp +++ b/src/add-ons/kernel/network/protocols/udp/udp.cpp @@ -1212,6 +1212,8 @@ net_protocol_module_info sUDPModule = { udp_connect, udp_accept, udp_control, + NULL, // getsockopt + NULL, // setsockopt udp_bind, udp_unbind, udp_listen, diff --git a/src/add-ons/kernel/network/stack/link.cpp b/src/add-ons/kernel/network/stack/link.cpp index 1f30cbf787..892b8e92fe 100644 --- a/src/add-ons/kernel/network/stack/link.cpp +++ b/src/add-ons/kernel/network/stack/link.cpp @@ -498,6 +498,8 @@ net_protocol_module_info gLinkModule = { link_connect, link_accept, link_control, + NULL, // getsockopt + NULL, // setsockopt link_bind, link_unbind, link_listen, diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index e851a3d64b..0d98905f4d 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -639,15 +639,9 @@ socket_getsockname(net_socket *socket, struct sockaddr *address, socklen_t *_add } -int -socket_getsockopt(net_socket *socket, int level, int option, void *value, - int *_length) +status_t +socket_get_option(net_socket *socket, int option, void *value, int *_length) { - if (level != SOL_SOCKET) { - return socket->first_info->control(socket->first_protocol, - level | LEVEL_GET_OPTION, option, value, (size_t *)_length); - } - switch (option) { case SO_SNDBUF: { @@ -747,6 +741,23 @@ socket_getsockopt(net_socket *socket, int level, int option, void *value, } +int +socket_getsockopt(net_socket *socket, int level, int option, void *value, + int *_length) +{ + status_t status = (level == SOL_SOCKET) ? B_OK : B_BAD_VALUE; + + if (socket->first_info->getsockopt) + status = socket->first_info->getsockopt(socket->first_protocol, level, + option, value, _length); + + if (status < B_OK) + return status; + + return socket_get_option(socket, option, value, _length); +} + + int socket_listen(net_socket *socket, int backlog) { @@ -940,15 +951,10 @@ socket_send(net_socket *socket, msghdr *header, const void *data, } -int -socket_setsockopt(net_socket *socket, int level, int option, const void *value, +status_t +socket_set_option(net_socket *socket, int option, const void *value, int length) { - if (level != SOL_SOCKET) { - return socket->first_info->control(socket->first_protocol, - level | LEVEL_SET_OPTION, option, (void *)value, (size_t *)&length); - } - switch (option) { // TODO: implement other options! case SO_LINGER: @@ -968,8 +974,6 @@ socket_setsockopt(net_socket *socket, int level, int option, const void *value, } case SO_SNDBUF: - // TODO: should be handled in the protocol modules - they can actually - // check if setting the value is allowed and within valid bounds. if (length != sizeof(uint32)) return B_BAD_VALUE; @@ -977,7 +981,6 @@ socket_setsockopt(net_socket *socket, int level, int option, const void *value, return B_OK; case SO_RCVBUF: - // TODO: see above (SO_SNDBUF) if (length != sizeof(uint32)) return B_BAD_VALUE; @@ -985,7 +988,6 @@ socket_setsockopt(net_socket *socket, int level, int option, const void *value, return B_OK; case SO_SNDLOWAT: - // TODO: see above (SO_SNDBUF) if (length != sizeof(uint32)) return B_BAD_VALUE; @@ -993,7 +995,6 @@ socket_setsockopt(net_socket *socket, int level, int option, const void *value, return B_OK; case SO_RCVLOWAT: - // TODO: see above (SO_SNDBUF) if (length != sizeof(uint32)) return B_BAD_VALUE; @@ -1057,6 +1058,23 @@ socket_setsockopt(net_socket *socket, int level, int option, const void *value, } +int +socket_setsockopt(net_socket *socket, int level, int option, const void *value, + int length) +{ + status_t status = (level == SOL_SOCKET) ? B_OK : B_BAD_VALUE; + + if (socket->first_info->setsockopt) + status = socket->first_info->setsockopt(socket->first_protocol, level, + option, value, length); + + if (status < B_OK) + return status; + + return socket_set_option(socket, option, value, length); +} + + int socket_shutdown(net_socket *socket, int direction) { @@ -1111,6 +1129,9 @@ net_socket_module_info gNetSocketModule = { socket_send_data, socket_receive_data, + socket_get_option, + socket_set_option, + socket_get_next_stat, // connections