support RFC 3678's Protocol-Independent setsockopt()s for IPv4 multicast.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20691 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos 2007-04-15 03:07:28 +00:00
parent 46527f6806
commit 6c501a4085
5 changed files with 130 additions and 26 deletions

View File

@ -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);

View File

@ -123,6 +123,9 @@ class RawSocket : public DoublyLinkedListLinkImpl<RawSocket>, 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<in_addr>::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<in_addr>::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<in_addr> &filter = protocol->multicast_filter;
MulticastFilter<in_addr>::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;

View File

@ -95,8 +95,9 @@ MulticastGroupInterfaceState<AddressType>::_Remove(Source *state)
template<typename AddressType>
MulticastGroupState<AddressType>::MulticastGroupState(const AddressType &address)
: fMulticastAddress(address), fFilterMode(kInclude)
MulticastGroupState<AddressType>::MulticastGroupState(
MulticastFilter<AddressType> *parent, const AddressType &address)
: fParent(parent), fMulticastAddress(address), fFilterMode(kInclude)
{
}
@ -227,6 +228,13 @@ MulticastGroupState<AddressType>::_RemoveInterface(InterfaceState *state)
}
template<typename AddressType>
MulticastFilter<AddressType>::MulticastFilter(net_socket *socket)
: fParent(socket)
{
}
template<typename AddressType>
MulticastFilter<AddressType>::~MulticastFilter()
{
@ -254,7 +262,7 @@ MulticastFilter<AddressType>::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;

View File

@ -13,6 +13,9 @@
#include <util/list.h>
struct net_interface;
struct net_socket;
template<typename AddressType> class MulticastFilter;
template<typename AddressType>
struct MulticastSource {
@ -48,7 +51,8 @@ private:
template<typename AddressType>
class MulticastGroupState {
public:
MulticastGroupState(const AddressType &address);
MulticastGroupState(MulticastFilter<AddressType> *parent,
const AddressType &address);
~MulticastGroupState();
const AddressType &Address() const { return fMulticastAddress; }
@ -80,6 +84,7 @@ private:
kExclude
};
MulticastFilter<AddressType> *fParent;
AddressType fMulticastAddress;
FilterMode fFilterMode;
InterfaceList fInterfaces;
@ -90,8 +95,11 @@ class MulticastFilter {
public:
typedef MulticastGroupState<AddressType> 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<GroupState> GroupStateLink;
typedef DoublyLinkedList<GroupState, GroupStateLink> States;
net_socket *fParent;
// TODO change this into an hash table or tree
States fStates;
};

View File

@ -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,