Skip to content

Commit

Permalink
Debug setsockopt JoinmulticastGroup failure on UDP (#3841)
Browse files Browse the repository at this point in the history
b/355289060
  • Loading branch information
haozheng-cobalt committed Jul 24, 2024
1 parent b865777 commit 6e5c6cc
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 6 deletions.
1 change: 1 addition & 0 deletions starboard/nplb/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ target(gtest_target_type, "nplb") {
"posix_compliance/posix_socket_create_test.cc",
"posix_compliance/posix_socket_errno_test.cc",
"posix_compliance/posix_socket_helpers.cc",
"posix_compliance/posix_socket_join_multicast_group_test.cc",
"posix_compliance/posix_socket_listen_test.cc",
"posix_compliance/posix_socket_receive_test.cc",
"posix_compliance/posix_socket_recvfrom_test.cc",
Expand Down
4 changes: 2 additions & 2 deletions starboard/nplb/posix_compliance/posix_socket_create_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ TEST(PosixSocketCreateTest, ATonOfTcp) {
for (int i = 0; i < kATon; ++i) {
int socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

EXPECT_TRUE(socket_fd > 0);
EXPECT_TRUE(socket_fd >= 0);
EXPECT_TRUE(close(socket_fd) == 0);
}
}
Expand All @@ -39,7 +39,7 @@ TEST(PosixSocketCreateTest, ManyTcpAtOnce) {
int sockets_fd[kMany] = {0};
for (int i = 0; i < kMany; ++i) {
sockets_fd[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ASSERT_TRUE(sockets_fd[i] > 0);
ASSERT_TRUE(sockets_fd[i] >= 0);
}

for (int i = 0; i < kMany; ++i) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2024 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "starboard/configuration.h"
#include "starboard/nplb/posix_compliance/posix_socket_helpers.h"

namespace starboard {
namespace nplb {
namespace {

int CreateMulticastSocket(const struct ip_mreq& address) {
int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
EXPECT_NE(-1, socket_fd);

int reuse = 1;
EXPECT_NE(-1, setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse)));

// It will choose a port for me.
struct sockaddr_in bind_address;
memset(&bind_address, 0, sizeof(bind_address));
bind_address.sin_family = AF_INET;
bind_address.sin_addr.s_addr = htonl(INADDR_ANY);
bind_address.sin_port = htons(0);

EXPECT_NE(-1, bind(socket_fd, (struct sockaddr*)&bind_address,
sizeof(bind_address)));

EXPECT_NE(-1, setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &address,
sizeof(address)));
return socket_fd;
}

int CreateSendSocket() {
int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
EXPECT_NE(-1, socket_fd);

int reuse = 1;
EXPECT_NE(-1, setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse)));
return socket_fd;
}

TEST(PosixSocketJoinMulticastGroupTest, SunnyDay) {
// "224.0.2.0" is an unassigned ad-hoc multicast address. Hopefully no one is
// spamming it on the local network.
// http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
struct ip_mreq address;
// Same as doing inet_addr("224.0.2.0");, however inet_addr is not supported
// yet in Starboard 16.
// TODO: we should support inet_addr for better handling endianness across
// different systems.
#if SB_IS_BIG_ENDIAN
address.imr_multiaddr.s_addr = (224 << 24) | (0 << 16) | (2 << 8) | 0;
#else
address.imr_multiaddr.s_addr = (0 << 24) | (2 << 16) | (0 << 8) | 224;
#endif
address.imr_interface.s_addr =
htonl(INADDR_ANY); // Use the default network interface

int receive_socket = CreateMulticastSocket(address);
int send_socket = CreateSendSocket();

// Get the bound port.
struct sockaddr_in local_address;
socklen_t local_address_len = sizeof(local_address);
EXPECT_NE(-1, getsockname(receive_socket, (struct sockaddr*)&local_address,
&local_address_len));

struct sockaddr_in send_address;
memset(&send_address, 0, sizeof(send_address));
send_address.sin_family = AF_INET;

#if SB_IS_BIG_ENDIAN
send_address.sin_addr.s_addr = (224 << 24) | (0 << 16) | (2 << 8) | 0;
#else
send_address.sin_addr.s_addr = (0 << 24) | (2 << 16) | (0 << 8) | 224;
#endif
send_address.sin_port = local_address.sin_port;

const char kBuf[] = "01234567890123456789";
char buf[sizeof(kBuf)] = {0};
while (true) {
ssize_t sent =
sendto(send_socket, kBuf, sizeof(kBuf), 0,
(struct sockaddr*)&send_address, sizeof(send_address));
if (sent < 0) {
if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
}

// Clean up the sockets.
EXPECT_NE(-1, close(send_socket));
EXPECT_NE(-1, close(receive_socket));
FAIL() << "Failed to send multicast packet: " << errno;
return;
}
EXPECT_EQ(sizeof(kBuf), sent);
break;
}

struct sockaddr_in receive_address;
socklen_t receive_address_len = sizeof(receive_address);
int64_t stop_time = CurrentMonotonicTime() + 1'000'000LL;

while (true) {
// Breaks the case where the test will hang in a loop when
// recvfrom always returns pending status.
ASSERT_LE(CurrentMonotonicTime(), stop_time) << "Multicast timed out.";
ssize_t received =
recvfrom(receive_socket, buf, sizeof(buf), 0,
(struct sockaddr*)&receive_address, &receive_address_len);
if (received < 0 &&
(errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK)) {
usleep(1000);
continue;
}
EXPECT_EQ(sizeof(kBuf), received);
break;
}

for (size_t i = 0; i < sizeof(kBuf); ++i) {
EXPECT_EQ(kBuf[i], buf[i]) << "position " << i;
}

EXPECT_NE(-1, close(send_socket));
EXPECT_NE(-1, close(receive_socket));
}

TEST(PosixSocketJoinMulticastGroupTest, RainyDayInvalidSocket) {
struct ip_mreq mreq;
#if SB_IS_BIG_ENDIAN
mreq.imr_multiaddr.s_addr = (224 << 24) | (0 << 16) | (2 << 8) | 0;
#else
mreq.imr_multiaddr.s_addr = (0 << 24) | (2 << 16) | (0 << 8) | 224;
#endif
mreq.imr_interface.s_addr =
htonl(INADDR_ANY); // Use the default network interface

EXPECT_EQ(-1,
setsockopt(-1, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)));
EXPECT_TRUE(errno == EBADF || errno == ECONNREFUSED);
}

