some getsockopt/setsockopt and multicast fixes.

* allow an ipv4 bind() to a multicast address.
 * bumped getsockopt/setsockopt kernel driver buffers to 256 bytes to at least handle structures which take one sockaddr_storage.
 * convert generic multicast delta API names to IPv4 ones before handling the specific option.
 * changed ipv4_getsockopt/ipv4_setsockopt a bit as the code gcc 2.95 was generating was a bit too funky.
 * properly pass setsockopt/getsockopt to handling protocols.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20939 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos
2007-04-30 19:42:12 +00:00
parent d3437fec0e
commit 73f1548be1
4 changed files with 106 additions and 117 deletions
+3 -3
View File
@@ -59,9 +59,9 @@ 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);
status_t (*get_option)(net_socket *socket, int option, void *value, status_t (*get_option)(net_socket *socket, int level, int option,
int *_length); void *value, int *_length);
status_t (*set_option)(net_socket *socket, int option, status_t (*set_option)(net_socket *socket, int level, int option,
const void *value, int length); const void *value, int length);
status_t (*get_next_stat)(uint32 *cookie, int family, struct net_stat *stat); status_t (*get_next_stat)(uint32 *cookie, int family, struct net_stat *stat);
@@ -428,9 +428,9 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
if (status < B_OK) if (status < B_OK)
return status; return status;
char valueBuffer[128]; char valueBuffer[256];
if (args.length > (int)sizeof(valueBuffer)) if (args.length > (int)sizeof(valueBuffer))
args.length = (int)sizeof(valueBuffer); return ENOBUFS;
status = sSocket->getsockopt(cookie->socket, args.level, args.option, status = sSocket->getsockopt(cookie->socket, args.level, args.option,
valueBuffer, &args.length); valueBuffer, &args.length);
@@ -451,9 +451,9 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
if (status < B_OK) if (status < B_OK)
return status; return status;
char valueBuffer[128]; char valueBuffer[256];
if (args.length > (int)sizeof(valueBuffer)) if (args.length > (int)sizeof(valueBuffer))
return B_BAD_VALUE; return ENOBUFS;
if (user_memcpy(valueBuffer, args.value, args.length) < B_OK) if (user_memcpy(valueBuffer, args.value, args.length) < B_OK)
return B_BAD_ADDRESS; return B_BAD_ADDRESS;
@@ -882,7 +882,7 @@ ipv4_delta_membership(ipv4_protocol *protocol, int option,
case IP_DROP_SOURCE_MEMBERSHIP: case IP_DROP_SOURCE_MEMBERSHIP:
group = filter.GetGroup(*groupAddr, false); group = filter.GetGroup(*groupAddr, false);
if (group == NULL) { if (group == NULL) {
if (option == IP_DROP_SOURCE_MEMBERSHIP if (option == IP_DROP_MEMBERSHIP
|| option == IP_DROP_SOURCE_MEMBERSHIP) || option == IP_DROP_SOURCE_MEMBERSHIP)
return EADDRNOTAVAIL; return EADDRNOTAVAIL;
else else
@@ -899,6 +899,28 @@ ipv4_delta_membership(ipv4_protocol *protocol, int option,
} }
static int
generic_to_ipv4(int option)
{
switch (option) {
case MCAST_JOIN_GROUP:
return IP_ADD_MEMBERSHIP;
case MCAST_JOIN_SOURCE_GROUP:
return IP_ADD_SOURCE_MEMBERSHIP;
case MCAST_LEAVE_GROUP:
return IP_DROP_MEMBERSHIP;
case MCAST_BLOCK_SOURCE:
return IP_BLOCK_SOURCE;
case MCAST_UNBLOCK_SOURCE:
return IP_UNBLOCK_SOURCE;
case MCAST_LEAVE_SOURCE_GROUP:
return IP_DROP_SOURCE_MEMBERSHIP;
}
return -1;
}
static status_t static status_t
ipv4_delta_membership(ipv4_protocol *protocol, int option, ipv4_delta_membership(ipv4_protocol *protocol, int option,
in_addr *interfaceAddr, in_addr *groupAddr, in_addr *sourceAddr) in_addr *interfaceAddr, in_addr *groupAddr, in_addr *sourceAddr)
@@ -918,8 +940,8 @@ ipv4_delta_membership(ipv4_protocol *protocol, int option,
if (interface == NULL) if (interface == NULL)
return ENODEV; return ENODEV;
return ipv4_delta_membership(protocol, option, interface, groupAddr, return ipv4_delta_membership(protocol, option, interface,
sourceAddr); groupAddr, sourceAddr);
} }
@@ -945,8 +967,8 @@ ipv4_generic_delta_membership(ipv4_protocol *protocol, int option,
if (_sourceAddr) if (_sourceAddr)
sourceAddr = &((const sockaddr_in *)_sourceAddr)->sin_addr; sourceAddr = &((const sockaddr_in *)_sourceAddr)->sin_addr;
return ipv4_delta_membership(protocol, option, interface, groupAddr, return ipv4_delta_membership(protocol, generic_to_ipv4(option), interface,
sourceAddr); groupAddr, sourceAddr);
} }
@@ -1092,50 +1114,41 @@ ipv4_getsockopt(net_protocol *_protocol, int level, int option, void *value,
{ {
ipv4_protocol *protocol = (ipv4_protocol *)_protocol; ipv4_protocol *protocol = (ipv4_protocol *)_protocol;
// as we are the last protocol in the chain (i.e. no socket protocol if (level == IPPROTO_IP) {
// below) we must call into the socket module directly. if (option == IP_HDRINCL)
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, return get_int_option(value, *_length,
(protocol->flags & IP_FLAG_HEADER_INCLUDED) != 0); (protocol->flags & IP_FLAG_HEADER_INCLUDED) != 0);
else if (option == IP_TTL)
case IP_TTL:
return get_int_option(value, *_length, protocol->time_to_live); return get_int_option(value, *_length, protocol->time_to_live);
else if (option == IP_TOS)
case IP_TOS:
return get_int_option(value, *_length, protocol->service_type); return get_int_option(value, *_length, protocol->service_type);
else if (IP_MULTICAST_TTL)
case IP_MULTICAST_TTL:
return get_int_option(value, *_length, return get_int_option(value, *_length,
protocol->multicast_time_to_live); protocol->multicast_time_to_live);
else if (option == IP_ADD_MEMBERSHIP
case IP_ADD_MEMBERSHIP: || option == IP_DROP_MEMBERSHIP
case IP_DROP_MEMBERSHIP: || option == IP_BLOCK_SOURCE
case IP_BLOCK_SOURCE: || option == IP_UNBLOCK_SOURCE
case IP_UNBLOCK_SOURCE: || option == IP_ADD_SOURCE_MEMBERSHIP
case IP_ADD_SOURCE_MEMBERSHIP: || option == IP_DROP_SOURCE_MEMBERSHIP
case IP_DROP_SOURCE_MEMBERSHIP: || option == MCAST_JOIN_GROUP
case MCAST_JOIN_GROUP: || option == MCAST_LEAVE_GROUP
case MCAST_LEAVE_GROUP: || option == MCAST_BLOCK_SOURCE
case MCAST_BLOCK_SOURCE: || option == MCAST_UNBLOCK_SOURCE
case MCAST_UNBLOCK_SOURCE: || option == MCAST_JOIN_SOURCE_GROUP
case MCAST_JOIN_SOURCE_GROUP: || option == MCAST_LEAVE_SOURCE_GROUP) {
case MCAST_LEAVE_SOURCE_GROUP:
// RFC 3678, Section 4.1: // RFC 3678, Section 4.1:
// ``An error of EOPNOTSUPP is returned if these options are // ``An error of EOPNOTSUPP is returned if these options are
// used with getsockopt().'' // used with getsockopt().''
return EOPNOTSUPP; return EOPNOTSUPP;
} else {
default:
dprintf("IPv4::getsockopt(): get unknown option: %d\n", option); dprintf("IPv4::getsockopt(): get unknown option: %d\n", option);
return ENOPROTOOPT; return ENOPROTOOPT;
} }
}
return sSocketModule->get_option(protocol->socket, level, option, value,
_length);
} }
@@ -1145,15 +1158,8 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
{ {
ipv4_protocol *protocol = (ipv4_protocol *)_protocol; ipv4_protocol *protocol = (ipv4_protocol *)_protocol;
if (level == SOL_SOCKET) if (level == IPPROTO_IP) {
return sSocketModule->set_option(protocol->socket, option, value, if (option == IP_HDRINCL) {
length);
else if (level != IPPROTO_IP)
return B_BAD_VALUE;
switch (option) {
case IP_HDRINCL:
{
int headerIncluded; int headerIncluded;
if (length != sizeof(int)) if (length != sizeof(int))
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -1165,21 +1171,15 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
else else
protocol->flags &= ~IP_FLAG_HEADER_INCLUDED; protocol->flags &= ~IP_FLAG_HEADER_INCLUDED;
return B_OK; return B_OK;
} } else if (option == IP_TTL) {
case IP_TTL:
return set_int_option(protocol->time_to_live, value, length); return set_int_option(protocol->time_to_live, value, length);
} else if (option == IP_TOS) {
case IP_TOS:
return set_int_option(protocol->service_type, value, length); return set_int_option(protocol->service_type, value, length);
} else if (option == IP_MULTICAST_TTL) {
case IP_MULTICAST_TTL:
return set_int_option(protocol->multicast_time_to_live, value, return set_int_option(protocol->multicast_time_to_live, value,
length); length);
} else if (option == IP_ADD_MEMBERSHIP
case IP_ADD_MEMBERSHIP: || option == IP_DROP_MEMBERSHIP) {
case IP_DROP_MEMBERSHIP:
{
ip_mreq mreq; ip_mreq mreq;
if (length != sizeof(ip_mreq)) if (length != sizeof(ip_mreq))
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -1188,13 +1188,10 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
return ipv4_delta_membership(protocol, option, &mreq.imr_interface, return ipv4_delta_membership(protocol, option, &mreq.imr_interface,
&mreq.imr_multiaddr, NULL); &mreq.imr_multiaddr, NULL);
} } else if (option == IP_BLOCK_SOURCE
|| option == IP_UNBLOCK_SOURCE
case IP_BLOCK_SOURCE: || option == IP_ADD_SOURCE_MEMBERSHIP
case IP_UNBLOCK_SOURCE: || option == IP_DROP_SOURCE_MEMBERSHIP) {
case IP_ADD_SOURCE_MEMBERSHIP:
case IP_DROP_SOURCE_MEMBERSHIP:
{
ip_mreq_source mreq; ip_mreq_source mreq;
if (length != sizeof(ip_mreq_source)) if (length != sizeof(ip_mreq_source))
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -1203,11 +1200,8 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
return ipv4_delta_membership(protocol, option, &mreq.imr_interface, return ipv4_delta_membership(protocol, option, &mreq.imr_interface,
&mreq.imr_multiaddr, &mreq.imr_sourceaddr); &mreq.imr_multiaddr, &mreq.imr_sourceaddr);
} } else if (option == MCAST_LEAVE_GROUP
|| option == MCAST_JOIN_GROUP) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
{
group_req greq; group_req greq;
if (length != sizeof(group_req)) if (length != sizeof(group_req))
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -1216,13 +1210,10 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
return ipv4_generic_delta_membership(protocol, option, return ipv4_generic_delta_membership(protocol, option,
greq.gr_interface, &greq.gr_group, NULL); greq.gr_interface, &greq.gr_group, NULL);
} } else if (option == MCAST_BLOCK_SOURCE
|| option == MCAST_UNBLOCK_SOURCE
case MCAST_BLOCK_SOURCE: || option == MCAST_JOIN_SOURCE_GROUP
case MCAST_UNBLOCK_SOURCE: || option == MCAST_LEAVE_SOURCE_GROUP) {
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
{
group_source_req greq; group_source_req greq;
if (length != sizeof(group_source_req)) if (length != sizeof(group_source_req))
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -1231,12 +1222,14 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
return ipv4_generic_delta_membership(protocol, option, return ipv4_generic_delta_membership(protocol, option,
greq.gsr_interface, &greq.gsr_group, &greq.gsr_source); greq.gsr_interface, &greq.gsr_group, &greq.gsr_source);
} } else {
default:
dprintf("IPv4::setsockopt(): set unknown option: %d\n", option); dprintf("IPv4::setsockopt(): set unknown option: %d\n", option);
return ENOPROTOOPT; return ENOPROTOOPT;
} }
}
return sSocketModule->set_option(protocol->socket, level, option,
value, length);
} }
@@ -1248,6 +1241,7 @@ ipv4_bind(net_protocol *protocol, const struct sockaddr *address)
// only INADDR_ANY and addresses of local interfaces are accepted: // only INADDR_ANY and addresses of local interfaces are accepted:
if (((sockaddr_in *)address)->sin_addr.s_addr == INADDR_ANY if (((sockaddr_in *)address)->sin_addr.s_addr == INADDR_ANY
|| IN_MULTICAST(((sockaddr_in *)address)->sin_addr.s_addr)
|| sDatalinkModule->is_local_address(sDomain, address, NULL, NULL)) { || sDatalinkModule->is_local_address(sDomain, address, NULL, NULL)) {
memcpy(&protocol->socket->address, address, sizeof(struct sockaddr_in)); memcpy(&protocol->socket->address, address, sizeof(struct sockaddr_in));
protocol->socket->address.ss_len = sizeof(struct sockaddr_in); protocol->socket->address.ss_len = sizeof(struct sockaddr_in);
+22 -27
View File
@@ -642,8 +642,12 @@ socket_getsockname(net_socket *socket, struct sockaddr *address, socklen_t *_add
status_t status_t
socket_get_option(net_socket *socket, int option, void *value, int *_length) socket_get_option(net_socket *socket, int level, int option, void *value,
int *_length)
{ {
if (level != SOL_SOCKET)
return ENOPROTOOPT;
switch (option) { switch (option) {
case SO_SNDBUF: case SO_SNDBUF:
{ {
@@ -747,20 +751,14 @@ int
socket_getsockopt(net_socket *socket, int level, int option, void *value, socket_getsockopt(net_socket *socket, int level, int option, void *value,
int *_length) int *_length)
{ {
status_t status = (level == SOL_SOCKET) ? B_OK : B_BAD_VALUE; for (net_protocol *protocol = socket->first_protocol;
protocol; protocol = protocol->next) {
if (protocol->module->getsockopt)
return protocol->module->getsockopt(protocol, level, option,
value, _length);
}
net_protocol *protocol = socket->first_protocol; return socket_get_option(socket, level, option, value, _length);
while (protocol && protocol->module->getsockopt == NULL)
protocol = protocol->next;
if (protocol)
status = protocol->module->getsockopt(protocol, level,
option, value, _length);
if (status < B_OK)
return status;
return socket_get_option(socket, option, value, _length);
} }
@@ -958,9 +956,12 @@ socket_send(net_socket *socket, msghdr *header, const void *data,
status_t status_t
socket_set_option(net_socket *socket, int option, const void *value, socket_set_option(net_socket *socket, int level, int option, const void *value,
int length) int length)
{ {
if (level != SOL_SOCKET)
return ENOPROTOOPT;
switch (option) { switch (option) {
// TODO: implement other options! // TODO: implement other options!
case SO_LINGER: case SO_LINGER:
@@ -1068,20 +1069,14 @@ int
socket_setsockopt(net_socket *socket, int level, int option, const void *value, socket_setsockopt(net_socket *socket, int level, int option, const void *value,
int length) int length)
{ {
status_t status = (level == SOL_SOCKET) ? B_OK : B_BAD_VALUE; for (net_protocol *protocol = socket->first_protocol;
protocol; protocol = protocol->next) {
net_protocol *protocol = socket->first_protocol; if (protocol->module->setsockopt)
while (protocol && protocol->module->setsockopt == NULL) return protocol->module->setsockopt(protocol, level, option,
protocol = protocol->next;
if (protocol)
status = protocol->module->setsockopt(protocol, level, option,
value, length); value, length);
}
if (status < B_OK) return socket_set_option(socket, level, option, value, length);
return status;
return socket_set_option(socket, option, value, length);
} }