* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Hugo Santos, hugosantos@gmail.com
*/
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/route.h>
#include <new>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sockio.h>
#include <KernelExport.h>
#include <net_datalink.h>
#include <net_device.h>
#include <NetUtilities.h>
#include "device_interfaces.h"
#include "domains.h"
#include "interfaces.h"
#include "routes.h"
#include "stack_private.h"
#include "utility.h"
#ifdef TRACE_DATALINK
# define TRACE(x...) dprintf(STACK_DEBUG_PREFIX x)
#else
# define TRACE(x...) ;
#endif
struct datalink_protocol : net_protocol {
struct net_domain_private* domain;
};
struct interface_protocol : net_datalink_protocol {
struct net_device_module_info* device_module;
struct net_device* device;
};
#ifdef TRACE_DATALINK
static const char*
option_to_string(int32 option)
{
# define CODE(x) case x: return #x;
switch (option) {
CODE(SIOCADDRT)
CODE(SIOCDELRT)
CODE(SIOCSIFADDR)
CODE(SIOCGIFADDR)
CODE(SIOCSIFDSTADDR)
CODE(SIOCGIFDSTADDR)
CODE(SIOCSIFFLAGS)
CODE(SIOCGIFFLAGS)
CODE(SIOCGIFBRDADDR)
CODE(SIOCSIFBRDADDR)
CODE(SIOCGIFCOUNT)
CODE(SIOCGIFCONF)
CODE(SIOCGIFINDEX)
CODE(SIOCGIFNAME)
CODE(SIOCGIFNETMASK)
CODE(SIOCSIFNETMASK)
CODE(SIOCGIFMETRIC)
CODE(SIOCSIFMETRIC)
CODE(SIOCDIFADDR)
CODE(SIOCAIFADDR)
CODE(SIOCADDMULTI)
CODE(SIOCDELMULTI)
CODE(SIOCGIFMTU)
CODE(SIOCSIFMTU)
CODE(SIOCSIFMEDIA)
CODE(SIOCGIFMEDIA)
CODE(SIOCGRTSIZE)
CODE(SIOCGRTTABLE)
CODE(SIOCGETRT)
CODE(SIOCGIFSTATS)
CODE(SIOCGIFTYPE)
CODE(SIOCSPACKETCAP)
CODE(SIOCCPACKETCAP)
CODE(SIOCSHIWAT)
CODE(SIOCGHIWAT)
CODE(SIOCSLOWAT)
CODE(SIOCGLOWAT)
CODE(SIOCATMARK)
CODE(SIOCSPGRP)
CODE(SIOCGPGRP)
CODE(SIOCGPRIVATE_0)
CODE(SIOCGPRIVATE_1)
CODE(SIOCSDRVSPEC)
CODE(SIOCGDRVSPEC)
CODE(SIOCSIFGENERIC)
CODE(SIOCGIFGENERIC)
CODE(B_SOCKET_SET_ALIAS)
CODE(B_SOCKET_GET_ALIAS)
CODE(B_SOCKET_COUNT_ALIASES)
default:
static char buffer[24];
snprintf(buffer, sizeof(buffer), "%" B_PRId32, option);
return buffer;
}
# undef CODE
}
#endif
static status_t
get_interface_name_or_index(net_domain* domain, int32 option, void* value,
size_t* _length)
{
ASSERT(option == SIOCGIFINDEX || option == SIOCGIFNAME);
size_t expected = option == SIOCGIFINDEX ? IF_NAMESIZE : sizeof(ifreq);
if (*_length > 0 && *_length < expected)
return B_BAD_VALUE;
ifreq request;
memset(&request, 0, sizeof(request));
if (user_memcpy(&request, value, expected) < B_OK)
return B_BAD_ADDRESS;
Interface* interface = NULL;
if (option == SIOCGIFINDEX)
interface = get_interface(domain, request.ifr_name);
else
interface = get_interface(domain, request.ifr_index);
if (interface == NULL)
return B_BAD_VALUE;
if (option == SIOCGIFINDEX)
request.ifr_index = interface->index;
else
strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
*_length = sizeof(ifreq);
interface->ReleaseReference();
return user_memcpy(value, &request, sizeof(ifreq));
}
static void
set_interface_address(net_interface_address*& target, InterfaceAddress* address)
{
if (target != NULL)
static_cast<InterfaceAddress*>(target)->ReleaseReference();
target = address;
}
static status_t
fill_address(const sockaddr* from, sockaddr* to, size_t maxLength)
{
if (from != NULL) {
return user_memcpy(to, from, min_c(from->sa_len, maxLength));
}
sockaddr empty;
empty.sa_len = 2;
empty.sa_family = AF_UNSPEC;
return user_memcpy(to, &empty, min_c(2, maxLength));
}
static void
update_device_send_stats(struct net_device* device, status_t status, size_t packetSize)
{
if (status == B_OK) {
atomic_add((int32*)&device->stats.send.packets, 1);
atomic_add64((int64*)&device->stats.send.bytes, packetSize);
} else {
if (status == ENOBUFS || status == EMSGSIZE)
atomic_add((int32*)&device->stats.send.dropped, 1);
else
atomic_add((int32*)&device->stats.send.errors, 1);
}
}
static status_t
datalink_control(net_domain* _domain, int32 option, void* value,
size_t* _length)
{
TRACE("%s(domain %p, option %s, value %p, length %zu)\n", __FUNCTION__,
_domain, option_to_string(option), value, *_length);
net_domain_private* domain = (net_domain_private*)_domain;
if (domain == NULL || domain->family == AF_LINK) {
return B_BAD_VALUE;
}
switch (option) {
case SIOCGIFINDEX:
case SIOCGIFNAME:
return get_interface_name_or_index(domain, option, value, _length);
case SIOCAIFADDR:
{
if (*_length > 0 && *_length < sizeof(struct ifaliasreq))
return B_BAD_VALUE;
struct ifaliasreq request;
if (user_memcpy(&request, value, sizeof(struct ifaliasreq)) != B_OK)
return B_BAD_ADDRESS;
Interface* interface = get_interface(domain, request.ifra_name);
if (interface != NULL) {
status_t status = add_interface_address(interface, domain,
request);
notify_interface_changed(interface);
interface->ReleaseReference();
return status;
}
net_device_interface* deviceInterface
= get_device_interface(request.ifra_name);
if (deviceInterface == NULL)
return B_DEVICE_NOT_FOUND;
status_t status = add_interface(request.ifra_name, domain, request,
deviceInterface);
put_device_interface(deviceInterface);
return status;
}
case SIOCDIFADDR:
{
struct ifreq request;
if (user_memcpy(&request, value, sizeof(struct ifreq)) != B_OK)
return B_BAD_ADDRESS;
Interface* interface = get_interface(domain, request.ifr_name);
if (interface == NULL)
return B_BAD_VALUE;
status_t status = B_OK;
if (request.ifr_addr.sa_family != AF_UNSPEC
&& request.ifr_addr.sa_len != 0) {
status = interface->Control(domain, SIOCDIFADDR, request,
(ifreq*)value, *_length);
} else
remove_interface(interface);
interface->ReleaseReference();
return status;
}
case SIOCGIFCOUNT:
{
struct ifconf config;
config.ifc_value = count_interfaces();
return user_memcpy(value, &config, sizeof(struct ifconf));
}
case SIOCGIFCONF:
{
struct ifconf config;
if (user_memcpy(&config, value, sizeof(struct ifconf)) < B_OK)
return B_BAD_ADDRESS;
size_t size = config.ifc_len;
status_t status
= list_interfaces(domain->family, config.ifc_buf, &size);
if (status != B_OK)
return status;
config.ifc_len = (int)size;
return user_memcpy(value, &config, sizeof(struct ifconf));
}
case SIOCGRTSIZE:
{
struct ifconf config;
config.ifc_value = route_table_size(domain);
return user_memcpy(value, &config, sizeof(struct ifconf));
}
case SIOCGRTTABLE:
{
struct ifconf config;
if (user_memcpy(&config, value, sizeof(struct ifconf)) < B_OK)
return B_BAD_ADDRESS;
return list_routes(domain, config.ifc_buf, config.ifc_len);
}
case SIOCGETRT:
return get_route_information(domain, value, *_length);
default:
{
size_t length = sizeof(struct ifreq);
if (*_length > 0 && *_length >= IF_NAMESIZE)
length = min_c(length, *_length);
struct ifreq request;
if (user_memcpy(&request, value, length) != B_OK)
return B_BAD_ADDRESS;
Interface* interface = get_interface(domain, request.ifr_name);
if (interface == NULL)
return B_BAD_VALUE;
status_t status = interface->Control(domain, option, request,
(ifreq*)value, *_length);
interface->ReleaseReference();
return status;
}
}
return B_BAD_VALUE;
}
static status_t
datalink_send_routed_data(struct net_route* route, net_buffer* buffer)
{
TRACE("%s(route %p, buffer %p)\n", __FUNCTION__, route, buffer);
InterfaceAddress* address = (InterfaceAddress*)route->interface_address;
Interface* interface = (Interface*)address->interface;
if ((route->flags & RTF_REJECT) != 0) {
TRACE(" rejected route\n");
return ENETUNREACH;
}
if ((route->flags & RTF_HOST) != 0) {
TRACE(" host route\n");
address->AcquireReference();
set_interface_address(buffer->interface_address, address);
}
if ((route->flags & RTF_LOCAL) != 0) {
TRACE(" local route\n");
address->AcquireReference();
set_interface_address(buffer->interface_address, address);
if (atomic_get(&interface->DeviceInterface()->monitor_count) > 0)
device_interface_monitor_receive(interface->DeviceInterface(), buffer);
const size_t packetSize = buffer->size;
status_t status = fifo_enqueue_buffer(
&interface->DeviceInterface()->receive_queue, buffer);
update_device_send_stats(interface->DeviceInterface()->device,
status, packetSize);
return status;
}
if ((route->flags & RTF_GATEWAY) != 0) {
TRACE(" gateway route\n");
if (route->gateway == NULL)
return B_MISMATCHED_VALUES;
memcpy(buffer->destination, route->gateway, route->gateway->sa_len);
}
domain_datalink* datalink
= interface->DomainDatalink(address->domain->family);
return datalink->first_info->send_data(datalink->first_protocol, buffer);
}
net_protocol_info::send_routed_data() on either the \a protocol (if
non-NULL), or the domain.
*/
static status_t
datalink_send_data(net_protocol* protocol, net_domain* domain,
net_buffer* buffer)
{
TRACE("%s(%p, domain %p, buffer %p)\n", __FUNCTION__, protocol, domain,
buffer);
if (protocol == NULL && domain == NULL)
return B_BAD_VALUE;
net_protocol_module_info* module = protocol != NULL
? protocol->module : domain->module;
if (domain == NULL)
domain = protocol->module->get_domain(protocol);
net_route* route = NULL;
status_t status;
if (protocol != NULL && protocol->socket != NULL
&& protocol->socket->bound_to_device != 0) {
status = get_device_route(domain, protocol->socket->bound_to_device,
&route);
} else
status = get_buffer_route(domain, buffer, &route);
TRACE(" route status: %s\n", strerror(status));
if (status != B_OK)
return status;
status = module->send_routed_data(protocol, route, buffer);
put_route(domain, route);
return status;
}
\param _interfaceAddress will be set to the interface address belonging to
that address if non-NULL. If the address \a _interfaceAddress points to
is not NULL, it is assumed that it already points to an address, which
is then released before the new address is assigned.
\param _matchedType will be set to either zero or MSG_BCAST if non-NULL.
*/
static bool
datalink_is_local_address(net_domain* domain, const struct sockaddr* address,
net_interface_address** _interfaceAddress, uint32* _matchedType)
{
TRACE("%s(domain %p, address %s)\n", __FUNCTION__, domain,
AddressString(domain, address).Data());
if (domain == NULL || address == NULL
|| domain->family != address->sa_family)
return false;
uint32 matchedType = 0;
InterfaceAddress* interfaceAddress = get_interface_address(address);
if (interfaceAddress == NULL) {
if ((domain->address_module->flags
& NET_ADDRESS_MODULE_FLAG_BROADCAST_ADDRESS) != 0) {
interfaceAddress
= get_interface_address_for_destination(domain, address);
matchedType = MSG_BCAST;
}
if (interfaceAddress == NULL) {
TRACE(" no\n");
return false;
}
}
TRACE(" it is, interface address %p\n", interfaceAddress);
if (_interfaceAddress != NULL)
set_interface_address(*_interfaceAddress, interfaceAddress);
else
interfaceAddress->ReleaseReference();
if (_matchedType != NULL)
*_matchedType = matchedType;
return true;
}
\param unconfiguredOnly only unconfigured interfaces are taken into account.
\param _interfaceAddress will be set to the first address of the interface
and domain belonging to that address if non-NULL. If the address
\a _interfaceAddress points to is not NULL, it is assumed that it
already points to an address, which is then released before the new
address is assigned.
*/
static bool
datalink_is_local_link_address(net_domain* domain, bool unconfiguredOnly,
const struct sockaddr* address, net_interface_address** _interfaceAddress)
{
if (domain == NULL || address == NULL || address->sa_family != AF_LINK)
return false;
#ifdef TRACE_DATALINK
uint8* data = LLADDR((sockaddr_dl*)address);
TRACE("%s(domain %p, unconfiguredOnly %d, address %02x:%02x:%02x:%02x:%02x"
":%02x)\n", __FUNCTION__, domain, unconfiguredOnly, data[0], data[1],
data[2], data[3], data[4], data[5]);
#endif
InterfaceAddress* interfaceAddress = get_interface_address_for_link(domain,
address, unconfiguredOnly);
if (interfaceAddress == NULL) {
TRACE(" no\n");
return false;
}
if (_interfaceAddress != NULL)
set_interface_address(*_interfaceAddress, interfaceAddress);
else
interfaceAddress->ReleaseReference();
return true;
}
static net_interface*
datalink_get_interface(net_domain* domain, uint32 index)
{
return get_interface(domain, index);
}
static net_interface*
datalink_get_interface_with_address(const sockaddr* address)
{
InterfaceAddress* interfaceAddress = get_interface_address(address);
if (interfaceAddress == NULL)
return NULL;
Interface* interface = static_cast<Interface*>(interfaceAddress->interface);
interface->AcquireReference();
interfaceAddress->ReleaseReference();
return interface;
}
static void
datalink_put_interface(net_interface* interface)
{
if (interface == NULL)
return;
((Interface*)interface)->ReleaseReference();
}
static net_interface_address*
datalink_get_interface_address(const struct sockaddr* address)
{
return get_interface_address(address);
}
\a _address. When you call this function the first time, \a _address must
point to a NULL address. Upon the next call, the reference to the previous
address is taken over again.
If you do not traverse the list to the end, you'll have to manually release
the reference to the address where you stopped.
\param interface The interface whose address list should be iterated over.
\param _address A pointer to the location where the next address should
be stored.
\return \c true if an address reference was returned, \c false if not.
*/
static bool
datalink_get_next_interface_address(net_interface* _interface,
net_interface_address** _address)
{
Interface* interface = (Interface*)_interface;
InterfaceAddress* address = (InterfaceAddress*)*_address;
bool gotOne = interface->GetNextAddress(&address);
*_address = address;
return gotOne;
}
static void
datalink_put_interface_address(net_interface_address* address)
{
if (address == NULL)
return;
((InterfaceAddress*)address)->ReleaseReference();
}
static status_t
datalink_join_multicast(net_interface* _interface, net_domain* domain,
const struct sockaddr* address)
{
Interface* interface = (Interface*)_interface;
domain_datalink* datalink = interface->DomainDatalink(domain->family);
return datalink->first_info->join_multicast(datalink->first_protocol,
address);
}
static status_t
datalink_leave_multicast(net_interface* _interface, net_domain* domain,
const struct sockaddr* address)
{
Interface* interface = (Interface*)_interface;
domain_datalink* datalink = interface->DomainDatalink(domain->family);
return datalink->first_info->leave_multicast(datalink->first_protocol,
address);
}
static status_t
datalink_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
}
static status_t
interface_protocol_init(net_interface* interface, net_domain* domain,
net_datalink_protocol** _protocol)
{
interface_protocol* protocol = new(std::nothrow) interface_protocol;
if (protocol == NULL)
return B_NO_MEMORY;
TRACE("%s(%p, interface %p - %s, domain %p)\n", __FUNCTION__, protocol,
interface, interface->name, domain);
protocol->device_module = interface->device->module;
protocol->device = interface->device;
*_protocol = protocol;
return B_OK;
}
static status_t
interface_protocol_uninit(net_datalink_protocol* protocol)
{
TRACE("%s(%p)\n", __FUNCTION__, protocol);
delete protocol;
return B_OK;
}
static status_t
interface_protocol_send_data(net_datalink_protocol* _protocol,
net_buffer* buffer)
{
TRACE("%s(%p, buffer %p)\n", __FUNCTION__, _protocol, buffer);
interface_protocol* protocol = (interface_protocol*)_protocol;
Interface* interface = (Interface*)protocol->interface;
if (atomic_get(&interface->DeviceInterface()->monitor_count) > 0)
device_interface_monitor_receive(interface->DeviceInterface(), buffer);
const size_t packetSize = buffer->size;
status_t status = protocol->device_module->send_data(protocol->device, buffer);
update_device_send_stats(protocol->device, status, packetSize);
return status;
}
static status_t
interface_protocol_up(net_datalink_protocol* protocol)
{
TRACE("%s(%p)\n", __FUNCTION__, protocol);
return B_OK;
}
static void
interface_protocol_down(net_datalink_protocol* _protocol)
{
TRACE("%s(%p)\n", __FUNCTION__, _protocol);
interface_protocol* protocol = (interface_protocol*)_protocol;
Interface* interface = (Interface*)protocol->interface;
net_device_interface* deviceInterface = interface->DeviceInterface();
if (deviceInterface->up_count == 0)
return;
deviceInterface->up_count--;
interface->WentDown();
if (deviceInterface->up_count > 0)
return;
down_device_interface(deviceInterface);
}
static status_t
interface_protocol_change_address(net_datalink_protocol* protocol,
net_interface_address* interfaceAddress, int32 option,
const struct sockaddr* oldAddress, const struct sockaddr* newAddress)
{
TRACE("%s(%p, interface address %p, option %s, old %p, new %p)\n",
__FUNCTION__, protocol, interfaceAddress, option_to_string(option),
oldAddress, newAddress);
switch (option) {
case SIOCSIFADDR:
case SIOCSIFNETMASK:
case SIOCSIFBRDADDR:
case SIOCSIFDSTADDR:
case SIOCDIFADDR:
return update_interface_address((InterfaceAddress*)interfaceAddress,
option, oldAddress, newAddress);
}
return B_OK;
}
static status_t
interface_protocol_control(net_datalink_protocol* _protocol, int32 option,
void* argument, size_t length)
{
TRACE("%s(%p, option %s, argument %p, length %zu)\n", __FUNCTION__,
_protocol, option_to_string(option), argument, length);
interface_protocol* protocol = (interface_protocol*)_protocol;
Interface* interface = (Interface*)protocol->interface;
switch (option) {
case SIOCGIFADDR:
case SIOCGIFNETMASK:
case SIOCGIFBRDADDR:
case SIOCGIFDSTADDR:
{
if (length == 0)
length = sizeof(ifreq);
else if (length < sizeof(ifreq))
return B_BAD_VALUE;
ifreq request;
if (user_memcpy(&request, argument, sizeof(struct ifreq)) != B_OK)
return B_BAD_ADDRESS;
InterfaceAddress* interfaceAddress
= get_interface_address(&request.ifr_addr);
if (interfaceAddress == NULL) {
interfaceAddress
= interface->FirstForFamily(protocol->domain->family);
if (interfaceAddress == NULL)
return B_BAD_VALUE;
}
size_t maxLength = length - offsetof(ifreq, ifr_addr);
status_t status = fill_address(
*interfaceAddress->AddressFor(option),
&((struct ifreq*)argument)->ifr_addr, maxLength);
interfaceAddress->ReleaseReference();
return status;
}
case B_SOCKET_COUNT_ALIASES:
{
ifreq request;
request.ifr_count = interface->CountAddresses();
return user_memcpy(&((struct ifreq*)argument)->ifr_count,
&request.ifr_count, sizeof(request.ifr_count));
}
case B_SOCKET_GET_ALIAS:
{
ifaliasreq request;
if (user_memcpy(&request, argument, sizeof(ifaliasreq)) != B_OK)
return B_BAD_ADDRESS;
InterfaceAddress* address = NULL;
if (request.ifra_index < 0) {
if (!protocol->domain->address_module->is_empty_address(
(const sockaddr*)&request.ifra_addr, false)) {
address = interface->AddressForLocal(protocol->domain,
(const sockaddr*)&request.ifra_addr);
} else {
address = interface->FirstForFamily(
protocol->domain->family);
}
request.ifra_index = interface->IndexOfAddress(address);
} else
address = interface->AddressAt(request.ifra_index);
if (address == NULL)
return B_BAD_VALUE;
status_t status = user_memcpy(
&((struct ifaliasreq*)argument)->ifra_index,
&request.ifra_index, sizeof(request.ifra_index));
if (status == B_OK) {
status = fill_address(address->local,
(sockaddr*)&((struct ifaliasreq*)argument)->ifra_addr,
sizeof(sockaddr_storage));
}
if (status == B_OK) {
status = fill_address(address->mask,
(sockaddr*)&((struct ifaliasreq*)argument)->ifra_mask,
sizeof(sockaddr_storage));
}
if (status == B_OK) {
status = fill_address(address->destination,
(sockaddr*)&((struct ifaliasreq*)argument)
->ifra_destination,
sizeof(sockaddr_storage));
}
address->ReleaseReference();
return status;
}
case SIOCGIFFLAGS:
{
struct ifreq request;
request.ifr_flags = interface->flags | interface->device->flags;
return user_memcpy(&((struct ifreq*)argument)->ifr_flags,
&request.ifr_flags, sizeof(request.ifr_flags));
}
case SIOCGIFSTATS:
{
return user_memcpy(&((struct ifreq*)argument)->ifr_stats,
&interface->DeviceInterface()->device->stats,
sizeof(struct ifreq_stats));
}
case SIOCGIFTYPE:
{
struct ifreq request;
request.ifr_type = interface->type;
return user_memcpy(&((struct ifreq*)argument)->ifr_type,
&request.ifr_type, sizeof(request.ifr_type));
}
case SIOCGIFMTU:
{
struct ifreq request;
request.ifr_mtu = interface->device->mtu;
return user_memcpy(&((struct ifreq*)argument)->ifr_mtu,
&request.ifr_mtu, sizeof(request.ifr_mtu));
}
case SIOCSIFMTU:
{
struct ifreq request;
if (user_memcpy(&request, argument, sizeof(struct ifreq)) != B_OK)
return B_BAD_ADDRESS;
status_t status = interface->device->module->set_mtu(
interface->device, request.ifr_mtu);
if (status == B_OK)
notify_interface_changed(interface);
return status;
}
case SIOCSIFMEDIA:
{
struct ifreq request;
if (user_memcpy(&request, argument, sizeof(struct ifreq)) != B_OK)
return B_BAD_ADDRESS;
status_t status
= interface->device->module->set_media(
interface->device, request.ifr_media);
if (status == B_NOT_SUPPORTED) {
status = interface->device->module->control(
interface->device, SIOCSIFMEDIA, &request, sizeof(request));
}
return status;
}
case SIOCGIFMEDIA:
{
const size_t copylen = offsetof(ifreq, ifr_media) + sizeof(ifreq::ifr_media);
if (length > 0 && length < copylen)
return B_BAD_VALUE;
struct ifreq request;
if (user_memcpy(&request, argument, copylen) != B_OK)
return B_BAD_ADDRESS;
request.ifr_media = interface->device->media;
return user_memcpy(argument, &request, copylen);
}
case SIOCGIFMETRIC:
{
struct ifreq request;
request.ifr_metric = interface->metric;
return user_memcpy(&((struct ifreq*)argument)->ifr_metric,
&request.ifr_metric, sizeof(request.ifr_metric));
}
case SIOCSIFMETRIC:
{
struct ifreq request;
if (user_memcpy(&request, argument, sizeof(struct ifreq)) < B_OK)
return B_BAD_ADDRESS;
interface->metric = request.ifr_metric;
notify_interface_changed(interface);
return B_OK;
}
case SIOCADDRT:
case SIOCDELRT:
return control_routes(interface, protocol->domain, option, argument,
length);
}
return protocol->device_module->control(protocol->device,
option, argument, length);
}
static status_t
interface_protocol_join_multicast(net_datalink_protocol* _protocol,
const sockaddr* address)
{
interface_protocol* protocol = (interface_protocol*)_protocol;
return protocol->device_module->add_multicast(protocol->device, address);
}
static status_t
interface_protocol_leave_multicast(net_datalink_protocol* _protocol,
const sockaddr* address)
{
interface_protocol* protocol = (interface_protocol*)_protocol;
return protocol->device_module->remove_multicast(protocol->device,
address);
}
net_datalink_module_info gNetDatalinkModule = {
{
NET_DATALINK_MODULE_NAME,
0,
datalink_std_ops
},
datalink_control,
datalink_send_routed_data,
datalink_send_data,
datalink_is_local_address,
datalink_is_local_link_address,
datalink_get_interface,
datalink_get_interface_with_address,
datalink_put_interface,
datalink_get_interface_address,
datalink_get_next_interface_address,
datalink_put_interface_address,
datalink_join_multicast,
datalink_leave_multicast,
add_route,
remove_route,
get_route,
get_buffer_route,
put_route,
register_route_info,
unregister_route_info,
update_route_info
};
net_datalink_protocol_module_info gDatalinkInterfaceProtocolModule = {
{
NULL,
0,
NULL
},
interface_protocol_init,
interface_protocol_uninit,
interface_protocol_send_data,
interface_protocol_up,
interface_protocol_down,
interface_protocol_change_address,
interface_protocol_control,
interface_protocol_join_multicast,
interface_protocol_leave_multicast,
};