initial steps towards IPv4 Multicast Filter Delta API (RFC 3678)

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20690 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos
2007-04-15 02:44:01 +00:00
parent 97e69082b6
commit 46527f6806
7 changed files with 568 additions and 0 deletions
+34
View File
@@ -80,6 +80,30 @@ struct sockaddr_in {
};
/* the address is therefore at sin_addr.s_addr */
/* RFC 3678 - Socket Interface Extensions for Multicast Source Filters */
struct ip_mreq {
struct in_addr imr_multiaddr; /* IP address of group */
struct in_addr imr_interface; /* IP address of interface */
};
struct ip_mreq_source {
struct in_addr imr_multiaddr; /* IP address of group */
struct in_addr imr_sourceaddr; /* IP address of source */
struct in_addr imr_interface; /* IP address of interface */
};
struct group_req {
uint32_t gr_interface; /* interface index */
struct sockaddr_storage gr_group; /* group address */
};
struct group_source_req {
uint32_t gsr_interface; /* interface index */
struct sockaddr_storage gsr_group; /* group address */
struct sockaddr_storage gsr_source; /* source address */
};
/*
* Options for use with [gs]etsockopt at the IP level.
* First word of comment is data type; bool is stored in int.
@@ -97,6 +121,16 @@ struct sockaddr_in {
#define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */
#define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */
#define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */
#define IP_BLOCK_SOURCE 14 /* ip_mreq_source */
#define IP_UNBLOCK_SOURCE 15 /* ip_mreq_source */
#define IP_ADD_SOURCE_MEMBERSHIP 16 /* ip_mreq_source */
#define IP_DROP_SOURCE_MEMBERSHIP 17 /* ip_mreq_source */
#define MCAST_JOIN_GROUP 18 /* group_req */
#define MCAST_BLOCK_SOURCE 19 /* group_source_req */
#define MCAST_UNBLOCK_SOURCE 20 /* group_source_req */
#define MCAST_LEAVE_GROUP 21 /* group_req */
#define MCAST_JOIN_SOURCE_GROUP 22 /* group_source_req */
#define MCAST_LEAVE_SOURCE_GROUP 23 /* group_source_req */
#define __IPADDR(x) ((uint32_t)htonl((uint32_t)(x)))
+2
View File
@@ -71,6 +71,8 @@ struct net_datalink_module_info {
const struct sockaddr *address,
net_interface **_interface,
uint32 *_matchedType);
net_interface *(*get_interface_with_address)(struct net_domain *domain,
const struct sockaddr *address);
// routes
status_t (*add_route)(struct net_domain *domain,
@@ -15,6 +15,7 @@ UsePrivateHeaders kernel net ;
KernelAddon ipv4 :
ipv4.cpp
ipv4_address.cpp
multicast.cpp
;
# Installation
@@ -8,6 +8,7 @@
#include "ipv4_address.h"
#include "multicast.h"
#include <net_datalink.h>
#include <net_protocol.h>
@@ -126,6 +127,8 @@ struct ipv4_protocol : net_protocol {
uint8 service_type;
uint8 time_to_live;
uint32 flags;
MulticastFilter<in_addr> multicast_filter;
};
// protocol flags
@@ -618,6 +621,86 @@ receiving_protocol(uint8 protocol)
}
static status_t
ipv4_delta_group(MulticastFilter<in_addr>::GroupState *group, int option,
net_interface *interface, in_addr *sourceAddr)
{
switch (option) {
case IP_ADD_MEMBERSHIP:
return group->Add(interface);
case IP_DROP_MEMBERSHIP:
return group->Drop(interface);
case IP_BLOCK_SOURCE:
return group->BlockSource(interface, *sourceAddr);
case IP_UNBLOCK_SOURCE:
return group->UnblockSource(interface, *sourceAddr);
case IP_ADD_SOURCE_MEMBERSHIP:
return group->AddSSM(interface, *sourceAddr);
case IP_DROP_SOURCE_MEMBERSHIP:
return group->DropSSM(interface, *sourceAddr);
}
return B_ERROR;
}
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;
MulticastFilter<in_addr> &filter = protocol->multicast_filter;
MulticastFilter<in_addr>::GroupState *group = NULL;
switch (option) {
case IP_ADD_MEMBERSHIP:
case IP_ADD_SOURCE_MEMBERSHIP:
group = filter.GetGroup(*groupAddr, true);
if (group == NULL)
return ENOBUFS;
break;
case IP_DROP_MEMBERSHIP:
case IP_BLOCK_SOURCE:
case IP_UNBLOCK_SOURCE:
case IP_DROP_SOURCE_MEMBERSHIP:
group = filter.GetGroup(*groupAddr, false);
if (group == NULL) {
if (option == IP_DROP_SOURCE_MEMBERSHIP
|| option == IP_DROP_SOURCE_MEMBERSHIP)
return EADDRNOTAVAIL;
else
return EINVAL;
}
break;
}
status_t status = ipv4_delta_group(group, option, interface, sourceAddr);
filter.ReturnGroup(group);
return status;
}
// #pragma mark -
@@ -756,6 +839,17 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value,
return user_memcpy(value, &serviceType, sizeof(serviceType));
}
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
case IP_BLOCK_SOURCE:
case IP_UNBLOCK_SOURCE:
case IP_ADD_SOURCE_MEMBERSHIP:
case IP_DROP_SOURCE_MEMBERSHIP:
// RFC 3678, Section 4.1:
// ``An error of EOPNOTSUPP is returned if these options are
// used with getsockopt().''
return EOPNOTSUPP;
default:
dprintf("IPv4::control(): get unknown option: %d\n", option);
return ENOPROTOOPT;
@@ -804,6 +898,34 @@ ipv4_control(net_protocol *_protocol, int level, int option, void *value,
return B_OK;
}
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
{
ip_mreq mreq;
if (*_length != sizeof(ip_mreq))
return B_BAD_VALUE;
if (user_memcpy(&mreq, value, sizeof(ip_mreq)) < B_OK)
return B_BAD_ADDRESS;
return ipv4_delta_membership(protocol, option, &mreq.imr_interface,
&mreq.imr_multiaddr, NULL);
}
case IP_BLOCK_SOURCE:
case IP_UNBLOCK_SOURCE:
case IP_ADD_SOURCE_MEMBERSHIP:
case IP_DROP_SOURCE_MEMBERSHIP:
{
ip_mreq_source mreq;
if (*_length != sizeof(ip_mreq_source))
return B_BAD_VALUE;
if (user_memcpy(&mreq, value, sizeof(ip_mreq_source)) < B_OK)
return B_BAD_ADDRESS;
return ipv4_delta_membership(protocol, option, &mreq.imr_interface,
&mreq.imr_multiaddr, &mreq.imr_sourceaddr);
}
default:
dprintf("IPv4::control(): set unknown option: %d\n", option);
return ENOPROTOOPT;
@@ -0,0 +1,272 @@
/*
* Copyright 2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Hugo Santos, hugosantos@gmail.com
*/
#include "multicast.h"
#include <netinet/in.h>
#include <new>
template class MulticastFilter<in_addr>;
template class MulticastGroupState<in_addr>;
template class MulticastGroupInterfaceState<in_addr>;
static inline bool
operator==(const in_addr &a1, const in_addr &a2)
{
return a1.s_addr == a2.s_addr;
}
using std::nothrow;
template<typename AddressType>
MulticastGroupInterfaceState<AddressType>::MulticastGroupInterfaceState(
net_interface *interface)
: fInterface(interface)
{
}
template<typename AddressType>
MulticastGroupInterfaceState<AddressType>::~MulticastGroupInterfaceState()
{
SourceList::Iterator iterator = fSources.GetIterator();
while (iterator.HasNext()) {
_Remove(iterator.Next());
}
}
template<typename AddressType> status_t
MulticastGroupInterfaceState<AddressType>::Add(const AddressType &address)
{
return _Get(address, true) ? B_OK : ENOBUFS;
}
template<typename AddressType> status_t
MulticastGroupInterfaceState<AddressType>::Remove(const AddressType &address)
{
Source *state = _Get(address, false);
if (state == NULL)
return EADDRNOTAVAIL;
_Remove(state);
return B_OK;
}
template<typename AddressType> MulticastGroupInterfaceState<AddressType>::Source *
MulticastGroupInterfaceState<AddressType>::_Get(const AddressType &address,
bool create)
{
SourceList::Iterator iterator = fSources.GetIterator();
while (iterator.HasNext()) {
Source *state = iterator.Next();
if (state->address == address)
return state;
}
if (!create)
return false;
Source *state = new (nothrow) Source;
if (state) {
state->address = address;
fSources.Add(state);
}
return state;
}
template<typename AddressType> void
MulticastGroupInterfaceState<AddressType>::_Remove(Source *state)
{
fSources.Remove(state);
delete state;
}
template<typename AddressType>
MulticastGroupState<AddressType>::MulticastGroupState(const AddressType &address)
: fMulticastAddress(address), fFilterMode(kInclude)
{
}
template<typename AddressType>
MulticastGroupState<AddressType>::~MulticastGroupState()
{
InterfaceList::Iterator iterator = fInterfaces.GetIterator();
while (iterator.HasNext())
_RemoveInterface(iterator.Next());
}
template<typename AddressType> status_t
MulticastGroupState<AddressType>::Add(net_interface *interface)
{
if (fFilterMode == kInclude && !fInterfaces.IsEmpty())
return EINVAL;
fFilterMode = kExclude;
return _GetInterface(interface, true) != NULL ? B_OK : ENOBUFS;
}
template<typename AddressType> status_t
MulticastGroupState<AddressType>::Drop(net_interface *interface)
{
InterfaceState *state = _GetInterface(interface, false);
if (state == NULL)
return EADDRNOTAVAIL;
_RemoveInterface(state);
if (fInterfaces.IsEmpty())
fFilterMode = kInclude;
return B_OK;
}
template<typename AddressType> status_t
MulticastGroupState<AddressType>::BlockSource(net_interface *interface,
const AddressType &sourceAddress)
{
if (fFilterMode != kExclude)
return EINVAL;
InterfaceState *state = _GetInterface(interface, false);
if (state == NULL)
return EINVAL;
return state->Add(sourceAddress);
}
template<typename AddressType> status_t
MulticastGroupState<AddressType>::UnblockSource(net_interface *interface,
const AddressType &sourceAddress)
{
if (fFilterMode != kExclude)
return EINVAL;
InterfaceState *state = _GetInterface(interface, false);
if (state == NULL)
return EINVAL;
return state->Remove(sourceAddress);
}
template<typename AddressType> status_t
MulticastGroupState<AddressType>::AddSSM(net_interface *interface,
const AddressType &sourceAddress)
{
if (fFilterMode == kExclude)
return EINVAL;
InterfaceState *state = _GetInterface(interface, true);
if (state == NULL)
return ENOBUFS;
return state->Add(sourceAddress);
}
template<typename AddressType> status_t
MulticastGroupState<AddressType>::DropSSM(net_interface *interface,
const AddressType &sourceAddress)
{
if (fFilterMode == kExclude)
return EINVAL;
InterfaceState *state = _GetInterface(interface, false);
if (state == NULL)
return EADDRNOTAVAIL;
return state->Remove(sourceAddress);
}
template<typename AddressType> MulticastGroupState<AddressType>::InterfaceState *
MulticastGroupState<AddressType>::_GetInterface(net_interface *interface,
bool create)
{
InterfaceList::Iterator iterator = fInterfaces.GetIterator();
while (iterator.HasNext()) {
InterfaceState *state = iterator.Next();
if (state->Interface() == interface)
return state;
}
if (!create)
return false;
InterfaceState *state = new (nothrow) InterfaceState(interface);
if (state)
fInterfaces.Add(state);
return state;
}
template<typename AddressType> void
MulticastGroupState<AddressType>::_RemoveInterface(InterfaceState *state)
{
fInterfaces.Remove(state);
delete state;
}
template<typename AddressType>
MulticastFilter<AddressType>::~MulticastFilter()
{
States::Iterator iterator = fStates.GetIterator();
while (iterator.HasNext()) {
GroupState *state = iterator.Next();
fStates.Remove(state);
delete state;
}
}
template<typename AddressType> MulticastFilter<AddressType>::GroupState *
MulticastFilter<AddressType>::GetGroup(const AddressType &groupAddress,
bool create)
{
States::Iterator iterator = fStates.GetIterator();
while (iterator.HasNext()) {
GroupState *state = iterator.Next();
if (state->Address() == groupAddress)
return state;
}
if (!create)
return NULL;
GroupState *state = new (nothrow) GroupState(groupAddress);
if (state)
fStates.Add(state);
return state;
}
template<typename AddressType> void
MulticastFilter<AddressType>::ReturnGroup(GroupState *group)
{
if (group->IsEmpty()) {
fStates.Remove(group);
delete group;
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Hugo Santos, hugosantos@gmail.com
*/
#ifndef _PRIVATE_MULTICAST_H_
#define _PRIVATE_MULTICAST_H_
#include <util/DoublyLinkedList.h>
#include <util/list.h>
struct net_interface;
template<typename AddressType>
struct MulticastSource {
AddressType address;
list_link link;
};
template<typename AddressType>
class MulticastGroupInterfaceState {
public:
MulticastGroupInterfaceState(net_interface *interface);
~MulticastGroupInterfaceState();
net_interface *Interface() const { return fInterface; }
status_t Add(const AddressType &address);
status_t Remove(const AddressType &address);
list_link link;
private:
typedef MulticastSource<AddressType> Source;
typedef DoublyLinkedListCLink<Source> SourceLink;
typedef DoublyLinkedList<Source, SourceLink> SourceList;
Source *_Get(const AddressType &address, bool create);
void _Remove(Source *state);
net_interface *fInterface;
// TODO make this an hash table as well
SourceList fSources;
};
template<typename AddressType>
class MulticastGroupState {
public:
MulticastGroupState(const AddressType &address);
~MulticastGroupState();
const AddressType &Address() const { return fMulticastAddress; }
bool IsEmpty() const
{ return fFilterMode == kInclude && fInterfaces.IsEmpty(); }
status_t Add(net_interface *interface);
status_t Drop(net_interface *interface);
status_t BlockSource(net_interface *interface,
const AddressType &sourceAddress);
status_t UnblockSource(net_interface *interface,
const AddressType &sourceAddress);
status_t AddSSM(net_interface *interface,
const AddressType &sourceAddress);
status_t DropSSM(net_interface *interface,
const AddressType &sourceAddress);
list_link link;
private:
typedef MulticastGroupInterfaceState<AddressType> InterfaceState;
typedef DoublyLinkedListCLink<InterfaceState> InterfaceStateLink;
typedef DoublyLinkedList<InterfaceState, InterfaceStateLink> InterfaceList;
InterfaceState *_GetInterface(net_interface *interface, bool create);
void _RemoveInterface(InterfaceState *state);
enum FilterMode {
kInclude,
kExclude
};
AddressType fMulticastAddress;
FilterMode fFilterMode;
InterfaceList fInterfaces;
};
template<typename AddressType>
class MulticastFilter {
public:
typedef MulticastGroupState<AddressType> GroupState;
~MulticastFilter();
GroupState *GetGroup(const AddressType &groupAddress, bool create);
void ReturnGroup(GroupState *group);
private:
typedef DoublyLinkedListCLink<GroupState> GroupStateLink;
typedef DoublyLinkedList<GroupState, GroupStateLink> States;
// TODO change this into an hash table or tree
States fStates;
};
#endif
@@ -419,6 +419,36 @@ datalink_is_local_address(net_domain *_domain, const struct sockaddr *address,
}
net_interface *
datalink_get_interface_with_address(net_domain *_domain,
const sockaddr *address)
{
net_domain_private *domain = (net_domain_private *)_domain;
if (domain == NULL)
return NULL;
BenaphoreLocker _(domain->lock);
net_interface *interface = NULL;
while (true) {
interface = (net_interface *)list_get_next_item(
&domain->interfaces, interface);
if (interface == NULL)
break;
if (address == NULL)
return interface;
if (domain->address_module->equal_addresses(interface->address,
address))
return interface;
}
return NULL;
}
static status_t
datalink_std_ops(int32 op, ...)
{
@@ -745,6 +775,7 @@ net_datalink_module_info gNetDatalinkModule = {
datalink_control,
datalink_send_data,
datalink_is_local_address,
datalink_get_interface_with_address,
add_route,
remove_route,