TEST(PosixSocketJoinMulticastGroupTest, RainyDayInvalidAddress) {
int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
EXPECT_NE(-1, socket_fd);

EXPECT_EQ(-1, setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, NULL,
sizeof(struct ip_mreq)));
EXPECT_TRUE(errno == EBADF || errno == EFAULT);

EXPECT_NE(-1, close(socket_fd));
}

} // namespace
} // namespace nplb
} // namespace starboard
3 changes: 3 additions & 0 deletions starboard/shared/win32/posix_emu/include/sys/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ int sb_setsockopt(int socket,
int option_len);
#define setsockopt sb_setsockopt

int sb_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
#define getsockname sb_getsockname

int posix_socket_get_fd_from_handle(SOCKET socket);
SOCKET posix_socket_get_handle_from_fd(int socket);

Expand Down
14 changes: 14 additions & 0 deletions starboard/shared/win32/posix_emu/socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,20 @@ int sb_fcntl(int fd, int cmd, ... /*arg*/) {
return 0;
}

int sb_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
FileOrSocket handle = handle_db_get(sockfd, false);

if (handle.is_file || handle.socket == INVALID_SOCKET) {
return -1;
}

int result = getsockname(handle.socket, addr, addrlen);
if (result == SOCKET_ERROR) {
set_errno();
}
return result;
}

int posix_socket_get_fd_from_handle(SOCKET socket) {
return handle_db_get_fd(socket);
}
Expand Down
20 changes: 16 additions & 4 deletions third_party/musl/src/starboard/network/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,13 @@ int getsockname(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict
}
SbSocketAddress out_address = {0};
int result = SbSocketGetLocalAddress(fileOrSock->socket, &out_address)? 0: -1;

struct sockaddr_in* addr_in = (struct sockaddr_in*)addr;
addr_in->sin_family = AF_INET;
addr_in->sin_addr.s_addr = out_address.address;
addr_in->sin_port = out_address.port;
*addrlen = sizeof(struct sockaddr_in);

return result;
}

Expand All @@ -780,8 +787,7 @@ int setsockopt (int sockfd, int level, int optname, const void* optval,
return -1;
}

if (level == SOL_SOCKET || level == SOL_TCP || level == IPPROTO_TCP) {

if (level == SOL_SOCKET || level == SOL_TCP || level == IPPROTO_TCP || level == IPPROTO_IP) {
int* operation = (int*)optval;
switch (optname){
case SO_BROADCAST:{
Expand Down Expand Up @@ -823,12 +829,18 @@ int setsockopt (int sockfd, int level, int optname, const void* optval,
return 0;
}
case TCP_NODELAY: {
int* operation = (int*)optval;
bool bool_value = *operation == 1? true:false;
return SbSocketSetTcpNoDelay(fileOrSock->socket, bool_value) == true? 0:-1;
}
case IP_ADD_MEMBERSHIP: {
SbSocketAddress* addr = (SbSocketAddress*)optval;
if (optval == NULL) {
errno = EFAULT;
return -1;
}
const struct ip_mreq* imreq = (const struct ip_mreq*)optval;
SbSocketAddress* addr = (SbSocketAddress*)malloc(sizeof(SbSocketAddress));
memcpy(addr->address, &(imreq->imr_multiaddr.s_addr), sizeof(imreq->imr_multiaddr.s_addr));

return SbSocketJoinMulticastGroup(fileOrSock->socket, addr) == true? 0:-1;
}
default:
Expand Down

0 comments on commit 6e5c6cc

Please sign in to comment.