* 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:
Axel Dörfler
2006-08-08 13:07:07 +00:00
parent 5adca30a18
commit c22d69bf1f
58 changed files with 13309 additions and 1 deletions
+1 -1
View File
@@ -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
+129
View File
@@ -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
+111
View File
@@ -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
+38
View File
@@ -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
+71
View File
@@ -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 */
+35
View File
@@ -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
+77
View File
@@ -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
+131
View File
@@ -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
+60
View File
@@ -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
+64
View File
@@ -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
+17
View File
@@ -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
+81
View File
@@ -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
+95
View File
@@ -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
+151
View File
@@ -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 */
+66
View File
@@ -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, &ethernet_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(&params, 0, sizeof(ether_init_params));
if (ioctl(device->fd, ETHER_INIT, &params, 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
+35
View File
@@ -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
+442
View File
@@ -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,
};
+19
View File
@@ -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 = &empty;
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
+147
View File
@@ -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_ */
+467
View File
@@ -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;
}
+48
View File
@@ -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
+939
View File
@@ -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
+20
View File
@@ -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 ;