diff --git a/headers/private/net/net_datalink.h b/headers/private/net/net_datalink.h index fd88a9bf9d..2b007a0454 100644 --- a/headers/private/net/net_datalink.h +++ b/headers/private/net/net_datalink.h @@ -71,6 +71,7 @@ struct net_datalink_module_info { const struct sockaddr *address, net_interface **_interface, uint32 *_matchedType); + net_interface *(*get_interface)(struct net_domain *domain, uint32 index); net_interface *(*get_interface_with_address)(struct net_domain *domain, const struct sockaddr *address); diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index c52cfa8bae..6811f0dc6e 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -123,6 +123,9 @@ class RawSocket : public DoublyLinkedListLinkImpl, public DatagramSoc }; struct ipv4_protocol : net_protocol { + ipv4_protocol(net_socket *socket) + : multicast_filter(socket) {} + RawSocket *raw; uint8 service_type; uint8 time_to_live; @@ -623,7 +626,7 @@ receiving_protocol(uint8 protocol) static status_t ipv4_delta_group(MulticastFilter::GroupState *group, int option, - net_interface *interface, in_addr *sourceAddr) + net_interface *interface, const in_addr *sourceAddr) { switch (option) { case IP_ADD_MEMBERSHIP: @@ -646,27 +649,9 @@ ipv4_delta_group(MulticastFilter::GroupState *group, int option, static status_t ipv4_delta_membership(ipv4_protocol *protocol, int option, - in_addr *interfaceAddr, in_addr *groupAddr, in_addr *sourceAddr) + net_interface *interface, const in_addr *groupAddr, + const in_addr *sourceAddr) { - net_interface *interface = NULL; - - if (interfaceAddr->s_addr == INADDR_ANY) { - interface = sDatalinkModule->get_interface_with_address(sDomain, NULL); - } else { - sockaddr_in address; - - memset(&address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_len = sizeof(address); - address.sin_addr = *interfaceAddr; - - interface = sDatalinkModule->get_interface_with_address(sDomain, - (sockaddr *)&address); - } - - if (interface == NULL) - return ENODEV; - MulticastFilter &filter = protocol->multicast_filter; MulticastFilter::GroupState *group = NULL; @@ -701,13 +686,68 @@ ipv4_delta_membership(ipv4_protocol *protocol, int option, } +static status_t +ipv4_delta_membership(ipv4_protocol *protocol, int option, + in_addr *interfaceAddr, in_addr *groupAddr, in_addr *sourceAddr) +{ + net_interface *interface = NULL; + + if (interfaceAddr->s_addr == INADDR_ANY) { + interface = sDatalinkModule->get_interface_with_address(sDomain, NULL); + } else { + sockaddr_in address; + + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_len = sizeof(address); + address.sin_addr = *interfaceAddr; + + interface = sDatalinkModule->get_interface_with_address(sDomain, + (sockaddr *)&address); + } + + if (interface == NULL) + return ENODEV; + + return ipv4_delta_membership(protocol, option, interface, groupAddr, + sourceAddr); +} + + +static status_t +ipv4_generic_delta_membership(ipv4_protocol *protocol, int option, + uint32 index, const sockaddr_storage *_groupAddr, + const sockaddr_storage *_sourceAddr) +{ + if (_groupAddr->ss_family != AF_INET) + return EINVAL; + + if (_sourceAddr && _sourceAddr->ss_family != AF_INET) + return EINVAL; + + net_interface *interface = sDatalinkModule->get_interface(sDomain, index); + if (interface == NULL) + return ENODEV; + + const in_addr *groupAddr, *sourceAddr = NULL; + + groupAddr = &((const sockaddr_in *)_groupAddr)->sin_addr; + + if (_sourceAddr) + sourceAddr = &((const sockaddr_in *)_sourceAddr)->sin_addr; + + return ipv4_delta_membership(protocol, option, interface, groupAddr, + sourceAddr); +} + + // #pragma mark - net_protocol * ipv4_init_protocol(net_socket *socket) { - ipv4_protocol *protocol = new (std::nothrow) ipv4_protocol; + ipv4_protocol *protocol = new (std::nothrow) ipv4_protocol(socket); if (protocol == NULL) return NULL; @@ -845,6 +885,12 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, 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().'' @@ -926,6 +972,34 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value, &mreq.imr_multiaddr, &mreq.imr_sourceaddr); } + case MCAST_JOIN_GROUP: + case MCAST_LEAVE_GROUP: + { + group_req greq; + if (*_length != sizeof(group_req)) + return B_BAD_VALUE; + if (user_memcpy(&greq, value, sizeof(group_req)) < B_OK) + return B_BAD_ADDRESS; + + return ipv4_generic_delta_membership(protocol, option, + greq.gr_interface, &greq.gr_group, NULL); + } + + case MCAST_BLOCK_SOURCE: + case MCAST_UNBLOCK_SOURCE: + case MCAST_JOIN_SOURCE_GROUP: + case MCAST_LEAVE_SOURCE_GROUP: + { + group_source_req greq; + 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; + + return ipv4_generic_delta_membership(protocol, option, + greq.gsr_interface, &greq.gsr_group, &greq.gsr_source); + } + default: dprintf("IPv4::control(): set unknown option: %d\n", option); return ENOPROTOOPT; diff --git a/src/add-ons/kernel/network/protocols/ipv4/multicast.cpp b/src/add-ons/kernel/network/protocols/ipv4/multicast.cpp index e93f9c1962..6dfdaac992 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/multicast.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/multicast.cpp @@ -95,8 +95,9 @@ MulticastGroupInterfaceState::_Remove(Source *state) template -MulticastGroupState::MulticastGroupState(const AddressType &address) - : fMulticastAddress(address), fFilterMode(kInclude) +MulticastGroupState::MulticastGroupState( + MulticastFilter *parent, const AddressType &address) + : fParent(parent), fMulticastAddress(address), fFilterMode(kInclude) { } @@ -227,6 +228,13 @@ MulticastGroupState::_RemoveInterface(InterfaceState *state) } +template +MulticastFilter::MulticastFilter(net_socket *socket) + : fParent(socket) +{ +} + + template MulticastFilter::~MulticastFilter() { @@ -254,7 +262,7 @@ MulticastFilter::GetGroup(const AddressType &groupAddress, if (!create) return NULL; - GroupState *state = new (nothrow) GroupState(groupAddress); + GroupState *state = new (nothrow) GroupState(this, groupAddress); if (state) fStates.Add(state); return state; diff --git a/src/add-ons/kernel/network/protocols/ipv4/multicast.h b/src/add-ons/kernel/network/protocols/ipv4/multicast.h index 9b8946fd41..84c0ab64ab 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/multicast.h +++ b/src/add-ons/kernel/network/protocols/ipv4/multicast.h @@ -13,6 +13,9 @@ #include struct net_interface; +struct net_socket; + +template class MulticastFilter; template struct MulticastSource { @@ -48,7 +51,8 @@ private: template class MulticastGroupState { public: - MulticastGroupState(const AddressType &address); + MulticastGroupState(MulticastFilter *parent, + const AddressType &address); ~MulticastGroupState(); const AddressType &Address() const { return fMulticastAddress; } @@ -80,6 +84,7 @@ private: kExclude }; + MulticastFilter *fParent; AddressType fMulticastAddress; FilterMode fFilterMode; InterfaceList fInterfaces; @@ -90,8 +95,11 @@ class MulticastFilter { public: typedef MulticastGroupState GroupState; + MulticastFilter(net_socket *parent); ~MulticastFilter(); + net_socket *Parent() const { return fParent; } + GroupState *GetGroup(const AddressType &groupAddress, bool create); void ReturnGroup(GroupState *group); @@ -99,6 +107,8 @@ private: typedef DoublyLinkedListCLink GroupStateLink; typedef DoublyLinkedList States; + net_socket *fParent; + // TODO change this into an hash table or tree States fStates; }; diff --git a/src/add-ons/kernel/network/stack/datalink.cpp b/src/add-ons/kernel/network/stack/datalink.cpp index 405a519552..178583f745 100644 --- a/src/add-ons/kernel/network/stack/datalink.cpp +++ b/src/add-ons/kernel/network/stack/datalink.cpp @@ -449,6 +449,16 @@ datalink_get_interface_with_address(net_domain *_domain, } +net_interface * +datalink_get_interface(net_domain *domain, uint32 index) +{ + if (index == 0) + return datalink_get_interface_with_address(domain, NULL); + + return find_interface(domain, index); +} + + static status_t datalink_std_ops(int32 op, ...) { @@ -775,6 +785,7 @@ net_datalink_module_info gNetDatalinkModule = { datalink_control, datalink_send_data, datalink_is_local_address, + datalink_get_interface, datalink_get_interface_with_address, add_route,