diff --git a/headers/private/net/net_protocol.h b/headers/private/net/net_protocol.h index 22ebac508e..d6c1ad4a76 100644 --- a/headers/private/net/net_protocol.h +++ b/headers/private/net/net_protocol.h @@ -79,6 +79,8 @@ struct net_protocol_module_info { ssize_t (*process_ancillary_data)(net_protocol *self, const ancillary_data_header *header, const void *data, void *buffer, size_t bufferSize); + ssize_t (*process_ancillary_data_no_container)(net_protocol *self, + net_buffer *buffer, void *buffer, size_t bufferSize); ssize_t (*send_data_no_buffer)(net_protocol *self, const iovec *vecs, size_t vecCount, ancillary_data_container *ancillaryData, diff --git a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp index 9ef9ccc35c..d465a05cf4 100644 --- a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp +++ b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp @@ -374,6 +374,7 @@ net_protocol_module_info sICMPModule = { icmp_error_reply, NULL, // add_ancillary_data() NULL, // process_ancillary_data() + NULL, // process_ancillary_data_no_container() NULL, // send_data_no_buffer() NULL // read_data_no_buffer() }; diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index ce28a9f5b1..28ebd0b58c 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -166,12 +166,14 @@ struct ipv4_protocol : net_protocol { uint8 time_to_live; uint8 multicast_time_to_live; uint32 flags; + struct sockaddr* interface_address; // for IP_MULTICAST_IF IPv4MulticastFilter multicast_filter; }; // protocol flags -#define IP_FLAG_HEADER_INCLUDED 0x01 +#define IP_FLAG_HEADER_INCLUDED 0x01 +#define IP_FLAG_RECEIVE_DEST_ADDR 0x02 static const int kDefaultTTL = 254; @@ -214,7 +216,8 @@ print_address(const in_addr* address, char* buf, size_t bufLen) RawSocket::RawSocket(net_socket* socket) - : DatagramSocket<>("ipv4 raw socket", socket) + : + DatagramSocket<>("ipv4 raw socket", socket) { } @@ -1013,6 +1016,7 @@ ipv4_init_protocol(net_socket* socket) protocol->time_to_live = kDefaultTTL; protocol->multicast_time_to_live = kDefaultMulticastTTL; protocol->flags = 0; + protocol->interface_address = NULL; return protocol; } @@ -1023,6 +1027,7 @@ ipv4_uninit_protocol(net_protocol* _protocol) ipv4_protocol* protocol = (ipv4_protocol*)_protocol; delete protocol->raw; + delete protocol->interface_address; delete protocol; return B_OK; } @@ -1118,6 +1123,10 @@ ipv4_getsockopt(net_protocol* _protocol, int level, int option, void* value, return get_int_option(value, *_length, (protocol->flags & IP_FLAG_HEADER_INCLUDED) != 0); } + if (option == IP_RECVDSTADDR) { + return get_int_option(value, *_length, + (protocol->flags & IP_FLAG_RECEIVE_DEST_ADDR) != 0); + } if (option == IP_TTL) return get_int_option(value, *_length, protocol->time_to_live); if (option == IP_TOS) @@ -1175,10 +1184,59 @@ ipv4_setsockopt(net_protocol* _protocol, int level, int option, return B_OK; } + if (option == IP_RECVDSTADDR) { + int getAddress; + if (length != sizeof(int)) + return B_BAD_VALUE; + if (user_memcpy(&getAddress, value, sizeof(int)) != B_OK) + return B_BAD_ADDRESS; + + if (getAddress && (protocol->socket->type == SOCK_DGRAM + || protocol->socket->type == SOCK_RAW)) + protocol->flags |= IP_FLAG_RECEIVE_DEST_ADDR; + else + protocol->flags &= ~IP_FLAG_RECEIVE_DEST_ADDR; + + return B_OK; + } if (option == IP_TTL) return set_int_option(protocol->time_to_live, value, length); if (option == IP_TOS) return set_int_option(protocol->service_type, value, length); + if (option == IP_MULTICAST_IF) { + if (length != sizeof(struct in_addr)) + return B_BAD_VALUE; + + struct sockaddr_in* address = new (std::nothrow) sockaddr_in; + if (address == NULL) + return B_NO_MEMORY; + + if (user_memcpy(&address->sin_addr, value, sizeof(struct in_addr)) + != B_OK) { + delete address; + return B_BAD_ADDRESS; + } + + // Using INADDR_ANY to remove the previous setting. + if (address->sin_addr.s_addr == htonl(INADDR_ANY)) { + delete address; + delete protocol->interface_address; + protocol->interface_address = NULL; + return B_OK; + } + + struct net_interface* interface + = sDatalinkModule->get_interface_with_address(sDomain, + (struct sockaddr*)address); + if (interface == NULL) { + delete address; + return EADDRNOTAVAIL; + } + + delete protocol->interface_address; + protocol->interface_address = (struct sockaddr*)address; + return B_OK; + } if (option == IP_MULTICAST_TTL) { return set_int_option(protocol->multicast_time_to_live, value, length); @@ -1405,6 +1463,25 @@ ipv4_send_data(net_protocol* _protocol, net_buffer* buffer) offsetof(ipv4_header, destination)>(buffer)); } + // handle IP_MULTICAST_IF + if (IN_MULTICAST(ntohl(((sockaddr_in*)buffer->destination)-> + sin_addr.s_addr)) && protocol->interface_address != NULL) { + net_interface* interface + = sDatalinkModule->get_interface_with_address(sDomain, + protocol->interface_address); + if (interface == NULL || (interface->flags & IFF_UP) == 0) + return EADDRNOTAVAIL; + + buffer->interface = interface; + + net_route* route = sDatalinkModule->get_route(sDomain, + interface->address); + if (route == NULL) + return ENETUNREACH; + + return sDatalinkModule->send_data(route, buffer); + } + return sDatalinkModule->send_datagram(protocol, sDomain, buffer); } @@ -1600,6 +1677,32 @@ ipv4_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code, } +ssize_t +ipv4_process_ancillary_data_no_container(net_protocol* protocol, + net_buffer* buffer, void* msgControl, size_t msgControlLen) +{ + ssize_t bytesWritten = 0; + + if ((((ipv4_protocol*)protocol)->flags & IP_FLAG_RECEIVE_DEST_ADDR) != 0) { + if (msgControlLen < CMSG_SPACE(sizeof(struct in_addr))) + return B_NO_MEMORY; + + cmsghdr* messageHeader = (cmsghdr*)msgControl; + messageHeader->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + messageHeader->cmsg_level = IPPROTO_IP; + messageHeader->cmsg_type = IP_RECVDSTADDR; + + memcpy(CMSG_DATA(messageHeader), + &((struct sockaddr_in*)buffer->destination)->sin_addr, + sizeof(struct in_addr)); + + bytesWritten += CMSG_SPACE(sizeof(struct in_addr)); + } + + return bytesWritten; +} + + // #pragma mark - @@ -1741,6 +1844,7 @@ net_protocol_module_info gIPv4Module = { ipv4_error_reply, NULL, // add_ancillary_data() NULL, // process_ancillary_data() + ipv4_process_ancillary_data_no_container, NULL, // send_data_no_buffer() NULL // read_data_no_buffer() }; diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp index 33c7c99cbf..5d3ef6bf0e 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp @@ -383,6 +383,7 @@ net_protocol_module_info gL2CAPModule = { l2cap_error_reply, NULL, // add_ancillary_data() NULL, // process_ancillary_data() + NULL, // process_ancillary_data_no_container() NULL, // send_data_no_buffer() NULL // read_data_no_buffer() }; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index ff1c3264b2..d8e45baba3 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -846,6 +846,7 @@ net_protocol_module_info sTCPModule = { tcp_error_reply, NULL, // add_ancillary_data() NULL, // process_ancillary_data() + NULL, // process_ancillary_data_no_container() NULL, // send_data_no_buffer() NULL // read_data_no_buffer() }; diff --git a/src/add-ons/kernel/network/protocols/udp/udp.cpp b/src/add-ons/kernel/network/protocols/udp/udp.cpp index f38d97ae8a..a232748439 100644 --- a/src/add-ons/kernel/network/protocols/udp/udp.cpp +++ b/src/add-ons/kernel/network/protocols/udp/udp.cpp @@ -1146,6 +1146,15 @@ udp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code, } +ssize_t +udp_process_ancillary_data_no_container(net_protocol *protocol, + net_buffer* buffer, void *data, size_t dataSize) +{ + return protocol->next->module->process_ancillary_data_no_container( + protocol, buffer, data, dataSize); +} + + // #pragma mark - module interface @@ -1256,6 +1265,7 @@ net_protocol_module_info sUDPModule = { udp_error_reply, NULL, // add_ancillary_data() NULL, // process_ancillary_data() + udp_process_ancillary_data_no_container, NULL, // send_data_no_buffer() NULL // read_data_no_buffer() }; diff --git a/src/add-ons/kernel/network/protocols/unix/unix.cpp b/src/add-ons/kernel/network/protocols/unix/unix.cpp index 65fc818b2e..05c19c3226 100644 --- a/src/add-ons/kernel/network/protocols/unix/unix.cpp +++ b/src/add-ons/kernel/network/protocols/unix/unix.cpp @@ -508,6 +508,7 @@ net_protocol_module_info gUnixModule = { unix_error_reply, unix_add_ancillary_data, unix_process_ancillary_data, + NULL, unix_send_data_no_buffer, unix_read_data_no_buffer }; diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index e79940e55d..c86db4470f 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -255,6 +255,32 @@ process_ancillary_data(net_socket* socket, ancillary_data_container* container, } +static status_t +process_ancillary_data(net_socket* socket, + net_buffer* buffer, msghdr* messageHeader) +{ + void *dataBuffer = messageHeader->msg_control; + ssize_t bytesWritten; + + if (dataBuffer == NULL) { + messageHeader->msg_controllen = 0; + return B_OK; + } + + if (socket->first_info->process_ancillary_data_no_container == NULL) + return EOPNOTSUPP; + + bytesWritten = socket->first_info->process_ancillary_data_no_container( + socket->first_protocol, buffer, dataBuffer, + messageHeader->msg_controllen); + if (bytesWritten < 0) + return bytesWritten; + messageHeader->msg_controllen = bytesWritten; + + return B_OK; +} + + static ssize_t socket_receive_no_buffer(net_socket* socket, msghdr* header, void* data, size_t length, int flags) @@ -1171,8 +1197,12 @@ socket_receive(net_socket* socket, msghdr* header, void* data, size_t length, // process ancillary data if (header != NULL) { if (buffer != NULL && header->msg_control != NULL) { - status = process_ancillary_data(socket, - gNetBufferModule.get_ancillary_data(buffer), header); + ancillary_data_container* container + = gNetBufferModule.get_ancillary_data(buffer); + if (container != NULL) + status = process_ancillary_data(socket, container, header); + else + status = process_ancillary_data(socket, buffer, header); if (status != B_OK) { gNetBufferModule.free(buffer); return status;