* Completed the previous commit and merger of the team/network/new_stack branch.
* Removed ppp_up and pppcontrol from the image for now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18457 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -27,7 +27,7 @@ BEOS_BIN = addattr alert basename beep cat catattr chgrp chmod chop chown clear
|
|||||||
join keymap kill less lessecho lesskey link listarea listattr listdev listimage
|
join keymap kill less lessecho lesskey link listarea listattr listdev listimage
|
||||||
listport listres listsem ln locate logger logname ls lsindex makebootable md5sum mimeset
|
listport listres listsem ln locate logger logname ls lsindex makebootable md5sum mimeset
|
||||||
mkdir mkindex modifiers mount mountvolume mv open pathchk ping play playfile playsound
|
mkdir mkindex modifiers mount mountvolume mv open pathchk ping play playfile playsound
|
||||||
playwav ppp_up pppconfig ps pwd
|
playwav ps pwd
|
||||||
query quit renice rm rmattr rmindex rmdir roster route safemode screen_blanker sed settype
|
query quit renice rm rmattr rmindex rmdir roster route safemode screen_blanker sed settype
|
||||||
setversion setvolume sh shutdown sleep sort split strace su sum sync sysinfo
|
setversion setvolume sh shutdown sleep sort split strace su sum sync sysinfo
|
||||||
tail tar tee top touch tput traceroute translate true tty uname unmount unzip unzipsfx
|
tail tar tee top touch tput traceroute translate true tty uname unmount unzip unzipsfx
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_BUFFER_UTILITIES_H
|
||||||
|
#define NET_BUFFER_UTILITIES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern net_buffer_module_info *sBufferModule;
|
||||||
|
|
||||||
|
class NetBufferModuleGetter {
|
||||||
|
public:
|
||||||
|
static net_buffer_module_info *Get() { return sBufferModule; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A class to retrieve and remove a header from a buffer
|
||||||
|
template<typename Type, typename Module = NetBufferModuleGetter > class NetBufferHeader {
|
||||||
|
public:
|
||||||
|
NetBufferHeader(net_buffer *buffer)
|
||||||
|
:
|
||||||
|
fBuffer(buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~NetBufferHeader()
|
||||||
|
{
|
||||||
|
Remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Status()
|
||||||
|
{
|
||||||
|
return fBuffer->size < sizeof(Type) ? B_BAD_VALUE : B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type &
|
||||||
|
Data()
|
||||||
|
{
|
||||||
|
Type *data;
|
||||||
|
if (Module::Get()->direct_access(fBuffer, 0, sizeof(Type),
|
||||||
|
(void **)&data) == B_OK)
|
||||||
|
return *data;
|
||||||
|
|
||||||
|
Module::Get()->read(fBuffer, 0, &fDataBuffer, sizeof(Type));
|
||||||
|
return fDataBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Remove()
|
||||||
|
{
|
||||||
|
if (fBuffer != NULL) {
|
||||||
|
Module::Get()->remove_header(fBuffer, sizeof(Type));
|
||||||
|
fBuffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Remove(size_t bytes)
|
||||||
|
{
|
||||||
|
if (fBuffer != NULL) {
|
||||||
|
Module::Get()->remove_header(fBuffer, bytes);
|
||||||
|
fBuffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Detach()
|
||||||
|
{
|
||||||
|
fBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
net_buffer *fBuffer;
|
||||||
|
Type fDataBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A class to add a header to a buffer
|
||||||
|
template<typename Type, typename Module = NetBufferModuleGetter > class NetBufferPrepend {
|
||||||
|
public:
|
||||||
|
NetBufferPrepend(net_buffer *buffer)
|
||||||
|
:
|
||||||
|
fBuffer(buffer),
|
||||||
|
fData(NULL)
|
||||||
|
{
|
||||||
|
fStatus = Module::Get()->prepend_size(buffer, sizeof(Type), (void **)&fData);
|
||||||
|
}
|
||||||
|
|
||||||
|
~NetBufferPrepend()
|
||||||
|
{
|
||||||
|
if (fBuffer != NULL)
|
||||||
|
Detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Status()
|
||||||
|
{
|
||||||
|
return fStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type &
|
||||||
|
Data()
|
||||||
|
{
|
||||||
|
if (fData != NULL)
|
||||||
|
return *fData;
|
||||||
|
|
||||||
|
return fDataBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: I'm not sure it's a good idea to have Detach() routines
|
||||||
|
// in NetBufferHeader and here with such a different outcome...
|
||||||
|
void
|
||||||
|
Detach()
|
||||||
|
{
|
||||||
|
if (fData == NULL)
|
||||||
|
Module::Get()->write(fBuffer, 0, &fDataBuffer, sizeof(Type));
|
||||||
|
fBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
net_buffer *fBuffer;
|
||||||
|
status_t fStatus;
|
||||||
|
Type *fData;
|
||||||
|
Type fDataBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_BUFFER_UTILITIES_H
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_UTILITIES_H
|
||||||
|
#define NET_UTILITIES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <net_datalink.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
class Checksum {
|
||||||
|
public:
|
||||||
|
struct BufferHelper {
|
||||||
|
BufferHelper(net_buffer *_buffer, net_buffer_module_info *_bufferModule)
|
||||||
|
: buffer(_buffer),
|
||||||
|
bufferModule(_bufferModule)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
net_buffer *buffer;
|
||||||
|
net_buffer_module_info *bufferModule;
|
||||||
|
};
|
||||||
|
|
||||||
|
Checksum();
|
||||||
|
|
||||||
|
Checksum& operator<<(uint8 val);
|
||||||
|
Checksum& operator<<(uint16 val);
|
||||||
|
Checksum& operator<<(uint32 val);
|
||||||
|
Checksum& operator<<(const BufferHelper &bufferHelper);
|
||||||
|
|
||||||
|
operator uint16();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 fSum;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Checksum::Checksum()
|
||||||
|
: fSum(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Checksum& Checksum::operator<<(uint8 _val) {
|
||||||
|
#if B_HOST_IS_LENDIAN
|
||||||
|
fSum += _val;
|
||||||
|
#else
|
||||||
|
uint16 val = _val;
|
||||||
|
fSum += val << 8;
|
||||||
|
#endif
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Checksum& Checksum::operator<<(uint16 val) {
|
||||||
|
fSum += val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Checksum& Checksum::operator<<(uint32 val) {
|
||||||
|
fSum += (val & 0xFFFF) + (val >> 16);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Checksum& Checksum::operator<<(const BufferHelper &bufferHelper) {
|
||||||
|
net_buffer *buffer = bufferHelper.buffer;
|
||||||
|
fSum += bufferHelper.bufferModule->checksum(buffer, 0, buffer->size, false);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Checksum::operator uint16() {
|
||||||
|
while (fSum >> 16) {
|
||||||
|
fSum = (fSum & 0xffff) + (fSum >> 16);
|
||||||
|
}
|
||||||
|
uint16 result = (uint16)fSum;
|
||||||
|
result ^= 0xFFFF;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// helper class that prints an address (and optionally a port) into a buffer that
|
||||||
|
// is automatically freed at end of scope:
|
||||||
|
class AddressString {
|
||||||
|
public:
|
||||||
|
inline AddressString(net_domain *domain, const sockaddr *address,
|
||||||
|
bool printPort = false)
|
||||||
|
: fBuffer(NULL)
|
||||||
|
{
|
||||||
|
domain->address_module->print_address(address, &fBuffer, printPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AddressString(net_domain *domain, const sockaddr &address,
|
||||||
|
bool printPort = false)
|
||||||
|
: fBuffer(NULL)
|
||||||
|
{
|
||||||
|
domain->address_module->print_address(&address, &fBuffer, printPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~AddressString()
|
||||||
|
{
|
||||||
|
free(fBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char *Data()
|
||||||
|
{
|
||||||
|
return fBuffer;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
char *fBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_UTILITIES_H
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef ARP_CONTROL_H
|
||||||
|
#define ARP_CONTROL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <ethernet.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
|
||||||
|
// ARP flags
|
||||||
|
#define ARP_FLAG_LOCAL 0x01
|
||||||
|
#define ARP_FLAG_REJECT 0x02
|
||||||
|
#define ARP_FLAG_PERMANENT 0x04
|
||||||
|
#define ARP_FLAG_PUBLISH 0x08
|
||||||
|
|
||||||
|
|
||||||
|
// generic syscall interface
|
||||||
|
#define ARP_SYSCALLS "network/arp"
|
||||||
|
|
||||||
|
#define ARP_SET_ENTRY 1
|
||||||
|
#define ARP_GET_ENTRY 2
|
||||||
|
#define ARP_GET_ENTRIES 3
|
||||||
|
#define ARP_DELETE_ENTRY 4
|
||||||
|
#define ARP_FLUSH_ENTRIES 5
|
||||||
|
#define ARP_IGNORE_REPLIES 6
|
||||||
|
|
||||||
|
struct arp_control {
|
||||||
|
in_addr_t address;
|
||||||
|
uint8 ethernet_address[ETHER_ADDRESS_LENGTH];
|
||||||
|
uint32 flags;
|
||||||
|
uint32 cookie;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ARP_CONTROL_H
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* ether_driver.h
|
||||||
|
*
|
||||||
|
* Ethernet driver: handles NE2000 and 3C503 cards
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||||
|
This file may be used under the terms of the Be Sample Code License.
|
||||||
|
*/
|
||||||
|
#ifndef _ETHER_DRIVER_H
|
||||||
|
#define _ETHER_DRIVER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <Drivers.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ioctls: belongs in a public header file
|
||||||
|
* somewhere, so that the net_server and other ethernet drivers can use.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
ETHER_GETADDR = B_DEVICE_OP_CODES_END, /* get ethernet address */
|
||||||
|
ETHER_INIT, /* set irq and port */
|
||||||
|
ETHER_NONBLOCK, /* set/unset nonblocking mode */
|
||||||
|
ETHER_ADDMULTI, /* add multicast addr */
|
||||||
|
ETHER_REMMULTI, /* rem multicast addr */
|
||||||
|
ETHER_SETPROMISC, /* set promiscuous */
|
||||||
|
ETHER_GETFRAMESIZE, /* get frame size */
|
||||||
|
ETHER_ADDTIMESTAMP, /* (try to) add timestamps to packets (BONE ext) */
|
||||||
|
ETHER_HASIOVECS, /* does the driver implement writev ? (BONE ext) (bool *) */
|
||||||
|
ETHER_GETIFTYPE, /* get the IFT_ type of the interface (int *) */
|
||||||
|
ETHER_GETLINKSTATE /* get line speed, quality, duplex mode, etc. */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 48-bit ethernet address, passed back from ETHER_GETADDR
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned char ebyte[6];
|
||||||
|
} ether_address_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* info passed to ETHER_INIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct ether_init_params {
|
||||||
|
short port;
|
||||||
|
short irq;
|
||||||
|
unsigned long mem;
|
||||||
|
} ether_init_params_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* info returned from ETHER_GETLINKSTATE
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct ether_link_state {
|
||||||
|
float link_speed; /* In Mbits per second */
|
||||||
|
float link_quality; /* Set to zero if not connected */
|
||||||
|
char duplex_mode; /* Set to 1 for full duplex, 0 for half */
|
||||||
|
} ether_link_state_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _ETHER_DRIVER_H */
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef ETHERNET_H
|
||||||
|
#define ETHERNET_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define ETHER_ADDRESS_LENGTH 6
|
||||||
|
#define ETHER_CRC_LENGTH 4
|
||||||
|
#define ETHER_HEADER_LENGTH 14
|
||||||
|
|
||||||
|
#define ETHER_MIN_FRAME_SIZE 64
|
||||||
|
#define ETHER_MAX_FRAME_SIZE 1514
|
||||||
|
|
||||||
|
struct ether_header {
|
||||||
|
uint8 destination[ETHER_ADDRESS_LENGTH];
|
||||||
|
uint8 source[ETHER_ADDRESS_LENGTH];
|
||||||
|
uint16 type;
|
||||||
|
} _PACKED;
|
||||||
|
|
||||||
|
#define ETHER_FRAME_TYPE 0x00010000
|
||||||
|
|
||||||
|
// ethernet types
|
||||||
|
#define ETHER_TYPE_IP 0x0800
|
||||||
|
#define ETHER_TYPE_ARP 0x0806
|
||||||
|
#define ETHER_TYPE_IPX 0x8137
|
||||||
|
#define ETHER_TYPE_IPV6 0x86dd
|
||||||
|
#define ETHER_TYPE_PPPOE_DISCOVERY 0x8863 // PPPoE discovery stage
|
||||||
|
#define ETHER_TYPE_PPPOE 0x8864 // PPPoE session stage
|
||||||
|
|
||||||
|
#endif // ETHERNET_H
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_BUFFER_H
|
||||||
|
#define NET_BUFFER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <module.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define NET_BUFFER_MODULE_NAME "network/stack/buffer/v1"
|
||||||
|
|
||||||
|
typedef struct net_buffer {
|
||||||
|
struct list_link link;
|
||||||
|
|
||||||
|
// TODO: we should think about moving the address fields into the buffer data itself
|
||||||
|
// via associated data or something like this. Or this structure as a whole, too...
|
||||||
|
struct sockaddr_storage source;
|
||||||
|
struct sockaddr_storage destination;
|
||||||
|
struct net_interface *interface;
|
||||||
|
uint32 flags;
|
||||||
|
uint32 size;
|
||||||
|
uint8 protocol;
|
||||||
|
} net_buffer;
|
||||||
|
|
||||||
|
struct net_buffer_module_info {
|
||||||
|
module_info info;
|
||||||
|
|
||||||
|
net_buffer * (*create)(size_t headerSpace);
|
||||||
|
void (*free)(net_buffer *buffer);
|
||||||
|
|
||||||
|
net_buffer * (*duplicate)(net_buffer *from);
|
||||||
|
net_buffer * (*clone)(net_buffer *from, bool shareFreeSpace);
|
||||||
|
net_buffer * (*split)(net_buffer *from, uint32 offset);
|
||||||
|
status_t (*merge)(net_buffer *buffer, net_buffer *with, bool after);
|
||||||
|
|
||||||
|
status_t (*prepend_size)(net_buffer *buffer, size_t size,
|
||||||
|
void **_contiguousBuffer);
|
||||||
|
status_t (*prepend)(net_buffer *buffer, const void *data,
|
||||||
|
size_t bytes);
|
||||||
|
status_t (*append_size)(net_buffer *buffer, size_t size,
|
||||||
|
void **_contiguousBuffer);
|
||||||
|
status_t (*append)(net_buffer *buffer, const void *data,
|
||||||
|
size_t bytes);
|
||||||
|
status_t (*insert)(net_buffer *buffer, uint32 offset,
|
||||||
|
const void *data, size_t bytes, uint32 flags);
|
||||||
|
status_t (*remove)(net_buffer *buffer, uint32 offset,
|
||||||
|
size_t bytes);
|
||||||
|
status_t (*remove_header)(net_buffer *buffer, size_t bytes);
|
||||||
|
status_t (*remove_trailer)(net_buffer *buffer, size_t bytes);
|
||||||
|
status_t (*trim)(net_buffer *buffer, size_t newSize);
|
||||||
|
|
||||||
|
status_t (*associate_data)(net_buffer *buffer, void *data);
|
||||||
|
|
||||||
|
status_t (*direct_access)(net_buffer *buffer, uint32 offset,
|
||||||
|
size_t bytes, void **_data);
|
||||||
|
status_t (*read)(net_buffer *buffer, uint32 offset, void *data,
|
||||||
|
size_t bytes);
|
||||||
|
status_t (*write)(net_buffer *buffer, uint32 offset,
|
||||||
|
const void *data, size_t bytes);
|
||||||
|
|
||||||
|
int32 (*checksum)(net_buffer *buffer, uint32 offset, size_t bytes,
|
||||||
|
bool finalize);
|
||||||
|
status_t (*get_memory_map)(net_buffer *buffer,
|
||||||
|
struct iovec *iovecs, uint32 vecCount);
|
||||||
|
uint32 (*get_iovecs)(net_buffer *buffer,
|
||||||
|
struct iovec *iovecs, uint32 vecCount);
|
||||||
|
uint32 (*count_iovecs)(net_buffer *buffer);
|
||||||
|
|
||||||
|
void (*dump)(net_buffer *buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_BUFFER_H
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_DATALINK_H
|
||||||
|
#define NET_DATALINK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <net_routing_info.h>
|
||||||
|
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define NET_DATALINK_MODULE_NAME "network/stack/datalink/v1"
|
||||||
|
|
||||||
|
typedef struct net_datalink_protocol net_datalink_protocol;
|
||||||
|
|
||||||
|
typedef struct net_domain {
|
||||||
|
const char *name;
|
||||||
|
int family;
|
||||||
|
struct list interfaces;
|
||||||
|
|
||||||
|
struct net_protocol_module_info *module;
|
||||||
|
struct net_address_module_info *address_module;
|
||||||
|
} net_domain;
|
||||||
|
|
||||||
|
struct net_interface {
|
||||||
|
struct list_link link;
|
||||||
|
struct net_domain *domain;
|
||||||
|
struct net_device *device;
|
||||||
|
struct net_datalink_protocol *first_protocol;
|
||||||
|
struct net_datalink_protocol_module_info *first_info;
|
||||||
|
|
||||||
|
char name[IF_NAMESIZE];
|
||||||
|
struct sockaddr *address;
|
||||||
|
struct sockaddr *destination;
|
||||||
|
struct sockaddr *mask;
|
||||||
|
uint32 index;
|
||||||
|
uint32 flags;
|
||||||
|
uint8 type;
|
||||||
|
uint32 mtu;
|
||||||
|
uint32 metric;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_route {
|
||||||
|
struct sockaddr *destination;
|
||||||
|
struct sockaddr *mask;
|
||||||
|
struct sockaddr *gateway;
|
||||||
|
uint32 flags;
|
||||||
|
uint32 mtu;
|
||||||
|
struct net_interface *interface;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_route_info {
|
||||||
|
struct list_link link;
|
||||||
|
struct net_route *route;
|
||||||
|
struct sockaddr address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_datalink_module_info {
|
||||||
|
module_info info;
|
||||||
|
|
||||||
|
status_t (*control)(struct net_domain *domain, int32 option, void *value,
|
||||||
|
size_t *_length);
|
||||||
|
status_t (*send_data)(struct net_route *route, struct net_buffer *buffer);
|
||||||
|
|
||||||
|
bool (*is_local_address)(struct net_domain *domain,
|
||||||
|
const struct sockaddr *address,
|
||||||
|
net_interface **_interface = NULL,
|
||||||
|
uint32 *_matchedType = NULL);
|
||||||
|
|
||||||
|
// routes
|
||||||
|
status_t (*add_route)(struct net_domain *domain,
|
||||||
|
const struct net_route *route);
|
||||||
|
status_t (*remove_route)(struct net_domain *domain,
|
||||||
|
const struct net_route *route);
|
||||||
|
struct net_route *(*get_route)(struct net_domain *domain,
|
||||||
|
const struct sockaddr *address);
|
||||||
|
void (*put_route)(struct net_domain *domain, struct net_route *route);
|
||||||
|
|
||||||
|
status_t (*register_route_info)(struct net_domain *domain,
|
||||||
|
struct net_route_info *info);
|
||||||
|
status_t (*unregister_route_info)(struct net_domain *domain,
|
||||||
|
struct net_route_info *info);
|
||||||
|
status_t (*update_route_info)(struct net_domain *domain,
|
||||||
|
struct net_route_info *info);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_address_module_info {
|
||||||
|
module_info info;
|
||||||
|
|
||||||
|
status_t (*copy_address)(const sockaddr *from, sockaddr **to,
|
||||||
|
bool replaceWithZeros = false, sockaddr *mask = NULL);
|
||||||
|
|
||||||
|
status_t (*mask_address)(const sockaddr *address, const sockaddr *mask,
|
||||||
|
sockaddr *result);
|
||||||
|
|
||||||
|
bool (*equal_addresses)(const sockaddr *a, const sockaddr *b);
|
||||||
|
bool (*equal_ports)(const sockaddr *a, const sockaddr *b);
|
||||||
|
bool (*equal_addresses_and_ports)(const sockaddr *a, const sockaddr *b);
|
||||||
|
bool (*equal_masked_addresses)(const sockaddr *a, const sockaddr *b,
|
||||||
|
const sockaddr *mask);
|
||||||
|
bool (*is_empty_address)(const sockaddr *address, bool checkPort = true);
|
||||||
|
|
||||||
|
int32 (*first_mask_bit)(sockaddr *mask);
|
||||||
|
|
||||||
|
bool (*check_mask)(sockaddr *address);
|
||||||
|
|
||||||
|
status_t (*print_address)(const sockaddr *address, char **buffer,
|
||||||
|
bool printPort);
|
||||||
|
|
||||||
|
uint16 (*get_port)(const sockaddr *address);
|
||||||
|
status_t (*set_port)(sockaddr *address, uint16 port);
|
||||||
|
|
||||||
|
status_t (*set_to)(sockaddr *address, const sockaddr *from);
|
||||||
|
status_t (*set_to_empty_address)(sockaddr *address);
|
||||||
|
|
||||||
|
uint32 (*hash_address_pair)(const sockaddr *ourAddress,
|
||||||
|
const sockaddr *peerAddress);
|
||||||
|
|
||||||
|
status_t (*checksum_address)(struct Checksum *checksum,
|
||||||
|
const sockaddr *address);
|
||||||
|
|
||||||
|
bool (*matches_broadcast_address)(const sockaddr *address,
|
||||||
|
const sockaddr *mask, const sockaddr *broadcastAddr);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_DATALINK_H
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_DATALINK_PROTOCOL_H
|
||||||
|
#define NET_DATALINK_PROTOCOL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct net_datalink_protocol {
|
||||||
|
struct net_datalink_protocol *next;
|
||||||
|
struct net_datalink_protocol_module_info *module;
|
||||||
|
struct net_interface *interface;
|
||||||
|
} net_datalink_protocol;
|
||||||
|
|
||||||
|
struct net_datalink_protocol_module_info {
|
||||||
|
module_info info;
|
||||||
|
|
||||||
|
status_t (*init_protocol)(struct net_interface *interface,
|
||||||
|
net_datalink_protocol **_protocol);
|
||||||
|
status_t (*uninit_protocol)(net_datalink_protocol *self);
|
||||||
|
|
||||||
|
status_t (*send_data)(net_datalink_protocol *self,
|
||||||
|
net_buffer *buffer);
|
||||||
|
|
||||||
|
status_t (*interface_up)(net_datalink_protocol *self);
|
||||||
|
void (*interface_down)(net_datalink_protocol *self);
|
||||||
|
|
||||||
|
status_t (*control)(net_datalink_protocol *self,
|
||||||
|
int32 op, void *argument, size_t length);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_DATALINK_PROTOCOL_H
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_DEVICE_H
|
||||||
|
#define NET_DEVICE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <module.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct net_hardware_address {
|
||||||
|
uint8 data[64];
|
||||||
|
uint8 length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_device {
|
||||||
|
struct net_device_module_info *module;
|
||||||
|
|
||||||
|
char name[IF_NAMESIZE];
|
||||||
|
uint32 index;
|
||||||
|
uint32 flags; // IFF_LOOPBACK, ...
|
||||||
|
uint32 type; // IFT_ETHER, ...
|
||||||
|
size_t mtu;
|
||||||
|
uint32 media;
|
||||||
|
size_t header_length;
|
||||||
|
|
||||||
|
struct net_hardware_address address;
|
||||||
|
|
||||||
|
ifreq_stats stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_device_module_info {
|
||||||
|
struct module_info info;
|
||||||
|
|
||||||
|
status_t (*init_device)(const char *name, struct net_device **_device);
|
||||||
|
status_t (*uninit_device)(struct net_device *device);
|
||||||
|
|
||||||
|
status_t (*up)(struct net_device *device);
|
||||||
|
void (*down)(struct net_device *device);
|
||||||
|
|
||||||
|
status_t (*control)(struct net_device *device, int32 op,
|
||||||
|
void *argument, size_t length);
|
||||||
|
|
||||||
|
status_t (*send_data)(struct net_device *device, struct net_buffer *buffer);
|
||||||
|
status_t (*receive_data)(struct net_device *device, struct net_buffer **_buffer);
|
||||||
|
|
||||||
|
status_t (*set_mtu)(struct net_device *device, size_t mtu);
|
||||||
|
status_t (*set_promiscuous)(struct net_device *device, bool promiscuous);
|
||||||
|
status_t (*set_media)(struct net_device *device, uint32 media);
|
||||||
|
|
||||||
|
status_t (*get_multicast_addrs)(struct net_device *device,
|
||||||
|
net_hardware_address **addressArray, uint32 count);
|
||||||
|
status_t (*set_multicast_addrs)(struct net_device *device,
|
||||||
|
const net_hardware_address **addressArray, uint32 count);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_DEVICE_H
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_PROTOCOL_H
|
||||||
|
#define NET_PROTOCOL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <net_socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
// level flags to pass to control()
|
||||||
|
#define LEVEL_SET_OPTION 0x10000000
|
||||||
|
#define LEVEL_GET_OPTION 0x20000000
|
||||||
|
#define LEVEL_DRIVER_IOCTL 0x0f000000
|
||||||
|
#define LEVEL_MASK 0x0fffffff
|
||||||
|
|
||||||
|
typedef struct net_protocol {
|
||||||
|
struct net_protocol *next;
|
||||||
|
struct net_protocol_module_info *module;
|
||||||
|
net_socket *socket;
|
||||||
|
} net_protocol;
|
||||||
|
|
||||||
|
struct net_protocol_module_info {
|
||||||
|
module_info info;
|
||||||
|
|
||||||
|
net_protocol *(*init_protocol)(net_socket *socket);
|
||||||
|
status_t (*uninit_protocol)(net_protocol *self);
|
||||||
|
|
||||||
|
status_t (*open)(net_protocol *self);
|
||||||
|
status_t (*close)(net_protocol *self);
|
||||||
|
status_t (*free)(net_protocol *self);
|
||||||
|
|
||||||
|
status_t (*connect)(net_protocol *self, const struct sockaddr *address);
|
||||||
|
status_t (*accept)(net_protocol *self, struct net_socket **_acceptedSocket);
|
||||||
|
status_t (*control)(net_protocol *self, int level, int option, void *value,
|
||||||
|
size_t *_length);
|
||||||
|
|
||||||
|
status_t (*bind)(net_protocol *self, struct sockaddr *address);
|
||||||
|
status_t (*unbind)(net_protocol *self, struct sockaddr *address);
|
||||||
|
status_t (*listen)(net_protocol *self, int count);
|
||||||
|
status_t (*shutdown)(net_protocol *self, int direction);
|
||||||
|
|
||||||
|
status_t (*send_data)(net_protocol *self, net_buffer *buffer);
|
||||||
|
status_t (*send_routed_data)(net_protocol *self,
|
||||||
|
struct net_route *route, net_buffer *buffer);
|
||||||
|
ssize_t (*send_avail)(net_protocol *self);
|
||||||
|
|
||||||
|
status_t (*read_data)(net_protocol *self, size_t numBytes, uint32 flags,
|
||||||
|
net_buffer **_buffer);
|
||||||
|
ssize_t (*read_avail)(net_protocol *self);
|
||||||
|
|
||||||
|
struct net_domain *(*get_domain)(net_protocol *self);
|
||||||
|
size_t (*get_mtu)(net_protocol *self, const struct sockaddr *address);
|
||||||
|
|
||||||
|
status_t (*receive_data)(net_buffer *data);
|
||||||
|
status_t (*error)(uint32 code, net_buffer *data);
|
||||||
|
|
||||||
|
status_t (*error_reply)(net_protocol *self, net_buffer *causedError,
|
||||||
|
uint32 code, void *errorData);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_PROTOCOL_H
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_ROUTING_INFO_H
|
||||||
|
#define NET_ROUTING_INFO_H
|
||||||
|
|
||||||
|
|
||||||
|
struct net_routing_info {
|
||||||
|
void *root;
|
||||||
|
void *default_route;
|
||||||
|
|
||||||
|
int addr_start;
|
||||||
|
int addr_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_ROUTING_INFO_H
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_SOCKET_H
|
||||||
|
#define NET_SOCKET_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define NET_SOCKET_MODULE_NAME "network/stack/socket/v1"
|
||||||
|
|
||||||
|
typedef struct net_socket {
|
||||||
|
struct net_protocol *first_protocol;
|
||||||
|
struct net_protocol_module_info *first_info;
|
||||||
|
|
||||||
|
int family;
|
||||||
|
int type;
|
||||||
|
int protocol;
|
||||||
|
|
||||||
|
struct sockaddr_storage address;
|
||||||
|
struct sockaddr_storage peer;
|
||||||
|
|
||||||
|
int options;
|
||||||
|
int linger;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32 buffer_size;
|
||||||
|
uint32 low_water_mark;
|
||||||
|
bigtime_t timeout;
|
||||||
|
} send, receive;
|
||||||
|
} net_socket;
|
||||||
|
|
||||||
|
struct net_socket_module_info {
|
||||||
|
struct module_info info;
|
||||||
|
|
||||||
|
status_t (*socket)(int family, int type, int protocol, net_socket **_socket);
|
||||||
|
status_t (*close)(net_socket *socket);
|
||||||
|
status_t (*free)(net_socket *socket);
|
||||||
|
|
||||||
|
status_t (*readv)(net_socket *socket, const iovec *vecs, size_t vecCount,
|
||||||
|
size_t *_length);
|
||||||
|
status_t (*writev)(net_socket *socket, const iovec *vecs, size_t vecCount,
|
||||||
|
size_t *_length);
|
||||||
|
status_t (*control)(net_socket *socket, int32 op, void *data, size_t length);
|
||||||
|
|
||||||
|
ssize_t (*read_avail)(net_socket *socket);
|
||||||
|
ssize_t (*send_avail)(net_socket *socket);
|
||||||
|
|
||||||
|
status_t (*send_data)(net_socket *socket, net_buffer *buffer);
|
||||||
|
status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags,
|
||||||
|
net_buffer **_buffer);
|
||||||
|
|
||||||
|
// standard socket API
|
||||||
|
int (*accept)(net_socket *socket, struct sockaddr *address,
|
||||||
|
socklen_t *_addressLength, net_socket **_acceptedSocket);
|
||||||
|
int (*bind)(net_socket *socket, const struct sockaddr *address,
|
||||||
|
socklen_t addressLength);
|
||||||
|
int (*connect)(net_socket *socket, const struct sockaddr *address,
|
||||||
|
socklen_t addressLength);
|
||||||
|
int (*getpeername)(net_socket *socket, struct sockaddr *address,
|
||||||
|
socklen_t *_addressLength);
|
||||||
|
int (*getsockname)(net_socket *socket, struct sockaddr *address,
|
||||||
|
socklen_t *_addressLength);
|
||||||
|
int (*getsockopt)(net_socket *socket, int level, int option,
|
||||||
|
void *optionValue, int *_optionLength);
|
||||||
|
int (*listen)(net_socket *socket, int backlog);
|
||||||
|
ssize_t (*recv)(net_socket *socket, void *data, size_t length, int flags);
|
||||||
|
ssize_t (*recvfrom)(net_socket *socket, void *data, size_t length, int flags,
|
||||||
|
struct sockaddr *address, socklen_t *_addressLength);
|
||||||
|
ssize_t (*send)(net_socket *socket, const void *data, size_t length, int flags);
|
||||||
|
ssize_t (*sendto)(net_socket *socket, const void *data, size_t length,
|
||||||
|
int flags, const struct sockaddr *address, socklen_t addressLength);
|
||||||
|
int (*setsockopt)(net_socket *socket, int level, int option,
|
||||||
|
const void *optionValue, int optionLength);
|
||||||
|
int (*shutdown)(net_socket *socket, int direction);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_SOCKET_H
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_STACK_H
|
||||||
|
#define NET_STACK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <module.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define NET_STACK_MODULE_NAME "network/stack/v1"
|
||||||
|
|
||||||
|
struct net_fifo {
|
||||||
|
benaphore lock;
|
||||||
|
sem_id notify;
|
||||||
|
int32 waiting;
|
||||||
|
|
||||||
|
size_t max_bytes;
|
||||||
|
size_t current_bytes;
|
||||||
|
|
||||||
|
struct list buffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*net_timer_func)(struct net_timer *timer, void *data);
|
||||||
|
|
||||||
|
struct net_timer {
|
||||||
|
struct list_link link;
|
||||||
|
net_timer_func hook;
|
||||||
|
void *data;
|
||||||
|
bigtime_t due;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int32 (*net_deframe_func)(struct net_device *device, struct net_buffer *buffer);
|
||||||
|
typedef status_t (*net_receive_func)(void *cookie, struct net_buffer *buffer);
|
||||||
|
|
||||||
|
struct net_stack_module_info {
|
||||||
|
module_info info;
|
||||||
|
|
||||||
|
status_t (*register_domain)(int family, const char *name,
|
||||||
|
struct net_protocol_module_info *module,
|
||||||
|
struct net_address_module_info *addressModule,
|
||||||
|
struct net_domain **_domain);
|
||||||
|
status_t (*unregister_domain)(struct net_domain *domain);
|
||||||
|
struct net_domain *(*get_domain)(int family);
|
||||||
|
|
||||||
|
status_t (*register_domain_protocols)(int family, int type, int protocol, ...);
|
||||||
|
status_t (*register_domain_datalink_protocols)(int family, int type, ...);
|
||||||
|
status_t (*register_domain_receiving_protocol)(int family, int type,
|
||||||
|
const char *moduleName);
|
||||||
|
|
||||||
|
status_t (*get_domain_receiving_protocol)(struct net_domain *domain, uint32 type,
|
||||||
|
struct net_protocol_module_info **_module);
|
||||||
|
status_t (*put_domain_receiving_protocol)(struct net_domain *domain, uint32 type);
|
||||||
|
|
||||||
|
// devices
|
||||||
|
status_t (*register_device_deframer)(struct net_device *device,
|
||||||
|
net_deframe_func deframeFunc);
|
||||||
|
status_t (*unregister_device_deframer)(struct net_device *device);
|
||||||
|
|
||||||
|
status_t (*register_domain_device_handler)(struct net_device *device,
|
||||||
|
int32 type, struct net_domain *domain);
|
||||||
|
status_t (*register_device_handler)(struct net_device *device, int32 type,
|
||||||
|
net_receive_func receiveFunc, void *cookie);
|
||||||
|
status_t (*unregister_device_handler)(struct net_device *device, int32 type);
|
||||||
|
|
||||||
|
status_t (*register_device_monitor)(struct net_device *device,
|
||||||
|
net_receive_func receiveFunc, void *cookie);
|
||||||
|
status_t (*unregister_device_monitor)(struct net_device *device,
|
||||||
|
net_receive_func receiveFunc, void *cookie);
|
||||||
|
|
||||||
|
status_t (*device_removed)(struct net_device *device);
|
||||||
|
|
||||||
|
// Utility Functions
|
||||||
|
|
||||||
|
// checksum
|
||||||
|
uint16 (*checksum)(uint8 *buffer, size_t length);
|
||||||
|
|
||||||
|
// fifo
|
||||||
|
status_t (*init_fifo)(struct net_fifo *fifo, const char *name, size_t maxBytes);
|
||||||
|
void (*uninit_fifo)(struct net_fifo *fifo);
|
||||||
|
status_t (*fifo_enqueue_buffer)(struct net_fifo *fifo, struct net_buffer *buffer);
|
||||||
|
ssize_t (*fifo_dequeue_buffer)(struct net_fifo *fifo, uint32 flags,
|
||||||
|
bigtime_t timeout, struct net_buffer **_buffer);
|
||||||
|
status_t (*clear_fifo)(struct net_fifo *fifo);
|
||||||
|
|
||||||
|
// timer
|
||||||
|
void (*init_timer)(struct net_timer *timer, net_timer_func hook, void *data);
|
||||||
|
void (*set_timer)(struct net_timer *timer, bigtime_t delay);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NET_STACK_H
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* This file may be used under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef NET_STACK_DRIVER_H
|
||||||
|
#define NET_STACK_DRIVER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
struct sockaddr;
|
||||||
|
|
||||||
|
#define NET_STACK_DRIVER_DEV "net/stack"
|
||||||
|
#define NET_STACK_DRIVER_PATH "/dev/" NET_STACK_DRIVER_DEV
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NET_STACK_IOCTL_BASE = 8800,
|
||||||
|
NET_STACK_IOCTL_END = 8999,
|
||||||
|
|
||||||
|
// ops not acting on an existing socket
|
||||||
|
NET_STACK_SOCKET = NET_STACK_IOCTL_BASE, // socket_args *
|
||||||
|
NET_STACK_GET_COOKIE, // void **
|
||||||
|
NET_STACK_CONTROL_NET_MODULE, // control_net_module_args *
|
||||||
|
NET_STACK_SYSCTL, // sysctl_args *
|
||||||
|
|
||||||
|
// ops acting on an existing socket
|
||||||
|
NET_STACK_BIND, // sockaddr_args *
|
||||||
|
NET_STACK_RECVFROM, // struct msghdr *
|
||||||
|
NET_STACK_RECV, // transfer_args *
|
||||||
|
NET_STACK_SENDTO, // struct msghdr *
|
||||||
|
NET_STACK_SEND, // transfer_args *
|
||||||
|
NET_STACK_LISTEN, // int_args * (value = backlog)
|
||||||
|
NET_STACK_ACCEPT, // sockaddr_args *
|
||||||
|
NET_STACK_CONNECT, // sockaddr_args *
|
||||||
|
NET_STACK_SHUTDOWN, // int_args * (value = how)
|
||||||
|
NET_STACK_GETSOCKOPT, // sockopt_args *
|
||||||
|
NET_STACK_SETSOCKOPT, // sockopt_args *
|
||||||
|
NET_STACK_GETSOCKNAME, // sockaddr_args *
|
||||||
|
NET_STACK_GETPEERNAME, // sockaddr_args *
|
||||||
|
NET_STACK_SOCKETPAIR, // socketpair_args *
|
||||||
|
|
||||||
|
// TODO: remove R5 select() emulation
|
||||||
|
NET_STACK_SELECT, // select_args *
|
||||||
|
NET_STACK_DESELECT, // select_args *
|
||||||
|
|
||||||
|
NET_STACK_NOTIFY_SOCKET_EVENT, // notify_socket_event_args * (userland stack only)
|
||||||
|
|
||||||
|
NET_STACK_IOCTL_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sockaddr_args { // used by NET_STACK_CONNECT/_BIND/_GETSOCKNAME/_GETPEERNAME
|
||||||
|
struct sockaddr *address;
|
||||||
|
socklen_t address_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sockopt_args { // used by NET_STACK_SETSOCKOPT/_GETSOCKOPT
|
||||||
|
int level;
|
||||||
|
int option;
|
||||||
|
void *value;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct transfer_args { // used by NET_STACK_SEND/_RECV
|
||||||
|
void *data;
|
||||||
|
size_t data_length;
|
||||||
|
int flags;
|
||||||
|
struct sockaddr *address; // only used for recvfrom() and sendto()
|
||||||
|
socklen_t address_length; // ""
|
||||||
|
};
|
||||||
|
|
||||||
|
struct socket_args { // used by NET_STACK_SOCKET
|
||||||
|
int family;
|
||||||
|
int type;
|
||||||
|
int protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct socketpair_args { // used by NET_STACK_SOCKETPAIR
|
||||||
|
void *cookie;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct accept_args { // used by NET_STACK_ACCEPT
|
||||||
|
void *cookie;
|
||||||
|
struct sockaddr *address;
|
||||||
|
socklen_t address_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysctl_args { // used by NET_STACK_SYSCTL
|
||||||
|
int *name;
|
||||||
|
uint namelen;
|
||||||
|
void *oldp;
|
||||||
|
size_t *oldlenp;
|
||||||
|
void *newp;
|
||||||
|
size_t newlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct control_net_module_args { // used by NET_STACK_CONTROL_NET_MODULE
|
||||||
|
const char *name;
|
||||||
|
uint32 op;
|
||||||
|
void *data;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Userland stack driver on_socket_event() callback mecanism implementation:
|
||||||
|
the driver start a kernel thread waiting on a port for
|
||||||
|
a NET_STACK_SOCKET_EVENT_MSG message, which come with a socket_event_data block.
|
||||||
|
|
||||||
|
The on_socket_event() mechanism stay in driver (kernelland) because it's
|
||||||
|
only there we can call kernel's notify_select_event() on BONE systems.
|
||||||
|
For non-BONE systems, we use our own r5_notify_select_event()
|
||||||
|
implementation, that could be moved into the userland net_server code,
|
||||||
|
but it'll have split (again!) the /dev/net/stack driver code...
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct notify_socket_event_args { // used by NET_STACK_NOTIFY_SOCKET_EVENT
|
||||||
|
port_id notify_port; // port waiting for notification, -1 = stop notify
|
||||||
|
void *cookie; // this cookie value will be pass back in the socket_event_args
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NET_STACK_SOCKET_EVENT_NOTIFICATION 'sevn'
|
||||||
|
|
||||||
|
struct socket_event_data {
|
||||||
|
uint32 event; // B_SELECT_READ, B_SELECT_WRITE or B_SELECT_ERROR
|
||||||
|
void *cookie; // The cookie as set in notify_socket_event_args for this socket
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
R5.0.3 and before select() kernel support is too buggy to be used, so
|
||||||
|
here are the structures we used to support select() on sockets, and *only* on
|
||||||
|
sockets!
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct select_args { // used by NET_STACK_SELECT and NET_STACK_DESELECT
|
||||||
|
struct selectsync *sync; // in fact, it's the area_id of a r5_selectsync struct!!!
|
||||||
|
uint32 ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct r5_selectsync {
|
||||||
|
sem_id lock; // lock this r5_selectsync
|
||||||
|
sem_id wakeup; // sem to release to wakeup select()
|
||||||
|
struct fd_set rbits; // read event bits field
|
||||||
|
struct fd_set wbits; // write event bits field
|
||||||
|
struct fd_set ebits; // exception event bits field
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* NET_STACK_DRIVER_H */
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* This file may be used under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef USERLAND_IPC_H
|
||||||
|
#define USERLAND_IPC_H
|
||||||
|
|
||||||
|
/*! userland_ipc - Communication between the network driver
|
||||||
|
and the userland stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
#include <Drivers.h>
|
||||||
|
|
||||||
|
#include "net_stack_driver.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NET_STACK_PORTNAME "net_server connection"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NET_STACK_OPEN = NET_STACK_IOCTL_MAX,
|
||||||
|
NET_STACK_CLOSE,
|
||||||
|
NET_STACK_NEW_CONNECTION,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_NET_AREAS 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
area_id id;
|
||||||
|
uint8 *offset;
|
||||||
|
} net_area_info;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32 op;
|
||||||
|
// int32 buffer;
|
||||||
|
uint8 *data;
|
||||||
|
int32 length;
|
||||||
|
int32 result;
|
||||||
|
net_area_info area[MAX_NET_AREAS];
|
||||||
|
} net_command;
|
||||||
|
|
||||||
|
#define CONNECTION_QUEUE_LENGTH 128
|
||||||
|
#define CONNECTION_COMMAND_SIZE 2048
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
port_id port;
|
||||||
|
area_id area;
|
||||||
|
thread_id socket_thread;
|
||||||
|
|
||||||
|
sem_id commandSemaphore; // command queue
|
||||||
|
uint32 numCommands,bufferSize;
|
||||||
|
} net_connection;
|
||||||
|
|
||||||
|
|
||||||
|
extern status_t init_userland_ipc(void);
|
||||||
|
extern void shutdown_userland_ipc(void);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // end of extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* USERLAND_IPC_H */
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network datalink_protocols ;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols arp ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols ethernet_frame ;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network datalink_protocols arp ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon <module>arp : kernel haiku_network datalink_protocols :
|
||||||
|
arp.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/datalink_protocols
|
||||||
|
: <module>arp ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network datalink_protocols ;
|
||||||
@@ -0,0 +1,974 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! Ethernet Address Resolution Protocol, see RFC 826.
|
||||||
|
|
||||||
|
|
||||||
|
#include <arp_control.h>
|
||||||
|
#include <net_datalink_protocol.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
#include <net_datalink.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
#include <NetBufferUtilities.h>
|
||||||
|
|
||||||
|
#include <generic_syscall.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
#include <util/khash.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_ARP
|
||||||
|
#ifdef TRACE_ARP
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct arp_header {
|
||||||
|
uint16 hardware_type;
|
||||||
|
uint16 protocol_type;
|
||||||
|
uint8 hardware_length;
|
||||||
|
uint8 protocol_length;
|
||||||
|
uint16 opcode;
|
||||||
|
|
||||||
|
// TODO: this should be a variable length header, but for our current
|
||||||
|
// usage (Ethernet/IPv4), this should work fine.
|
||||||
|
uint8 hardware_sender[6];
|
||||||
|
in_addr_t protocol_sender;
|
||||||
|
uint8 hardware_target[6];
|
||||||
|
in_addr_t protocol_target;
|
||||||
|
} _PACKED;
|
||||||
|
|
||||||
|
#define ARP_OPCODE_REQUEST 1
|
||||||
|
#define ARP_OPCODE_REPLY 2
|
||||||
|
|
||||||
|
#define ARP_HARDWARE_TYPE_ETHER 1
|
||||||
|
|
||||||
|
struct arp_entry {
|
||||||
|
arp_entry *next;
|
||||||
|
in_addr_t protocol_address;
|
||||||
|
sockaddr_dl hardware_address;
|
||||||
|
uint32 flags;
|
||||||
|
sem_id resolved_sem;
|
||||||
|
net_buffer *request_buffer;
|
||||||
|
net_timer timer;
|
||||||
|
uint32 timer_state;
|
||||||
|
bigtime_t timestamp;
|
||||||
|
net_datalink_protocol *protocol;
|
||||||
|
|
||||||
|
static int Compare(void *_entry, const void *_key);
|
||||||
|
static uint32 Hash(void *_entry, const void *_key, uint32 range);
|
||||||
|
static arp_entry *Lookup(in_addr_t protocolAddress);
|
||||||
|
static arp_entry *Add(in_addr_t protocolAddress, sockaddr_dl *hardwareAddress,
|
||||||
|
uint32 flags);
|
||||||
|
};
|
||||||
|
|
||||||
|
// see arp_control.h for flags
|
||||||
|
|
||||||
|
#define ARP_NO_STATE 0
|
||||||
|
#define ARP_STATE_REQUEST 1
|
||||||
|
#define ARP_STATE_LAST_REQUEST 5
|
||||||
|
#define ARP_STATE_REQUEST_FAILED 6
|
||||||
|
#define ARP_STATE_REMOVE_FAILED 7
|
||||||
|
#define ARP_STATE_STALE 8
|
||||||
|
|
||||||
|
#define ARP_STALE_TIMEOUT 30 * 60000000LL // 30 minutes
|
||||||
|
#define ARP_REJECT_TIMEOUT 20000000LL // 20 seconds
|
||||||
|
#define ARP_REQUEST_TIMEOUT 1000000LL // 1 second
|
||||||
|
|
||||||
|
struct arp_protocol : net_datalink_protocol {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void arp_timer(struct net_timer *timer, void *data);
|
||||||
|
|
||||||
|
struct net_buffer_module_info *sBufferModule;
|
||||||
|
static net_stack_module_info *sStackModule;
|
||||||
|
static hash_table *sCache;
|
||||||
|
static benaphore sCacheLock;
|
||||||
|
static bool sIgnoreReplies;
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ int
|
||||||
|
arp_entry::Compare(void *_entry, const void *_key)
|
||||||
|
{
|
||||||
|
arp_entry *entry = (arp_entry *)_entry;
|
||||||
|
in_addr_t key = (in_addr_t)_key;
|
||||||
|
|
||||||
|
if (entry->protocol_address == key)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ uint32
|
||||||
|
arp_entry::Hash(void *_entry, const void *_key, uint32 range)
|
||||||
|
{
|
||||||
|
arp_entry *entry = (arp_entry *)_entry;
|
||||||
|
in_addr_t key = (in_addr_t)_key;
|
||||||
|
|
||||||
|
// TODO: check if this makes a good hash...
|
||||||
|
#define HASH(o) (((o >> 24) ^ (o >> 16) ^ (o >> 8) ^ o) % range)
|
||||||
|
|
||||||
|
#ifdef TRACE_ARP
|
||||||
|
in_addr_t a = entry ? entry->protocol_address : key;
|
||||||
|
dprintf("%ld.%ld.%ld.%ld: Hash: %lu\n", a >> 24, (a >> 16) & 0xff,
|
||||||
|
(a >> 8) & 0xff, a & 0xff, HASH(a));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (entry != NULL)
|
||||||
|
return HASH(entry->protocol_address);
|
||||||
|
|
||||||
|
return HASH(key);
|
||||||
|
#undef HASH
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ arp_entry *
|
||||||
|
arp_entry::Lookup(in_addr_t address)
|
||||||
|
{
|
||||||
|
return (arp_entry *)hash_lookup(sCache, (void *)address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ arp_entry *
|
||||||
|
arp_entry::Add(in_addr_t protocolAddress, sockaddr_dl *hardwareAddress,
|
||||||
|
uint32 flags)
|
||||||
|
{
|
||||||
|
arp_entry *entry = new (std::nothrow) arp_entry;
|
||||||
|
if (entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
entry->protocol_address = protocolAddress;
|
||||||
|
entry->flags = flags;
|
||||||
|
entry->timestamp = system_time();
|
||||||
|
entry->protocol = NULL;
|
||||||
|
entry->request_buffer = NULL;
|
||||||
|
entry->timer_state = ARP_NO_STATE;
|
||||||
|
sStackModule->init_timer(&entry->timer, arp_timer, entry);
|
||||||
|
|
||||||
|
if (hardwareAddress != NULL) {
|
||||||
|
// this entry is already resolved
|
||||||
|
entry->hardware_address = *hardwareAddress;
|
||||||
|
entry->hardware_address.sdl_e_type = ETHER_TYPE_IP;
|
||||||
|
entry->resolved_sem = -1;
|
||||||
|
} else {
|
||||||
|
// this entry still needs to be resolved
|
||||||
|
entry->hardware_address.sdl_alen = 0;
|
||||||
|
|
||||||
|
char name[32];
|
||||||
|
snprintf(name, sizeof(name), "arp %08lx", protocolAddress);
|
||||||
|
entry->resolved_sem = create_sem(0, name);
|
||||||
|
if (entry->resolved_sem < B_OK) {
|
||||||
|
delete entry;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entry->hardware_address.sdl_len != sizeof(sockaddr_dl)) {
|
||||||
|
// explicitly set correct length in case our caller hasn't...
|
||||||
|
entry->hardware_address.sdl_len = sizeof(sockaddr_dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash_insert(sCache, entry) != B_OK) {
|
||||||
|
delete entry;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Updates the entry determined by \a protocolAddress with the specified
|
||||||
|
\a hardwareAddress.
|
||||||
|
If such an entry does not exist yet, a new entry is added. If you try
|
||||||
|
to update a local existing entry but didn't ask for it (by setting
|
||||||
|
\a flags to ARP_FLAG_LOCAL), an error is returned.
|
||||||
|
|
||||||
|
This function does not lock the cache - you have to do it yourself
|
||||||
|
before calling it.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
arp_update_entry(in_addr_t protocolAddress, sockaddr_dl *hardwareAddress,
|
||||||
|
uint32 flags, arp_entry **_entry = NULL)
|
||||||
|
{
|
||||||
|
arp_entry *entry = arp_entry::Lookup(protocolAddress);
|
||||||
|
if (entry != NULL) {
|
||||||
|
// We disallow updating of entries that had been resolved before,
|
||||||
|
// but to a different address.
|
||||||
|
// Right now, you have to manually purge the ARP entries (or wait some
|
||||||
|
// time) to let us switch to the new address.
|
||||||
|
if (entry->hardware_address.sdl_alen != 0
|
||||||
|
&& memcmp(LLADDR(&entry->hardware_address), hardwareAddress, ETHER_ADDRESS_LENGTH)) {
|
||||||
|
dprintf("ARP host %08lx updated with different hardware address %02x:%02x:%02x:%02x:%02x:%02x.\n",
|
||||||
|
protocolAddress, hardwareAddress->sdl_data[0] & 0xff, hardwareAddress->sdl_data[1] & 0xff,
|
||||||
|
hardwareAddress->sdl_data[2] & 0xff, hardwareAddress->sdl_data[3] & 0xff,
|
||||||
|
hardwareAddress->sdl_data[4] & 0xff, hardwareAddress->sdl_data[5] & 0xff);
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->hardware_address = *hardwareAddress;
|
||||||
|
entry->timestamp = system_time();
|
||||||
|
} else {
|
||||||
|
entry = arp_entry::Add(protocolAddress, hardwareAddress, flags);
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if someone was waiting for this ARP request to be resolved
|
||||||
|
if (entry->resolved_sem >= B_OK) {
|
||||||
|
delete_sem(entry->resolved_sem);
|
||||||
|
entry->resolved_sem = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->request_buffer != NULL) {
|
||||||
|
sBufferModule->free(entry->request_buffer);
|
||||||
|
entry->request_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((entry->flags & ARP_FLAG_PERMANENT) == 0) {
|
||||||
|
// (re)start the stale timer
|
||||||
|
entry->timer_state = ARP_STATE_STALE;
|
||||||
|
sStackModule->set_timer(&entry->timer, ARP_STALE_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entry)
|
||||||
|
*_entry = entry;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
arp_update_local(net_datalink_protocol *protocol)
|
||||||
|
{
|
||||||
|
net_interface *interface = protocol->interface;
|
||||||
|
|
||||||
|
if (interface->address == NULL) {
|
||||||
|
// interface has not yet been set
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_dl address;
|
||||||
|
address.sdl_len = sizeof(sockaddr_dl);
|
||||||
|
address.sdl_family = AF_DLI;
|
||||||
|
address.sdl_type = IFT_ETHER;
|
||||||
|
address.sdl_e_type = ETHER_TYPE_IP;
|
||||||
|
address.sdl_nlen = 0;
|
||||||
|
address.sdl_slen = 0;
|
||||||
|
address.sdl_alen = interface->device->address.length;
|
||||||
|
memcpy(LLADDR(&address), interface->device->address.data, address.sdl_alen);
|
||||||
|
|
||||||
|
arp_entry *entry;
|
||||||
|
status_t status = arp_update_entry(((sockaddr_in *)interface->address)->sin_addr.s_addr,
|
||||||
|
&address, ARP_FLAG_LOCAL | ARP_FLAG_PERMANENT, &entry);
|
||||||
|
if (status == B_OK)
|
||||||
|
entry->protocol = protocol;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
handle_arp_request(net_buffer *buffer, arp_header &header)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sCacheLock);
|
||||||
|
|
||||||
|
if (!sIgnoreReplies) {
|
||||||
|
arp_update_entry(header.protocol_sender, (sockaddr_dl *)&buffer->source, 0);
|
||||||
|
// remember the address of the sender as we might need it later
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this request is for us
|
||||||
|
|
||||||
|
arp_entry *entry = arp_entry::Lookup(header.protocol_target);
|
||||||
|
if (entry == NULL || (entry->flags & (ARP_FLAG_LOCAL | ARP_FLAG_PUBLISH)) == 0) {
|
||||||
|
// We're not the one to answer this request
|
||||||
|
// TODO: instead of letting the other's request time-out, can we reply
|
||||||
|
// failure somehow?
|
||||||
|
TRACE((" not for us\n"));
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a reply (by reusing the buffer we got)
|
||||||
|
|
||||||
|
TRACE((" send reply!\n"));
|
||||||
|
header.opcode = htons(ARP_OPCODE_REPLY);
|
||||||
|
|
||||||
|
memcpy(header.hardware_target, header.hardware_sender, ETHER_ADDRESS_LENGTH);
|
||||||
|
header.protocol_target = header.protocol_sender;
|
||||||
|
memcpy(header.hardware_sender, LLADDR(&entry->hardware_address), ETHER_ADDRESS_LENGTH);
|
||||||
|
header.protocol_sender = entry->protocol_address;
|
||||||
|
|
||||||
|
// exchange source and destination address
|
||||||
|
memcpy(LLADDR((sockaddr_dl *)&buffer->source), header.hardware_sender,
|
||||||
|
ETHER_ADDRESS_LENGTH);
|
||||||
|
memcpy(LLADDR((sockaddr_dl *)&buffer->destination), header.hardware_target,
|
||||||
|
ETHER_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
buffer->flags = 0;
|
||||||
|
// make sure this won't be a broadcast message
|
||||||
|
|
||||||
|
return entry->protocol->next->module->send_data(entry->protocol->next, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_arp_reply(net_buffer *buffer, arp_header &header)
|
||||||
|
{
|
||||||
|
if (sIgnoreReplies)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BenaphoreLocker locker(sCacheLock);
|
||||||
|
arp_update_entry(header.protocol_sender, (sockaddr_dl *)&buffer->source, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
arp_receive(void *cookie, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
TRACE(("ARP receive\n"));
|
||||||
|
|
||||||
|
NetBufferHeader<arp_header> bufferHeader(buffer);
|
||||||
|
if (bufferHeader.Status() < B_OK)
|
||||||
|
return bufferHeader.Status();
|
||||||
|
|
||||||
|
arp_header &header = bufferHeader.Data();
|
||||||
|
uint16 opcode = ntohs(header.opcode);
|
||||||
|
|
||||||
|
#ifdef TRACE_ARP
|
||||||
|
dprintf(" hw sender: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
header.hardware_sender[0], header.hardware_sender[1], header.hardware_sender[2],
|
||||||
|
header.hardware_sender[3], header.hardware_sender[4], header.hardware_sender[5]);
|
||||||
|
dprintf(" proto sender: %ld.%ld.%ld.%ld\n", header.protocol_sender >> 24, (header.protocol_sender >> 16) & 0xff,
|
||||||
|
(header.protocol_sender >> 8) & 0xff, header.protocol_sender & 0xff);
|
||||||
|
dprintf(" hw target: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
header.hardware_target[0], header.hardware_target[1], header.hardware_target[2],
|
||||||
|
header.hardware_target[3], header.hardware_target[4], header.hardware_target[5]);
|
||||||
|
dprintf(" proto target: %ld.%ld.%ld.%ld\n", header.protocol_target >> 24, (header.protocol_target >> 16) & 0xff,
|
||||||
|
(header.protocol_target >> 8) & 0xff, header.protocol_target & 0xff);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ntohs(header.protocol_type) != ETHER_TYPE_IP
|
||||||
|
|| ntohs(header.hardware_type) != ARP_HARDWARE_TYPE_ETHER)
|
||||||
|
return B_BAD_TYPE;
|
||||||
|
|
||||||
|
// check if the packet is okay
|
||||||
|
|
||||||
|
if (header.hardware_length != ETHER_ADDRESS_LENGTH
|
||||||
|
|| header.protocol_length != sizeof(in_addr_t))
|
||||||
|
return B_BAD_DATA;
|
||||||
|
|
||||||
|
bufferHeader.Detach();
|
||||||
|
|
||||||
|
// handle packet
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case ARP_OPCODE_REQUEST:
|
||||||
|
TRACE((" got ARP request\n"));
|
||||||
|
if (handle_arp_request(buffer, header) == B_OK) {
|
||||||
|
// the function will take care of the buffer if everything went well
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARP_OPCODE_REPLY:
|
||||||
|
TRACE((" got ARP reply\n"));
|
||||||
|
handle_arp_reply(buffer, header);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dprintf("unknown ARP opcode %d\n", opcode);
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sBufferModule->free(buffer);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
arp_timer(struct net_timer *timer, void *data)
|
||||||
|
{
|
||||||
|
arp_entry *entry = (arp_entry *)data;
|
||||||
|
TRACE(("ARP timer %ld, entry %p!\n", entry->timer_state, entry));
|
||||||
|
|
||||||
|
switch (entry->timer_state) {
|
||||||
|
case ARP_NO_STATE:
|
||||||
|
// who are you kidding?
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARP_STATE_REQUEST_FAILED:
|
||||||
|
// requesting the ARP entry failed, we keep it around for a while, though,
|
||||||
|
// so that we won't try to request the same address again too soon.
|
||||||
|
TRACE((" requesting ARP entry %p failed!\n", entry));
|
||||||
|
entry->timer_state = ARP_STATE_REMOVE_FAILED;
|
||||||
|
entry->flags |= ARP_FLAG_REJECT;
|
||||||
|
sStackModule->set_timer(&entry->timer, ARP_REJECT_TIMEOUT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARP_STATE_REMOVE_FAILED:
|
||||||
|
case ARP_STATE_STALE:
|
||||||
|
// the entry has aged so much that we're going to remove it
|
||||||
|
TRACE((" remove ARP entry %p!\n", entry));
|
||||||
|
|
||||||
|
benaphore_lock(&sCacheLock);
|
||||||
|
hash_remove(sCache, entry);
|
||||||
|
benaphore_unlock(&sCacheLock);
|
||||||
|
|
||||||
|
delete entry;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (entry->timer_state > ARP_STATE_LAST_REQUEST)
|
||||||
|
break;
|
||||||
|
|
||||||
|
TRACE((" send request for ARP entry %p!\n", entry));
|
||||||
|
net_buffer *request = entry->request_buffer;
|
||||||
|
if (entry->timer_state < ARP_STATE_LAST_REQUEST) {
|
||||||
|
// we'll still need our buffer, so in order to prevent it being
|
||||||
|
// freed by a successful send, we need to clone it
|
||||||
|
request = sBufferModule->clone(request, true);
|
||||||
|
if (request == NULL) {
|
||||||
|
// cloning failed - that means we won't be able to send as
|
||||||
|
// many requests as originally planned
|
||||||
|
request = entry->request_buffer;
|
||||||
|
entry->timer_state = ARP_STATE_LAST_REQUEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're trying to resolve the address, so keep sending requests
|
||||||
|
status_t status = entry->protocol->next->module->send_data(
|
||||||
|
entry->protocol->next, request);
|
||||||
|
if (status < B_OK)
|
||||||
|
sBufferModule->free(request);
|
||||||
|
|
||||||
|
if (entry->timer_state == ARP_STATE_LAST_REQUEST) {
|
||||||
|
// buffer has been freed on send
|
||||||
|
entry->request_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->timer_state++;
|
||||||
|
sStackModule->set_timer(&entry->timer, ARP_REQUEST_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Checks if the ARP \a entry has already been resolved. If it wasn't yet,
|
||||||
|
and MSG_DONTWAIT is not set in \a flags, it will wait for the entry to
|
||||||
|
become resolved.
|
||||||
|
You need to have the sCacheLock held when calling this function - but
|
||||||
|
note that the lock may be interrupted (in which case entry is updated).
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
arp_check_resolved(arp_entry **_entry, uint32 flags)
|
||||||
|
{
|
||||||
|
arp_entry *entry = *_entry;
|
||||||
|
|
||||||
|
if ((entry->flags & ARP_FLAG_REJECT) != 0)
|
||||||
|
return EHOSTUNREACH;
|
||||||
|
|
||||||
|
if (entry->resolved_sem < B_OK)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// we need to wait for this entry to become resolved
|
||||||
|
|
||||||
|
if ((flags & MSG_DONTWAIT) != 0)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
// store information we cannot access anymore after having unlocked the cache
|
||||||
|
sem_id waitSem = entry->resolved_sem;
|
||||||
|
in_addr_t address = entry->protocol_address;
|
||||||
|
|
||||||
|
benaphore_unlock(&sCacheLock);
|
||||||
|
|
||||||
|
status_t status = acquire_sem_etc(waitSem, 1, B_RELATIVE_TIMEOUT, 5 * 1000000);
|
||||||
|
|
||||||
|
benaphore_lock(&sCacheLock);
|
||||||
|
|
||||||
|
if (status == B_TIMED_OUT)
|
||||||
|
return EHOSTUNREACH;
|
||||||
|
|
||||||
|
// retrieve the entry again, as we reacquired the cache lock
|
||||||
|
entry = arp_entry::Lookup(address);
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
*_entry = entry;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Address resolver function: prepares and sends the ARP request necessary
|
||||||
|
to retrieve the hardware address for \a address.
|
||||||
|
You need to have the sCacheLock held when calling this function - but
|
||||||
|
note that the lock will be interrupted here if everything goes well.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
arp_resolve(net_datalink_protocol *protocol, in_addr_t address, arp_entry **_entry)
|
||||||
|
{
|
||||||
|
// create an unresolved ARP entry as a placeholder
|
||||||
|
arp_entry *entry = arp_entry::Add(address, NULL, 0);
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// prepare ARP request
|
||||||
|
|
||||||
|
entry->request_buffer = sBufferModule->create(256);
|
||||||
|
if (entry->request_buffer == NULL) {
|
||||||
|
// TODO: do something with the entry
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetBufferPrepend<arp_header> bufferHeader(entry->request_buffer);
|
||||||
|
status_t status = bufferHeader.Status();
|
||||||
|
if (status < B_OK) {
|
||||||
|
// TODO: do something with the entry
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare ARP header
|
||||||
|
|
||||||
|
net_device *device = protocol->interface->device;
|
||||||
|
arp_header &header = bufferHeader.Data();
|
||||||
|
|
||||||
|
header.hardware_type = htons(ARP_HARDWARE_TYPE_ETHER);
|
||||||
|
header.protocol_type = htons(ETHER_TYPE_IP);
|
||||||
|
header.hardware_length = ETHER_ADDRESS_LENGTH;
|
||||||
|
header.protocol_length = sizeof(in_addr_t);
|
||||||
|
header.opcode = htons(ARP_OPCODE_REQUEST);
|
||||||
|
|
||||||
|
memcpy(header.hardware_sender, device->address.data, ETHER_ADDRESS_LENGTH);
|
||||||
|
header.protocol_sender = ((sockaddr_in *)protocol->interface->address)->sin_addr.s_addr;
|
||||||
|
memset(header.hardware_target, 0, ETHER_ADDRESS_LENGTH);
|
||||||
|
header.protocol_target = address;
|
||||||
|
|
||||||
|
// prepare source and target addresses
|
||||||
|
|
||||||
|
struct sockaddr_dl &source = *(struct sockaddr_dl *)&entry->request_buffer->source;
|
||||||
|
source.sdl_len = sizeof(sockaddr_dl);
|
||||||
|
source.sdl_family = AF_DLI;
|
||||||
|
source.sdl_index = device->index;
|
||||||
|
source.sdl_type = IFT_ETHER;
|
||||||
|
source.sdl_e_type = ETHER_TYPE_ARP;
|
||||||
|
source.sdl_nlen = source.sdl_slen = 0;
|
||||||
|
source.sdl_alen = ETHER_ADDRESS_LENGTH;
|
||||||
|
memcpy(source.sdl_data, device->address.data, ETHER_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
entry->request_buffer->flags = MSG_BCAST;
|
||||||
|
// this is a broadcast packet, we don't need to fill in the destination
|
||||||
|
|
||||||
|
entry->protocol = protocol;
|
||||||
|
entry->timer_state = ARP_STATE_REQUEST;
|
||||||
|
sStackModule->set_timer(&entry->timer, 0);
|
||||||
|
// start request timer
|
||||||
|
|
||||||
|
sem_id waitSem = entry->resolved_sem;
|
||||||
|
benaphore_unlock(&sCacheLock);
|
||||||
|
|
||||||
|
// TODO: resend the request periodically via timer
|
||||||
|
// (and abort it that way, too)
|
||||||
|
|
||||||
|
status = acquire_sem_etc(waitSem, 1, B_RELATIVE_TIMEOUT, 5 * 1000000);
|
||||||
|
// wait for the entry to become resolved
|
||||||
|
|
||||||
|
benaphore_lock(&sCacheLock);
|
||||||
|
|
||||||
|
// retrieve the entry again, as we reacquired the cache lock
|
||||||
|
entry = arp_entry::Lookup(address);
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
if (status == B_TIMED_OUT) {
|
||||||
|
// we didn't get a response, mark ARP entry as non-existant
|
||||||
|
entry->flags = ARP_FLAG_REJECT;
|
||||||
|
// TODO: remove the ARP entry after some time
|
||||||
|
return EHOSTUNREACH;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_entry = entry;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
arp_control(const char *subsystem, uint32 function,
|
||||||
|
void *buffer, size_t bufferSize)
|
||||||
|
{
|
||||||
|
struct arp_control control;
|
||||||
|
if (bufferSize != sizeof(struct arp_control))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
if (user_memcpy(&control, buffer, sizeof(struct arp_control)) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
BenaphoreLocker locker(sCacheLock);
|
||||||
|
|
||||||
|
switch (function) {
|
||||||
|
case ARP_SET_ENTRY:
|
||||||
|
sockaddr_dl hardwareAddress;
|
||||||
|
|
||||||
|
hardwareAddress.sdl_len = sizeof(sockaddr_dl);
|
||||||
|
hardwareAddress.sdl_family = AF_DLI;
|
||||||
|
hardwareAddress.sdl_index = 0;
|
||||||
|
hardwareAddress.sdl_type = IFT_ETHER;
|
||||||
|
hardwareAddress.sdl_e_type = ETHER_TYPE_IP;
|
||||||
|
hardwareAddress.sdl_nlen = hardwareAddress.sdl_slen = 0;
|
||||||
|
hardwareAddress.sdl_alen = ETHER_ADDRESS_LENGTH;
|
||||||
|
memcpy(hardwareAddress.sdl_data, control.ethernet_address, ETHER_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
return arp_update_entry(control.address, &hardwareAddress,
|
||||||
|
control.flags & (ARP_FLAG_PUBLISH | ARP_FLAG_PERMANENT | ARP_FLAG_REJECT));
|
||||||
|
|
||||||
|
case ARP_GET_ENTRY:
|
||||||
|
{
|
||||||
|
arp_entry *entry = arp_entry::Lookup(control.address);
|
||||||
|
if (entry == NULL || entry->resolved_sem < B_OK)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
memcpy(control.ethernet_address, entry->hardware_address.sdl_data,
|
||||||
|
ETHER_ADDRESS_LENGTH);
|
||||||
|
control.flags = entry->flags;
|
||||||
|
return user_memcpy(buffer, &control, sizeof(struct arp_control));
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARP_GET_ENTRIES:
|
||||||
|
{
|
||||||
|
hash_iterator iterator;
|
||||||
|
hash_open(sCache, &iterator);
|
||||||
|
|
||||||
|
arp_entry *entry;
|
||||||
|
uint32 i = 0;
|
||||||
|
while ((entry = (arp_entry *)hash_next(sCache, &iterator)) != NULL
|
||||||
|
&& i < control.cookie) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
hash_close(sCache, &iterator, false);
|
||||||
|
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
control.cookie++;
|
||||||
|
control.address = entry->protocol_address;
|
||||||
|
memcpy(control.ethernet_address, entry->hardware_address.sdl_data,
|
||||||
|
ETHER_ADDRESS_LENGTH);
|
||||||
|
control.flags = entry->flags;
|
||||||
|
|
||||||
|
return user_memcpy(buffer, &control, sizeof(struct arp_control));
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARP_DELETE_ENTRY:
|
||||||
|
{
|
||||||
|
arp_entry *entry = arp_entry::Lookup(control.address);
|
||||||
|
if (entry == NULL || entry->resolved_sem < B_OK)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
if ((entry->flags & ARP_FLAG_LOCAL) != 0)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// schedule a timer to remove this entry
|
||||||
|
entry->timer_state = ARP_STATE_REMOVE_FAILED;
|
||||||
|
sStackModule->set_timer(&entry->timer, 0);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARP_FLUSH_ENTRIES:
|
||||||
|
{
|
||||||
|
hash_iterator iterator;
|
||||||
|
hash_open(sCache, &iterator);
|
||||||
|
|
||||||
|
arp_entry *entry;
|
||||||
|
while ((entry = (arp_entry *)hash_next(sCache, &iterator)) != NULL) {
|
||||||
|
// we never flush local ARP entries
|
||||||
|
if ((entry->flags & ARP_FLAG_LOCAL) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// schedule a timer to remove this entry
|
||||||
|
entry->timer_state = ARP_STATE_REMOVE_FAILED;
|
||||||
|
sStackModule->set_timer(&entry->timer, 0);
|
||||||
|
}
|
||||||
|
hash_close(sCache, &iterator, false);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARP_IGNORE_REPLIES:
|
||||||
|
sIgnoreReplies = control.flags != 0;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
arp_init()
|
||||||
|
{
|
||||||
|
status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule);
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
status = benaphore_init(&sCacheLock, "arp cache");
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
sCache = hash_init(64, offsetof(struct arp_entry, next),
|
||||||
|
&arp_entry::Compare, &arp_entry::Hash);
|
||||||
|
if (sCache == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
goto err3;
|
||||||
|
}
|
||||||
|
|
||||||
|
register_generic_syscall(ARP_SYSCALLS, arp_control, 1, 0);
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err3:
|
||||||
|
benaphore_destroy(&sCacheLock);
|
||||||
|
err2:
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
err1:
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
arp_uninit()
|
||||||
|
{
|
||||||
|
unregister_generic_syscall(ARP_SYSCALLS, 1);
|
||||||
|
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
arp_init_protocol(struct net_interface *interface, net_datalink_protocol **_protocol)
|
||||||
|
{
|
||||||
|
// We currently only support a single family and type!
|
||||||
|
if (interface->domain->family != AF_INET
|
||||||
|
|| interface->device->type != IFT_ETHER)
|
||||||
|
return B_BAD_TYPE;
|
||||||
|
|
||||||
|
status_t status = sStackModule->register_device_handler(interface->device,
|
||||||
|
ETHER_FRAME_TYPE | ETHER_TYPE_ARP, &arp_receive, NULL);
|
||||||
|
if (status == B_OK) {
|
||||||
|
// We also register the domain as a handler for deframed packets;
|
||||||
|
// while the ethernet_frame module is not really connected to our
|
||||||
|
// domain, we are.
|
||||||
|
status = sStackModule->register_domain_device_handler(interface->device,
|
||||||
|
ETHER_FRAME_TYPE | ETHER_TYPE_IP, interface->domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
arp_protocol *protocol = new (std::nothrow) arp_protocol;
|
||||||
|
if (protocol == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
*_protocol = protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
arp_uninit_protocol(net_datalink_protocol *protocol)
|
||||||
|
{
|
||||||
|
sStackModule->unregister_device_handler(protocol->interface->device,
|
||||||
|
ETHER_FRAME_TYPE | ETHER_TYPE_ARP);
|
||||||
|
sStackModule->unregister_device_handler(protocol->interface->device,
|
||||||
|
ETHER_FRAME_TYPE | ETHER_TYPE_IP);
|
||||||
|
|
||||||
|
delete protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
arp_send_data(net_datalink_protocol *protocol,
|
||||||
|
net_buffer *buffer)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sCacheLock);
|
||||||
|
|
||||||
|
// Lookup source (us)
|
||||||
|
|
||||||
|
arp_entry *entry = arp_entry::Lookup(
|
||||||
|
((struct sockaddr_in *)&buffer->source)->sin_addr.s_addr);
|
||||||
|
if (entry == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
memcpy(&buffer->source, &entry->hardware_address,
|
||||||
|
entry->hardware_address.sdl_len);
|
||||||
|
|
||||||
|
// Lookup destination (we may need to wait for this)
|
||||||
|
entry = arp_entry::Lookup(
|
||||||
|
((struct sockaddr_in *)&buffer->destination)->sin_addr.s_addr);
|
||||||
|
if (entry == NULL) {
|
||||||
|
// The ARP entry does not yet exist, if we're allowed to wait,
|
||||||
|
// we'll send an ARP request and try to change that.
|
||||||
|
if ((buffer->flags & MSG_DONTWAIT) != 0) {
|
||||||
|
// TODO: implement delaying packet send after ARP response!
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t status = arp_resolve(protocol,
|
||||||
|
((struct sockaddr_in *)&buffer->destination)->sin_addr.s_addr, &entry);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
} else {
|
||||||
|
// The entry exists, but we have to check if it has already been
|
||||||
|
// resolved and is valid.
|
||||||
|
status_t status = arp_check_resolved(&entry, buffer->flags);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&buffer->destination, &entry->hardware_address,
|
||||||
|
entry->hardware_address.sdl_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol->next->module->send_data(protocol->next, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
arp_up(net_datalink_protocol *_protocol)
|
||||||
|
{
|
||||||
|
arp_protocol *protocol = (arp_protocol *)_protocol;
|
||||||
|
status_t status = protocol->next->module->interface_up(protocol->next);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// cache this device's address for later use
|
||||||
|
|
||||||
|
status = arp_update_local(protocol);
|
||||||
|
if (status < B_OK) {
|
||||||
|
protocol->next->module->interface_down(protocol->next);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
arp_down(net_datalink_protocol *protocol)
|
||||||
|
{
|
||||||
|
// remove local ARP entry from the cache
|
||||||
|
|
||||||
|
if (protocol->interface->address != NULL) {
|
||||||
|
BenaphoreLocker locker(sCacheLock);
|
||||||
|
|
||||||
|
arp_entry *entry = arp_entry::Lookup(
|
||||||
|
((sockaddr_in *)protocol->interface->address)->sin_addr.s_addr);
|
||||||
|
if (entry != NULL) {
|
||||||
|
hash_remove(sCache, entry);
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol->next->module->interface_down(protocol->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
arp_control(net_datalink_protocol *protocol,
|
||||||
|
int32 op, void *argument, size_t length)
|
||||||
|
{
|
||||||
|
if (op == SIOCSIFADDR && (protocol->interface->flags & IFF_UP) != 0) {
|
||||||
|
// The interface may get a new address, so we need to update our
|
||||||
|
// local entries.
|
||||||
|
in_addr_t oldAddress = 0;
|
||||||
|
if (protocol->interface->address != NULL)
|
||||||
|
oldAddress = ((sockaddr_in *)protocol->interface->address)->sin_addr.s_addr;
|
||||||
|
|
||||||
|
status_t status = protocol->next->module->control(protocol->next,
|
||||||
|
SIOCSIFADDR, argument, length);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
arp_update_local(protocol);
|
||||||
|
|
||||||
|
if (oldAddress == ((sockaddr_in *)protocol->interface->address)->sin_addr.s_addr
|
||||||
|
|| oldAddress == 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// remove previous address from cache
|
||||||
|
// TODO: we should be able to do this (add/remove) in one atomic operation!
|
||||||
|
|
||||||
|
BenaphoreLocker locker(sCacheLock);
|
||||||
|
|
||||||
|
arp_entry *entry = arp_entry::Lookup(oldAddress);
|
||||||
|
if (entry != NULL) {
|
||||||
|
hash_remove(sCache, entry);
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol->next->module->control(protocol->next,
|
||||||
|
op, argument, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
arp_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
return arp_init();
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return arp_uninit();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static net_datalink_protocol_module_info sARPModule = {
|
||||||
|
{
|
||||||
|
"network/datalink_protocols/arp/v1",
|
||||||
|
0,
|
||||||
|
arp_std_ops
|
||||||
|
},
|
||||||
|
arp_init_protocol,
|
||||||
|
arp_uninit_protocol,
|
||||||
|
arp_send_data,
|
||||||
|
arp_up,
|
||||||
|
arp_down,
|
||||||
|
arp_control,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sARPModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network datalink_protocols ethernet_frame ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon ethernet_frame : kernel haiku_network datalink_protocols :
|
||||||
|
ethernet_frame.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/datalink_protocols
|
||||||
|
: ethernet_frame ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network datalink_protocols ;
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <ethernet.h>
|
||||||
|
#include <net_datalink_protocol.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
#include <net_datalink.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
#include <NetBufferUtilities.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <new>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct ethernet_frame_protocol : net_datalink_protocol {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const uint8 kBroadcastAddress[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
|
|
||||||
|
struct net_buffer_module_info *sBufferModule;
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
ethernet_deframe(net_device *device, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
//dprintf("asked to deframe buffer for device %s\n", device->name);
|
||||||
|
|
||||||
|
NetBufferHeader<ether_header> bufferHeader(buffer);
|
||||||
|
if (bufferHeader.Status() < B_OK)
|
||||||
|
return bufferHeader.Status();
|
||||||
|
|
||||||
|
ether_header &header = bufferHeader.Data();
|
||||||
|
uint16 type = ntohs(header.type);
|
||||||
|
|
||||||
|
struct sockaddr_dl &source = *(struct sockaddr_dl *)&buffer->source;
|
||||||
|
struct sockaddr_dl &destination = *(struct sockaddr_dl *)&buffer->destination;
|
||||||
|
|
||||||
|
source.sdl_len = sizeof(sockaddr_dl);
|
||||||
|
source.sdl_family = AF_DLI;
|
||||||
|
source.sdl_index = device->index;
|
||||||
|
source.sdl_type = IFT_ETHER;
|
||||||
|
source.sdl_e_type = type;
|
||||||
|
source.sdl_nlen = source.sdl_slen = 0;
|
||||||
|
source.sdl_alen = ETHER_ADDRESS_LENGTH;
|
||||||
|
memcpy(source.sdl_data, header.source, ETHER_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
destination.sdl_len = sizeof(sockaddr_dl);
|
||||||
|
destination.sdl_family = AF_DLI;
|
||||||
|
destination.sdl_index = device->index;
|
||||||
|
destination.sdl_type = IFT_ETHER;
|
||||||
|
destination.sdl_nlen = destination.sdl_slen = 0;
|
||||||
|
destination.sdl_alen = ETHER_ADDRESS_LENGTH;
|
||||||
|
memcpy(destination.sdl_data, header.destination, ETHER_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
// mark buffer if it was a broadcast/multicast packet
|
||||||
|
if (!memcmp(header.destination, kBroadcastAddress, ETHER_ADDRESS_LENGTH))
|
||||||
|
buffer->flags |= MSG_BCAST;
|
||||||
|
else if (header.destination[0] & 0x01)
|
||||||
|
buffer->flags |= MSG_MCAST;
|
||||||
|
|
||||||
|
return ETHER_FRAME_TYPE | type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_frame_init(struct net_interface *interface, net_datalink_protocol **_protocol)
|
||||||
|
{
|
||||||
|
net_stack_module_info *stack;
|
||||||
|
status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&stack);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = stack->register_device_deframer(interface->device, ðernet_deframe);
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
ethernet_frame_protocol *protocol = new (std::nothrow) ethernet_frame_protocol;
|
||||||
|
if (protocol == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
*_protocol = protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_frame_uninit(net_datalink_protocol *protocol)
|
||||||
|
{
|
||||||
|
net_stack_module_info *stack;
|
||||||
|
if (get_module(NET_STACK_MODULE_NAME, (module_info **)&stack) == B_OK) {
|
||||||
|
stack->unregister_device_deframer(protocol->interface->device);
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_frame_send_data(net_datalink_protocol *protocol,
|
||||||
|
net_buffer *buffer)
|
||||||
|
{
|
||||||
|
struct sockaddr_dl &source = *(struct sockaddr_dl *)&buffer->source;
|
||||||
|
struct sockaddr_dl &destination = *(struct sockaddr_dl *)&buffer->destination;
|
||||||
|
|
||||||
|
if (source.sdl_family != AF_DLI || source.sdl_type != IFT_ETHER)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
NetBufferPrepend<ether_header> bufferHeader(buffer);
|
||||||
|
if (bufferHeader.Status() < B_OK)
|
||||||
|
return bufferHeader.Status();
|
||||||
|
|
||||||
|
ether_header &header = bufferHeader.Data();
|
||||||
|
|
||||||
|
header.type = htons(source.sdl_e_type);
|
||||||
|
memcpy(header.source, source.sdl_data, ETHER_ADDRESS_LENGTH);
|
||||||
|
if (buffer->flags & MSG_BCAST)
|
||||||
|
memcpy(header.destination, kBroadcastAddress, ETHER_ADDRESS_LENGTH);
|
||||||
|
else
|
||||||
|
memcpy(header.destination, destination.sdl_data, ETHER_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
bufferHeader.Detach();
|
||||||
|
// make sure the framing is already written to the buffer at this point
|
||||||
|
|
||||||
|
return protocol->next->module->send_data(protocol->next, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_frame_up(net_datalink_protocol *_protocol)
|
||||||
|
{
|
||||||
|
ethernet_frame_protocol *protocol = (ethernet_frame_protocol *)_protocol;
|
||||||
|
|
||||||
|
return protocol->next->module->interface_up(protocol->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ethernet_frame_down(net_datalink_protocol *protocol)
|
||||||
|
{
|
||||||
|
return protocol->next->module->interface_down(protocol->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_frame_control(net_datalink_protocol *protocol,
|
||||||
|
int32 op, void *argument, size_t length)
|
||||||
|
{
|
||||||
|
return protocol->next->module->control(protocol->next, op, argument, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
ethernet_frame_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
return get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule);
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static net_datalink_protocol_module_info sEthernetFrameModule = {
|
||||||
|
{
|
||||||
|
"network/datalink_protocols/ethernet_frame/v1",
|
||||||
|
0,
|
||||||
|
ethernet_frame_std_ops
|
||||||
|
},
|
||||||
|
ethernet_frame_init,
|
||||||
|
ethernet_frame_uninit,
|
||||||
|
ethernet_frame_send_data,
|
||||||
|
ethernet_frame_up,
|
||||||
|
ethernet_frame_down,
|
||||||
|
ethernet_frame_control,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sEthernetFrameModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network devices ;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network devices ethernet ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network devices loopback ;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network devices ethernet ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon ethernet : kernel haiku_network devices :
|
||||||
|
ethernet.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/devices
|
||||||
|
: ethernet ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network protocols ;
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <ether_driver.h>
|
||||||
|
#include <ethernet.h>
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct ethernet_device : net_device {
|
||||||
|
int fd;
|
||||||
|
uint32 frame_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct net_buffer_module_info *sBufferModule;
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_init(const char *name, net_device **_device)
|
||||||
|
{
|
||||||
|
// make sure this is a device in /dev/net, but not the
|
||||||
|
// networking (userland) stack driver
|
||||||
|
if (strncmp(name, "/dev/net/", 9) || !strcmp(name, "/dev/net/stack")
|
||||||
|
|| !strcmp(name, "/dev/net/userland_server"))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
ethernet_device *device = new (std::nothrow) ethernet_device;
|
||||||
|
if (device == NULL) {
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(device, 0, sizeof(ethernet_device));
|
||||||
|
|
||||||
|
strcpy(device->name, name);
|
||||||
|
device->flags = IFF_BROADCAST;
|
||||||
|
device->type = IFT_ETHER;
|
||||||
|
device->mtu = 1500;
|
||||||
|
device->header_length = ETHER_HEADER_LENGTH;
|
||||||
|
device->fd = -1;
|
||||||
|
|
||||||
|
*_device = device;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_uninit(net_device *device)
|
||||||
|
{
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
delete device;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_up(net_device *_device)
|
||||||
|
{
|
||||||
|
ethernet_device *device = (ethernet_device *)_device;
|
||||||
|
|
||||||
|
device->fd = open(device->name, O_RDWR);
|
||||||
|
if (device->fd < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
ether_init_params params;
|
||||||
|
memset(¶ms, 0, sizeof(ether_init_params));
|
||||||
|
if (ioctl(device->fd, ETHER_INIT, ¶ms, sizeof(ether_init_params)) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) {
|
||||||
|
// this call is obviously optional
|
||||||
|
device->frame_size = ETHER_MAX_FRAME_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->address.length = ETHER_ADDRESS_LENGTH;
|
||||||
|
device->mtu = device->frame_size - device->header_length;
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
close(device->fd);
|
||||||
|
device->fd = -1;
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ethernet_down(net_device *_device)
|
||||||
|
{
|
||||||
|
ethernet_device *device = (ethernet_device *)_device;
|
||||||
|
close(device->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_control(net_device *_device, int32 op, void *argument,
|
||||||
|
size_t length)
|
||||||
|
{
|
||||||
|
ethernet_device *device = (ethernet_device *)_device;
|
||||||
|
return ioctl(device->fd, op, argument, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_send_data(net_device *_device, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
ethernet_device *device = (ethernet_device *)_device;
|
||||||
|
|
||||||
|
dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags);
|
||||||
|
if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (sBufferModule->count_iovecs(buffer) > 1) {
|
||||||
|
dprintf("scattered I/O is not yet supported by ethernet device.\n");
|
||||||
|
return B_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iovec iovec;
|
||||||
|
sBufferModule->get_iovecs(buffer, &iovec, 1);
|
||||||
|
|
||||||
|
dump_block((const char *)iovec.iov_base, buffer->size, " ");
|
||||||
|
ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len);
|
||||||
|
dprintf("sent: %ld\n", bytesWritten);
|
||||||
|
if (bytesWritten < 0) {
|
||||||
|
device->stats.send.errors++;
|
||||||
|
return bytesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->stats.send.packets++;
|
||||||
|
device->stats.send.bytes += bytesWritten;
|
||||||
|
|
||||||
|
sBufferModule->free(buffer);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_receive_data(net_device *_device, net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
ethernet_device *device = (ethernet_device *)_device;
|
||||||
|
|
||||||
|
// TODO: better header space
|
||||||
|
net_buffer *buffer = sBufferModule->create(256);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
// TODO: this only works for standard ethernet frames - we need iovecs
|
||||||
|
// for jumbo frame support (or a separate read buffer)!
|
||||||
|
// It would be even nicer to get net_buffers from the ethernet driver
|
||||||
|
// directly.
|
||||||
|
|
||||||
|
ssize_t bytesRead;
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
status_t status = sBufferModule->append_size(buffer, device->frame_size, &data);
|
||||||
|
if (status == B_OK && data == NULL) {
|
||||||
|
dprintf("scattered I/O is not yet supported by ethernet device.\n");
|
||||||
|
status = B_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
bytesRead = read(device->fd, data, device->frame_size);
|
||||||
|
if (bytesRead < 0) {
|
||||||
|
device->stats.receive.errors++;
|
||||||
|
status = bytesRead;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = sBufferModule->trim(buffer, bytesRead);
|
||||||
|
if (status < B_OK) {
|
||||||
|
device->stats.receive.dropped++;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->stats.receive.bytes += bytesRead;
|
||||||
|
device->stats.receive.packets++;
|
||||||
|
|
||||||
|
*_buffer = buffer;
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
sBufferModule->free(buffer);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_set_mtu(net_device *_device, size_t mtu)
|
||||||
|
{
|
||||||
|
ethernet_device *device = (ethernet_device *)_device;
|
||||||
|
|
||||||
|
if (mtu > device->frame_size - ETHER_HEADER_LENGTH
|
||||||
|
|| mtu <= ETHER_HEADER_LENGTH + 10)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
device->mtu = mtu;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_set_promiscuous(net_device *device, bool promiscuous)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_set_media(net_device *device, uint32 media)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_get_multicast_addrs(struct net_device *device,
|
||||||
|
net_hardware_address **addressArray, uint32 count)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ethernet_set_multicast_addrs(struct net_device *device,
|
||||||
|
const net_hardware_address **addressArray, uint32 count)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
ethernet_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_device_module_info sEthernetModule = {
|
||||||
|
{
|
||||||
|
"network/devices/ethernet/v1",
|
||||||
|
0,
|
||||||
|
ethernet_std_ops
|
||||||
|
},
|
||||||
|
ethernet_init,
|
||||||
|
ethernet_uninit,
|
||||||
|
ethernet_up,
|
||||||
|
ethernet_down,
|
||||||
|
ethernet_control,
|
||||||
|
ethernet_send_data,
|
||||||
|
ethernet_receive_data,
|
||||||
|
ethernet_set_mtu,
|
||||||
|
ethernet_set_promiscuous,
|
||||||
|
ethernet_set_media,
|
||||||
|
ethernet_get_multicast_addrs,
|
||||||
|
ethernet_set_multicast_addrs
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sEthernetModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network devices loopback ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon loopback : kernel haiku_network devices :
|
||||||
|
loopback.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/devices
|
||||||
|
: loopback ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network protocols ;
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct loopback_device : net_device {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_init(const char *name, net_device **_device)
|
||||||
|
{
|
||||||
|
if (strncmp(name, "loop", 4))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
loopback_device *device = new (std::nothrow) loopback_device;
|
||||||
|
if (device == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset(device, 0, sizeof(loopback_device));
|
||||||
|
|
||||||
|
strcpy(device->name, name);
|
||||||
|
device->flags = IFF_LOOPBACK;
|
||||||
|
device->type = IFT_LOOP;
|
||||||
|
device->mtu = 16384;
|
||||||
|
|
||||||
|
*_device = device;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_uninit(net_device *device)
|
||||||
|
{
|
||||||
|
delete device;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_up(net_device *device)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
loopback_down(net_device *device)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_control(net_device *device, int32 op, void *argument,
|
||||||
|
size_t length)
|
||||||
|
{
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_send_data(net_device *device, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_receive_data(net_device *device, net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_set_mtu(net_device *device, size_t mtu)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_set_promiscuous(net_device *device, bool promiscuous)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
loopback_set_media(net_device *device, uint32 media)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
loopback_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_device_module_info sLoopbackModule = {
|
||||||
|
{
|
||||||
|
"network/devices/loopback/v1",
|
||||||
|
0,
|
||||||
|
loopback_std_ops
|
||||||
|
},
|
||||||
|
loopback_init,
|
||||||
|
loopback_uninit,
|
||||||
|
loopback_up,
|
||||||
|
loopback_down,
|
||||||
|
loopback_control,
|
||||||
|
loopback_send_data,
|
||||||
|
loopback_receive_data,
|
||||||
|
loopback_set_mtu,
|
||||||
|
loopback_set_promiscuous,
|
||||||
|
loopback_set_media,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sLoopbackModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network protocols ;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network protocols icmp ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network protocols ipv4 ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network protocols tcp ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel network protocols udp ;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network protocols icmp ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon icmp : kernel haiku_network protocols :
|
||||||
|
icmp.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||||
|
: icmp ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network protocols ;
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_datalink.h>
|
||||||
|
#include <net_protocol.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
#include <NetBufferUtilities.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct icmp_header {
|
||||||
|
uint8 type;
|
||||||
|
uint8 code;
|
||||||
|
uint16 checksum;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16 id;
|
||||||
|
uint16 sequence;
|
||||||
|
} echo;
|
||||||
|
struct {
|
||||||
|
in_addr_t gateway;
|
||||||
|
} redirect;
|
||||||
|
struct {
|
||||||
|
uint16 _reserved;
|
||||||
|
uint16 next_mtu;
|
||||||
|
} path_mtu;
|
||||||
|
uint32 zero;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ICMP_TYPE_ECHO_REPLY 0
|
||||||
|
#define ICMP_TYPE_UNREACH 3
|
||||||
|
#define ICMP_TYPE_REDIRECT 5
|
||||||
|
#define ICMP_TYPE_ECHO_REQUEST 8
|
||||||
|
|
||||||
|
// type unreach codes
|
||||||
|
#define ICMP_CODE_UNREACH_NEED_FRAGMENT 4 // this is used for path MTU discovery
|
||||||
|
|
||||||
|
struct icmp_protocol : net_protocol {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static net_stack_module_info *sStackModule;
|
||||||
|
struct net_buffer_module_info *sBufferModule;
|
||||||
|
|
||||||
|
|
||||||
|
net_protocol *
|
||||||
|
icmp_init_protocol(net_socket *socket)
|
||||||
|
{
|
||||||
|
icmp_protocol *protocol = new (std::nothrow) icmp_protocol;
|
||||||
|
if (protocol == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_uninit_protocol(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
delete protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_open(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_close(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_free(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_connect(net_protocol *protocol, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_control(net_protocol *protocol, int level, int option, void *value,
|
||||||
|
size_t *_length)
|
||||||
|
{
|
||||||
|
return protocol->next->module->control(protocol->next, level, option,
|
||||||
|
value, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_bind(net_protocol *protocol, struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_unbind(net_protocol *protocol, struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_listen(net_protocol *protocol, int count)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_shutdown(net_protocol *protocol, int direction)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_send_data(net_protocol *protocol, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return protocol->next->module->send_data(protocol->next, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_send_routed_data(net_protocol *protocol, struct net_route *route,
|
||||||
|
net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return protocol->next->module->send_routed_data(protocol->next, route, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
icmp_send_avail(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
|
||||||
|
net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
icmp_read_avail(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct net_domain *
|
||||||
|
icmp_get_domain(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return protocol->next->module->get_domain(protocol->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
icmp_get_mtu(net_protocol *protocol, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return protocol->next->module->get_mtu(protocol->next, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_receive_data(net_buffer *buffer)
|
||||||
|
{
|
||||||
|
dprintf("ICMP received some data, buffer length %lu\n", buffer->size);
|
||||||
|
|
||||||
|
NetBufferHeader<icmp_header> bufferHeader(buffer);
|
||||||
|
if (bufferHeader.Status() < B_OK)
|
||||||
|
return bufferHeader.Status();
|
||||||
|
|
||||||
|
icmp_header &header = bufferHeader.Data();
|
||||||
|
bufferHeader.Detach();
|
||||||
|
// the pointer stays valid after this
|
||||||
|
|
||||||
|
dprintf(" got type %u, code %u, checksum %u\n", header.type, header.code,
|
||||||
|
ntohs(header.checksum));
|
||||||
|
dprintf(" computed checksum: %ld\n", sBufferModule->checksum(buffer, 0, buffer->size, true));
|
||||||
|
if (sBufferModule->checksum(buffer, 0, buffer->size, true) != 0)
|
||||||
|
return B_BAD_DATA;
|
||||||
|
|
||||||
|
switch (header.type) {
|
||||||
|
case ICMP_TYPE_ECHO_REPLY:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ICMP_TYPE_ECHO_REQUEST:
|
||||||
|
{
|
||||||
|
net_domain *domain;
|
||||||
|
if (buffer->interface != NULL)
|
||||||
|
domain = buffer->interface->domain;
|
||||||
|
else
|
||||||
|
domain = sStackModule->get_domain(buffer->source.ss_family);
|
||||||
|
if (domain == NULL || domain->module == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
net_buffer *reply = sBufferModule->duplicate(buffer);
|
||||||
|
if (reply == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// switch source/destination address
|
||||||
|
memcpy(&reply->source, &buffer->destination, buffer->destination.ss_len);
|
||||||
|
memcpy(&reply->destination, &buffer->source, buffer->source.ss_len);
|
||||||
|
|
||||||
|
// There already is an ICMP header, and we'll reuse it
|
||||||
|
icmp_header *header;
|
||||||
|
status_t status = sBufferModule->direct_access(reply,
|
||||||
|
0, sizeof(icmp_header), (void **)&header);
|
||||||
|
if (status == B_OK) {
|
||||||
|
header->type = ICMP_TYPE_ECHO_REPLY;
|
||||||
|
header->code = 0;
|
||||||
|
header->checksum = 0;
|
||||||
|
header->checksum = sBufferModule->checksum(reply, 0, reply->size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == B_OK)
|
||||||
|
status = domain->module->send_data(NULL, reply);
|
||||||
|
|
||||||
|
if (status < B_OK) {
|
||||||
|
sBufferModule->free(reply);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sBufferModule->free(buffer);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_error(uint32 code, net_buffer *data)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
icmp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
|
||||||
|
void *errorData)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
icmp_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
{
|
||||||
|
status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule);
|
||||||
|
if (status < B_OK) {
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
sStackModule->register_domain_protocols(AF_INET, SOCK_DGRAM, IPPROTO_ICMP,
|
||||||
|
"network/protocols/icmp/v1",
|
||||||
|
"network/protocols/ipv4/v1",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
sStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_ICMP,
|
||||||
|
"network/protocols/icmp/v1");
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_protocol_module_info sICMPModule = {
|
||||||
|
{
|
||||||
|
"network/protocols/icmp/v1",
|
||||||
|
0,
|
||||||
|
icmp_std_ops
|
||||||
|
},
|
||||||
|
icmp_init_protocol,
|
||||||
|
icmp_uninit_protocol,
|
||||||
|
icmp_open,
|
||||||
|
icmp_close,
|
||||||
|
icmp_free,
|
||||||
|
icmp_connect,
|
||||||
|
icmp_accept,
|
||||||
|
icmp_control,
|
||||||
|
icmp_bind,
|
||||||
|
icmp_unbind,
|
||||||
|
icmp_listen,
|
||||||
|
icmp_shutdown,
|
||||||
|
icmp_send_data,
|
||||||
|
icmp_send_routed_data,
|
||||||
|
icmp_send_avail,
|
||||||
|
icmp_read_data,
|
||||||
|
icmp_read_avail,
|
||||||
|
icmp_get_domain,
|
||||||
|
icmp_get_mtu,
|
||||||
|
icmp_receive_data,
|
||||||
|
icmp_error,
|
||||||
|
icmp_error_reply,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sICMPModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network protocols ipv4 ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon ipv4 : kernel haiku_network protocols :
|
||||||
|
ipv4.cpp
|
||||||
|
ipv4_address.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||||
|
: ipv4 ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network protocols ;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,399 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
* Oliver Tappe, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_datalink.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <NetUtilities.h>
|
||||||
|
|
||||||
|
#include <memory.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Routing utility function: copies address \a from into a new address
|
||||||
|
that is put into \a to.
|
||||||
|
If \a replaceWithZeros is set \a from will be replaced by an empty
|
||||||
|
address.
|
||||||
|
If a \a mask is given it is applied to \a from (such that \a to is the
|
||||||
|
result of \a from & \a mask).
|
||||||
|
\return B_OK if the address could be copied
|
||||||
|
\return B_NO_MEMORY if the new address could not be allocated
|
||||||
|
\return B_MISMATCHED_VALUES if \a address does not match family AF_INET
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_copy_address(const sockaddr *from, sockaddr **to,
|
||||||
|
bool replaceWithZeros = false, sockaddr *mask = NULL)
|
||||||
|
{
|
||||||
|
if (replaceWithZeros) {
|
||||||
|
*to = (sockaddr *)malloc(sizeof(sockaddr_in));
|
||||||
|
if (*to == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset(*to, 0, sizeof(sockaddr_in));
|
||||||
|
(*to)->sa_family = AF_INET;
|
||||||
|
(*to)->sa_len = sizeof(sockaddr_in);
|
||||||
|
} else {
|
||||||
|
if (from == NULL)
|
||||||
|
return B_OK;
|
||||||
|
if (from->sa_family != AF_INET)
|
||||||
|
return B_MISMATCHED_VALUES;
|
||||||
|
|
||||||
|
*to = (sockaddr *)malloc(sizeof(sockaddr_in));
|
||||||
|
if (*to == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memcpy(*to, from, sizeof(sockaddr_in));
|
||||||
|
|
||||||
|
if (mask != NULL) {
|
||||||
|
((sockaddr_in *)*to)->sin_addr.s_addr
|
||||||
|
&= ((sockaddr_in *)mask)->sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Routing utility function: applies \a mask to given \a address and puts
|
||||||
|
the resulting address into \a result.
|
||||||
|
\return B_OK if the mask has been applied
|
||||||
|
\return B_BAD_VALUE if \a address or \a mask is NULL
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_mask_address(const sockaddr *address, const sockaddr *mask, sockaddr *result)
|
||||||
|
{
|
||||||
|
if (address == NULL || result == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
memcpy(result, address, sizeof(sockaddr_in));
|
||||||
|
if (mask != NULL) {
|
||||||
|
((sockaddr_in *)result)->sin_addr.s_addr
|
||||||
|
&= ((sockaddr_in *)mask)->sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Checks if the given \a address is the empty address. By default, the port
|
||||||
|
is checked, too, but you can avoid that by passing \a checkPort = false.
|
||||||
|
\return true if \a address is NULL, uninitialized or the empty address,
|
||||||
|
false if not
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ipv4_is_empty_address(const sockaddr *address, bool checkPort)
|
||||||
|
{
|
||||||
|
if (address == NULL || address->sa_len == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return ((sockaddr_in *)address)->sin_addr.s_addr == 0
|
||||||
|
&& (!checkPort || ((sockaddr_in *)address)->sin_port == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Compares the IP-addresses of the two given address structures \a a and \a b.
|
||||||
|
\return true if IP-addresses of \a a and \a b are equal, false if not
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ipv4_equal_addresses(const sockaddr *a, const sockaddr *b)
|
||||||
|
{
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return true;
|
||||||
|
if (a != NULL && b == NULL)
|
||||||
|
return ipv4_is_empty_address(a, false);
|
||||||
|
if (a == NULL && b != NULL)
|
||||||
|
return ipv4_is_empty_address(b, false);
|
||||||
|
|
||||||
|
return ((sockaddr_in *)a)->sin_addr.s_addr == ((sockaddr_in *)b)->sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Compares the ports of the two given address structures \a a and \a b.
|
||||||
|
\return true if ports of \a a and \a b are equal, false if not
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ipv4_equal_ports(const sockaddr *a, const sockaddr *b)
|
||||||
|
{
|
||||||
|
uint16 portA = a ? ((sockaddr_in *)a)->sin_port : 0;
|
||||||
|
uint16 portB = b ? ((sockaddr_in *)b)->sin_port : 0;
|
||||||
|
return portA == portB;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Compares the IP-addresses and ports of the two given address structures
|
||||||
|
\a a and \a b.
|
||||||
|
\return true if IP-addresses and ports of \a a and \a b are equal, false if not
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ipv4_equal_addresses_and_ports(const sockaddr *a, const sockaddr *b)
|
||||||
|
{
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return true;
|
||||||
|
if (a != NULL && b == NULL)
|
||||||
|
return ipv4_is_empty_address(a, true);
|
||||||
|
if (a == NULL && b != NULL)
|
||||||
|
return ipv4_is_empty_address(b, true);
|
||||||
|
|
||||||
|
return ((sockaddr_in *)a)->sin_addr.s_addr == ((sockaddr_in *)b)->sin_addr.s_addr
|
||||||
|
&& ((sockaddr_in *)a)->sin_port == ((sockaddr_in *)b)->sin_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Applies the given \a mask two \a a and \a b and then checks whether
|
||||||
|
the masked addresses match.
|
||||||
|
\return true if \a a matches \a b after masking both, false if not
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ipv4_equal_masked_addresses(const sockaddr *a, const sockaddr *b,
|
||||||
|
const sockaddr *mask)
|
||||||
|
{
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
sockaddr emptyAddr;
|
||||||
|
if (a == NULL || b == NULL) {
|
||||||
|
memset(&emptyAddr, 0, sizeof(sockaddr_in));
|
||||||
|
if (a == NULL)
|
||||||
|
a = &emptyAddr;
|
||||||
|
else if (b == NULL)
|
||||||
|
b = &emptyAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 aValue = ((sockaddr_in *)a)->sin_addr.s_addr;
|
||||||
|
uint32 bValue = ((sockaddr_in *)b)->sin_addr.s_addr;
|
||||||
|
|
||||||
|
if (!mask)
|
||||||
|
return aValue == bValue;
|
||||||
|
|
||||||
|
uint32 maskValue = ((sockaddr_in *)mask)->sin_addr.s_addr;
|
||||||
|
return (aValue & maskValue) == (bValue & maskValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Routing utility function: determines the least significant bit that is set
|
||||||
|
in the given \a mask.
|
||||||
|
\return the number of the first bit that is set (0-32, where 32 means
|
||||||
|
that there's no bit set in the mask).
|
||||||
|
*/
|
||||||
|
static int32
|
||||||
|
ipv4_first_mask_bit(sockaddr *_mask)
|
||||||
|
{
|
||||||
|
if (_mask == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint32 mask = ntohl(((sockaddr_in *)_mask)->sin_addr.s_addr);
|
||||||
|
|
||||||
|
// TODO: this can be optimized, there are also some nice assembler mnemonics for this
|
||||||
|
int8 bit = 0;
|
||||||
|
for (uint32 bitMask = 1; bit < 32; bitMask <<= 1, bit++) {
|
||||||
|
if (mask & bitMask)
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Routing utility function: checks the given \a mask for correctness (which
|
||||||
|
means that (starting with LSB) consists zero or more unset bits, followed
|
||||||
|
by bits that are all set).
|
||||||
|
\return true if \a mask is ok, false if not
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ipv4_check_mask(const sockaddr *_mask)
|
||||||
|
{
|
||||||
|
if (_mask == NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32 mask = ntohl(((sockaddr_in *)_mask)->sin_addr.s_addr);
|
||||||
|
|
||||||
|
// A mask (from LSB) starts with zeros, after the first one, only ones
|
||||||
|
// are allowed:
|
||||||
|
bool zero = true;
|
||||||
|
int8 bit = 0;
|
||||||
|
for (uint32 bitMask = 1; bit < 32; bitMask <<= 1, bit++) {
|
||||||
|
if (mask & bitMask) {
|
||||||
|
if (zero)
|
||||||
|
zero = false;
|
||||||
|
} else if (!zero)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Creates a buffer for the given \a address and prints the address into
|
||||||
|
it (hexadecimal representation in host byte order or '<none>').
|
||||||
|
If \a printPort is set, the port is printed, too.
|
||||||
|
\return B_OK if the address could be printed, \a buffer will point to
|
||||||
|
the resulting string
|
||||||
|
\return B_BAD_VALUE if no buffer has been given
|
||||||
|
\return B_NO_MEMORY if the buffer could not be allocated
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_print_address(const sockaddr *address, char **_buffer, bool printPort)
|
||||||
|
{
|
||||||
|
if (_buffer == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
int bufLen = printPort ? 15 : 9;
|
||||||
|
char *buffer = (char *)malloc(bufLen);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
if (address == NULL)
|
||||||
|
strcpy(buffer, "<none>");
|
||||||
|
else if (printPort) {
|
||||||
|
sprintf(buffer, "%08lx:%u", ntohl(((sockaddr_in *)address)->sin_addr.s_addr),
|
||||||
|
ntohs(((sockaddr_in *)address)->sin_port));
|
||||||
|
} else
|
||||||
|
sprintf(buffer, "%08lx", ntohl(((sockaddr_in *)address)->sin_addr.s_addr));
|
||||||
|
*_buffer = buffer;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Determines the port of the given \a address.
|
||||||
|
\return uint16 representing the port-nr
|
||||||
|
*/
|
||||||
|
static uint16
|
||||||
|
ipv4_get_port(const sockaddr *address)
|
||||||
|
{
|
||||||
|
if (address == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ((sockaddr_in *)address)->sin_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the port of the given \a address to \a port.
|
||||||
|
\return B_OK if the port has been set
|
||||||
|
\return B_BAD_VALUE if \a address is NULL
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_set_port(sockaddr *address, uint16 port)
|
||||||
|
{
|
||||||
|
if (address == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
((sockaddr_in *)address)->sin_port = port;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets \a address to \a from.
|
||||||
|
\return B_OK if \a from has been copied into \a address
|
||||||
|
\return B_BAD_VALUE if either \a address or \a from is NULL
|
||||||
|
\return B_MISMATCHED_VALUES if from is not of family AF_INET
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_set_to(sockaddr *address, const sockaddr *from)
|
||||||
|
{
|
||||||
|
if (address == NULL || from == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (from->sa_family != AF_INET)
|
||||||
|
return B_MISMATCHED_VALUES;
|
||||||
|
|
||||||
|
memcpy(address, from, sizeof(sockaddr_in));
|
||||||
|
address->sa_len = sizeof(sockaddr_in);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets \a address to the empty address (0.0.0.0).
|
||||||
|
\return B_OK if \a address has been set
|
||||||
|
\return B_BAD_VALUE if \a address is NULL
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_set_to_empty_address(sockaddr *address)
|
||||||
|
{
|
||||||
|
if (address == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
memset(address, 0, sizeof(sockaddr_in));
|
||||||
|
address->sa_len = sizeof(sockaddr_in);
|
||||||
|
address->sa_family = AF_INET;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Computes a hash-value of the given addresses \a ourAddress
|
||||||
|
and \a peerAddress.
|
||||||
|
\return uint32 representing the hash-value
|
||||||
|
*/
|
||||||
|
static uint32
|
||||||
|
ipv4_hash_address_pair(const sockaddr *ourAddress, const sockaddr *peerAddress)
|
||||||
|
{
|
||||||
|
int32 hash = (((sockaddr_in *)ourAddress)->sin_port
|
||||||
|
| ((sockaddr_in *)peerAddress)->sin_port << 16)
|
||||||
|
^ ((sockaddr_in *)ourAddress)->sin_addr.s_addr
|
||||||
|
^ ((sockaddr_in *)peerAddress)->sin_addr.s_addr;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Adds the given \a address to the IP-checksum \a checksum.
|
||||||
|
\return B_OK if \a address has been added to the checksum
|
||||||
|
\return B_BAD_VALUE if either \a address or \a checksum is NULL
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ipv4_checksum_address(Checksum *checksum, const sockaddr *address)
|
||||||
|
{
|
||||||
|
if (checksum == NULL || address == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
(*checksum) << (uint32)((sockaddr_in *)address)->sin_addr.s_addr;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_address_module_info gIPv4AddressModule = {
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
ipv4_copy_address,
|
||||||
|
ipv4_mask_address,
|
||||||
|
ipv4_equal_addresses,
|
||||||
|
ipv4_equal_ports,
|
||||||
|
ipv4_equal_addresses_and_ports,
|
||||||
|
ipv4_equal_masked_addresses,
|
||||||
|
ipv4_is_empty_address,
|
||||||
|
ipv4_first_mask_bit,
|
||||||
|
ipv4_check_mask,
|
||||||
|
ipv4_print_address,
|
||||||
|
ipv4_get_port,
|
||||||
|
ipv4_set_port,
|
||||||
|
ipv4_set_to,
|
||||||
|
ipv4_set_to_empty_address,
|
||||||
|
ipv4_hash_address_pair,
|
||||||
|
ipv4_checksum_address,
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Oliver Tappe, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef IPV4_ADDRESS_H
|
||||||
|
#define IPV4_ADDRESS_H
|
||||||
|
|
||||||
|
extern struct net_address_module_info gIPv4AddressModule;
|
||||||
|
|
||||||
|
#endif // IPV4_ADDRESS_H
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network protocols tcp ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon tcp : kernel haiku_network protocols :
|
||||||
|
tcp.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||||
|
: tcp ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network protocols ;
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_protocol.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "tcp.h"
|
||||||
|
|
||||||
|
struct tcp_protocol : net_protocol {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
net_protocol *
|
||||||
|
tcp_init_protocol(net_socket *socket)
|
||||||
|
{
|
||||||
|
tcp_protocol *protocol = new (std::nothrow) tcp_protocol;
|
||||||
|
if (protocol == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_uninit_protocol(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
delete protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_open(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_close(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_free(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_connect(net_protocol *protocol, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_control(net_protocol *protocol, int level, int option, void *value,
|
||||||
|
size_t *_length)
|
||||||
|
{
|
||||||
|
return protocol->next->module->control(protocol->next, level, option,
|
||||||
|
value, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_bind(net_protocol *protocol, struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_unbind(net_protocol *protocol, struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_listen(net_protocol *protocol, int count)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_shutdown(net_protocol *protocol, int direction)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_send_data(net_protocol *protocol, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return protocol->next->module->send_data(protocol->next, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_send_routed_data(net_protocol *protocol, struct net_route *route,
|
||||||
|
net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return protocol->next->module->send_routed_data(protocol->next, route, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
tcp_send_avail(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
|
||||||
|
net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
tcp_read_avail(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct net_domain *
|
||||||
|
tcp_get_domain(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return protocol->next->module->get_domain(protocol->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
tcp_get_mtu(net_protocol *protocol, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return protocol->next->module->get_mtu(protocol->next, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_receive_data(net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_error(uint32 code, net_buffer *data)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
tcp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
|
||||||
|
void *errorData)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
tcp_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
{
|
||||||
|
net_stack_module_info *stack;
|
||||||
|
status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&stack);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
stack->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_IP,
|
||||||
|
"network/protocols/tcp/v1",
|
||||||
|
"network/protocols/ipv4/v1",
|
||||||
|
NULL);
|
||||||
|
stack->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||||
|
"network/protocols/tcp/v1",
|
||||||
|
"network/protocols/ipv4/v1",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
stack->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
|
||||||
|
"network/protocols/tcp/v1");
|
||||||
|
|
||||||
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_protocol_module_info sTCPModule = {
|
||||||
|
{
|
||||||
|
"network/protocols/tcp/v1",
|
||||||
|
0,
|
||||||
|
tcp_std_ops
|
||||||
|
},
|
||||||
|
tcp_init_protocol,
|
||||||
|
tcp_uninit_protocol,
|
||||||
|
tcp_open,
|
||||||
|
tcp_close,
|
||||||
|
tcp_free,
|
||||||
|
tcp_connect,
|
||||||
|
tcp_accept,
|
||||||
|
tcp_control,
|
||||||
|
tcp_bind,
|
||||||
|
tcp_unbind,
|
||||||
|
tcp_listen,
|
||||||
|
tcp_shutdown,
|
||||||
|
tcp_send_data,
|
||||||
|
tcp_send_routed_data,
|
||||||
|
tcp_send_avail,
|
||||||
|
tcp_read_data,
|
||||||
|
tcp_read_avail,
|
||||||
|
tcp_get_domain,
|
||||||
|
tcp_get_mtu,
|
||||||
|
tcp_receive_data,
|
||||||
|
tcp_error,
|
||||||
|
tcp_error_reply,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sTCPModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Andrew Galante, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CLOSED,
|
||||||
|
LISTEN,
|
||||||
|
SYN_SENT,
|
||||||
|
SYN_RCVD,
|
||||||
|
ESTABLISHED,
|
||||||
|
CLOSE_WAIT,
|
||||||
|
LAST_ACK,
|
||||||
|
FIN_WAIT1,
|
||||||
|
FIN_WAIT2,
|
||||||
|
CLOSING,
|
||||||
|
TIME_WAIT
|
||||||
|
} tcp_state;
|
||||||
|
|
||||||
|
struct tcp_header {
|
||||||
|
uint16 source_port;
|
||||||
|
uint16 destination_port;
|
||||||
|
uint32 sequence_num;
|
||||||
|
uint32 acknowledge_num;
|
||||||
|
struct {
|
||||||
|
#if B_HOST_IS_LENDIAN == 1
|
||||||
|
uint8 reserved : 4;
|
||||||
|
uint8 header_length : 4;
|
||||||
|
#else
|
||||||
|
uint8 header_length : 4;
|
||||||
|
uint8 reserved : 4;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
uint8 flags;
|
||||||
|
uint16 advertised_window;
|
||||||
|
uint16 checksum;
|
||||||
|
uint16 urgent_ptr;
|
||||||
|
uint32 options;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TCP flag constants */
|
||||||
|
#define TCP_FLG_CWR 0x80 /* Congestion Window Reduced */
|
||||||
|
#define TCP_FLG_ECN 0x40 /* Explicit Congestion Notification echo */
|
||||||
|
#define TCP_FLG_URG 0x20 /* URGent */
|
||||||
|
#define TCP_FLG_ACK 0x10 /* ACKnowledge */
|
||||||
|
#define TCP_FLG_PUS 0x08 /* PUSh */
|
||||||
|
#define TCP_FLG_RST 0x04 /* ReSeT */
|
||||||
|
#define TCP_FLG_SYN 0x02 /* SYNchronize */
|
||||||
|
#define TCP_FLG_FIN 0x01 /* FINish */
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network protocols udp ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon udp : kernel haiku_network protocols :
|
||||||
|
udp.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||||
|
: udp ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network protocols ;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel network stack ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
|
if $(TARGET_PLATFORM) != haiku {
|
||||||
|
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||||
|
# Needed for <support/Errors.h> and maybe other stuff.
|
||||||
|
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||||
|
# We need the public network headers also when not compiling for Haiku.
|
||||||
|
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
KernelAddon stack : kernel haiku_network :
|
||||||
|
datalink.cpp
|
||||||
|
domains.cpp
|
||||||
|
interfaces.cpp
|
||||||
|
net_buffer.cpp
|
||||||
|
net_socket.cpp
|
||||||
|
link.cpp
|
||||||
|
radix.c
|
||||||
|
routes.cpp
|
||||||
|
stack.cpp
|
||||||
|
utility.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network
|
||||||
|
: stack ;
|
||||||
|
|
||||||
|
Package haiku-networkingkit-cvs :
|
||||||
|
haiku :
|
||||||
|
boot home config add-ons kernel haiku_network ;
|
||||||
@@ -0,0 +1,750 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, 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;
|
||||||
|
|
||||||
|
return list_domain_interfaces(config.ifc_buf, config.ifc_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
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);
|
||||||
|
|
||||||
|
uint32 matchedType = 0;
|
||||||
|
|
||||||
|
net_interface *interface = NULL;
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface *)list_get_next_item(
|
||||||
|
&domain->interfaces, interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
if (interface->address == NULL)
|
||||||
|
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)
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
|
||||||
|
thread_id thread = spawn_kernel_thread(device_reader_thread, name,
|
||||||
|
B_REAL_TIME_DISPLAY_PRIORITY - 10, deviceInterface);
|
||||||
|
if (thread < B_OK)
|
||||||
|
return thread;
|
||||||
|
|
||||||
|
device->flags |= IFF_UP;
|
||||||
|
resume_thread(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;
|
||||||
|
net_device *device = protocol->device;
|
||||||
|
|
||||||
|
// TODO: locking!
|
||||||
|
if (deviceInterface->up_count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
deviceInterface->up_count--;
|
||||||
|
|
||||||
|
if (deviceInterface->up_count > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
device->flags &= ~IFF_UP;
|
||||||
|
protocol->device_module->down(protocol->device);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef DATALINK_H
|
||||||
|
#define DATALINK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_datalink.h>
|
||||||
|
|
||||||
|
|
||||||
|
status_t datalink_control(struct net_domain *domain, int32 option,
|
||||||
|
void *value, size_t *_length);
|
||||||
|
status_t datalink_send_data(struct net_route *route, net_buffer *buffer);
|
||||||
|
|
||||||
|
#endif // DATALINK_H
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "domains.h"
|
||||||
|
#include "interfaces.h"
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_DOMAINS
|
||||||
|
#ifdef TRACE_DOMAINS
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static benaphore sDomainLock;
|
||||||
|
static list sDomains;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Scans the domain list for the specified family.
|
||||||
|
You need to hold the sDomainLock when calling this function.
|
||||||
|
*/
|
||||||
|
static net_domain_private *
|
||||||
|
lookup_domain(int family)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = NULL;
|
||||||
|
while (true) {
|
||||||
|
domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
|
||||||
|
if (domain == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (domain->family == family)
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the domain of the specified family.
|
||||||
|
*/
|
||||||
|
net_domain *
|
||||||
|
get_domain(int family)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sDomainLock);
|
||||||
|
return lookup_domain(family);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32
|
||||||
|
count_domain_interfaces()
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sDomainLock);
|
||||||
|
|
||||||
|
net_domain_private *domain = NULL;
|
||||||
|
uint32 count = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
|
||||||
|
if (domain == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
net_interface *interface = NULL;
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface *)list_get_next_item(&domain->interfaces,
|
||||||
|
interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Dumps a list of all interfaces into the supplied userland buffer.
|
||||||
|
If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is
|
||||||
|
returned.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
list_domain_interfaces(void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sDomainLock);
|
||||||
|
|
||||||
|
net_domain_private *domain = NULL;
|
||||||
|
size_t spaceLeft = size;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
domain = (net_domain_private *)list_get_next_item(&sDomains, domain);
|
||||||
|
if (domain == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
net_interface *interface = NULL;
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface *)list_get_next_item(&domain->interfaces,
|
||||||
|
interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size = IF_NAMESIZE + (interface->address ? interface->address->sa_len : 1);
|
||||||
|
if (spaceLeft < size)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
ifreq request;
|
||||||
|
strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
|
||||||
|
if (interface->address != NULL)
|
||||||
|
memcpy(&request.ifr_addr, interface->address, interface->address->sa_len);
|
||||||
|
else {
|
||||||
|
// empty address
|
||||||
|
request.ifr_addr.sa_len = 2;
|
||||||
|
request.ifr_addr.sa_family = AF_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_memcpy(buffer, &request, size) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
buffer = (void *)((addr_t)buffer + size);
|
||||||
|
spaceLeft -= size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
add_interface_to_domain(net_domain *_domain,
|
||||||
|
struct ifreq& request)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
|
||||||
|
const char *deviceName = request.ifr_parameter.device[0]
|
||||||
|
? request.ifr_parameter.device : request.ifr_name;
|
||||||
|
net_device_interface *deviceInterface = get_device_interface(deviceName);
|
||||||
|
if (deviceInterface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
if (find_interface(domain, request.ifr_name) != NULL)
|
||||||
|
return B_NAME_IN_USE;
|
||||||
|
|
||||||
|
net_interface_private *interface;
|
||||||
|
status_t status = create_interface(domain,
|
||||||
|
request.ifr_name, request.ifr_parameter.base_name[0]
|
||||||
|
? request.ifr_parameter.base_name : request.ifr_name,
|
||||||
|
deviceInterface, &interface);
|
||||||
|
if (status < B_OK) {
|
||||||
|
put_device_interface(deviceInterface);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_item(&domain->interfaces, interface);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Removes the interface from its domain, and deletes it.
|
||||||
|
You need to hold the domain's lock when calling this function.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
remove_interface_from_domain(net_interface *interface)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = (net_domain_private *)interface->domain;
|
||||||
|
|
||||||
|
list_remove_item(&domain->interfaces, interface);
|
||||||
|
delete_interface((net_interface_private *)interface);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_domain(int family, const char *name,
|
||||||
|
struct net_protocol_module_info *module,
|
||||||
|
struct net_address_module_info *addressModule,
|
||||||
|
net_domain **_domain)
|
||||||
|
{
|
||||||
|
TRACE(("register_domain(%d, %s)\n", family, name));
|
||||||
|
BenaphoreLocker locker(sDomainLock);
|
||||||
|
|
||||||
|
struct net_domain_private *domain = lookup_domain(family);
|
||||||
|
if (domain != NULL)
|
||||||
|
return B_NAME_IN_USE;
|
||||||
|
|
||||||
|
domain = new (std::nothrow) net_domain_private;
|
||||||
|
if (domain == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t status = benaphore_init(&domain->lock, name);
|
||||||
|
if (status < B_OK) {
|
||||||
|
delete domain;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain->family = family;
|
||||||
|
domain->name = name;
|
||||||
|
domain->module = module;
|
||||||
|
domain->address_module = addressModule;
|
||||||
|
|
||||||
|
list_init(&domain->interfaces);
|
||||||
|
|
||||||
|
list_add_item(&sDomains, domain);
|
||||||
|
|
||||||
|
*_domain = domain;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
unregister_domain(net_domain *_domain)
|
||||||
|
{
|
||||||
|
TRACE(("unregister_domain(%p, %d, %s)\n", _domain, _domain->family, _domain->name));
|
||||||
|
|
||||||
|
net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
BenaphoreLocker locker(sDomainLock);
|
||||||
|
|
||||||
|
list_remove_item(&sDomains, domain);
|
||||||
|
|
||||||
|
net_interface_private *interface = NULL;
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface_private *)list_remove_head_item(&domain->interfaces);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
delete_interface(interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
benaphore_destroy(&domain->lock);
|
||||||
|
delete domain;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
init_domains()
|
||||||
|
{
|
||||||
|
if (benaphore_init(&sDomainLock, "net domains") < B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
list_init_etc(&sDomains, offsetof(struct net_domain_private, link));
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
uninit_domains()
|
||||||
|
{
|
||||||
|
benaphore_destroy(&sDomainLock);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef DOMAINS_H
|
||||||
|
#define DOMAINS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "routes.h"
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct net_domain_private : net_domain {
|
||||||
|
struct list_link link;
|
||||||
|
|
||||||
|
benaphore lock;
|
||||||
|
|
||||||
|
RouteList routes;
|
||||||
|
RouteInfoList route_infos;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
status_t init_domains();
|
||||||
|
status_t uninit_domains();
|
||||||
|
|
||||||
|
uint32 count_domain_interfaces();
|
||||||
|
status_t list_domain_interfaces(void *buffer, size_t size);
|
||||||
|
status_t add_interface_to_domain(net_domain *domain, struct ifreq& request);
|
||||||
|
status_t remove_interface_from_domain(net_interface *interface);
|
||||||
|
|
||||||
|
net_domain *get_domain(int family);
|
||||||
|
status_t register_domain(int family, const char *name,
|
||||||
|
struct net_protocol_module_info *module,
|
||||||
|
struct net_address_module_info *addressModule,
|
||||||
|
net_domain **_domain);
|
||||||
|
status_t unregister_domain(net_domain *domain);
|
||||||
|
|
||||||
|
#endif // DOMAINS_H
|
||||||
@@ -0,0 +1,610 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "domains.h"
|
||||||
|
#include "interfaces.h"
|
||||||
|
#include "stack_private.h"
|
||||||
|
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_INTERFACES
|
||||||
|
#ifdef TRACE_INTERFACES
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static benaphore sInterfaceLock;
|
||||||
|
static list sInterfaces;
|
||||||
|
static uint32 sInterfaceIndex;
|
||||||
|
static uint32 sDeviceIndex;
|
||||||
|
|
||||||
|
|
||||||
|
static net_device_interface *
|
||||||
|
find_device_interface(const char *name)
|
||||||
|
{
|
||||||
|
net_device_interface *interface = NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
interface = (net_device_interface *)list_get_next_item(&sInterfaces, interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!strcmp(interface->name, name))
|
||||||
|
return interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
domain_receive_adapter(void *cookie, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = (net_domain_private *)cookie;
|
||||||
|
return domain->module->receive_data(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - interfaces
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Searches for a specific interface in a domain by name.
|
||||||
|
You need to have the domain's lock hold when calling this function.
|
||||||
|
*/
|
||||||
|
struct net_interface_private *
|
||||||
|
find_interface(struct net_domain *domain, const char *name)
|
||||||
|
{
|
||||||
|
net_interface_private *interface = NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface_private *)list_get_next_item(
|
||||||
|
&domain->interfaces, interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!strcmp(interface->name, name))
|
||||||
|
return interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Searches for a specific interface in a domain by index.
|
||||||
|
You need to have the domain's lock hold when calling this function.
|
||||||
|
*/
|
||||||
|
struct net_interface_private *
|
||||||
|
find_interface(struct net_domain *domain, uint32 index)
|
||||||
|
{
|
||||||
|
net_interface_private *interface = NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface_private *)list_get_next_item(
|
||||||
|
&domain->interfaces, interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (interface->index == index)
|
||||||
|
return interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
create_interface(net_domain *domain, const char *name, const char *baseName,
|
||||||
|
net_device_interface *deviceInterface, net_interface_private **_interface)
|
||||||
|
{
|
||||||
|
net_interface_private *interface =
|
||||||
|
new (std::nothrow) net_interface_private;
|
||||||
|
if (interface == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
strlcpy(interface->name, name, IF_NAMESIZE);
|
||||||
|
strlcpy(interface->base_name, baseName, IF_NAMESIZE);
|
||||||
|
interface->domain = domain;
|
||||||
|
interface->device = deviceInterface->device;
|
||||||
|
|
||||||
|
interface->address = NULL;
|
||||||
|
interface->destination = NULL;
|
||||||
|
interface->mask = NULL;
|
||||||
|
|
||||||
|
interface->index = ++sInterfaceIndex;
|
||||||
|
interface->flags = deviceInterface->device->flags & ~IFF_UP;
|
||||||
|
interface->type = 0;
|
||||||
|
interface->mtu = deviceInterface->device->mtu;
|
||||||
|
interface->metric = 0;
|
||||||
|
interface->device_interface = deviceInterface;
|
||||||
|
|
||||||
|
status_t status = get_domain_datalink_protocols(interface);
|
||||||
|
if (status < B_OK) {
|
||||||
|
delete interface;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab a reference to the networking stack, to make sure it won't be
|
||||||
|
// unloaded as long as an interface exists
|
||||||
|
module_info *module;
|
||||||
|
get_module(NET_STARTER_MODULE_NAME, &module);
|
||||||
|
|
||||||
|
*_interface = interface;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_interface(net_interface_private *interface)
|
||||||
|
{
|
||||||
|
put_device_interface(interface->device_interface);
|
||||||
|
|
||||||
|
free(interface->address);
|
||||||
|
free(interface->destination);
|
||||||
|
free(interface->mask);
|
||||||
|
|
||||||
|
delete interface;
|
||||||
|
|
||||||
|
// Release reference of the stack - at this point, our stack may be unloaded
|
||||||
|
// if no other interfaces or sockets are left
|
||||||
|
put_module(NET_STARTER_MODULE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
put_interface(struct net_interface_private *interface)
|
||||||
|
{
|
||||||
|
// TODO: reference counting
|
||||||
|
// TODO: better locking scheme
|
||||||
|
benaphore_unlock(&((net_domain_private *)interface->domain)->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct net_interface_private *
|
||||||
|
get_interface(net_domain *_domain, const char *name)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
benaphore_lock(&domain->lock);
|
||||||
|
|
||||||
|
net_interface_private *interface = NULL;
|
||||||
|
while (true) {
|
||||||
|
interface = (net_interface_private *)list_get_next_item(
|
||||||
|
&domain->interfaces, interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!strcmp(interface->name, name))
|
||||||
|
return interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
benaphore_unlock(&domain->lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - device interfaces
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
get_device_interface_address(net_device_interface *interface, sockaddr *_address)
|
||||||
|
{
|
||||||
|
sockaddr_dl &address = *(sockaddr_dl *)_address;
|
||||||
|
|
||||||
|
address.sdl_family = AF_LINK;
|
||||||
|
address.sdl_index = interface->device->index;
|
||||||
|
address.sdl_type = interface->device->type;
|
||||||
|
address.sdl_nlen = strlen(interface->name);
|
||||||
|
address.sdl_slen = 0;
|
||||||
|
memcpy(address.sdl_data, interface->name, address.sdl_nlen);
|
||||||
|
|
||||||
|
address.sdl_alen = interface->device->address.length;
|
||||||
|
memcpy(LLADDR(&address), interface->device->address.data, address.sdl_alen);
|
||||||
|
|
||||||
|
address.sdl_len = sizeof(sockaddr_dl) - sizeof(address.sdl_data)
|
||||||
|
+ address.sdl_nlen + address.sdl_alen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32
|
||||||
|
count_device_interfaces()
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
net_device_interface *interface = NULL;
|
||||||
|
uint32 count = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
interface = (net_device_interface *)list_get_next_item(&sInterfaces,
|
||||||
|
interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Dumps a list of all interfaces into the supplied userland buffer.
|
||||||
|
If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is
|
||||||
|
returned.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
list_device_interfaces(void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
net_device_interface *interface = NULL;
|
||||||
|
size_t spaceLeft = size;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
interface = (net_device_interface *)list_get_next_item(&sInterfaces,
|
||||||
|
interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ifreq request;
|
||||||
|
strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
|
||||||
|
get_device_interface_address(interface, &request.ifr_addr);
|
||||||
|
|
||||||
|
size = IF_NAMESIZE + request.ifr_addr.sa_len;
|
||||||
|
if (spaceLeft < size)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
if (user_memcpy(buffer, &request, size) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
buffer = (void *)((addr_t)buffer + size);
|
||||||
|
spaceLeft -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Releases the reference for the interface. When all references are
|
||||||
|
released, the interface is removed.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
put_device_interface(struct net_device_interface *interface)
|
||||||
|
{
|
||||||
|
if (atomic_add(&interface->ref_count, -1) != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we need to remove this interface!
|
||||||
|
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
list_remove_item(&sInterfaces, interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface->module->uninit_device(interface->device);
|
||||||
|
put_module(interface->module->info.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Finds an interface by the specified index and grabs a reference to it.
|
||||||
|
*/
|
||||||
|
struct net_device_interface *
|
||||||
|
get_device_interface(uint32 index)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
net_device_interface *interface = NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
interface = (net_device_interface *)list_get_next_item(&sInterfaces, interface);
|
||||||
|
if (interface == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (interface->device->index == index) {
|
||||||
|
if (atomic_add(&interface->ref_count, 1) != 0)
|
||||||
|
return interface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Finds an interface by the specified name and grabs a reference to it.
|
||||||
|
If the interface does not yet exist, a new one is created.
|
||||||
|
*/
|
||||||
|
struct net_device_interface *
|
||||||
|
get_device_interface(const char *name)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
net_device_interface *interface = find_device_interface(name);
|
||||||
|
if (interface != NULL) {
|
||||||
|
if (atomic_add(&interface->ref_count, 1) != 0)
|
||||||
|
return interface;
|
||||||
|
|
||||||
|
// try to recreate interface - it just got removed
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cookie = open_module_list("network/devices");
|
||||||
|
if (cookie == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char moduleName[B_FILE_NAME_LENGTH];
|
||||||
|
size_t length = sizeof(moduleName);
|
||||||
|
if (read_next_module_name(cookie, moduleName, &length) != B_OK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
TRACE(("get_device_interface: ask \"%s\" for %s\n", moduleName, name));
|
||||||
|
|
||||||
|
net_device_module_info *module;
|
||||||
|
if (get_module(moduleName, (module_info **)&module) == B_OK) {
|
||||||
|
net_device *device;
|
||||||
|
status_t status = module->init_device(name, &device);
|
||||||
|
if (status == B_OK) {
|
||||||
|
// create new module interface for this
|
||||||
|
interface = new (std::nothrow) net_device_interface;
|
||||||
|
if (interface != NULL) {
|
||||||
|
interface->name = device->name;
|
||||||
|
interface->module = module;
|
||||||
|
interface->device = device;
|
||||||
|
interface->up_count = 0;
|
||||||
|
interface->ref_count = 1;
|
||||||
|
interface->deframe_func = NULL;
|
||||||
|
interface->deframe_ref_count = 0;
|
||||||
|
|
||||||
|
device->index = ++sDeviceIndex;
|
||||||
|
device->module = module;
|
||||||
|
|
||||||
|
list_add_item(&sInterfaces, interface);
|
||||||
|
return interface;
|
||||||
|
} else
|
||||||
|
module->uninit_device(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
put_module(moduleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - devices
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Unregisters a previously registered deframer function.
|
||||||
|
This function is part of the net_manager_module_info API.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
unregister_device_deframer(net_device *device)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
// find device interface for this device
|
||||||
|
net_device_interface *interface = find_device_interface(device->name);
|
||||||
|
if (interface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
if (--interface->deframe_ref_count == 0)
|
||||||
|
interface->deframe_func = NULL;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Registers the deframer function for the specified \a device.
|
||||||
|
Note, however, that right now, you can only register one single
|
||||||
|
deframer function per device.
|
||||||
|
|
||||||
|
If the need arises, we might want to lift that limitation at a
|
||||||
|
later time (which would require a slight API change, though).
|
||||||
|
|
||||||
|
This function is part of the net_manager_module_info API.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
register_device_deframer(net_device *device, net_deframe_func deframeFunc)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
// find device interface for this device
|
||||||
|
net_device_interface *interface = find_device_interface(device->name);
|
||||||
|
if (interface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
if (interface->deframe_func != NULL && interface->deframe_func != deframeFunc)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
interface->deframe_func = deframeFunc;
|
||||||
|
interface->deframe_ref_count++;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_domain_device_handler(struct net_device *device, int32 type,
|
||||||
|
struct net_domain *_domain)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
if (domain->module == NULL || domain->module->receive_data == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
return register_device_handler(device, type, &domain_receive_adapter, domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_device_handler(struct net_device *device, int32 type,
|
||||||
|
net_receive_func receiveFunc, void *cookie)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
// find device interface for this device
|
||||||
|
net_device_interface *interface = find_device_interface(device->name);
|
||||||
|
if (interface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
// see if such a handler already for this device
|
||||||
|
|
||||||
|
DeviceHandlerList::Iterator iterator = interface->receive_funcs.GetIterator();
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_device_handler *handler = iterator.Next();
|
||||||
|
|
||||||
|
if (handler->type == type)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new handler
|
||||||
|
|
||||||
|
net_device_handler *handler = new (std::nothrow) net_device_handler;
|
||||||
|
if (handler == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
handler->func = receiveFunc;
|
||||||
|
handler->type = type;
|
||||||
|
handler->cookie = cookie;
|
||||||
|
interface->receive_funcs.Add(handler);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
unregister_device_handler(struct net_device *device, int32 type)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
// find device interface for this device
|
||||||
|
net_device_interface *interface = find_device_interface(device->name);
|
||||||
|
if (interface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
// search for the handler
|
||||||
|
|
||||||
|
DeviceHandlerList::Iterator iterator = interface->receive_funcs.GetIterator();
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_device_handler *handler = iterator.Next();
|
||||||
|
|
||||||
|
if (handler->type == type) {
|
||||||
|
// found it
|
||||||
|
iterator.Remove();
|
||||||
|
delete handler;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_device_monitor(struct net_device *device,
|
||||||
|
net_receive_func receiveFunc, void *cookie)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
// find device interface for this device
|
||||||
|
net_device_interface *interface = find_device_interface(device->name);
|
||||||
|
if (interface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
// Add new monitor
|
||||||
|
|
||||||
|
net_device_monitor *monitor = new (std::nothrow) net_device_monitor;
|
||||||
|
if (monitor == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
monitor->func = receiveFunc;
|
||||||
|
monitor->cookie = cookie;
|
||||||
|
interface->monitor_funcs.Add(monitor);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
unregister_device_monitor(struct net_device *device,
|
||||||
|
net_receive_func receiveFunc, void *cookie)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
|
||||||
|
// find device interface for this device
|
||||||
|
net_device_interface *interface = find_device_interface(device->name);
|
||||||
|
if (interface == NULL)
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
// search for the monitor
|
||||||
|
|
||||||
|
DeviceMonitorList::Iterator iterator = interface->monitor_funcs.GetIterator();
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_device_monitor *monitor = iterator.Next();
|
||||||
|
|
||||||
|
if (monitor->cookie == cookie && monitor->func == receiveFunc) {
|
||||||
|
// found it
|
||||||
|
iterator.Remove();
|
||||||
|
delete monitor;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
This function is called by device modules once their device got
|
||||||
|
physically removed, ie. a USB networking card is unplugged.
|
||||||
|
It is part of the net_manager_module_info API.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
device_removed(net_device *device)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sInterfaceLock);
|
||||||
|
// TODO: all what this function should do is to clear the IFF_UP flag of the interfaces.
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
init_interfaces()
|
||||||
|
{
|
||||||
|
if (benaphore_init(&sInterfaceLock, "net interfaces") < B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
list_init(&sInterfaces);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
uninit_interfaces()
|
||||||
|
{
|
||||||
|
benaphore_destroy(&sInterfaceLock);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef INTERFACES_H
|
||||||
|
#define INTERFACES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_datalink.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct net_device_handler : public DoublyLinkedListLinkImpl<net_device_handler> {
|
||||||
|
net_receive_func func;
|
||||||
|
int32 type;
|
||||||
|
void *cookie;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_device_monitor : public DoublyLinkedListLinkImpl<net_device_monitor> {
|
||||||
|
net_receive_func func;
|
||||||
|
void *cookie;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<net_device_handler> DeviceHandlerList;
|
||||||
|
typedef DoublyLinkedList<net_device_monitor> DeviceMonitorList;
|
||||||
|
|
||||||
|
struct net_device_interface {
|
||||||
|
struct list_link link;
|
||||||
|
const char *name;
|
||||||
|
struct net_device_module_info *module;
|
||||||
|
struct net_device *device;
|
||||||
|
uint32 up_count;
|
||||||
|
// a device can be brought up by more than one interface
|
||||||
|
int32 ref_count;
|
||||||
|
|
||||||
|
net_deframe_func deframe_func;
|
||||||
|
int32 deframe_ref_count;
|
||||||
|
|
||||||
|
DeviceMonitorList monitor_funcs;
|
||||||
|
DeviceHandlerList receive_funcs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_interface_private : net_interface {
|
||||||
|
char base_name[IF_NAMESIZE];
|
||||||
|
net_device_interface *device_interface;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
status_t init_interfaces();
|
||||||
|
status_t uninit_interfaces();
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
struct net_interface_private *find_interface(struct net_domain *domain,
|
||||||
|
const char *name);
|
||||||
|
struct net_interface_private *find_interface(struct net_domain *domain,
|
||||||
|
uint32 index);
|
||||||
|
void put_interface(struct net_interface_private *interface);
|
||||||
|
struct net_interface_private *get_interface(net_domain *domain,
|
||||||
|
const char *name);
|
||||||
|
status_t create_interface(net_domain *domain, const char *name,
|
||||||
|
const char *baseName, net_device_interface *deviceInterface,
|
||||||
|
struct net_interface_private **_interface);
|
||||||
|
void delete_interface(net_interface_private *interface);
|
||||||
|
|
||||||
|
// device interfaces
|
||||||
|
void get_device_interface_address(net_device_interface *interface,
|
||||||
|
sockaddr *address);
|
||||||
|
uint32 count_device_interfaces();
|
||||||
|
status_t list_device_interfaces(void *buffer, size_t size);
|
||||||
|
void put_device_interface(struct net_device_interface *interface);
|
||||||
|
struct net_device_interface *get_device_interface(uint32 index);
|
||||||
|
struct net_device_interface *get_device_interface(const char *name);
|
||||||
|
|
||||||
|
// devices
|
||||||
|
status_t unregister_device_deframer(net_device *device);
|
||||||
|
status_t register_device_deframer(net_device *device, net_deframe_func deframeFunc);
|
||||||
|
status_t register_domain_device_handler(struct net_device *device, int32 type,
|
||||||
|
struct net_domain *domain);
|
||||||
|
status_t register_device_handler(struct net_device *device, int32 type,
|
||||||
|
net_receive_func receiveFunc, void *cookie);
|
||||||
|
status_t unregister_device_handler(struct net_device *device, int32 type);
|
||||||
|
status_t register_device_monitor(struct net_device *device,
|
||||||
|
net_receive_func receiveFunc, void *cookie);
|
||||||
|
status_t unregister_device_monitor(struct net_device *device,
|
||||||
|
net_receive_func receiveFunc, void *cookie);
|
||||||
|
status_t device_removed(net_device *device);
|
||||||
|
|
||||||
|
#endif // INTERFACES_H
|
||||||
@@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, 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 "link.h"
|
||||||
|
#include "stack_private.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct link_protocol : net_protocol {
|
||||||
|
net_fifo fifo;
|
||||||
|
char registered_interface[IF_NAMESIZE];
|
||||||
|
bool registered_monitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct net_domain *sDomain;
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
link_monitor_data(void *cookie, net_buffer *packet)
|
||||||
|
{
|
||||||
|
link_protocol *protocol = (link_protocol *)cookie;
|
||||||
|
|
||||||
|
// we need to make a clone for that buffer and pass it to the socket
|
||||||
|
net_buffer *buffer = gNetBufferModule.clone(packet, false);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return fifo_enqueue_buffer(&protocol->fifo, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
net_protocol *
|
||||||
|
link_init_protocol(net_socket *socket)
|
||||||
|
{
|
||||||
|
link_protocol *protocol = new (std::nothrow) link_protocol;
|
||||||
|
if (protocol == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (init_fifo(&protocol->fifo, "packet monitor socket", 65536) < B_OK) {
|
||||||
|
delete protocol;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol->registered_monitor = false;
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_uninit_protocol(net_protocol *_protocol)
|
||||||
|
{
|
||||||
|
link_protocol *protocol = (link_protocol *)_protocol;
|
||||||
|
|
||||||
|
if (protocol->registered_monitor) {
|
||||||
|
net_device_interface *interface = get_device_interface(protocol->registered_interface);
|
||||||
|
if (interface != NULL) {
|
||||||
|
unregister_device_monitor(interface->device, link_monitor_data, protocol);
|
||||||
|
put_device_interface(interface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uninit_fifo(&protocol->fifo);
|
||||||
|
delete protocol;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_open(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_close(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_free(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_connect(net_protocol *protocol, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_control(net_protocol *_protocol, int level, int option, void *value,
|
||||||
|
size_t *_length)
|
||||||
|
{
|
||||||
|
link_protocol *protocol = (link_protocol *)_protocol;
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case SIOCGIFINDEX:
|
||||||
|
{
|
||||||
|
// get index of interface
|
||||||
|
struct ifreq request;
|
||||||
|
if (user_memcpy(&request, value, IF_NAMESIZE) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
net_device_interface *interface = get_device_interface(request.ifr_name);
|
||||||
|
if (interface != NULL) {
|
||||||
|
request.ifr_index = interface->device->index;
|
||||||
|
put_device_interface(interface);
|
||||||
|
} else
|
||||||
|
request.ifr_index = 0;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
net_device_interface *interface = get_device_interface(request.ifr_index);
|
||||||
|
if (interface != NULL) {
|
||||||
|
strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
|
||||||
|
put_device_interface(interface);
|
||||||
|
} else
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
return user_memcpy(value, &request, sizeof(struct ifreq));
|
||||||
|
}
|
||||||
|
|
||||||
|
case SIOCGIFCOUNT:
|
||||||
|
{
|
||||||
|
// count number of interfaces
|
||||||
|
struct ifconf config;
|
||||||
|
config.ifc_value = count_device_interfaces();
|
||||||
|
|
||||||
|
return user_memcpy(value, &config, sizeof(struct ifconf));
|
||||||
|
}
|
||||||
|
|
||||||
|
case SIOCGIFCONF:
|
||||||
|
{
|
||||||
|
// count number of interfaces
|
||||||
|
struct ifconf config;
|
||||||
|
if (user_memcpy(&config, value, sizeof(struct ifconf)) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
return list_device_interfaces(config.ifc_buf, config.ifc_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
case SIOCGIFADDR:
|
||||||
|
{
|
||||||
|
// get address of interface
|
||||||
|
struct ifreq request;
|
||||||
|
if (user_memcpy(&request, value, IF_NAMESIZE) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
net_device_interface *interface = get_device_interface(request.ifr_name);
|
||||||
|
if (interface != NULL) {
|
||||||
|
get_device_interface_address(interface, &request.ifr_addr);
|
||||||
|
put_device_interface(interface);
|
||||||
|
} else
|
||||||
|
return ENODEV;
|
||||||
|
|
||||||
|
return user_memcpy(&((struct ifreq *)value)->ifr_addr,
|
||||||
|
&request.ifr_addr, request.ifr_addr.sa_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
case SIOCSPACKETCAP:
|
||||||
|
{
|
||||||
|
// start packet monitoring
|
||||||
|
|
||||||
|
if (protocol->registered_monitor)
|
||||||
|
return B_BUSY;
|
||||||
|
|
||||||
|
struct ifreq request;
|
||||||
|
if (user_memcpy(&request, value, IF_NAMESIZE) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
net_device_interface *interface = get_device_interface(request.ifr_name);
|
||||||
|
status_t status;
|
||||||
|
if (interface != NULL) {
|
||||||
|
status = register_device_monitor(interface->device,
|
||||||
|
link_monitor_data, protocol);
|
||||||
|
if (status == B_OK) {
|
||||||
|
// we're now registered
|
||||||
|
strlcpy(protocol->registered_interface, request.ifr_name, IF_NAMESIZE);
|
||||||
|
protocol->registered_monitor = true;
|
||||||
|
}
|
||||||
|
put_device_interface(interface);
|
||||||
|
} else
|
||||||
|
status = ENODEV;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SIOCCPACKETCAP:
|
||||||
|
{
|
||||||
|
// stop packet monitoring
|
||||||
|
|
||||||
|
if (!protocol->registered_monitor)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
struct ifreq request;
|
||||||
|
if (user_memcpy(&request, value, IF_NAMESIZE) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
net_device_interface *interface = get_device_interface(request.ifr_name);
|
||||||
|
status_t status;
|
||||||
|
if (interface != NULL) {
|
||||||
|
status = unregister_device_monitor(interface->device,
|
||||||
|
link_monitor_data, protocol);
|
||||||
|
if (status == B_OK) {
|
||||||
|
// we're now no longer registered
|
||||||
|
protocol->registered_monitor = false;
|
||||||
|
}
|
||||||
|
put_device_interface(interface);
|
||||||
|
} else
|
||||||
|
status = ENODEV;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return datalink_control(sDomain, option, value, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_bind(net_protocol *protocol, struct sockaddr *address)
|
||||||
|
{
|
||||||
|
// TODO: bind to a specific interface and ethernet type
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_unbind(net_protocol *protocol, struct sockaddr *address)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_listen(net_protocol *protocol, int count)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_shutdown(net_protocol *protocol, int direction)
|
||||||
|
{
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_send_data(net_protocol *protocol, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_send_routed_data(net_protocol *protocol, struct net_route *route,
|
||||||
|
net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
link_send_avail(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_read_data(net_protocol *_protocol, size_t numBytes, uint32 flags,
|
||||||
|
net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
link_protocol *protocol = (link_protocol *)_protocol;
|
||||||
|
|
||||||
|
dprintf("link_read is waiting for data...\n");
|
||||||
|
|
||||||
|
net_buffer *buffer;
|
||||||
|
status_t status = fifo_dequeue_buffer(&protocol->fifo,
|
||||||
|
flags, protocol->socket->receive.timeout, &buffer);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (numBytes < buffer->size) {
|
||||||
|
// discard any data behind the amount requested
|
||||||
|
gNetBufferModule.trim(buffer, numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
*_buffer = buffer;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
link_read_avail(net_protocol *_protocol)
|
||||||
|
{
|
||||||
|
link_protocol *protocol = (link_protocol *)_protocol;
|
||||||
|
return protocol->fifo.current_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct net_domain *
|
||||||
|
link_get_domain(net_protocol *protocol)
|
||||||
|
{
|
||||||
|
return sDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
link_get_mtu(net_protocol *protocol, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
// TODO: for now
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_receive_data(net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_error(uint32 code, net_buffer *data)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
link_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
|
||||||
|
void *errorData)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
link_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
return register_domain(AF_LINK, "link", NULL, NULL, &sDomain);
|
||||||
|
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
unregister_domain(sDomain);
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
link_init()
|
||||||
|
{
|
||||||
|
register_domain_protocols(AF_LINK, SOCK_DGRAM, 0, "network/stack/link/v1", NULL);
|
||||||
|
|
||||||
|
register_domain_datalink_protocols(AF_LINK, IFT_ETHER,
|
||||||
|
"network/datalink_protocols/ethernet_frame/v1",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_protocol_module_info gLinkModule = {
|
||||||
|
{
|
||||||
|
"network/stack/link/v1",
|
||||||
|
0,
|
||||||
|
link_std_ops
|
||||||
|
},
|
||||||
|
link_init_protocol,
|
||||||
|
link_uninit_protocol,
|
||||||
|
link_open,
|
||||||
|
link_close,
|
||||||
|
link_free,
|
||||||
|
link_connect,
|
||||||
|
link_accept,
|
||||||
|
link_control,
|
||||||
|
link_bind,
|
||||||
|
link_unbind,
|
||||||
|
link_listen,
|
||||||
|
link_shutdown,
|
||||||
|
link_send_data,
|
||||||
|
link_send_routed_data,
|
||||||
|
link_send_avail,
|
||||||
|
link_read_data,
|
||||||
|
link_read_avail,
|
||||||
|
link_get_domain,
|
||||||
|
link_get_mtu,
|
||||||
|
link_receive_data,
|
||||||
|
link_error,
|
||||||
|
link_error_reply,
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef LINK_H
|
||||||
|
#define LINK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_protocol.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern net_protocol_module_info gLinkModule;
|
||||||
|
|
||||||
|
void link_init();
|
||||||
|
|
||||||
|
#endif // LINK_H
|
||||||
@@ -0,0 +1,902 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_BUFFER
|
||||||
|
#ifdef TRACE_BUFFER
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 2048
|
||||||
|
|
||||||
|
struct data_node {
|
||||||
|
struct list_link link;
|
||||||
|
struct data_header *header;
|
||||||
|
size_t offset; // the net_buffer-wide offset of this node
|
||||||
|
uint8 *start; // points to the start of the data
|
||||||
|
size_t used; // defines how much memory is used by this node
|
||||||
|
size_t header_space;
|
||||||
|
size_t tail_space;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data_header {
|
||||||
|
int32 ref_count;
|
||||||
|
addr_t physical_address;
|
||||||
|
size_t size;
|
||||||
|
uint8 *data_end;
|
||||||
|
size_t data_space;
|
||||||
|
data_node *first_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_buffer_private : net_buffer {
|
||||||
|
struct list buffers;
|
||||||
|
data_node first_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static status_t append_data(net_buffer *buffer, const void *data, size_t size);
|
||||||
|
|
||||||
|
|
||||||
|
static data_header *
|
||||||
|
create_data_header(size_t size, size_t headerSpace)
|
||||||
|
{
|
||||||
|
// TODO: don't use malloc!
|
||||||
|
data_header *header = (data_header *)malloc(size);
|
||||||
|
if (header == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
header->ref_count = 1;
|
||||||
|
header->physical_address = 0;
|
||||||
|
// TODO: initialize this correctly
|
||||||
|
header->size = size;
|
||||||
|
header->data_space = headerSpace;
|
||||||
|
header->data_end = (uint8 *)header + sizeof(struct data_header);
|
||||||
|
header->first_node = NULL;
|
||||||
|
|
||||||
|
TRACE((" create new data header %p\n", header));
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
release_data_header(data_header *header)
|
||||||
|
{
|
||||||
|
if (atomic_add(&header->ref_count, -1) != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TRACE((" free header %p\n", header));
|
||||||
|
free(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void
|
||||||
|
acquire_data_header(data_header *header)
|
||||||
|
{
|
||||||
|
atomic_add(&header->ref_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_data_header_space(data_header *header, uint8 *data, size_t size)
|
||||||
|
{
|
||||||
|
if (header->data_end != data + size) {
|
||||||
|
// this wasn't the last allocation, unfortunately, there is nothing
|
||||||
|
// to do for us, then
|
||||||
|
// TODO: if the need arises, a simple free list could do wonder
|
||||||
|
// TODO: remove_data_node() currently calls this function no matter
|
||||||
|
// where the node had been placed - this would need to be changed
|
||||||
|
// then, too.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
header->data_end -= size;
|
||||||
|
header->data_space += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8 *
|
||||||
|
alloc_data_header_space(data_header *header, size_t size)
|
||||||
|
{
|
||||||
|
if (header->data_space < size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
uint8 *data = header->data_end;
|
||||||
|
header->data_end += size;
|
||||||
|
header->data_space -= size;
|
||||||
|
|
||||||
|
if (header->first_node != NULL)
|
||||||
|
header->first_node->header_space -= size;
|
||||||
|
#if 0
|
||||||
|
else
|
||||||
|
dprintf("add data to a header without first node - could overwrite something!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_data_node(data_node *node, data_header *header, size_t headerSpace)
|
||||||
|
{
|
||||||
|
node->header = header;
|
||||||
|
node->offset = 0;
|
||||||
|
node->start = (uint8 *)header + sizeof(data_header) + headerSpace;
|
||||||
|
node->used = 0;
|
||||||
|
node->header_space = headerSpace;
|
||||||
|
node->tail_space = header->size - headerSpace - sizeof(data_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static data_node *
|
||||||
|
add_data_node(data_header *header)
|
||||||
|
{
|
||||||
|
data_node *node = (data_node *)alloc_data_header_space(header, sizeof(data_node));
|
||||||
|
if (node == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TRACE((" add data node %p to header %p\n", node, header));
|
||||||
|
acquire_data_header(header);
|
||||||
|
memset(node, 0, sizeof(struct data_node));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
remove_data_node(data_node *node)
|
||||||
|
{
|
||||||
|
data_header *header = node->header;
|
||||||
|
|
||||||
|
TRACE((" remove data node %p from header %p\n", node, header));
|
||||||
|
free_data_header_space(header, (uint8 *)node, sizeof(data_node));
|
||||||
|
if (header->first_node == node)
|
||||||
|
header->first_node = NULL;
|
||||||
|
|
||||||
|
release_data_header(node->header);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static net_buffer *
|
||||||
|
create_buffer(size_t headerSpace)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)malloc(sizeof(struct net_buffer_private));
|
||||||
|
if (buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TRACE(("create buffer %p\n", buffer));
|
||||||
|
|
||||||
|
data_header *header = create_data_header(BUFFER_SIZE, headerSpace);
|
||||||
|
if (header == NULL) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_data_node(&buffer->first_node, header, headerSpace);
|
||||||
|
header->first_node = &buffer->first_node;
|
||||||
|
|
||||||
|
list_init(&buffer->buffers);
|
||||||
|
list_add_item(&buffer->buffers, &buffer->first_node);
|
||||||
|
|
||||||
|
buffer->source.ss_len = 0;
|
||||||
|
buffer->destination.ss_len = 0;
|
||||||
|
buffer->interface = NULL;
|
||||||
|
buffer->flags = 0;
|
||||||
|
buffer->size = 0;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_buffer(net_buffer *_buffer)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
TRACE(("free buffer %p\n", buffer));
|
||||||
|
|
||||||
|
data_node *node;
|
||||||
|
while ((node = (data_node *)list_remove_head_item(&buffer->buffers)) != NULL) {
|
||||||
|
remove_data_node(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Creates a duplicate of the \a buffer. The new buffer does not share internal
|
||||||
|
storage; they are completely independent from each other.
|
||||||
|
*/
|
||||||
|
static net_buffer *
|
||||||
|
duplicate_buffer(net_buffer *_buffer)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
net_buffer *duplicate = create_buffer(buffer->first_node.header_space);
|
||||||
|
if (duplicate == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// copy the data from the source buffer
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (true) {
|
||||||
|
if (append_data(duplicate, node->start, node->used) < B_OK) {
|
||||||
|
free_buffer(duplicate);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy meta data from source buffer
|
||||||
|
|
||||||
|
memcpy(&duplicate->source, &buffer->source, buffer->source.ss_len);
|
||||||
|
memcpy(&duplicate->destination, &buffer->destination, buffer->destination.ss_len);
|
||||||
|
|
||||||
|
duplicate->flags = buffer->flags;
|
||||||
|
duplicate->interface = buffer->interface;
|
||||||
|
duplicate->size = buffer->size;
|
||||||
|
duplicate->protocol = buffer->protocol;
|
||||||
|
|
||||||
|
return duplicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Clones the buffer by grabbing another reference to the underlying data.
|
||||||
|
If that data changes, it will be changed in the clone as well.
|
||||||
|
|
||||||
|
If \a shareFreeSpace is \c true, the cloned buffer may claim the free
|
||||||
|
space in the original buffer as the original buffer can still do. If you
|
||||||
|
are using this, it's your responsibility that only one of the buffers
|
||||||
|
will do this.
|
||||||
|
*/
|
||||||
|
static net_buffer *
|
||||||
|
clone_buffer(net_buffer *_buffer, bool shareFreeSpace)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
net_buffer_private *clone = (net_buffer_private *)malloc(sizeof(struct net_buffer_private));
|
||||||
|
if (clone == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data_node *node = &clone->first_node;
|
||||||
|
data_node *sourceNode = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
if (sourceNode == NULL) {
|
||||||
|
free(clone);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_init(&clone->buffers);
|
||||||
|
|
||||||
|
// grab reference to this buffer - all additional nodes will get
|
||||||
|
// theirs in add_data_node()
|
||||||
|
atomic_add(&sourceNode->header->ref_count, 1);
|
||||||
|
|
||||||
|
while (sourceNode != NULL) {
|
||||||
|
node->header = sourceNode->header;
|
||||||
|
node->start = sourceNode->start;
|
||||||
|
node->used = sourceNode->used;
|
||||||
|
node->offset = sourceNode->offset;
|
||||||
|
|
||||||
|
if (shareFreeSpace) {
|
||||||
|
// both buffers could claim the free space - note that this option
|
||||||
|
// has to be used carefully
|
||||||
|
node->header_space = sourceNode->header_space;
|
||||||
|
node->tail_space = sourceNode->tail_space;
|
||||||
|
} else {
|
||||||
|
// the free space stays with the original buffer
|
||||||
|
node->header_space = 0;
|
||||||
|
node->tail_space = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add node to clone's list of buffers
|
||||||
|
list_add_item(&clone->buffers, node);
|
||||||
|
|
||||||
|
sourceNode = (data_node *)list_get_next_item(&buffer->buffers, sourceNode);
|
||||||
|
if (sourceNode == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
node = add_data_node(sourceNode->header);
|
||||||
|
if (node == NULL) {
|
||||||
|
// There was not enough space left for another node in this buffer
|
||||||
|
// TODO: handle this case!
|
||||||
|
panic("clone buffer hits size limit... (fix me)");
|
||||||
|
free(clone);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy meta data from source buffer
|
||||||
|
|
||||||
|
memcpy(&clone->source, &buffer->source, buffer->source.ss_len);
|
||||||
|
memcpy(&clone->destination, &buffer->destination, buffer->destination.ss_len);
|
||||||
|
|
||||||
|
clone->flags = buffer->flags;
|
||||||
|
clone->interface = buffer->interface;
|
||||||
|
clone->size = buffer->size;
|
||||||
|
clone->protocol = buffer->protocol;
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Merges the second buffer with the first. If \a after is \c true, the
|
||||||
|
second buffer's contents will be appended to the first ones, else they
|
||||||
|
will be prepended.
|
||||||
|
The second buffer will be freed if this function succeeds.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
merge_buffer(net_buffer *_buffer, net_buffer *_with, bool after)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
net_buffer_private *with = (net_buffer_private *)_with;
|
||||||
|
if (with == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
TRACE(("merge buffer %p with %p (%s)\n", buffer, with, after ? "after" : "before"));
|
||||||
|
// TODO: this is currently very simplistic, I really need to finish the
|
||||||
|
// harder part of this implementation (data_node management per header)
|
||||||
|
|
||||||
|
if (!after) {
|
||||||
|
// change offset of all nodes already in the buffer
|
||||||
|
data_node *node = NULL;
|
||||||
|
while (true) {
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
node->offset += with->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data_node *last = NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
data_node *node = (data_node *)list_get_next_item(&with->buffers, last);
|
||||||
|
if (node == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((uint8 *)node > (uint8 *)node->header
|
||||||
|
&& (uint8 *)node < (uint8 *)node->header + node->header->size) {
|
||||||
|
// The node is already in the buffer, we can just move it
|
||||||
|
// over to the new owner
|
||||||
|
list_remove_item(&with->buffers, node);
|
||||||
|
} else {
|
||||||
|
// we need a new place for this node
|
||||||
|
data_node *newNode = add_data_node(node->header);
|
||||||
|
if (newNode == NULL) {
|
||||||
|
// TODO: this can't work right now as add_data_node() also grabs a reference
|
||||||
|
// to the header - but in this case, we would need two references, one
|
||||||
|
// for the data, one for the node, and there is no mechanism for this.
|
||||||
|
#if 0
|
||||||
|
// try again on the buffers own header
|
||||||
|
newNode = add_data_node(buffer->first_node.header);
|
||||||
|
if (newNode == NULL)
|
||||||
|
#endif
|
||||||
|
// TODO: try to revert buffers to their initial state!!
|
||||||
|
return ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = node;
|
||||||
|
*newNode = *node;
|
||||||
|
node = newNode;
|
||||||
|
// the old node will get freed with its buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
if (after) {
|
||||||
|
list_add_item(&buffer->buffers, node);
|
||||||
|
node->offset = buffer->size;
|
||||||
|
} else
|
||||||
|
list_add_link_to_head(&buffer->buffers, node);
|
||||||
|
|
||||||
|
buffer->size += node->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the data has been merged completely at this point
|
||||||
|
free_buffer(with);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Writes into existing allocated memory.
|
||||||
|
\return B_BAD_VALUE if you write outside of the buffers current
|
||||||
|
bounds.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
write_data(net_buffer *_buffer, size_t offset, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
if (offset + size > buffer->size)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
if (size == 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// find first node to write into
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (node->offset + node->used < offset) {
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= node->offset;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t written = min_c(size, node->used - offset);
|
||||||
|
memcpy(node->start + offset, data, written);
|
||||||
|
|
||||||
|
size -= written;
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
read_data(net_buffer *_buffer, size_t offset, void *data, size_t size)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
if (offset + size > buffer->size)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
if (size == 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// find first node to read from
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (node->offset + node->used < offset) {
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= node->offset;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t bytesRead = min_c(size, node->used - offset);
|
||||||
|
memcpy(data, node->start + offset, bytesRead);
|
||||||
|
|
||||||
|
size -= bytesRead;
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
prepend_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
|
||||||
|
if (node->header_space < size) {
|
||||||
|
// we need to prepend a new buffer
|
||||||
|
|
||||||
|
// TODO: implement me!
|
||||||
|
panic("prepending buffer not implemented\n");
|
||||||
|
|
||||||
|
if (_contiguousBuffer)
|
||||||
|
*_contiguousBuffer = NULL;
|
||||||
|
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the data fits into this buffer
|
||||||
|
node->header_space -= size;
|
||||||
|
node->start -= size;
|
||||||
|
node->used += size;
|
||||||
|
|
||||||
|
if (_contiguousBuffer)
|
||||||
|
*_contiguousBuffer = node->start;
|
||||||
|
|
||||||
|
buffer->size += size;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
prepend_data(net_buffer *buffer, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
void *contiguousBuffer;
|
||||||
|
status_t status = prepend_size(buffer, size, &contiguousBuffer);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (contiguousBuffer)
|
||||||
|
memcpy(contiguousBuffer, data, size);
|
||||||
|
else
|
||||||
|
write_data(buffer, 0, data, size);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
append_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
data_node *node = (data_node *)list_get_last_item(&buffer->buffers);
|
||||||
|
|
||||||
|
if (node->tail_space < size) {
|
||||||
|
// we need to append a new buffer
|
||||||
|
|
||||||
|
// compute how many buffers we're going to need
|
||||||
|
// TODO: this doesn't leave any tail space, if that should be desired...
|
||||||
|
uint32 tailSpace = node->tail_space;
|
||||||
|
uint32 minimalHeaderSpace = sizeof(data_header) + 2 * sizeof(data_node);
|
||||||
|
uint32 sizeNeeded = size - tailSpace;
|
||||||
|
uint32 count = (sizeNeeded + BUFFER_SIZE - minimalHeaderSpace - 1)
|
||||||
|
/ (BUFFER_SIZE - minimalHeaderSpace);
|
||||||
|
uint32 averageHeaderSpace = BUFFER_SIZE - sizeNeeded / count - sizeof(data_header);
|
||||||
|
uint32 averageSize = BUFFER_SIZE - sizeof(data_header) - averageHeaderSpace;
|
||||||
|
|
||||||
|
// allocate space left in the node
|
||||||
|
node->tail_space -= tailSpace;
|
||||||
|
node->used += tailSpace;
|
||||||
|
buffer->size += tailSpace;
|
||||||
|
|
||||||
|
// allocate all buffers
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < count; i++) {
|
||||||
|
data_header *header = create_data_header(BUFFER_SIZE, averageHeaderSpace);
|
||||||
|
if (header == NULL) {
|
||||||
|
// TODO: free up headers we already allocated!
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = (data_node *)alloc_data_header_space(header, sizeof(data_node));
|
||||||
|
// this can't fail as we made sure there will be enough header space
|
||||||
|
|
||||||
|
init_data_node(node, header, averageHeaderSpace);
|
||||||
|
node->header_space = header->data_space;
|
||||||
|
node->tail_space -= averageSize;
|
||||||
|
node->used = averageSize;
|
||||||
|
node->offset = buffer->size;
|
||||||
|
buffer->size += averageSize;
|
||||||
|
|
||||||
|
list_add_item(&buffer->buffers, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_contiguousBuffer)
|
||||||
|
*_contiguousBuffer = NULL;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the data fits into this buffer
|
||||||
|
node->tail_space -= size;
|
||||||
|
|
||||||
|
if (_contiguousBuffer)
|
||||||
|
*_contiguousBuffer = node->start + node->used;
|
||||||
|
|
||||||
|
node->used += size;
|
||||||
|
buffer->size += size;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
append_data(net_buffer *buffer, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
size_t used = buffer->size;
|
||||||
|
|
||||||
|
void *contiguousBuffer;
|
||||||
|
status_t status = append_size(buffer, size, &contiguousBuffer);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (contiguousBuffer)
|
||||||
|
memcpy(contiguousBuffer, data, size);
|
||||||
|
else
|
||||||
|
write_data(buffer, used, data, size);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Removes bytes from the beginning of the buffer.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
remove_header(net_buffer *_buffer, size_t bytes)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
if (bytes > buffer->size)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
size_t left = bytes;
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (node != NULL && left > 0) {
|
||||||
|
size_t cut = min_c(node->used, left);
|
||||||
|
node->offset = 0;
|
||||||
|
node->start += cut;
|
||||||
|
node->header_space += cut;
|
||||||
|
node->used -= cut;
|
||||||
|
|
||||||
|
left -= cut;
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust offset of following nodes
|
||||||
|
while (node != NULL) {
|
||||||
|
node->offset -= bytes;
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->size -= bytes;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Trims the buffer to the specified \a newSize by removing space from
|
||||||
|
the end of the buffer.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
trim_data(net_buffer *_buffer, size_t newSize)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
if (newSize > buffer->size)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
if (newSize == buffer->size)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (node->offset + node->used < newSize) {
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL) {
|
||||||
|
// trim size greater than buffer size
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 diff = node->used + node->offset - newSize;
|
||||||
|
node->tail_space += diff;
|
||||||
|
node->used -= diff;
|
||||||
|
|
||||||
|
if (node->used > 0)
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
|
||||||
|
while (node != NULL) {
|
||||||
|
data_node *next = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
list_remove_item(&buffer->buffers, node);
|
||||||
|
remove_data_node(node);
|
||||||
|
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->size = newSize;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tries to directly access the requested space in the buffer.
|
||||||
|
If the space is contiguous, the function will succeed and place a pointer
|
||||||
|
to that space into \a _contiguousBuffer.
|
||||||
|
|
||||||
|
\return B_BAD_VALUE if the offset is outside of the buffer's bounds.
|
||||||
|
\return B_ERROR in case the buffer is not contiguous at that location.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
direct_access(net_buffer *_buffer, uint32 offset, size_t size,
|
||||||
|
void **_contiguousBuffer)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
if (offset + size > buffer->size)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// find node to access
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (node->offset + node->used < offset) {
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= node->offset;
|
||||||
|
|
||||||
|
if (size > node->used - offset)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
*_contiguousBuffer = node->start + offset;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32
|
||||||
|
checksum_data(net_buffer *_buffer, uint32 offset, size_t size, bool finalize)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
|
||||||
|
if (offset + size > buffer->size || size == 0)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// find first node to read from
|
||||||
|
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
while (node->offset + node->used < offset) {
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= node->offset;
|
||||||
|
|
||||||
|
// Since the maximum buffer size is 65536 bytes, it's impossible
|
||||||
|
// to overlap 32 bit - we don't need to handle this overlap in
|
||||||
|
// the loop, we can safely do it afterwards
|
||||||
|
uint32 sum = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t bytes = min_c(size, node->used - offset);
|
||||||
|
if ((offset + node->offset) & 1) {
|
||||||
|
// if we're at an uneven offset, we have to swap the checksum
|
||||||
|
sum += __swap_int16(compute_checksum(node->start + offset, bytes));
|
||||||
|
} else
|
||||||
|
sum += compute_checksum(node->start + offset, bytes);
|
||||||
|
|
||||||
|
size -= bytes;
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sum >> 16) {
|
||||||
|
sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finalize)
|
||||||
|
return (uint16)sum;
|
||||||
|
|
||||||
|
return (uint16)~sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
get_iovecs(net_buffer *_buffer, struct iovec *iovecs, uint32 vecCount)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
uint32 count = 0;
|
||||||
|
|
||||||
|
while (count < vecCount) {
|
||||||
|
iovecs[count].iov_base = node->start;
|
||||||
|
iovecs[count].iov_len = node->used;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
count_iovecs(net_buffer *_buffer)
|
||||||
|
{
|
||||||
|
net_buffer_private *buffer = (net_buffer_private *)_buffer;
|
||||||
|
data_node *node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
uint32 count = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
node = (data_node *)list_get_next_item(&buffer->buffers, node);
|
||||||
|
if (node == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_buffer_module_info gNetBufferModule = {
|
||||||
|
{
|
||||||
|
NET_BUFFER_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
std_ops
|
||||||
|
},
|
||||||
|
create_buffer,
|
||||||
|
free_buffer,
|
||||||
|
|
||||||
|
duplicate_buffer,
|
||||||
|
clone_buffer,
|
||||||
|
NULL, // split
|
||||||
|
merge_buffer,
|
||||||
|
|
||||||
|
prepend_size,
|
||||||
|
prepend_data,
|
||||||
|
append_size,
|
||||||
|
append_data,
|
||||||
|
NULL, // insert
|
||||||
|
NULL, // remove
|
||||||
|
remove_header,
|
||||||
|
NULL, // remove_trailer
|
||||||
|
trim_data,
|
||||||
|
|
||||||
|
NULL, // associate_data
|
||||||
|
|
||||||
|
direct_access,
|
||||||
|
read_data,
|
||||||
|
write_data,
|
||||||
|
|
||||||
|
checksum_data,
|
||||||
|
|
||||||
|
NULL, // get_memory_map
|
||||||
|
get_iovecs,
|
||||||
|
count_iovecs,
|
||||||
|
|
||||||
|
NULL, // dump
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,553 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "stack_private.h"
|
||||||
|
|
||||||
|
#include <net_protocol.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
create_socket(int family, int type, int protocol, net_socket **_socket)
|
||||||
|
{
|
||||||
|
struct net_socket *socket = new (std::nothrow) net_socket;
|
||||||
|
if (socket == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset(socket, 0, sizeof(net_socket));
|
||||||
|
socket->family = family;
|
||||||
|
socket->type = type;
|
||||||
|
socket->protocol = protocol;
|
||||||
|
|
||||||
|
// set defaults (may be overridden by the protocols)
|
||||||
|
socket->send.buffer_size = 65536;
|
||||||
|
socket->send.low_water_mark = 1;
|
||||||
|
socket->send.timeout = B_INFINITE_TIMEOUT;
|
||||||
|
socket->receive.buffer_size = 65536;
|
||||||
|
socket->receive.low_water_mark = 1;
|
||||||
|
socket->receive.timeout = B_INFINITE_TIMEOUT;
|
||||||
|
|
||||||
|
status_t status = get_domain_protocols(socket);
|
||||||
|
if (status < B_OK) {
|
||||||
|
delete socket;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = socket->first_info->open(socket->first_protocol);
|
||||||
|
if (status < B_OK) {
|
||||||
|
put_domain_protocols(socket);
|
||||||
|
delete socket;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_socket = socket;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_close(net_socket *socket)
|
||||||
|
{
|
||||||
|
return socket->first_info->close(socket->first_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_free(net_socket *socket)
|
||||||
|
{
|
||||||
|
status_t status = socket->first_info->free(socket->first_protocol);
|
||||||
|
|
||||||
|
put_domain_protocols(socket);
|
||||||
|
delete socket;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_readv(net_socket *socket, const iovec *vecs, size_t vecCount, size_t *_length)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_writev(net_socket *socket, const iovec *vecs, size_t vecCount, size_t *_length)
|
||||||
|
{
|
||||||
|
if (socket->peer.ss_len == 0)
|
||||||
|
return ECONNRESET;
|
||||||
|
|
||||||
|
if (socket->address.ss_len == 0) {
|
||||||
|
// TODO: bind?!
|
||||||
|
return ENETUNREACH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: useful, maybe even computed header space!
|
||||||
|
net_buffer *buffer = gNetBufferModule.create(256);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
// copy data into buffer
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < vecCount; i++) {
|
||||||
|
if (gNetBufferModule.append(buffer, vecs[i].iov_base,
|
||||||
|
vecs[i].iov_len) < B_OK) {
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
return ENOBUFS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//buffer->source = (sockaddr *)&socket->address;
|
||||||
|
//buffer->destination = (sockaddr *)&socket->peer;
|
||||||
|
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||||
|
memcpy(&buffer->destination, &socket->peer, socket->peer.ss_len);
|
||||||
|
|
||||||
|
ssize_t bytesWritten = socket->first_info->send_data(socket->first_protocol,
|
||||||
|
buffer);
|
||||||
|
if (bytesWritten < B_OK) {
|
||||||
|
*_length = 0;
|
||||||
|
return bytesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_length = bytesWritten;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_control(net_socket *socket, int32 op, void *data, size_t length)
|
||||||
|
{
|
||||||
|
return socket->first_info->control(socket->first_protocol,
|
||||||
|
LEVEL_DRIVER_IOCTL, op, data, &length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
socket_read_avail(net_socket *socket)
|
||||||
|
{
|
||||||
|
return socket->first_info->read_avail(socket->first_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
socket_send_avail(net_socket *socket)
|
||||||
|
{
|
||||||
|
return socket->first_info->send_avail(socket->first_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_send_data(net_socket *socket, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
return socket->first_info->send_data(socket->first_protocol,
|
||||||
|
buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
socket_receive_data(net_socket *socket, size_t length, uint32 flags,
|
||||||
|
net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
return socket->first_info->read_data(socket->first_protocol,
|
||||||
|
length, flags, _buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - standard socket API
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_accept(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength,
|
||||||
|
net_socket **_acceptedSocket)
|
||||||
|
{
|
||||||
|
net_socket *accepted;
|
||||||
|
status_t status = socket->first_info->accept(socket->first_protocol,
|
||||||
|
&accepted);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (address && *_addressLength > 0) {
|
||||||
|
memcpy(address, &accepted->peer, min_c(*_addressLength, accepted->peer.ss_len));
|
||||||
|
*_addressLength = accepted->peer.ss_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_acceptedSocket = accepted;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength)
|
||||||
|
{
|
||||||
|
sockaddr empty;
|
||||||
|
if (address == NULL) {
|
||||||
|
// special - try to bind to an empty address, like INADDR_ANY
|
||||||
|
memset(&empty, 0, sizeof(sockaddr));
|
||||||
|
empty.sa_len = sizeof(sockaddr);
|
||||||
|
empty.sa_family = socket->family;
|
||||||
|
|
||||||
|
address = ∅
|
||||||
|
addressLength = sizeof(sockaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket->address.ss_len != 0) {
|
||||||
|
status_t status = socket->first_info->unbind(socket->first_protocol,
|
||||||
|
(sockaddr *)&socket->address);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&socket->address, address, sizeof(sockaddr));
|
||||||
|
|
||||||
|
status_t status = socket->first_info->bind(socket->first_protocol,
|
||||||
|
(sockaddr *)address);
|
||||||
|
if (status < B_OK) {
|
||||||
|
// clear address again, as binding failed
|
||||||
|
socket->address.ss_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_connect(net_socket *socket, const struct sockaddr *address, socklen_t addressLength)
|
||||||
|
{
|
||||||
|
if (address == NULL || addressLength == 0)
|
||||||
|
return ENETUNREACH;
|
||||||
|
|
||||||
|
if (socket->address.ss_len == 0) {
|
||||||
|
// try to bind first
|
||||||
|
status_t status = socket_bind(socket, NULL, 0);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket->first_info->connect(socket->first_protocol, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_getpeername(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength)
|
||||||
|
{
|
||||||
|
if (socket->peer.ss_len == 0)
|
||||||
|
return ENOTCONN;
|
||||||
|
|
||||||
|
memcpy(address, &socket->peer, min_c(*_addressLength, socket->peer.ss_len));
|
||||||
|
*_addressLength = socket->peer.ss_len;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_getsockname(net_socket *socket, struct sockaddr *address, socklen_t *_addressLength)
|
||||||
|
{
|
||||||
|
if (socket->address.ss_len == 0)
|
||||||
|
return ENOTCONN;
|
||||||
|
|
||||||
|
memcpy(address, &socket->address, min_c(*_addressLength, socket->address.ss_len));
|
||||||
|
*_addressLength = socket->address.ss_len;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_getsockopt(net_socket *socket, int level, int option, void *value,
|
||||||
|
int *_length)
|
||||||
|
{
|
||||||
|
if (level != SOL_SOCKET) {
|
||||||
|
return socket->first_info->control(socket->first_protocol,
|
||||||
|
level | LEVEL_GET_OPTION, option, value, (size_t *)_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case SO_SNDBUF:
|
||||||
|
{
|
||||||
|
uint32 *size = (uint32 *)value;
|
||||||
|
*size = socket->send.buffer_size;
|
||||||
|
*_length = sizeof(uint32);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SO_RCVBUF:
|
||||||
|
{
|
||||||
|
uint32 *size = (uint32 *)value;
|
||||||
|
*size = socket->receive.buffer_size;
|
||||||
|
*_length = sizeof(uint32);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ENOPROTOOPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_listen(net_socket *socket, int backlog)
|
||||||
|
{
|
||||||
|
return socket->first_info->listen(socket->first_protocol, backlog);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
socket_recv(net_socket *socket, void *data, size_t length, int flags)
|
||||||
|
{
|
||||||
|
net_buffer *buffer;
|
||||||
|
status_t status = socket->first_info->read_data(
|
||||||
|
socket->first_protocol, length, flags, &buffer);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
ssize_t bytesReceived = buffer->size;
|
||||||
|
gNetBufferModule.read(buffer, 0, data, bytesReceived);
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
|
||||||
|
return bytesReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
socket_recvfrom(net_socket *socket, void *data, size_t length, int flags,
|
||||||
|
struct sockaddr *address, socklen_t *_addressLength)
|
||||||
|
{
|
||||||
|
net_buffer *buffer;
|
||||||
|
status_t status = socket->first_info->read_data(
|
||||||
|
socket->first_protocol, length, flags, &buffer);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
ssize_t bytesReceived = buffer->size;
|
||||||
|
gNetBufferModule.read(buffer, 0, data, bytesReceived);
|
||||||
|
|
||||||
|
// copy source address
|
||||||
|
if (address != NULL && *_addressLength > 0) {
|
||||||
|
*_addressLength = min_c(buffer->source.ss_len, *_addressLength);
|
||||||
|
memcpy(address, &buffer->source, *_addressLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
|
||||||
|
return bytesReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
socket_send(net_socket *socket, const void *data, size_t length, int flags)
|
||||||
|
{
|
||||||
|
if (socket->peer.ss_len == 0)
|
||||||
|
return EDESTADDRREQ;
|
||||||
|
|
||||||
|
if (socket->address.ss_len == 0) {
|
||||||
|
// try to bind first
|
||||||
|
status_t status = socket_bind(socket, NULL, 0);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: useful, maybe even computed header space!
|
||||||
|
net_buffer *buffer = gNetBufferModule.create(256);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
// copy data into buffer
|
||||||
|
if (gNetBufferModule.append(buffer, data, length) < B_OK) {
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
return ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->flags = flags;
|
||||||
|
//buffer->source = (sockaddr *)&socket->address;
|
||||||
|
//buffer->destination = (sockaddr *)&socket->peer;
|
||||||
|
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||||
|
memcpy(&buffer->destination, &socket->peer, socket->peer.ss_len);
|
||||||
|
|
||||||
|
status_t status = socket->first_info->send_data(socket->first_protocol, buffer);
|
||||||
|
if (status < B_OK) {
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
socket_sendto(net_socket *socket, const void *data, size_t length, int flags,
|
||||||
|
const struct sockaddr *address, socklen_t addressLength)
|
||||||
|
{
|
||||||
|
if ((address == NULL || addressLength == 0) && socket->peer.ss_len != 0) {
|
||||||
|
// socket is connected, we use that address:
|
||||||
|
address = (struct sockaddr *)&socket->peer;
|
||||||
|
addressLength = socket->peer.ss_len;
|
||||||
|
}
|
||||||
|
if (address == NULL || addressLength == 0) {
|
||||||
|
// don't know where to send to:
|
||||||
|
return EDESTADDRREQ;
|
||||||
|
}
|
||||||
|
if (socket->peer.ss_len != 0) {
|
||||||
|
// an address has been given but socket is connected already:
|
||||||
|
return EISCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket->address.ss_len == 0) {
|
||||||
|
// try to bind first
|
||||||
|
status_t status = socket_bind(socket, NULL, 0);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: useful, maybe even computed header space!
|
||||||
|
net_buffer *buffer = gNetBufferModule.create(256);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
// copy data into buffer
|
||||||
|
if (gNetBufferModule.append(buffer, data, length) < B_OK) {
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
return ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->flags = flags;
|
||||||
|
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||||
|
memcpy(&buffer->destination, address, addressLength);
|
||||||
|
|
||||||
|
status_t status = socket->first_info->send_data(socket->first_protocol, buffer);
|
||||||
|
if (status < B_OK) {
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_setsockopt(net_socket *socket, int level, int option, const void *value,
|
||||||
|
int length)
|
||||||
|
{
|
||||||
|
if (level != SOL_SOCKET) {
|
||||||
|
return socket->first_info->control(socket->first_protocol,
|
||||||
|
level | LEVEL_SET_OPTION, option, (void *)value, (size_t *)&length);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
// TODO: implement other options!
|
||||||
|
case SO_LINGER:
|
||||||
|
{
|
||||||
|
if (length < (int)sizeof(struct linger))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
struct linger *linger = (struct linger *)value;
|
||||||
|
if (linger->l_onoff) {
|
||||||
|
socket->options |= SO_LINGER;
|
||||||
|
socket->linger = linger->l_linger;
|
||||||
|
} else {
|
||||||
|
socket->options &= ~SO_LINGER;
|
||||||
|
socket->linger = 0;
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SO_SNDBUF:
|
||||||
|
if (length != sizeof(uint32))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
socket->send.buffer_size = *(const uint32 *)value;
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
case SO_RCVBUF:
|
||||||
|
if (length != sizeof(uint32))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
socket->receive.buffer_size = *(const uint32 *)value;
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ENOPROTOOPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
socket_shutdown(net_socket *socket, int direction)
|
||||||
|
{
|
||||||
|
return socket->first_info->shutdown(socket->first_protocol, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
socket_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
{
|
||||||
|
// initialize the main stack if not done so already
|
||||||
|
module_info *module;
|
||||||
|
return get_module(NET_STARTER_MODULE_NAME, &module);
|
||||||
|
}
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return put_module(NET_STARTER_MODULE_NAME);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_socket_module_info gNetSocketModule = {
|
||||||
|
{
|
||||||
|
NET_SOCKET_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
socket_std_ops
|
||||||
|
},
|
||||||
|
create_socket,
|
||||||
|
socket_close,
|
||||||
|
socket_free,
|
||||||
|
|
||||||
|
socket_readv,
|
||||||
|
socket_writev,
|
||||||
|
socket_control,
|
||||||
|
|
||||||
|
socket_read_avail,
|
||||||
|
socket_send_avail,
|
||||||
|
|
||||||
|
socket_send_data,
|
||||||
|
socket_receive_data,
|
||||||
|
|
||||||
|
// standard socket API
|
||||||
|
socket_accept,
|
||||||
|
socket_bind,
|
||||||
|
socket_connect,
|
||||||
|
socket_getpeername,
|
||||||
|
socket_getsockname,
|
||||||
|
socket_getsockopt,
|
||||||
|
socket_listen,
|
||||||
|
socket_recv,
|
||||||
|
socket_recvfrom,
|
||||||
|
socket_send,
|
||||||
|
socket_sendto,
|
||||||
|
socket_setsockopt,
|
||||||
|
socket_shutdown,
|
||||||
|
};
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1988, 1989, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef _RADIX_H_
|
||||||
|
#define _RADIX_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Radix search tree node layout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct radix_node {
|
||||||
|
struct radix_mask *rn_mklist; /* list of masks contained in subtree */
|
||||||
|
struct radix_node *rn_parent; /* parent */
|
||||||
|
short rn_bit; /* bit offset; -1-index(netmask) */
|
||||||
|
char rn_bmask; /* node: mask for bit test*/
|
||||||
|
uint8 rn_flags; /* enumerated next */
|
||||||
|
union {
|
||||||
|
struct { /* leaf only data: */
|
||||||
|
uint8 *rn_Key; /* object of search */
|
||||||
|
uint8 *rn_Mask; /* netmask, if present */
|
||||||
|
struct radix_node *rn_Dupedkey;
|
||||||
|
} rn_leaf;
|
||||||
|
struct { /* node only data: */
|
||||||
|
int rn_Off; /* where to start compare */
|
||||||
|
struct radix_node *rn_L;/* progeny */
|
||||||
|
struct radix_node *rn_R;/* progeny */
|
||||||
|
} rn_node;
|
||||||
|
} rn_u;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RNF_NORMAL 1 /* leaf contains normal route */
|
||||||
|
#define RNF_ROOT 2 /* leaf is root leaf for tree */
|
||||||
|
#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
|
||||||
|
|
||||||
|
#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
|
||||||
|
#define rn_key rn_u.rn_leaf.rn_Key
|
||||||
|
#define rn_mask rn_u.rn_leaf.rn_Mask
|
||||||
|
#define rn_offset rn_u.rn_node.rn_Off
|
||||||
|
#define rn_left rn_u.rn_node.rn_L
|
||||||
|
#define rn_right rn_u.rn_node.rn_R
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Annotations to tree concerning potential routes applying to subtrees.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct radix_mask {
|
||||||
|
short rm_bit; /* bit offset; -1-index(netmask) */
|
||||||
|
char rm_unused; /* cf. rn_bmask */
|
||||||
|
uint8 rm_flags; /* cf. rn_flags */
|
||||||
|
struct radix_mask *rm_mklist; /* more masks to try */
|
||||||
|
union {
|
||||||
|
uint8 *rmu_mask; /* the mask */
|
||||||
|
struct radix_node *rmu_leaf; /* for normal routes */
|
||||||
|
} rm_rmu;
|
||||||
|
int rm_refs; /* # of references to this struct */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define rm_mask rm_rmu.rmu_mask
|
||||||
|
#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
|
||||||
|
|
||||||
|
typedef int walktree_f_t(struct radix_node *, void *);
|
||||||
|
|
||||||
|
struct radix_node_head {
|
||||||
|
struct radix_node *rnh_treetop;
|
||||||
|
int rnh_addrsize; /* permit, but not require fixed keys */
|
||||||
|
int rnh_pktsize; /* permit, but not require fixed keys */
|
||||||
|
struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
|
||||||
|
(void *v, void *mask,
|
||||||
|
struct radix_node_head *head, struct radix_node nodes[]);
|
||||||
|
struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
|
||||||
|
(void *v, void *mask,
|
||||||
|
struct radix_node_head *head, struct radix_node nodes[]);
|
||||||
|
struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
|
||||||
|
(void *v, void *mask, struct radix_node_head *head);
|
||||||
|
struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
|
||||||
|
(void *v, void *mask, struct radix_node_head *head);
|
||||||
|
struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
|
||||||
|
(void *v, struct radix_node_head *head);
|
||||||
|
struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
|
||||||
|
(void *v, void *mask, struct radix_node_head *head);
|
||||||
|
struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
|
||||||
|
(void *v, struct radix_node_head *head);
|
||||||
|
int (*rnh_walktree) /* traverse tree */
|
||||||
|
(struct radix_node_head *head, walktree_f_t *f, void *w);
|
||||||
|
int (*rnh_walktree_from) /* traverse tree below a */
|
||||||
|
(struct radix_node_head *head, void *a, void *m,
|
||||||
|
walktree_f_t *f, void *w);
|
||||||
|
void (*rnh_close) /* do something when the last ref drops */
|
||||||
|
(struct radix_node *rn, struct radix_node_head *head);
|
||||||
|
struct radix_node rnh_nodes[3]; /* empty tree for common case */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "{"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void rn_init(void);
|
||||||
|
int rn_inithead(void **, int);
|
||||||
|
int rn_refines(void *, void *);
|
||||||
|
struct radix_node *rn_addmask(void *, int, int);
|
||||||
|
struct radix_node *rn_addroute (void *, void *, struct radix_node_head *,
|
||||||
|
struct radix_node [2]);
|
||||||
|
struct radix_node *rn_delete(void *, void *, struct radix_node_head *);
|
||||||
|
struct radix_node *rn_lookup (void *v_arg, void *m_arg,
|
||||||
|
struct radix_node_head *head);
|
||||||
|
struct radix_node *rn_match(void *, struct radix_node_head *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _RADIX_H_ */
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "domains.h"
|
||||||
|
#include "routes.h"
|
||||||
|
#include "stack_private.h"
|
||||||
|
|
||||||
|
#include <net_device.h>
|
||||||
|
#include <NetUtilities.h>
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_ROUTES
|
||||||
|
#ifdef TRACE_ROUTES
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
net_route_private::net_route_private()
|
||||||
|
{
|
||||||
|
destination = mask = gateway = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
net_route_private::~net_route_private()
|
||||||
|
{
|
||||||
|
free(destination);
|
||||||
|
free(mask);
|
||||||
|
free(gateway);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
user_copy_address(const sockaddr *from, sockaddr **to)
|
||||||
|
{
|
||||||
|
if (from == NULL) {
|
||||||
|
*to = NULL;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr address;
|
||||||
|
if (user_memcpy(&address, from, sizeof(struct sockaddr)) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
*to = (sockaddr *)malloc(address.sa_len);
|
||||||
|
if (*to == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
if (address.sa_len > sizeof(struct sockaddr)) {
|
||||||
|
if (user_memcpy(*to, from, address.sa_len) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
} else
|
||||||
|
memcpy(*to, &address, address.sa_len);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static net_route_private *
|
||||||
|
find_route(struct net_domain *_domain, const net_route *description)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
RouteList::Iterator iterator = domain->routes.GetIterator();
|
||||||
|
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_route_private *route = iterator.Next();
|
||||||
|
|
||||||
|
if ((route->flags & (RTF_GATEWAY | RTF_HOST | RTF_LOCAL)) ==
|
||||||
|
(description->flags & (RTF_GATEWAY | RTF_HOST | RTF_LOCAL))
|
||||||
|
&& domain->address_module->equal_masked_addresses(route->destination,
|
||||||
|
description->destination, description->mask)
|
||||||
|
&& domain->address_module->equal_addresses(route->mask,
|
||||||
|
description->mask)
|
||||||
|
&& domain->address_module->equal_addresses(route->gateway,
|
||||||
|
description->gateway))
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static net_route_private *
|
||||||
|
find_route(struct net_domain *_domain, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
|
||||||
|
// TODO: the following only works for IPv4 routes!
|
||||||
|
if (domain->family != AF_INET)
|
||||||
|
panic("you should have known better...");
|
||||||
|
|
||||||
|
// find last matching route
|
||||||
|
|
||||||
|
RouteList::Iterator iterator = domain->routes.GetIterator();
|
||||||
|
TRACE(("test address %s for routes...\n", AddressString(domain, address).Data()));
|
||||||
|
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_route_private *route = iterator.Next();
|
||||||
|
|
||||||
|
bool found;
|
||||||
|
if (route->mask != NULL) {
|
||||||
|
sockaddr maskedAddress;
|
||||||
|
domain->address_module->mask_address(address, route->mask,
|
||||||
|
&maskedAddress);
|
||||||
|
found = domain->address_module->equal_addresses(&maskedAddress,
|
||||||
|
route->destination);
|
||||||
|
} else {
|
||||||
|
found = domain->address_module->equal_addresses(address,
|
||||||
|
route->destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
TRACE((" found route: %s, flags %lx\n",
|
||||||
|
AddressString(domain, route->destination).Data(), route->flags));
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
put_route_internal(struct net_domain_private *domain, net_route *_route)
|
||||||
|
{
|
||||||
|
net_route_private *route = (net_route_private *)_route;
|
||||||
|
if (route == NULL || atomic_add(&route->ref_count, -1) != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove route
|
||||||
|
|
||||||
|
domain->routes.Remove(route);
|
||||||
|
delete route;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct net_route *
|
||||||
|
get_route_internal(struct net_domain_private *domain, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
net_route_private *route = find_route(domain, address);
|
||||||
|
if (route != NULL && atomic_add(&route->ref_count, 1) == 0) {
|
||||||
|
// route has been deleted already
|
||||||
|
route = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
update_route_infos(struct net_domain_private *domain)
|
||||||
|
{
|
||||||
|
RouteInfoList::Iterator iterator = domain->route_infos.GetIterator();
|
||||||
|
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_route_info *info = iterator.Next();
|
||||||
|
|
||||||
|
put_route_internal(domain, info->route);
|
||||||
|
info->route = get_route_internal(domain, &info->address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Determines the size of a buffer large enough to contain the whole
|
||||||
|
routing table.
|
||||||
|
*/
|
||||||
|
uint32
|
||||||
|
route_table_size(net_domain_private *domain)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(domain->lock);
|
||||||
|
uint32 size = 0;
|
||||||
|
|
||||||
|
RouteList::Iterator iterator = domain->routes.GetIterator();
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_route_private *route = iterator.Next();
|
||||||
|
size += IF_NAMESIZE + sizeof(route_entry);
|
||||||
|
|
||||||
|
if (route->destination)
|
||||||
|
size += route->destination->sa_len;
|
||||||
|
if (route->mask)
|
||||||
|
size += route->mask->sa_len;
|
||||||
|
if (route->gateway)
|
||||||
|
size += route->gateway->sa_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Dumps a list of all routes into the supplied userland buffer.
|
||||||
|
If the routes don't fit into the buffer, an error (\c ENOBUFS) is
|
||||||
|
returned.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
list_routes(net_domain_private *domain, void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
RouteList::Iterator iterator = domain->routes.GetIterator();
|
||||||
|
size_t spaceLeft = size;
|
||||||
|
|
||||||
|
sockaddr zeros;
|
||||||
|
memset(&zeros, 0, sizeof(sockaddr));
|
||||||
|
zeros.sa_family = domain->family;
|
||||||
|
zeros.sa_len = sizeof(sockaddr);
|
||||||
|
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
net_route *route = iterator.Next();
|
||||||
|
|
||||||
|
size = IF_NAMESIZE + sizeof(route_entry);
|
||||||
|
|
||||||
|
sockaddr *destination = NULL;
|
||||||
|
sockaddr *mask = NULL;
|
||||||
|
sockaddr *gateway = NULL;
|
||||||
|
uint8 *next = (uint8 *)buffer + size;
|
||||||
|
|
||||||
|
if (route->destination != NULL) {
|
||||||
|
destination = (sockaddr *)next;
|
||||||
|
next += route->destination->sa_len;
|
||||||
|
size += route->destination->sa_len;
|
||||||
|
}
|
||||||
|
if (route->mask != NULL) {
|
||||||
|
mask = (sockaddr *)next;
|
||||||
|
next += route->mask->sa_len;
|
||||||
|
size += route->mask->sa_len;
|
||||||
|
}
|
||||||
|
if (route->gateway != NULL) {
|
||||||
|
gateway = (sockaddr *)next;
|
||||||
|
next += route->gateway->sa_len;
|
||||||
|
size += route->gateway->sa_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spaceLeft < size)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
ifreq request;
|
||||||
|
strlcpy(request.ifr_name, route->interface->name, IF_NAMESIZE);
|
||||||
|
request.ifr_route.destination = destination;
|
||||||
|
request.ifr_route.mask = mask;
|
||||||
|
request.ifr_route.gateway = gateway;
|
||||||
|
request.ifr_route.mtu = route->mtu;
|
||||||
|
request.ifr_route.flags = route->flags;
|
||||||
|
|
||||||
|
if (user_memcpy(buffer, &request, size) < B_OK
|
||||||
|
|| (route->destination != NULL && user_memcpy(request.ifr_route.destination, route->destination, route->destination->sa_len) < B_OK)
|
||||||
|
|| (route->mask != NULL && user_memcpy(request.ifr_route.mask, route->mask, route->mask->sa_len) < B_OK)
|
||||||
|
|| (route->gateway != NULL && user_memcpy(request.ifr_route.gateway, route->gateway, route->gateway->sa_len) < B_OK))
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
buffer = (void *)next;
|
||||||
|
spaceLeft -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
control_routes(struct net_interface *interface, int32 option, void *argument, size_t length)
|
||||||
|
{
|
||||||
|
net_domain_private *domain = (net_domain_private *)interface->domain;
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case SIOCADDRT:
|
||||||
|
case SIOCDELRT:
|
||||||
|
{
|
||||||
|
// add or remove a route
|
||||||
|
if (length != sizeof(struct ifreq))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
route_entry entry;
|
||||||
|
if (user_memcpy(&entry, &((ifreq *)argument)->ifr_route, sizeof(route_entry)) != B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
net_route_private route;
|
||||||
|
status_t status;
|
||||||
|
if ((status = user_copy_address(entry.destination, &route.destination)) != B_OK
|
||||||
|
|| (status = user_copy_address(entry.mask, &route.mask)) != B_OK
|
||||||
|
|| (status = user_copy_address(entry.gateway, &route.gateway)) != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
route.mtu = entry.mtu;
|
||||||
|
route.flags = entry.flags;
|
||||||
|
route.interface = interface;
|
||||||
|
|
||||||
|
if (option == SIOCADDRT)
|
||||||
|
return add_route(domain, &route);
|
||||||
|
|
||||||
|
return remove_route(domain, &route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
add_route(struct net_domain *_domain, const struct net_route *newRoute)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
|
||||||
|
TRACE(("add route to domain %s: dest %s, mask %s, gw %s, flags %lx\n",
|
||||||
|
domain->name,
|
||||||
|
AddressString(domain, newRoute->destination ? newRoute->destination : NULL).Data(),
|
||||||
|
AddressString(domain, newRoute->mask ? newRoute->mask : NULL).Data(),
|
||||||
|
AddressString(domain, newRoute->gateway ? newRoute->gateway : NULL).Data(),
|
||||||
|
newRoute->flags));
|
||||||
|
|
||||||
|
if (domain == NULL || newRoute == NULL || newRoute->interface == NULL
|
||||||
|
|| ((newRoute->flags & RTF_HOST) != 0 && newRoute->mask != NULL)
|
||||||
|
|| ((newRoute->flags & RTF_DEFAULT) == 0 && newRoute->destination == NULL)
|
||||||
|
|| ((newRoute->flags & RTF_GATEWAY) != 0 && newRoute->gateway == NULL)
|
||||||
|
|| !domain->address_module->check_mask(newRoute->mask))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
net_route_private *route = find_route(domain, newRoute);
|
||||||
|
if (route != NULL)
|
||||||
|
return B_FILE_EXISTS;
|
||||||
|
|
||||||
|
route = new (std::nothrow) net_route_private;
|
||||||
|
if (route == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
if (domain->address_module->copy_address(newRoute->destination,
|
||||||
|
&route->destination, (newRoute->flags & RTF_DEFAULT) != 0,
|
||||||
|
newRoute->mask) != B_OK
|
||||||
|
|| domain->address_module->copy_address(newRoute->mask, &route->mask,
|
||||||
|
(newRoute->flags & RTF_DEFAULT) != 0) != B_OK
|
||||||
|
|| domain->address_module->copy_address(newRoute->gateway,
|
||||||
|
&route->gateway) != B_OK) {
|
||||||
|
delete route;
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
route->flags = newRoute->flags;
|
||||||
|
route->interface = newRoute->interface;
|
||||||
|
route->mtu = 0;
|
||||||
|
route->ref_count = 1;
|
||||||
|
|
||||||
|
// TODO: for now...
|
||||||
|
//BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
// Insert the route sorted by completeness of its mask
|
||||||
|
|
||||||
|
RouteList::Iterator iterator = domain->routes.GetIterator();
|
||||||
|
net_route_private *before = NULL;
|
||||||
|
|
||||||
|
while ((before = iterator.Next()) != NULL) {
|
||||||
|
// if the before mask is less specific than the one of the route,
|
||||||
|
// we can insert it before that route.
|
||||||
|
if (domain->address_module->first_mask_bit(before->mask)
|
||||||
|
> domain->address_module->first_mask_bit(route->mask))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain->routes.Insert(before, route);
|
||||||
|
update_route_infos(domain);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
remove_route(struct net_domain *_domain, const struct net_route *removeRoute)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
|
||||||
|
TRACE(("remove route from domain %s: dest %s, mask %s, gw %s, flags %lx\n",
|
||||||
|
domain->name,
|
||||||
|
AddressString(domain, removeRoute->destination ? removeRoute->destination : NULL).Data(),
|
||||||
|
AddressString(domain, removeRoute->mask ? removeRoute->mask : NULL).Data(),
|
||||||
|
AddressString(domain, removeRoute->gateway ? removeRoute->gateway : NULL).Data(),
|
||||||
|
removeRoute->flags));
|
||||||
|
|
||||||
|
// TODO: for now...
|
||||||
|
//BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
net_route_private *route = find_route(domain, removeRoute);
|
||||||
|
if (route == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
put_route_internal(domain, route);
|
||||||
|
update_route_infos(domain);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct net_route *
|
||||||
|
get_route(struct net_domain *_domain, const struct sockaddr *address)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
return get_route_internal(domain, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
put_route(struct net_domain *_domain, net_route *route)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
put_route_internal(domain, (net_route *)route);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_route_info(struct net_domain *_domain,
|
||||||
|
struct net_route_info *info)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
domain->route_infos.Add(info);
|
||||||
|
info->route = get_route_internal(domain, &info->address);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
unregister_route_info(struct net_domain *_domain,
|
||||||
|
struct net_route_info *info)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
BenaphoreLocker locker(domain->lock);
|
||||||
|
|
||||||
|
domain->route_infos.Remove(info);
|
||||||
|
if (info->route != NULL)
|
||||||
|
put_route_internal(domain, info->route);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
update_route_info(struct net_domain *domain,
|
||||||
|
struct net_route_info *info)
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef ROUTES_H
|
||||||
|
#define ROUTES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_datalink.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct net_route_private : net_route, public DoublyLinkedListLinkImpl<net_route_private> {
|
||||||
|
int32 ref_count;
|
||||||
|
|
||||||
|
net_route_private();
|
||||||
|
~net_route_private();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<net_route_private> RouteList;
|
||||||
|
typedef DoublyLinkedList<net_route_info, DoublyLinkedListCLink<net_route_info> > RouteInfoList;
|
||||||
|
|
||||||
|
|
||||||
|
uint32 route_table_size(struct net_domain_private *domain);
|
||||||
|
status_t list_routes(struct net_domain_private *domain, void *buffer, size_t size);
|
||||||
|
status_t control_routes(struct net_interface *interface,
|
||||||
|
int32 option, void *argument, size_t length);
|
||||||
|
|
||||||
|
status_t add_route(struct net_domain *domain,
|
||||||
|
const struct net_route *route);
|
||||||
|
status_t remove_route(struct net_domain *domain,
|
||||||
|
const struct net_route *route);
|
||||||
|
struct net_route *get_route(struct net_domain *domain, const struct sockaddr *address);
|
||||||
|
void put_route(struct net_domain *domain, struct net_route *route);
|
||||||
|
|
||||||
|
status_t register_route_info(struct net_domain *domain,
|
||||||
|
struct net_route_info *info);
|
||||||
|
status_t unregister_route_info(struct net_domain *domain,
|
||||||
|
struct net_route_info *info);
|
||||||
|
status_t update_route_info(struct net_domain *domain,
|
||||||
|
struct net_route_info *info);
|
||||||
|
|
||||||
|
#endif // ROUTES_H
|
||||||
@@ -0,0 +1,939 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "domains.h"
|
||||||
|
#include "interfaces.h"
|
||||||
|
#include "link.h"
|
||||||
|
#include "stack_private.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
#include <net_datalink_protocol.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
#include <net_protocol.h>
|
||||||
|
#include <net_stack.h>
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
#include <util/khash.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_STACK
|
||||||
|
#ifdef TRACE_STACK
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_CHAIN_MODULES 5
|
||||||
|
|
||||||
|
struct chain_key {
|
||||||
|
int family;
|
||||||
|
int type;
|
||||||
|
int protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct family {
|
||||||
|
family(int type);
|
||||||
|
|
||||||
|
void Acquire();
|
||||||
|
void Release();
|
||||||
|
|
||||||
|
static int Compare(void *_family, const void *_key);
|
||||||
|
static uint32 Hash(void *_family, const void *_key, uint32 range);
|
||||||
|
static struct family *Lookup(int type);
|
||||||
|
static struct family *Add(int type);
|
||||||
|
|
||||||
|
struct family *next;
|
||||||
|
int type;
|
||||||
|
int32 ref_count;
|
||||||
|
struct list chains;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct chain {
|
||||||
|
chain(int family, int type, int protocol);
|
||||||
|
~chain();
|
||||||
|
|
||||||
|
status_t Acquire();
|
||||||
|
void Release();
|
||||||
|
void Uninitialize();
|
||||||
|
|
||||||
|
static int Compare(void *_chain, const void *_key);
|
||||||
|
static uint32 Hash(void *_chain, const void *_key, uint32 range);
|
||||||
|
static struct chain *Lookup(hash_table *chains, int family, int type,
|
||||||
|
int protocol);
|
||||||
|
static struct chain *Add(hash_table *chains, int family, int type,
|
||||||
|
int protocol, va_list modules);
|
||||||
|
static struct chain *Add(hash_table *chains, int family, int type,
|
||||||
|
int protocol, ...);
|
||||||
|
static void DeleteChains(hash_table *chains);
|
||||||
|
|
||||||
|
chain *next;
|
||||||
|
struct list_link family_link;
|
||||||
|
struct family *parent;
|
||||||
|
|
||||||
|
int family;
|
||||||
|
int type;
|
||||||
|
int protocol;
|
||||||
|
|
||||||
|
int32 ref_count;
|
||||||
|
uint32 flags;
|
||||||
|
const char *modules[MAX_CHAIN_MODULES + 1];
|
||||||
|
module_info *infos[MAX_CHAIN_MODULES + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CHAIN_MISSING_MODULE 0x02
|
||||||
|
#define CHAIN_INITIALIZED 0x01
|
||||||
|
|
||||||
|
static benaphore sChainLock;
|
||||||
|
static benaphore sInitializeChainLock;
|
||||||
|
static hash_table *sProtocolChains;
|
||||||
|
static hash_table *sDatalinkProtocolChains;
|
||||||
|
static hash_table *sReceivingProtocolChains;
|
||||||
|
static hash_table *sFamilies;
|
||||||
|
static bool sInitialized;
|
||||||
|
|
||||||
|
|
||||||
|
family::family(int _type)
|
||||||
|
:
|
||||||
|
type(_type),
|
||||||
|
ref_count(0)
|
||||||
|
{
|
||||||
|
list_init_etc(&chains, offsetof(struct chain, family_link));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
family::Acquire()
|
||||||
|
{
|
||||||
|
atomic_add(&ref_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
family::Release()
|
||||||
|
{
|
||||||
|
if (atomic_add(&ref_count, -1) > 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TRACE(("family %d unused, uninit chains\n", type));
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
struct chain *chain = NULL;
|
||||||
|
while (true) {
|
||||||
|
chain = (struct chain *)list_get_next_item(&chains, chain);
|
||||||
|
if (chain == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
chain->Uninitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ int
|
||||||
|
family::Compare(void *_family, const void *_key)
|
||||||
|
{
|
||||||
|
struct family *family = (struct family *)_family;
|
||||||
|
int key = (int)_key;
|
||||||
|
|
||||||
|
if (family->type == key)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ uint32
|
||||||
|
family::Hash(void *_family, const void *_key, uint32 range)
|
||||||
|
{
|
||||||
|
struct family *family = (struct family *)_family;
|
||||||
|
int key = (int)_key;
|
||||||
|
|
||||||
|
if (family != NULL)
|
||||||
|
return family->type % range;
|
||||||
|
|
||||||
|
return key % range;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ struct family *
|
||||||
|
family::Lookup(int type)
|
||||||
|
{
|
||||||
|
return (struct family *)hash_lookup(sFamilies, (void *)type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ struct family *
|
||||||
|
family::Add(int type)
|
||||||
|
{
|
||||||
|
struct family *family = new (std::nothrow) ::family(type);
|
||||||
|
if (family == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (hash_insert(sFamilies, family) != B_OK) {
|
||||||
|
delete family;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return family;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
chain::chain(int _family, int _type, int _protocol)
|
||||||
|
:
|
||||||
|
family(_family),
|
||||||
|
type(_type),
|
||||||
|
protocol(_protocol),
|
||||||
|
ref_count(0),
|
||||||
|
flags(0)
|
||||||
|
{
|
||||||
|
parent = ::family::Lookup(family);
|
||||||
|
if (parent == NULL)
|
||||||
|
parent = ::family::Add(family);
|
||||||
|
|
||||||
|
for (int32 i = 0; i < MAX_CHAIN_MODULES; i++) {
|
||||||
|
modules[i] = NULL;
|
||||||
|
infos[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
chain::~chain()
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < MAX_CHAIN_MODULES; i++) {
|
||||||
|
free((char *)modules[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
chain::Acquire()
|
||||||
|
{
|
||||||
|
if (atomic_add(&ref_count, 1) > 0) {
|
||||||
|
if (flags & CHAIN_MISSING_MODULE) {
|
||||||
|
atomic_add(&ref_count, -1);
|
||||||
|
return EAFNOSUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((flags & CHAIN_INITIALIZED) == 0) {
|
||||||
|
benaphore_lock(&sInitializeChainLock);
|
||||||
|
benaphore_unlock(&sInitializeChainLock);
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->Acquire();
|
||||||
|
|
||||||
|
if ((flags & CHAIN_INITIALIZED) != 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
TRACE(("initializing chain %d.%d.%d\n", family, type, protocol));
|
||||||
|
BenaphoreLocker locker(&sInitializeChainLock);
|
||||||
|
|
||||||
|
for (int32 i = 0; modules[i] != NULL; i++) {
|
||||||
|
if (get_module(modules[i], &infos[i]) < B_OK) {
|
||||||
|
flags |= CHAIN_MISSING_MODULE;
|
||||||
|
|
||||||
|
// put already opened modules
|
||||||
|
while (i-- > 0) {
|
||||||
|
put_module(modules[i]);
|
||||||
|
}
|
||||||
|
return EAFNOSUPPORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= CHAIN_INITIALIZED;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
chain::Release()
|
||||||
|
{
|
||||||
|
if (atomic_add(&ref_count, -1) > 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TRACE(("chain %d.%d.%d unused\n", family, type, protocol));
|
||||||
|
parent->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
chain::Uninitialize()
|
||||||
|
{
|
||||||
|
if ((flags & CHAIN_INITIALIZED) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TRACE(("uninit chain %d.%d.%d\n", family, type, protocol));
|
||||||
|
BenaphoreLocker locker(sInitializeChainLock);
|
||||||
|
|
||||||
|
for (int32 i = 0; modules[i] != NULL; i++) {
|
||||||
|
put_module(modules[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags &= ~CHAIN_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ int
|
||||||
|
chain::Compare(void *_chain, const void *_key)
|
||||||
|
{
|
||||||
|
const chain_key *key = (const chain_key *)_key;
|
||||||
|
struct chain *chain = (struct chain *)_chain;
|
||||||
|
|
||||||
|
if (chain->family == key->family
|
||||||
|
&& chain->type == key->type
|
||||||
|
&& chain->protocol == key->protocol)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ uint32
|
||||||
|
chain::Hash(void *_chain, const void *_key, uint32 range)
|
||||||
|
{
|
||||||
|
const chain_key *key = (const chain_key *)_key;
|
||||||
|
struct chain *chain = (struct chain *)_chain;
|
||||||
|
|
||||||
|
// TODO: check if this makes a good hash...
|
||||||
|
#define HASH(o) ((uint32)(((o)->family) ^ ((o)->type) ^ ((o)->protocol)) % range)
|
||||||
|
TRACE(("%d.%d.%d: Hash: %lu\n", chain ? chain->family : key->family,
|
||||||
|
chain ? chain->type : key->type, chain ? chain->protocol : key->protocol,
|
||||||
|
chain ? HASH(chain) : HASH(key)));
|
||||||
|
|
||||||
|
if (chain != NULL)
|
||||||
|
return HASH(chain);
|
||||||
|
|
||||||
|
return HASH(key);
|
||||||
|
#undef HASH
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static */ struct chain *
|
||||||
|
chain::Lookup(hash_table *chains, int family, int type, int protocol)
|
||||||
|
{
|
||||||
|
struct chain_key key = { family, type, protocol };
|
||||||
|
return (struct chain *)hash_lookup(chains, &key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ struct chain *
|
||||||
|
chain::Add(hash_table *chains, int family, int type, int protocol, va_list modules)
|
||||||
|
{
|
||||||
|
struct chain *chain = new (std::nothrow) ::chain(family, type, protocol);
|
||||||
|
if (chain == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (chain->parent == NULL || hash_insert(chains, chain) != B_OK) {
|
||||||
|
delete chain;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(("Add chain %d.%d.%d:\n", family, type, protocol));
|
||||||
|
const char *module;
|
||||||
|
int32 count = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
module = va_arg(modules, const char *);
|
||||||
|
if (module == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
TRACE((" [%ld] %s\n", count, module));
|
||||||
|
chain->modules[count] = strdup(module);
|
||||||
|
if (chain->modules[count] == NULL
|
||||||
|
|| ++count >= MAX_CHAIN_MODULES) {
|
||||||
|
hash_remove(chains, chain);
|
||||||
|
delete chain;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chains == sProtocolChains && count == 0) {
|
||||||
|
hash_remove(chains, chain);
|
||||||
|
delete chain;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ struct chain *
|
||||||
|
chain::Add(hash_table *chains, int family, int type, int protocol, ...)
|
||||||
|
{
|
||||||
|
va_list modules;
|
||||||
|
va_start(modules, protocol);
|
||||||
|
|
||||||
|
struct chain *chain = Add(chains, family, type, 0, modules);
|
||||||
|
|
||||||
|
va_end(modules);
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
chain::DeleteChains(hash_table *chains)
|
||||||
|
{
|
||||||
|
uint32 cookie = 0;
|
||||||
|
while (true) {
|
||||||
|
struct chain *chain = (struct chain *)hash_remove_first(chains, &cookie);
|
||||||
|
if (chain == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
chain->Uninitialize();
|
||||||
|
delete chain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
uninit_domain_protocols(net_socket *socket)
|
||||||
|
{
|
||||||
|
net_protocol *protocol = socket->first_protocol;
|
||||||
|
while (protocol != NULL) {
|
||||||
|
net_protocol *next = protocol->next;
|
||||||
|
protocol->module->uninit_protocol(protocol);
|
||||||
|
|
||||||
|
protocol = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket->first_protocol = NULL;
|
||||||
|
socket->first_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
get_domain_protocols(net_socket *socket)
|
||||||
|
{
|
||||||
|
struct chain *chain;
|
||||||
|
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
chain = chain::Lookup(sProtocolChains, socket->family, socket->type,
|
||||||
|
socket->type == SOCK_RAW ? 0 : socket->protocol);
|
||||||
|
// in SOCK_RAW mode, we ignore the protocol information
|
||||||
|
if (chain == NULL) {
|
||||||
|
// TODO: if we want to be POSIX compatible, we should also support
|
||||||
|
// the error codes EPROTONOSUPPORT and EPROTOTYPE.
|
||||||
|
return EAFNOSUPPORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create net_protocol objects for the protocols in the chain
|
||||||
|
|
||||||
|
status_t status = chain->Acquire();
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
net_protocol *last = NULL;
|
||||||
|
|
||||||
|
for (int32 i = 0; chain->infos[i] != NULL; i++) {
|
||||||
|
net_protocol *protocol =
|
||||||
|
((net_protocol_module_info *)chain->infos[i])->init_protocol(socket);
|
||||||
|
if (protocol == NULL) {
|
||||||
|
// free protocols we already initialized
|
||||||
|
uninit_domain_protocols(socket);
|
||||||
|
chain->Release();
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol->module = (net_protocol_module_info *)chain->infos[i];
|
||||||
|
protocol->socket = socket;
|
||||||
|
protocol->next = NULL;
|
||||||
|
|
||||||
|
if (last == NULL) {
|
||||||
|
socket->first_protocol = protocol;
|
||||||
|
socket->first_info = protocol->module;
|
||||||
|
} else
|
||||||
|
last->next = protocol;
|
||||||
|
|
||||||
|
last = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
put_domain_protocols(net_socket *socket)
|
||||||
|
{
|
||||||
|
struct chain *chain;
|
||||||
|
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
chain = chain::Lookup(sProtocolChains, socket->family, socket->type,
|
||||||
|
socket->protocol);
|
||||||
|
if (chain == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uninit_domain_protocols(socket);
|
||||||
|
chain->Release();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
uninit_domain_datalink_protocols(net_interface *interface)
|
||||||
|
{
|
||||||
|
net_datalink_protocol *protocol = interface->first_protocol;
|
||||||
|
while (protocol != NULL) {
|
||||||
|
net_datalink_protocol *next = protocol->next;
|
||||||
|
protocol->module->uninit_protocol(protocol);
|
||||||
|
|
||||||
|
protocol = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface->first_protocol = NULL;
|
||||||
|
interface->first_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
get_domain_datalink_protocols(net_interface *_interface)
|
||||||
|
{
|
||||||
|
struct net_interface_private *interface = (net_interface_private *)_interface;
|
||||||
|
struct chain *chain;
|
||||||
|
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
chain = chain::Lookup(sDatalinkProtocolChains, interface->domain->family,
|
||||||
|
interface->device_interface->device->type, 0);
|
||||||
|
if (chain == NULL)
|
||||||
|
return EAFNOSUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create net_protocol objects for the protocols in the chain
|
||||||
|
|
||||||
|
status_t status = chain->Acquire();
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
net_datalink_protocol *last = NULL;
|
||||||
|
|
||||||
|
for (int32 i = 0; chain->infos[i] != NULL; i++) {
|
||||||
|
net_datalink_protocol *protocol;
|
||||||
|
status_t status = ((net_datalink_protocol_module_info *)chain->infos[i])->init_protocol(
|
||||||
|
interface, &protocol);
|
||||||
|
if (status < B_OK) {
|
||||||
|
// free protocols we already initialized
|
||||||
|
uninit_domain_datalink_protocols(interface);
|
||||||
|
chain->Release();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol->module = (net_datalink_protocol_module_info *)chain->infos[i];
|
||||||
|
protocol->interface = interface;
|
||||||
|
protocol->next = NULL;
|
||||||
|
|
||||||
|
if (last == NULL) {
|
||||||
|
interface->first_protocol = protocol;
|
||||||
|
interface->first_info = protocol->module;
|
||||||
|
} else
|
||||||
|
last->next = protocol;
|
||||||
|
|
||||||
|
last = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
put_domain_datalink_protocols(net_interface *_interface)
|
||||||
|
{
|
||||||
|
struct net_interface_private *interface = (net_interface_private *)_interface;
|
||||||
|
struct chain *chain;
|
||||||
|
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
chain = chain::Lookup(sDatalinkProtocolChains, interface->domain->family,
|
||||||
|
interface->device_interface->device->type, 0);
|
||||||
|
if (chain == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uninit_domain_datalink_protocols(interface);
|
||||||
|
chain->Release();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
get_domain_receiving_protocol(net_domain *_domain, uint32 type,
|
||||||
|
net_protocol_module_info **_module)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
struct chain *chain;
|
||||||
|
|
||||||
|
TRACE(("get_domain_receiving_protocol(family %d, type %lu)\n", domain->family, type));
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
chain = chain::Lookup(sReceivingProtocolChains, domain->family,
|
||||||
|
type, 0);
|
||||||
|
if (chain == NULL)
|
||||||
|
return EAFNOSUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t status = chain->Acquire();
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
*_module = (net_protocol_module_info *)chain->infos[0];
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
put_domain_receiving_protocol(net_domain *_domain, uint32 type)
|
||||||
|
{
|
||||||
|
struct net_domain_private *domain = (net_domain_private *)_domain;
|
||||||
|
struct chain *chain;
|
||||||
|
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
chain = chain::Lookup(sReceivingProtocolChains, domain->family,
|
||||||
|
type, 0);
|
||||||
|
if (chain == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain->Release();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_domain_protocols(int family, int type, int protocol, ...)
|
||||||
|
{
|
||||||
|
if (type == SOCK_RAW) {
|
||||||
|
// in SOCK_RAW mode, we ignore the protocol information
|
||||||
|
protocol = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
struct chain *chain = chain::Lookup(sProtocolChains, family, type, protocol);
|
||||||
|
if (chain != NULL)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
va_list modules;
|
||||||
|
va_start(modules, protocol);
|
||||||
|
|
||||||
|
chain = chain::Add(sProtocolChains, family, type, protocol, modules);
|
||||||
|
|
||||||
|
va_end(modules);
|
||||||
|
|
||||||
|
if (chain == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
register_domain_datalink_protocols(int family, int type, ...)
|
||||||
|
{
|
||||||
|
TRACE(("register_domain_datalink_protocol(%d.%d)\n", family, type));
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
struct chain *chain = chain::Lookup(sDatalinkProtocolChains, family, type, 0);
|
||||||
|
if (chain != NULL)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
va_list modules;
|
||||||
|
va_start(modules, type);
|
||||||
|
|
||||||
|
chain = chain::Add(sDatalinkProtocolChains, family, type, 0, modules);
|
||||||
|
|
||||||
|
va_end(modules);
|
||||||
|
|
||||||
|
if (chain == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// Add datalink interface protocol as the last protocol in the chain; it's name
|
||||||
|
// stays unset, so that it won't be part of the release/acquire process.
|
||||||
|
|
||||||
|
uint32 count = 0;
|
||||||
|
while (chain->modules[count] != NULL) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain->infos[count] = (module_info *)&gDatalinkInterfaceProtocolModule;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
register_domain_receiving_protocol(int family, int type, const char *moduleName)
|
||||||
|
{
|
||||||
|
TRACE(("register_domain_receiving_protocol(%d.%d, %s)\n", family, type,
|
||||||
|
moduleName));
|
||||||
|
|
||||||
|
BenaphoreLocker locker(&sChainLock);
|
||||||
|
|
||||||
|
struct chain *chain = chain::Lookup(sReceivingProtocolChains, family, type, 0);
|
||||||
|
if (chain != NULL)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
chain = chain::Add(sReceivingProtocolChains, family, type, 0, moduleName, NULL);
|
||||||
|
if (chain == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
scan_modules(const char *path)
|
||||||
|
{
|
||||||
|
void *cookie = open_module_list(path);
|
||||||
|
if (cookie == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char name[B_FILE_NAME_LENGTH];
|
||||||
|
size_t length = sizeof(name);
|
||||||
|
if (read_next_module_name(cookie, name, &length) != B_OK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
TRACE(("scan %s\n", name));
|
||||||
|
|
||||||
|
module_info *module;
|
||||||
|
if (get_module(name, &module) == B_OK) {
|
||||||
|
// we don't need the module right now, but we give it a chance
|
||||||
|
// to register itself
|
||||||
|
put_module(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
init_stack()
|
||||||
|
{
|
||||||
|
status_t status = init_domains();
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = init_interfaces();
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
status = init_timers();
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
if (benaphore_init(&sChainLock, "net chains") < B_OK)
|
||||||
|
goto err3;
|
||||||
|
if (benaphore_init(&sInitializeChainLock, "net intialize chains") < B_OK)
|
||||||
|
goto err4;
|
||||||
|
|
||||||
|
sFamilies = hash_init(10, offsetof(struct family, next),
|
||||||
|
&family::Compare, &family::Hash);
|
||||||
|
if (sFamilies == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
goto err5;
|
||||||
|
}
|
||||||
|
|
||||||
|
sProtocolChains = hash_init(10, offsetof(struct chain, next),
|
||||||
|
&chain::Compare, &chain::Hash);
|
||||||
|
if (sProtocolChains == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
goto err6;
|
||||||
|
}
|
||||||
|
|
||||||
|
sDatalinkProtocolChains = hash_init(10, offsetof(struct chain, next),
|
||||||
|
&chain::Compare, &chain::Hash);
|
||||||
|
if (sDatalinkProtocolChains == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
goto err7;
|
||||||
|
}
|
||||||
|
|
||||||
|
sReceivingProtocolChains = hash_init(10, offsetof(struct chain, next),
|
||||||
|
&chain::Compare, &chain::Hash);
|
||||||
|
if (sReceivingProtocolChains == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
goto err8;
|
||||||
|
}
|
||||||
|
|
||||||
|
sInitialized = true;
|
||||||
|
|
||||||
|
link_init();
|
||||||
|
scan_modules("network/protocols");
|
||||||
|
scan_modules("network/datalink_protocols");
|
||||||
|
|
||||||
|
// TODO: for now!
|
||||||
|
register_domain_datalink_protocols(AF_INET, IFT_LOOP, NULL);
|
||||||
|
register_domain_datalink_protocols(AF_INET, IFT_ETHER,
|
||||||
|
"network/datalink_protocols/arp/v1",
|
||||||
|
"network/datalink_protocols/ethernet_frame/v1",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err8:
|
||||||
|
hash_uninit(sDatalinkProtocolChains);
|
||||||
|
err7:
|
||||||
|
hash_uninit(sProtocolChains);
|
||||||
|
err6:
|
||||||
|
hash_uninit(sFamilies);
|
||||||
|
err5:
|
||||||
|
benaphore_destroy(&sInitializeChainLock);
|
||||||
|
err4:
|
||||||
|
benaphore_destroy(&sChainLock);
|
||||||
|
err3:
|
||||||
|
uninit_timers();
|
||||||
|
err2:
|
||||||
|
uninit_interfaces();
|
||||||
|
err1:
|
||||||
|
uninit_domains();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
uninit_stack()
|
||||||
|
{
|
||||||
|
TRACE(("Unloading network stack\n"));
|
||||||
|
|
||||||
|
uninit_timers();
|
||||||
|
uninit_interfaces();
|
||||||
|
uninit_domains();
|
||||||
|
|
||||||
|
benaphore_destroy(&sChainLock);
|
||||||
|
benaphore_destroy(&sInitializeChainLock);
|
||||||
|
|
||||||
|
// remove chains and families
|
||||||
|
|
||||||
|
chain::DeleteChains(sProtocolChains);
|
||||||
|
chain::DeleteChains(sDatalinkProtocolChains);
|
||||||
|
chain::DeleteChains(sReceivingProtocolChains);
|
||||||
|
|
||||||
|
uint32 cookie = 0;
|
||||||
|
while (true) {
|
||||||
|
struct family *family = (struct family *)hash_remove_first(sFamilies, &cookie);
|
||||||
|
if (family == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
delete family;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_uninit(sProtocolChains);
|
||||||
|
hash_uninit(sDatalinkProtocolChains);
|
||||||
|
hash_uninit(sReceivingProtocolChains);
|
||||||
|
hash_uninit(sFamilies);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
starter_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
return init_stack();
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return uninit_stack();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
stack_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
return sInitialized ? B_OK : B_BUSY;
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static net_stack_module_info sNetStackModule = {
|
||||||
|
{
|
||||||
|
NET_STACK_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
stack_std_ops
|
||||||
|
},
|
||||||
|
register_domain,
|
||||||
|
unregister_domain,
|
||||||
|
get_domain,
|
||||||
|
|
||||||
|
register_domain_protocols,
|
||||||
|
register_domain_datalink_protocols,
|
||||||
|
register_domain_receiving_protocol,
|
||||||
|
|
||||||
|
get_domain_receiving_protocol,
|
||||||
|
put_domain_receiving_protocol,
|
||||||
|
|
||||||
|
register_device_deframer,
|
||||||
|
unregister_device_deframer,
|
||||||
|
register_domain_device_handler,
|
||||||
|
register_device_handler,
|
||||||
|
unregister_device_handler,
|
||||||
|
register_device_monitor,
|
||||||
|
unregister_device_monitor,
|
||||||
|
device_removed,
|
||||||
|
|
||||||
|
checksum,
|
||||||
|
|
||||||
|
init_fifo,
|
||||||
|
uninit_fifo,
|
||||||
|
fifo_enqueue_buffer,
|
||||||
|
fifo_dequeue_buffer,
|
||||||
|
clear_fifo,
|
||||||
|
|
||||||
|
init_timer,
|
||||||
|
set_timer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static module_info sNetStarterModule = {
|
||||||
|
NET_STARTER_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
starter_std_ops
|
||||||
|
};
|
||||||
|
|
||||||
|
module_info *modules[] = {
|
||||||
|
(module_info *)&sNetStackModule,
|
||||||
|
(module_info *)&sNetStarterModule,
|
||||||
|
(module_info *)&gNetBufferModule,
|
||||||
|
(module_info *)&gNetSocketModule,
|
||||||
|
(module_info *)&gNetDatalinkModule,
|
||||||
|
(module_info *)&gLinkModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef STACK_PRIVATE_H
|
||||||
|
#define STACK_PRIVATE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <net_datalink.h>
|
||||||
|
#include <net_datalink_protocol.h>
|
||||||
|
#include <net_protocol.h>
|
||||||
|
#include <net_socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define NET_STARTER_MODULE_NAME "network/stack/starter/v1"
|
||||||
|
|
||||||
|
extern net_buffer_module_info gNetBufferModule;
|
||||||
|
extern net_socket_module_info gNetSocketModule;
|
||||||
|
extern net_datalink_module_info gNetDatalinkModule;
|
||||||
|
extern net_datalink_protocol_module_info gDatalinkInterfaceProtocolModule;
|
||||||
|
|
||||||
|
// stack.cpp
|
||||||
|
status_t register_domain_datalink_protocols(int family, int type, ...);
|
||||||
|
status_t register_domain_protocols(int family, int type, int protocol, ...);
|
||||||
|
status_t get_domain_protocols(net_socket *socket);
|
||||||
|
status_t put_domain_protocols(net_socket *socket);
|
||||||
|
status_t get_domain_datalink_protocols(net_interface *interface);
|
||||||
|
status_t put_domain_datalink_protocols(net_interface *interface);
|
||||||
|
|
||||||
|
#endif // STACK_PRIVATE_H
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "stack_private.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
|
||||||
|
static struct list sTimers;
|
||||||
|
static benaphore sTimerLock;
|
||||||
|
static sem_id sTimerWaitSem;
|
||||||
|
static thread_id sTimerThread;
|
||||||
|
|
||||||
|
|
||||||
|
uint16
|
||||||
|
compute_checksum(uint8 *_buffer, size_t length)
|
||||||
|
{
|
||||||
|
uint16 *buffer = (uint16 *)_buffer;
|
||||||
|
uint32 sum = 0;
|
||||||
|
|
||||||
|
// TODO: unfold loop for speed
|
||||||
|
// TODO: write processor dependent version for speed
|
||||||
|
while (length >= 2) {
|
||||||
|
sum += *buffer++;
|
||||||
|
length -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length) {
|
||||||
|
// give the last byte it's proper endian-aware treatment
|
||||||
|
#if B_HOST_IS_LENDIAN
|
||||||
|
sum += *(uint8 *)buffer;
|
||||||
|
#else
|
||||||
|
uint8 ordered[2];
|
||||||
|
ordered[0] = *(uint8 *)buffer;
|
||||||
|
ordered[1] = 0;
|
||||||
|
sum += *(uint16 *)ordered;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sum >> 16) {
|
||||||
|
sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16
|
||||||
|
checksum(uint8 *buffer, size_t length)
|
||||||
|
{
|
||||||
|
return ~compute_checksum(buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - FIFOs
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
init_fifo(net_fifo *fifo, const char *name, size_t maxBytes)
|
||||||
|
{
|
||||||
|
status_t status = benaphore_init(&fifo->lock, name);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
fifo->notify = create_sem(1, name);
|
||||||
|
if (fifo->notify < B_OK) {
|
||||||
|
benaphore_destroy(&fifo->lock);
|
||||||
|
return fifo->notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
fifo->max_bytes = maxBytes;
|
||||||
|
fifo->current_bytes = 0;
|
||||||
|
fifo->waiting = 0;
|
||||||
|
|
||||||
|
list_init(&fifo->buffers);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
uninit_fifo(net_fifo *fifo)
|
||||||
|
{
|
||||||
|
clear_fifo(fifo);
|
||||||
|
|
||||||
|
benaphore_destroy(&fifo->lock);
|
||||||
|
delete_sem(fifo->notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
fifo_enqueue_buffer(net_fifo *fifo, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(fifo->lock);
|
||||||
|
|
||||||
|
if (fifo->max_bytes > 0 && fifo->current_bytes + buffer->size > fifo->max_bytes)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
list_add_item(&fifo->buffers, buffer);
|
||||||
|
fifo->current_bytes += buffer->size;
|
||||||
|
|
||||||
|
if (fifo->waiting > 0) {
|
||||||
|
fifo->waiting--;
|
||||||
|
release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE);
|
||||||
|
// we still hold the benaphore lock, so it makes no sense
|
||||||
|
// to reschedule after having released the sync semaphore
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the first buffer from the FIFO. If there is no buffer, it
|
||||||
|
will wait depending on the \a flags and \a timeout.
|
||||||
|
The following flags are supported (the rest is ignored):
|
||||||
|
MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your
|
||||||
|
socket is O_NONBLOCK, you should specify this flag. A \a timeout of
|
||||||
|
zero is equivalent to this flag, though.
|
||||||
|
MSG_PEEK - returns a clone of the buffer and keep the original
|
||||||
|
in the FIFO.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout,
|
||||||
|
net_buffer **_buffer)
|
||||||
|
{
|
||||||
|
benaphore_lock(&fifo->lock);
|
||||||
|
bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
net_buffer *buffer = (net_buffer *)list_get_first_item(&fifo->buffers);
|
||||||
|
if (buffer != NULL) {
|
||||||
|
if ((flags & MSG_PEEK) != 0) {
|
||||||
|
// we need to clone the buffer for inspection; we can't give a
|
||||||
|
// handle to a buffer that we're still using
|
||||||
|
buffer = gNetBufferModule.clone(buffer, false);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
status = B_NO_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
list_remove_item(&fifo->buffers, buffer);
|
||||||
|
|
||||||
|
*_buffer = buffer;
|
||||||
|
status = B_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dontWait)
|
||||||
|
fifo->waiting++;
|
||||||
|
|
||||||
|
// we need to wait until a new buffer becomes available
|
||||||
|
benaphore_unlock(&fifo->lock);
|
||||||
|
|
||||||
|
if (dontWait)
|
||||||
|
return B_WOULD_BLOCK;
|
||||||
|
|
||||||
|
status = acquire_sem_etc(fifo->notify, 1,
|
||||||
|
B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// try again
|
||||||
|
benaphore_lock(&fifo->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & MSG_PEEK) != 0 && fifo->waiting > 0) {
|
||||||
|
// another thread is waiting for data, since we didn't eat the
|
||||||
|
// buffer, it gets it
|
||||||
|
fifo->waiting--;
|
||||||
|
release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
benaphore_unlock(&fifo->lock);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
clear_fifo(net_fifo *fifo)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(fifo->lock);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
net_buffer *buffer = (net_buffer *)list_remove_head_item(&fifo->buffers);
|
||||||
|
if (buffer == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
gNetBufferModule.free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
fifo->current_bytes = 0;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - Timer
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
timer_thread(void * /*data*/)
|
||||||
|
{
|
||||||
|
status_t status = B_OK;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bigtime_t timeout = B_INFINITE_TIMEOUT;
|
||||||
|
|
||||||
|
if (status == B_TIMED_OUT || status == B_OK) {
|
||||||
|
// scan timers for new timeout and/or execute a timer
|
||||||
|
if (benaphore_lock(&sTimerLock) < B_OK)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
struct net_timer *timer = NULL;
|
||||||
|
while (true) {
|
||||||
|
timer = (net_timer *)list_get_next_item(&sTimers, timer);
|
||||||
|
if (timer == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (timer->due < system_time()) {
|
||||||
|
// execute timer
|
||||||
|
list_remove_item(&sTimers, timer);
|
||||||
|
timer->due = -1;
|
||||||
|
|
||||||
|
benaphore_unlock(&sTimerLock);
|
||||||
|
timer->hook(timer, timer->data);
|
||||||
|
benaphore_lock(&sTimerLock);
|
||||||
|
|
||||||
|
timer = NULL;
|
||||||
|
// restart scanning as we unlocked the list
|
||||||
|
} else {
|
||||||
|
// calculate new timeout
|
||||||
|
if (timer->due < timeout)
|
||||||
|
timeout = timer->due;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
benaphore_unlock(&sTimerLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
|
||||||
|
// the wait sem normally can't be acquired, so we
|
||||||
|
// have to look at the status value the call returns:
|
||||||
|
//
|
||||||
|
// B_OK - a new timer has been added or canceled
|
||||||
|
// B_TIMED_OUT - look for timers to be executed
|
||||||
|
// B_BAD_SEM_ID - we are asked to quit
|
||||||
|
} while (status != B_BAD_SEM_ID);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Initializes a timer before use. You can also use this function to change
|
||||||
|
a timer later on, but make sure you have canceled it before using set_timer().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
init_timer(net_timer *timer, net_timer_func hook, void *data)
|
||||||
|
{
|
||||||
|
timer->hook = hook;
|
||||||
|
timer->data = data;
|
||||||
|
timer->due = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets or cancels a timer. When the \a delay is below zero, an eventually running
|
||||||
|
timer is canceled, if not, it is scheduled to be executed after the specified
|
||||||
|
\a delay.
|
||||||
|
You need to have initialized the timer before calling this function.
|
||||||
|
In case you need to change a running timer, you have to cancel it first, before
|
||||||
|
making any changes.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
set_timer(net_timer *timer, bigtime_t delay)
|
||||||
|
{
|
||||||
|
BenaphoreLocker locker(sTimerLock);
|
||||||
|
|
||||||
|
if (timer->due > 0) {
|
||||||
|
// this timer is already scheduled, cancel it
|
||||||
|
list_remove_item(&sTimers, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delay >= 0) {
|
||||||
|
// add this timer
|
||||||
|
timer->due = system_time() + delay;
|
||||||
|
list_add_item(&sTimers, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify timer about the change
|
||||||
|
release_sem(sTimerWaitSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
init_timers(void)
|
||||||
|
{
|
||||||
|
list_init(&sTimers);
|
||||||
|
|
||||||
|
status_t status = benaphore_init(&sTimerLock, "net timer");
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
sTimerWaitSem = create_sem(0, "net timer wait");
|
||||||
|
if (sTimerWaitSem < B_OK) {
|
||||||
|
status = sTimerWaitSem;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sTimerThread = spawn_kernel_thread(timer_thread, "net timer",
|
||||||
|
B_NORMAL_PRIORITY, NULL);
|
||||||
|
if (sTimerThread < B_OK) {
|
||||||
|
status = sTimerThread;
|
||||||
|
goto err2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resume_thread(sTimerThread);
|
||||||
|
|
||||||
|
err1:
|
||||||
|
benaphore_destroy(&sTimerLock);
|
||||||
|
err2:
|
||||||
|
delete_sem(sTimerWaitSem);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
uninit_timers(void)
|
||||||
|
{
|
||||||
|
benaphore_destroy(&sTimerLock);
|
||||||
|
delete_sem(sTimerWaitSem);
|
||||||
|
|
||||||
|
status_t status;
|
||||||
|
wait_for_thread(sTimerThread, &status);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Axel Dörfler, [email protected]
|
||||||
|
*/
|
||||||
|
#ifndef NET_UTILITY_H
|
||||||
|
#define NET_UTILITY_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <net_stack.h>
|
||||||
|
|
||||||
|
|
||||||
|
// checksums
|
||||||
|
uint16 compute_checksum(uint8 *_buffer, size_t length);
|
||||||
|
uint16 checksum(uint8 *buffer, size_t length);
|
||||||
|
|
||||||
|
// fifos
|
||||||
|
status_t init_fifo(net_fifo *fifo, const char *name, size_t maxBytes);
|
||||||
|
void uninit_fifo(net_fifo *fifo);
|
||||||
|
status_t fifo_enqueue_buffer(net_fifo *fifo, struct net_buffer *buffer);
|
||||||
|
ssize_t fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout,
|
||||||
|
struct net_buffer **_buffer);
|
||||||
|
status_t clear_fifo(net_fifo *fifo);
|
||||||
|
|
||||||
|
// timer
|
||||||
|
void init_timer(net_timer *timer, net_timer_func hook, void *data);
|
||||||
|
void set_timer(net_timer *timer, bigtime_t delay);
|
||||||
|
status_t init_timers(void);
|
||||||
|
void uninit_timers(void);
|
||||||
|
|
||||||
|
#endif // NET_UTILITY_H
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
SubDir HAIKU_TOP src kits network ;
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
|
||||||
|
SharedLibrary libnetwork.so :
|
||||||
|
interfaces.cpp
|
||||||
|
socket.cpp
|
||||||
|
:
|
||||||
|
<libnetwork_dns>dns_dst.o
|
||||||
|
<libnetwork_dns>dns_inet.o
|
||||||
|
<libnetwork_dns>dns_irs.o
|
||||||
|
<libnetwork_dns>dns_isc.o
|
||||||
|
<libnetwork_dns>dns_nameser.o
|
||||||
|
<libnetwork_dns>dns_resolv.o
|
||||||
|
<libnetwork_dns>dns_private.o
|
||||||
|
|
||||||
|
be
|
||||||
|
;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src kits network dns ;
|
||||||
Reference in New Issue
Block a user