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:
@@ -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)))
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user