I have a server application which binds a server socket to a Linux network device which got created after I have hot-plugged an corresponding USB-Ethernet adapter to an USB port of my local machine:
// Bind the socket to the network device corresponding to the USB Ethernet adapter
const char *device = "enxf8e43b4f4bd2";
if (setsockopt(server_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)) < 0) {
std::cerr << "Failed to bind to device\n";
close(server_fd);
return 1;
}
However, when the USB Ethernet device is removed while the socket is still active, one does not receive any error from the select, receive or send functions, i.e. it is still possible to send data via the client socket (got from the accept() call after a client connected) without getting any error although the corresponding network device got removed before the sending of data got triggered(the device entry at the appropriate /sys/bus/usb/devices path for that USB Ethernet adapter got removed; the ip link command does also not list the network device anymore):
...
printf("Remove the USP-Ethernet adapter first and press any key afterwards to send data to the client\n");
std::getchar();
int number = send(client_socket, hello_reply, strlen(hello_reply), 0);
if (number == -1) {
// I would expect this branch to get executed, but it doesn't.
std::cout << "send error: " << strerror(errno) << std::endl;
close(client_socket);
} else if (number == 0) {
// This branch isn't also executed.
printf("nothing sent\n");
} else {
// This branch gets executed and indicates the number of bytes being sent although the
// network device associated with the USB Ethernet adapter isn't available anymore.
// Any explaination on this?
printf("number of bytes sent to client: %d\n", number);
}...
Also explicitly quering any errors on the socket via the code below, doesn't indicate any error on the socket:
// check the validity state of the client socket
int error = 0;
socklen_t error_len = sizeof(error);
if (getsockopt(client_socket, SOL_SOCKET, SO_ERROR, &error, &error_len) < 0) {
std::cerr << "Error getting socket options: " << strerror(errno) << std::endl;
return -1;
}
if (error != 0) {
std::cout << "client socket socket error: " << strerror(error) << std::endl;
return -1;
} else {
// This branch gets executed.
printf("client socket is ok\n");
}
Why don't the functions select, poll, send and receive - here as an example the send() one - report an error when the USB Ethernet adapter-specific network device (the socket got bound to) is removed? Is there a way to detect the "validity" of the concerned socket?
Update:
However, if one explicitly queries via getsockopt and the SO_BINDTODEVICE option to which network device the client socket is bound to after the associated USB Ethernet device was removed, the error code ENODEV (= No such device) gets returned:
if (getsockopt(client_socket, SOL_SOCKET, SO_BINDTODEVICE, &iface, &len) == -1) {
// Path entered after the USB Ethernet device was removed -> ENODEV is set.
if(errno == ENODEV) {
std::cerr << "Error: " << strerror(errno) << std::endl;
}
close(client_socket);
return 1;
}
It is incomprehensible why the functions (select, send, write, read, etc.) don't fail with this error code, instead it is necessary to explicitly ask for the presence of the network interface via the getsockopt() call.