using SIOCGETRT. Patch by Hugo Santos. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20494 a95241bf-73f2-0310-859d-f6bbb57e9c96
778 lines
18 KiB
C++
778 lines
18 KiB
C++
/*
|
|
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Axel Dörfler, [email protected]
|
|
*/
|
|
|
|
|
|
#include "datalink.h"
|
|
#include "domains.h"
|
|
#include "interfaces.h"
|
|
#include "routes.h"
|
|
#include "stack_private.h"
|
|
|
|
#include <net_device.h>
|
|
#include <KernelExport.h>
|
|
#include <util/AutoLock.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
#include <sys/sockio.h>
|
|
|
|
#include <new>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
static status_t
|
|
device_reader_thread(void *_interface)
|
|
{
|
|
net_device_interface *interface = (net_device_interface *)_interface;
|
|
net_device *device = interface->device;
|
|
status_t status = B_OK;
|
|
int32 tries = 0;
|
|
|
|
while ((device->flags & IFF_UP) != 0) {
|
|
net_buffer *buffer;
|
|
status = device->module->receive_data(device, &buffer);
|
|
if (status == B_OK) {
|
|
//dprintf("received buffer of %ld bytes length\n", buffer->size);
|
|
tries = 0;
|
|
|
|
// feed device monitors
|
|
// TODO: locking!
|
|
DeviceMonitorList::Iterator iterator = interface->monitor_funcs.GetIterator();
|
|
while (iterator.HasNext()) {
|
|
net_device_monitor *monitor = iterator.Next();
|
|
monitor->func(monitor->cookie, buffer);
|
|
}
|
|
|
|
int32 type = interface->deframe_func(device, buffer);
|
|
if (type >= 0) {
|
|
// find handler for this packet
|
|
// TODO: locking!
|
|
DeviceHandlerList::Iterator iterator = interface->receive_funcs.GetIterator();
|
|
status = B_ERROR;
|
|
|
|
while (iterator.HasNext()) {
|
|
net_device_handler *handler = iterator.Next();
|
|
|
|
if (handler->type == type) {
|
|
status = handler->func(handler->cookie, buffer);
|
|
if (status == B_OK)
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
status = type;
|
|
|
|
if (status == B_OK) {
|
|
// the buffer no longer belongs to us
|
|
continue;
|
|
}
|
|
|
|
gNetBufferModule.free(buffer);
|
|
}
|
|
|
|
if (status < B_OK) {
|
|
// this is a near real-time thread - don't render the system unusable
|
|
// in case of a device going down
|
|
snooze(10000);
|
|
|
|
if (++tries > 20) {
|
|
// TODO: bring down the interface!
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static struct sockaddr **
|
|
interface_address(net_interface *interface, int32 option)
|
|
{
|
|
switch (option) {
|
|
case SIOCSIFADDR:
|
|
case SIOCGIFADDR:
|
|
return &interface->address;
|
|
|
|
case SIOCSIFNETMASK:
|
|
case SIOCGIFNETMASK:
|
|
return &interface->mask;
|
|
|
|
case SIOCSIFBRDADDR:
|
|
case SIOCSIFDSTADDR:
|
|
case SIOCGIFBRDADDR:
|
|
case SIOCGIFDSTADDR:
|
|
return &interface->destination;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
remove_default_routes(net_interface_private *interface, int32 option)
|
|
{
|
|
net_route route;
|
|
route.destination = interface->address;
|
|
route.gateway = NULL;
|
|
route.interface = interface;
|
|
|
|
if (interface->mask != NULL && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) {
|
|
route.mask = interface->mask;
|
|
route.flags = 0;
|
|
remove_route(interface->domain, &route);
|
|
}
|
|
|
|
if (option == SIOCSIFADDR) {
|
|
route.mask = NULL;
|
|
route.flags = RTF_LOCAL | RTF_HOST;
|
|
remove_route(interface->domain, &route);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
add_default_routes(net_interface_private *interface, int32 option)
|
|
{
|
|
net_route route;
|
|
route.destination = interface->address;
|
|
route.gateway = NULL;
|
|
route.interface = interface;
|
|
|
|
if (interface->mask != NULL && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) {
|
|
route.mask = interface->mask;
|
|
route.flags = 0;
|
|
add_route(interface->domain, &route);
|
|
}
|
|
|
|
if (option == SIOCSIFADDR) {
|
|
route.mask = NULL;
|
|
route.flags = RTF_LOCAL | RTF_HOST;
|
|
add_route(interface->domain, &route);
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - datalink module
|
|
|
|
|
|
status_t
|
|
datalink_control(net_domain *_domain, int32 option, void *value,
|
|
size_t *_length)
|
|
{
|
|
net_domain_private *domain = (net_domain_private *)_domain;
|
|
if (domain == NULL || domain->family == AF_LINK) {
|
|
// the AF_LINK family is already handled completely in the link protocol
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
switch (option) {
|
|
case SIOCGIFINDEX:
|
|
{
|
|
// get index of interface
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, value, IF_NAMESIZE) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
benaphore_lock(&domain->lock);
|
|
|
|
net_interface *interface = find_interface(domain,
|
|
request.ifr_name);
|
|
if (interface != NULL)
|
|
request.ifr_index = interface->index;
|
|
else
|
|
request.ifr_index = 0;
|
|
|
|
benaphore_unlock(&domain->lock);
|
|
|
|
if (request.ifr_index == 0)
|
|
return ENODEV;
|
|
|
|
return user_memcpy(value, &request, sizeof(struct ifreq));
|
|
}
|
|
case SIOCGIFNAME:
|
|
{
|
|
// get name of interface via index
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, value, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
benaphore_lock(&domain->lock);
|
|
status_t status = B_OK;
|
|
|
|
net_interface *interface = find_interface(domain,
|
|
request.ifr_index);
|
|
if (interface != NULL)
|
|
strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
|
|
else
|
|
status = B_BAD_VALUE;
|
|
|
|
benaphore_unlock(&domain->lock);
|
|
|
|
if (status < B_OK)
|
|
return status;
|
|
|
|
return user_memcpy(value, &request, sizeof(struct ifreq));
|
|
}
|
|
|
|
case SIOCAIFADDR:
|
|
{
|
|
// add new interface address
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, value, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
return add_interface_to_domain(domain, request);
|
|
}
|
|
case SIOCDIFADDR:
|
|
{
|
|
// remove interface address
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, value, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
benaphore_lock(&domain->lock);
|
|
status_t status;
|
|
|
|
net_interface *interface = find_interface(domain,
|
|
request.ifr_name);
|
|
if (interface != NULL)
|
|
status = remove_interface_from_domain(interface);
|
|
else
|
|
status = ENODEV;
|
|
|
|
benaphore_unlock(&domain->lock);
|
|
|
|
return status;
|
|
}
|
|
|
|
case SIOCGIFCOUNT:
|
|
{
|
|
// count number of interfaces
|
|
struct ifconf config;
|
|
config.ifc_value = count_domain_interfaces();
|
|
|
|
return user_memcpy(value, &config, sizeof(struct ifconf));
|
|
}
|
|
|
|
case SIOCGIFCONF:
|
|
{
|
|
// retrieve ifreqs for all interfaces
|
|
struct ifconf config;
|
|
if (user_memcpy(&config, value, sizeof(struct ifconf)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
status_t result = list_domain_interfaces(config.ifc_buf,
|
|
(size_t *)&config.ifc_len);
|
|
if (result != B_OK)
|
|
return result;
|
|
|
|
return user_memcpy(value, &config, sizeof(struct ifconf));
|
|
}
|
|
|
|
case SIOCGRTSIZE:
|
|
{
|
|
// determine size of buffer to hold the routing table
|
|
struct ifconf config;
|
|
config.ifc_value = route_table_size(domain);
|
|
|
|
return user_memcpy(value, &config, sizeof(struct ifconf));
|
|
}
|
|
case SIOCGRTTABLE:
|
|
{
|
|
// retrieve all routes for this domain
|
|
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:
|
|
{
|
|
// try to pass the request to an existing interface
|
|
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, value, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
benaphore_lock(&domain->lock);
|
|
status_t status = B_OK;
|
|
|
|
net_interface *interface = find_interface(domain,
|
|
request.ifr_name);
|
|
if (interface != NULL) {
|
|
// filter out bringing the interface up or down
|
|
if (option == SIOCSIFFLAGS
|
|
&& ((uint32)request.ifr_flags & IFF_UP) != (interface->flags & IFF_UP)) {
|
|
if ((interface->flags & IFF_UP) != 0) {
|
|
// bring the interface down
|
|
interface->flags &= ~IFF_UP;
|
|
interface->first_info->interface_down(interface->first_protocol);
|
|
} else {
|
|
// bring it up
|
|
status = interface->first_info->interface_up(interface->first_protocol);
|
|
if (status == B_OK)
|
|
interface->flags |= IFF_UP;
|
|
}
|
|
|
|
request.ifr_flags = interface->flags;
|
|
}
|
|
|
|
if (status == B_OK) {
|
|
// pass the request into the datalink protocol stack
|
|
status = interface->first_info->control(interface->first_protocol,
|
|
option, value, *_length);
|
|
}
|
|
} else
|
|
status = B_BAD_VALUE;
|
|
|
|
benaphore_unlock(&domain->lock);
|
|
return status;
|
|
}
|
|
}
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
|
|
status_t
|
|
datalink_send_data(struct net_route *route, net_buffer *buffer)
|
|
{
|
|
net_interface *interface = route->interface;
|
|
net_domain *domain = interface->domain;
|
|
|
|
//dprintf("send buffer (%ld bytes) to interface %s (route flags %lx)\n",
|
|
// buffer->size, interface->name, route->flags);
|
|
|
|
if (route->flags & RTF_REJECT)
|
|
return ENETUNREACH;
|
|
|
|
if (route->flags & RTF_LOCAL) {
|
|
// this one goes back to the domain directly
|
|
return domain->module->receive_data(buffer);
|
|
}
|
|
|
|
if (route->flags & RTF_GATEWAY) {
|
|
// this route involves a gateway, we need to use the gateway address
|
|
// instead of the destination address:
|
|
if (route->gateway == NULL)
|
|
return B_MISMATCHED_VALUES;
|
|
memcpy(&buffer->destination, route->gateway, sizeof(sockaddr));
|
|
}
|
|
|
|
// this goes out to the datalink protocols
|
|
return interface->first_info->send_data(interface->first_protocol, buffer);
|
|
}
|
|
|
|
|
|
/*!
|
|
Tests if \a address is a local address in the domain.
|
|
\param _interface will be set to the interface belonging to that address
|
|
if non-NULL.
|
|
\param _matchedType will be set to either zero or MSG_BCAST if non-NULL.
|
|
*/
|
|
bool
|
|
datalink_is_local_address(net_domain *_domain, const struct sockaddr *address,
|
|
net_interface **_interface, uint32 *_matchedType)
|
|
{
|
|
net_domain_private *domain = (net_domain_private *)_domain;
|
|
if (domain == NULL || address == NULL)
|
|
return false;
|
|
|
|
BenaphoreLocker locker(domain->lock);
|
|
|
|
net_interface *interface = NULL;
|
|
net_interface *fallback = NULL;
|
|
uint32 matchedType = 0;
|
|
|
|
while (true) {
|
|
interface = (net_interface *)list_get_next_item(
|
|
&domain->interfaces, interface);
|
|
if (interface == NULL)
|
|
break;
|
|
if (interface->address == NULL) {
|
|
fallback = interface;
|
|
continue;
|
|
}
|
|
|
|
// check for matching unicast address first
|
|
if (domain->address_module->equal_addresses(interface->address, address))
|
|
break;
|
|
|
|
// check for matching broadcast address if interface support broadcasting
|
|
if (interface->flags & IFF_BROADCAST
|
|
&& domain->address_module->equal_addresses(interface->destination,
|
|
address)) {
|
|
matchedType = MSG_BCAST;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (interface == NULL) {
|
|
interface = fallback;
|
|
if (interface == NULL)
|
|
return false;
|
|
}
|
|
|
|
if (_interface != NULL)
|
|
*_interface = interface;
|
|
if (_matchedType != NULL)
|
|
*_matchedType = matchedType;
|
|
return true;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - net_datalink_protocol
|
|
|
|
|
|
status_t
|
|
interface_protocol_init(struct net_interface *_interface, net_datalink_protocol **_protocol)
|
|
{
|
|
net_interface_private *interface = (net_interface_private *)_interface;
|
|
|
|
interface_protocol *protocol = new (std::nothrow) interface_protocol;
|
|
if (protocol == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
protocol->device_module = interface->device->module;
|
|
protocol->device = interface->device;
|
|
|
|
*_protocol = protocol;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
interface_protocol_uninit(net_datalink_protocol *protocol)
|
|
{
|
|
delete protocol;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
interface_protocol_send_data(net_datalink_protocol *_protocol,
|
|
net_buffer *buffer)
|
|
{
|
|
interface_protocol *protocol = (interface_protocol *)_protocol;
|
|
net_interface_private *interface = (net_interface_private *)protocol->interface;
|
|
|
|
// feed device monitors
|
|
// TODO: locking!
|
|
DeviceMonitorList::Iterator iterator =
|
|
interface->device_interface->monitor_funcs.GetIterator();
|
|
while (iterator.HasNext()) {
|
|
net_device_monitor *monitor = iterator.Next();
|
|
monitor->func(monitor->cookie, buffer);
|
|
}
|
|
|
|
return protocol->device_module->send_data(protocol->device, buffer);
|
|
}
|
|
|
|
|
|
status_t
|
|
interface_protocol_up(net_datalink_protocol *_protocol)
|
|
{
|
|
interface_protocol *protocol = (interface_protocol *)_protocol;
|
|
net_device_interface *deviceInterface =
|
|
((net_interface_private *)protocol->interface)->device_interface;
|
|
net_device *device = protocol->device;
|
|
|
|
// TODO: locking!
|
|
|
|
if (deviceInterface->up_count != 0) {
|
|
deviceInterface->up_count++;
|
|
return B_OK;
|
|
}
|
|
|
|
status_t status = protocol->device_module->up(device);
|
|
if (status < B_OK)
|
|
return status;
|
|
|
|
// give the thread a nice name
|
|
char name[B_OS_NAME_LENGTH];
|
|
snprintf(name, sizeof(name), "%s reader", device->name);
|
|
|
|
deviceInterface->reader_thread = spawn_kernel_thread(device_reader_thread, name,
|
|
B_REAL_TIME_DISPLAY_PRIORITY - 10, deviceInterface);
|
|
if (deviceInterface->reader_thread < B_OK)
|
|
return deviceInterface->reader_thread;
|
|
|
|
device->flags |= IFF_UP;
|
|
resume_thread(deviceInterface->reader_thread);
|
|
|
|
deviceInterface->up_count = 1;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
interface_protocol_down(net_datalink_protocol *_protocol)
|
|
{
|
|
interface_protocol *protocol = (interface_protocol *)_protocol;
|
|
net_device_interface *deviceInterface =
|
|
((net_interface_private *)protocol->interface)->device_interface;
|
|
|
|
// TODO: locking!
|
|
if (deviceInterface->up_count == 0)
|
|
return;
|
|
|
|
deviceInterface->up_count--;
|
|
|
|
if (deviceInterface->up_count > 0)
|
|
return;
|
|
|
|
down_device_interface(deviceInterface);
|
|
}
|
|
|
|
|
|
status_t
|
|
interface_protocol_control(net_datalink_protocol *_protocol,
|
|
int32 option, void *argument, size_t length)
|
|
{
|
|
interface_protocol *protocol = (interface_protocol *)_protocol;
|
|
net_interface_private *interface = (net_interface_private *)protocol->interface;
|
|
|
|
switch (option) {
|
|
case SIOCSIFADDR:
|
|
case SIOCSIFNETMASK:
|
|
case SIOCSIFBRDADDR:
|
|
case SIOCSIFDSTADDR:
|
|
{
|
|
// set logical interface address
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, argument, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
sockaddr **_address = interface_address(interface, option);
|
|
if (_address == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
sockaddr *address = *_address;
|
|
sockaddr *original = address;
|
|
|
|
// allocate new address if needed
|
|
if (address == NULL
|
|
|| (address->sa_len < request.ifr_addr.sa_len
|
|
&& request.ifr_addr.sa_len > sizeof(struct sockaddr))) {
|
|
address = (sockaddr *)malloc(
|
|
max_c(request.ifr_addr.sa_len, sizeof(struct sockaddr)));
|
|
}
|
|
|
|
// copy new address over
|
|
if (address != NULL) {
|
|
remove_default_routes(interface, option);
|
|
|
|
if (original != address) {
|
|
free(original);
|
|
*_address = address;
|
|
}
|
|
|
|
memcpy(address, &request.ifr_addr, request.ifr_addr.sa_len);
|
|
add_default_routes(interface, option);
|
|
}
|
|
|
|
return address != NULL ? B_OK : B_NO_MEMORY;
|
|
}
|
|
|
|
case SIOCGIFADDR:
|
|
case SIOCGIFNETMASK:
|
|
case SIOCGIFBRDADDR:
|
|
case SIOCGIFDSTADDR:
|
|
{
|
|
// get logical interface address
|
|
sockaddr **_address = interface_address(interface, option);
|
|
if (_address == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
struct ifreq request;
|
|
|
|
sockaddr *address = *_address;
|
|
if (address != NULL)
|
|
memcpy(&request.ifr_addr, address, address->sa_len);
|
|
else {
|
|
request.ifr_addr.sa_len = 2;
|
|
request.ifr_addr.sa_family = AF_UNSPEC;
|
|
}
|
|
|
|
// copy address over
|
|
return user_memcpy(&((struct ifreq *)argument)->ifr_addr,
|
|
&request.ifr_addr, request.ifr_addr.sa_len);
|
|
}
|
|
|
|
case SIOCGIFFLAGS:
|
|
{
|
|
// get flags
|
|
struct ifreq request;
|
|
request.ifr_flags = interface->flags;
|
|
|
|
return user_memcpy(&((struct ifreq *)argument)->ifr_flags,
|
|
&request.ifr_flags, sizeof(request.ifr_flags));
|
|
}
|
|
case SIOCSIFFLAGS:
|
|
{
|
|
// set flags
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, argument, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
// TODO: check flags!
|
|
interface->flags = request.ifr_flags;
|
|
return B_OK;
|
|
}
|
|
|
|
case SIOCGIFPARAM:
|
|
{
|
|
// get interface parameter
|
|
struct ifreq request;
|
|
strlcpy(request.ifr_parameter.base_name, interface->base_name, IF_NAMESIZE);
|
|
strlcpy(request.ifr_parameter.device, interface->device_interface->name,
|
|
IF_NAMESIZE);
|
|
request.ifr_parameter.sub_type = 0;
|
|
// TODO: for now, we ignore the sub type...
|
|
|
|
return user_memcpy(&((struct ifreq *)argument)->ifr_parameter,
|
|
&request.ifr_parameter, sizeof(request.ifr_parameter));
|
|
}
|
|
|
|
case SIOCGIFSTATS:
|
|
{
|
|
// get stats
|
|
return user_memcpy(&((struct ifreq *)argument)->ifr_stats,
|
|
&interface->device_interface->device->stats,
|
|
sizeof(struct ifreq_stats));
|
|
}
|
|
|
|
case SIOCGIFTYPE:
|
|
{
|
|
// get type
|
|
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:
|
|
{
|
|
// get MTU
|
|
struct ifreq request;
|
|
request.ifr_mtu = interface->mtu;
|
|
|
|
return user_memcpy(&((struct ifreq *)argument)->ifr_mtu,
|
|
&request.ifr_mtu, sizeof(request.ifr_mtu));
|
|
}
|
|
case SIOCSIFMTU:
|
|
{
|
|
// set MTU
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, argument, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
// check for valid bounds
|
|
if (request.ifr_mtu < 100 || (uint32)request.ifr_mtu > interface->device->mtu)
|
|
return B_BAD_VALUE;
|
|
|
|
interface->mtu = request.ifr_mtu;
|
|
return B_OK;
|
|
}
|
|
|
|
case SIOCGIFMETRIC:
|
|
{
|
|
// get metric
|
|
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:
|
|
{
|
|
// set metric
|
|
struct ifreq request;
|
|
if (user_memcpy(&request, argument, sizeof(struct ifreq)) < B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
interface->metric = request.ifr_metric;
|
|
return B_OK;
|
|
}
|
|
|
|
case SIOCADDRT:
|
|
case SIOCDELRT:
|
|
// interface related route options
|
|
return control_routes(interface, option, argument, length);
|
|
}
|
|
|
|
return protocol->device_module->control(protocol->device,
|
|
option, argument, length);
|
|
}
|
|
|
|
|
|
net_datalink_module_info gNetDatalinkModule = {
|
|
{
|
|
NET_DATALINK_MODULE_NAME,
|
|
0,
|
|
datalink_std_ops
|
|
},
|
|
|
|
datalink_control,
|
|
datalink_send_data,
|
|
datalink_is_local_address,
|
|
|
|
add_route,
|
|
remove_route,
|
|
get_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_control,
|
|
};
|