Work in progress commit by Atis Elsts (I'm posting his ChangeLog comments
directly here), I made only a few style changes: * introduced 'has_broadcast_address' field in struct net_address_module_info - REVIEW: the name, and the status of this field for UNIX and L2CAP families * ipv6 address family support * ipv6 address printing * ipv6 protocol support * ipv6 multicast support - TODO: add and remove multicast routes in a more proper way - TODO: support MLD * ipv6 datalink protocol support * icmpv6 protocol support (EchoRequest and EchoResponse messages) * ipv6 neigbor discovery protocol support (Advertisement and Solicitation messages) - TODO: only the very basic support is present, the protocol state machine is by no means completed - TODO: replying to Solicitation does not work too good ATM (visible, when pinging Haiku from outside) * added Jenkin's hash algorith * minor changes in existing IPv4 code - cleanup function ipv4_get_loopback_address(), written by myself * add tests: raw, udp, tcp/udp, mullicast sender * add 'hoplimit' field in struct net_buffer - TODO: this is just a hack, more generic approach would be better. * add 'receive_data' function pointer in struct net_datalink_protocol_module_info - TODO: this is also more like a hack, to support information passing from ICMPv6 to IPv6_datagram level. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37604 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -36,6 +36,7 @@ typedef struct net_buffer {
|
||||
uint32 flags;
|
||||
uint32 size;
|
||||
uint8 protocol;
|
||||
uint8 hoplimit;
|
||||
} net_buffer;
|
||||
|
||||
struct ancillary_data_container;
|
||||
|
||||
@@ -100,6 +100,7 @@ struct net_datalink_module_info {
|
||||
|
||||
struct net_address_module_info {
|
||||
module_info info;
|
||||
bool has_broadcast_address;
|
||||
|
||||
status_t (*copy_address)(const sockaddr *from, sockaddr **to,
|
||||
bool replaceWithZeros, const sockaddr *mask);
|
||||
|
||||
@@ -24,6 +24,7 @@ struct net_datalink_protocol_module_info {
|
||||
|
||||
status_t (*send_data)(net_datalink_protocol *self,
|
||||
net_buffer *buffer);
|
||||
status_t (*receive_data)(net_buffer *buffer);
|
||||
|
||||
status_t (*interface_up)(net_datalink_protocol *self);
|
||||
void (*interface_down)(net_datalink_protocol *self);
|
||||
|
||||
@@ -3,4 +3,5 @@ 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 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols ipv4_datagram ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols ipv6_datagram ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols loopback_frame ;
|
||||
|
||||
@@ -1095,6 +1095,7 @@ static net_datalink_protocol_module_info sARPModule = {
|
||||
arp_init_protocol,
|
||||
arp_uninit_protocol,
|
||||
arp_send_data,
|
||||
NULL, // receive_data
|
||||
arp_up,
|
||||
arp_down,
|
||||
arp_control,
|
||||
|
||||
@@ -210,6 +210,7 @@ static net_datalink_protocol_module_info sEthernetFrameModule = {
|
||||
ethernet_frame_init,
|
||||
ethernet_frame_uninit,
|
||||
ethernet_frame_send_data,
|
||||
NULL, // receive_data
|
||||
ethernet_frame_up,
|
||||
ethernet_frame_down,
|
||||
ethernet_frame_control,
|
||||
|
||||
@@ -136,6 +136,7 @@ net_datalink_protocol_module_info gIPv4DataLinkModule = {
|
||||
ipv4_datalink_init,
|
||||
ipv4_datalink_uninit,
|
||||
ipv4_datalink_send_data,
|
||||
NULL, // receive_data
|
||||
ipv4_datalink_up,
|
||||
ipv4_datalink_down,
|
||||
ipv4_datalink_control,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel network datalink_protocols ipv6_datagram ;
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
UsePrivateHeaders kernel net ;
|
||||
|
||||
KernelAddon ipv6_datagram :
|
||||
ipv6_datagram.cpp
|
||||
;
|
||||
|
||||
# Installation
|
||||
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/datalink_protocols
|
||||
: ipv6_datagram ;
|
||||
|
||||
Package haiku-networkingkit-cvs :
|
||||
haiku :
|
||||
boot home config add-ons kernel haiku_network datalink_protocols ;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -171,6 +171,7 @@ static net_datalink_protocol_module_info sLoopbackFrameModule = {
|
||||
loopback_frame_init,
|
||||
loopback_frame_uninit,
|
||||
loopback_frame_send_data,
|
||||
NULL, // receive_data
|
||||
loopback_frame_up,
|
||||
loopback_frame_down,
|
||||
loopback_frame_control,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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 icmp6 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network protocols ipv4 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network protocols ipv6 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network protocols l2cap ;
|
||||
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 icmp6 ;
|
||||
|
||||
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 kernel net ;
|
||||
|
||||
KernelAddon icmp6 :
|
||||
icmp6.cpp
|
||||
;
|
||||
|
||||
# Installation
|
||||
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||
: icmp6 ;
|
||||
|
||||
Package haiku-networkingkit-cvs :
|
||||
haiku :
|
||||
boot home config add-ons kernel haiku_network protocols ;
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <net_datalink.h>
|
||||
#include <net_protocol.h>
|
||||
#include <net_stack.h>
|
||||
#include <net_datalink_protocol.h>
|
||||
#include <NetBufferUtilities.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../ipv6/ipv6_utils.h" // ipv6_checksum()
|
||||
|
||||
|
||||
#define TRACE_ICMP6
|
||||
#ifdef TRACE_ICMP6
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
typedef NetBufferField<uint16, offsetof(icmp6_hdr, icmp6_cksum)> ICMP6ChecksumField;
|
||||
|
||||
|
||||
net_buffer_module_info *gBufferModule;
|
||||
static net_stack_module_info *sStackModule;
|
||||
static net_datalink_protocol_module_info *sIPv6DatalinkModule;
|
||||
|
||||
|
||||
net_protocol *
|
||||
icmp6_init_protocol(net_socket *socket)
|
||||
{
|
||||
net_protocol *protocol = new (std::nothrow) net_protocol;
|
||||
if (protocol == NULL)
|
||||
return NULL;
|
||||
|
||||
return protocol;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_uninit_protocol(net_protocol *protocol)
|
||||
{
|
||||
delete protocol;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_open(net_protocol *protocol)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_close(net_protocol *protocol)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_free(net_protocol *protocol)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_connect(net_protocol *protocol, const struct sockaddr *address)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_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
|
||||
icmp6_getsockopt(net_protocol *protocol, int level, int option,
|
||||
void *value, int *length)
|
||||
{
|
||||
return protocol->next->module->getsockopt(protocol->next, level, option,
|
||||
value, length);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_setsockopt(net_protocol *protocol, int level, int option,
|
||||
const void *value, int length)
|
||||
{
|
||||
return protocol->next->module->setsockopt(protocol->next, level, option,
|
||||
value, length);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_bind(net_protocol *protocol, const struct sockaddr *address)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_unbind(net_protocol *protocol, struct sockaddr *address)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_listen(net_protocol *protocol, int count)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_shutdown(net_protocol *protocol, int direction)
|
||||
{
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_send_data(net_protocol *protocol, net_buffer *buffer)
|
||||
{
|
||||
return protocol->next->module->send_data(protocol->next, buffer);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_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
|
||||
icmp6_send_avail(net_protocol *protocol)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
|
||||
net_buffer **_buffer)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
icmp6_read_avail(net_protocol *protocol)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
struct net_domain *
|
||||
icmp6_get_domain(net_protocol *protocol)
|
||||
{
|
||||
return protocol->next->module->get_domain(protocol->next);
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
icmp6_get_mtu(net_protocol *protocol, const struct sockaddr *address)
|
||||
{
|
||||
return protocol->next->module->get_mtu(protocol->next, address);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_receive_data(net_buffer *buffer)
|
||||
{
|
||||
TRACE(("ICMPv6 received some data, buffer length %lu\n", buffer->size));
|
||||
|
||||
NetBufferHeaderReader<icmp6_hdr> bufferHeader(buffer);
|
||||
if (bufferHeader.Status() < B_OK)
|
||||
return bufferHeader.Status();
|
||||
|
||||
icmp6_hdr &header = bufferHeader.Data();
|
||||
|
||||
TRACE((" got type %u, code %u, checksum 0x%x\n", header.icmp6_type,
|
||||
header.icmp6_code, header.icmp6_cksum));
|
||||
|
||||
// compute and check the checksum
|
||||
uint16 checksum;
|
||||
checksum = gBufferModule->checksum(buffer, 0, buffer->size, false);
|
||||
checksum = ipv6_checksum(&((sockaddr_in6*)buffer->source)->sin6_addr,
|
||||
&((sockaddr_in6*)buffer->destination)->sin6_addr,
|
||||
buffer->size, IPPROTO_ICMPV6, checksum);
|
||||
|
||||
TRACE((" computed checksum: %ld\n", checksum));
|
||||
|
||||
if (checksum != 0)
|
||||
return B_BAD_DATA;
|
||||
|
||||
switch (header.icmp6_type) {
|
||||
case ICMP6_ECHO_REPLY:
|
||||
break;
|
||||
|
||||
case ICMP6_ECHO_REQUEST:
|
||||
{
|
||||
net_domain *domain;
|
||||
if (buffer->interface != NULL) {
|
||||
domain = buffer->interface->domain;
|
||||
|
||||
// We only reply to echo requests of our local interface; we
|
||||
// don't reply to broadcast requests
|
||||
if (!domain->address_module->equal_addresses(
|
||||
buffer->interface->address, buffer->destination))
|
||||
break;
|
||||
} else
|
||||
domain = sStackModule->get_domain(buffer->source->sa_family);
|
||||
|
||||
if (domain == NULL || domain->module == NULL)
|
||||
break;
|
||||
|
||||
net_buffer *reply = gBufferModule->duplicate(buffer);
|
||||
if (reply == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
gBufferModule->swap_addresses(reply);
|
||||
|
||||
// There already is an ICMP header, and we'll reuse it
|
||||
NetBufferHeaderReader<icmp6_hdr> header(reply);
|
||||
|
||||
header->icmp6_type = ICMP6_ECHO_REPLY;
|
||||
header->icmp6_code = 0;
|
||||
header->icmp6_cksum = 0;
|
||||
|
||||
header.Sync();
|
||||
|
||||
checksum = gBufferModule->checksum(buffer, 0, buffer->size, false);
|
||||
*ICMP6ChecksumField(reply) =
|
||||
ipv6_checksum(&((sockaddr_in6*)buffer->source)->sin6_addr,
|
||||
&((sockaddr_in6*)buffer->destination)->sin6_addr,
|
||||
buffer->size, IPPROTO_ICMPV6, checksum);
|
||||
|
||||
status_t status = domain->module->send_data(NULL, reply);
|
||||
if (status < B_OK) {
|
||||
gBufferModule->free(reply);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// forward unrecognized messages to datalink layer
|
||||
return sIPv6DatalinkModule->receive_data(buffer);
|
||||
}
|
||||
|
||||
gBufferModule->free(buffer);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_deliver_data(net_protocol *protocol, net_buffer *buffer)
|
||||
{
|
||||
// TODO: does this look OK?
|
||||
return icmp6_receive_data(buffer);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_error(uint32 code, net_buffer *data)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
icmp6_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
|
||||
void *errorData)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
icmp6_init()
|
||||
{
|
||||
sStackModule->register_domain_protocols(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6,
|
||||
"network/protocols/icmp6/v1",
|
||||
"network/protocols/ipv6/v1",
|
||||
NULL);
|
||||
|
||||
sStackModule->register_domain_receiving_protocol(AF_INET6, IPPROTO_ICMPV6,
|
||||
"network/protocols/icmp6/v1");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
icmp6_std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
return icmp6_init();
|
||||
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
net_protocol_module_info sICMP6Module = {
|
||||
{
|
||||
"network/protocols/icmp6/v1",
|
||||
0,
|
||||
icmp6_std_ops
|
||||
},
|
||||
NET_PROTOCOL_ATOMIC_MESSAGES,
|
||||
|
||||
icmp6_init_protocol,
|
||||
icmp6_uninit_protocol,
|
||||
icmp6_open,
|
||||
icmp6_close,
|
||||
icmp6_free,
|
||||
icmp6_connect,
|
||||
icmp6_accept,
|
||||
icmp6_control,
|
||||
icmp6_getsockopt,
|
||||
icmp6_setsockopt,
|
||||
icmp6_bind,
|
||||
icmp6_unbind,
|
||||
icmp6_listen,
|
||||
icmp6_shutdown,
|
||||
icmp6_send_data,
|
||||
icmp6_send_routed_data,
|
||||
icmp6_send_avail,
|
||||
icmp6_read_data,
|
||||
icmp6_read_avail,
|
||||
icmp6_get_domain,
|
||||
icmp6_get_mtu,
|
||||
icmp6_receive_data,
|
||||
icmp6_deliver_data,
|
||||
icmp6_error,
|
||||
icmp6_error_reply,
|
||||
NULL, // add_ancillary_data()
|
||||
NULL, // process_ancillary_data()
|
||||
NULL, // process_ancillary_data_no_container()
|
||||
NULL, // send_data_no_buffer()
|
||||
NULL // read_data_no_buffer()
|
||||
};
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{NET_STACK_MODULE_NAME, (module_info **)&sStackModule},
|
||||
{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
|
||||
{"network/datalink_protocols/ipv6_datagram/v1",
|
||||
(module_info **)&sIPv6DatalinkModule},
|
||||
{}
|
||||
};
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sICMP6Module,
|
||||
NULL
|
||||
};
|
||||
@@ -1595,6 +1595,7 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
memcpy(buffer->destination, &destination, sizeof(sockaddr_in));
|
||||
|
||||
uint8 protocol = buffer->protocol = header.protocol;
|
||||
buffer->hoplimit = header.time_to_live;
|
||||
|
||||
// remove any trailing/padding data
|
||||
status_t status = gBufferModule->trim(buffer, packetLength);
|
||||
|
||||
@@ -489,14 +489,15 @@ ipv4_checksum_address(struct Checksum *checksum, const sockaddr *address)
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ipv4_get_loopback_address(sockaddr *result)
|
||||
ipv4_get_loopback_address(sockaddr *_address)
|
||||
{
|
||||
sockaddr_in *resultIn = (sockaddr_in *)result;
|
||||
memset(resultIn, 0, sizeof(resultIn));
|
||||
resultIn->sin_len = sizeof(sockaddr_in);
|
||||
resultIn->sin_family = AF_INET;
|
||||
resultIn->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
sockaddr_in *address = (sockaddr_in *)_address;
|
||||
memset(address, 0, sizeof(sockaddr_in));
|
||||
address->sin_len = sizeof(sockaddr_in);
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
}
|
||||
|
||||
|
||||
@@ -506,6 +507,7 @@ net_address_module_info gIPv4AddressModule = {
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
true, // has_broadcast_address
|
||||
ipv4_copy_address,
|
||||
ipv4_mask_address,
|
||||
ipv4_equal_addresses,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel network protocols ipv6 ;
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
UsePrivateHeaders net ;
|
||||
|
||||
KernelAddon ipv6 :
|
||||
ipv6.cpp
|
||||
ipv6_address.cpp
|
||||
ipv6_utils.cpp
|
||||
multicast.cpp
|
||||
;
|
||||
|
||||
# Installation
|
||||
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||
: ipv6 ;
|
||||
|
||||
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,569 @@
|
||||
/*
|
||||
* Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, axeld@pinc-software.de
|
||||
* Oliver Tappe, zooey@hirschkaefer.de
|
||||
* Atis Elsts, the.kfx@gmail.com
|
||||
*/
|
||||
|
||||
|
||||
#include <net_datalink.h>
|
||||
|
||||
#include <NetUtilities.h>
|
||||
|
||||
#include <memory.h>
|
||||
#include <netinet6/in6.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ipv6_address.h"
|
||||
#include "ipv6_utils.h"
|
||||
#include "jenkins.h"
|
||||
|
||||
|
||||
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
||||
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
|
||||
|
||||
|
||||
static void
|
||||
ipv6_mask_adress_inplace(sockaddr *address, const sockaddr *mask)
|
||||
{
|
||||
in6_addr &i6addr = ((sockaddr_in6 *)address)->sin6_addr;
|
||||
const in6_addr &i6mask = ((const sockaddr_in6 *)mask)->sin6_addr;
|
||||
|
||||
for (uint32 i = 0; i < sizeof(in6_addr); i++)
|
||||
i6addr.s6_addr[i] &= i6mask.s6_addr[i];
|
||||
}
|
||||
|
||||
|
||||
/*! 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_BAD_VALUE if any of \a from or \a mask refers to an uninitialized
|
||||
address
|
||||
\return B_MISMATCHED_VALUES if \a address does not match family AF_INET
|
||||
*/
|
||||
static status_t
|
||||
ipv6_copy_address(const sockaddr *from, sockaddr **to,
|
||||
bool replaceWithZeros = false, const sockaddr *mask = NULL)
|
||||
{
|
||||
if (replaceWithZeros) {
|
||||
*to = (sockaddr *)malloc(sizeof(sockaddr_in6));
|
||||
if (*to == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(*to, 0, sizeof(sockaddr_in6));
|
||||
(*to)->sa_family = AF_INET6;
|
||||
(*to)->sa_len = sizeof(sockaddr_in6);
|
||||
} else {
|
||||
if (from == NULL)
|
||||
return B_OK;
|
||||
if (from->sa_len == 0 || (mask != NULL && mask->sa_len == 0))
|
||||
return B_BAD_VALUE;
|
||||
if (from->sa_family != AF_INET6)
|
||||
return B_MISMATCHED_VALUES;
|
||||
|
||||
*to = (sockaddr *)malloc(sizeof(sockaddr_in6));
|
||||
if (*to == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memcpy(*to, from, sizeof(sockaddr_in6));
|
||||
|
||||
if (mask != NULL)
|
||||
ipv6_mask_adress_inplace(*to, mask);
|
||||
}
|
||||
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 is NULL or if any of \a address or \a mask
|
||||
refers to an uninitialized address
|
||||
*/
|
||||
static status_t
|
||||
ipv6_mask_address(const sockaddr *address, const sockaddr *mask,
|
||||
sockaddr *result)
|
||||
{
|
||||
if (address == NULL || address->sa_len == 0 || result == NULL
|
||||
|| (mask != NULL && mask->sa_len == 0))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
memcpy(result, address, sizeof(sockaddr_in6));
|
||||
if (mask != NULL)
|
||||
ipv6_mask_adress_inplace(result, mask);
|
||||
|
||||
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
|
||||
ipv6_is_empty_address(const sockaddr *_address, bool checkPort)
|
||||
{
|
||||
if (_address == NULL || _address->sa_len == 0)
|
||||
return true;
|
||||
|
||||
const sockaddr_in6 *address = (const sockaddr_in6 *)_address;
|
||||
if (checkPort && address->sin6_port != 0) return false;
|
||||
return IN6_IS_ADDR_UNSPECIFIED(&address->sin6_addr);
|
||||
}
|
||||
|
||||
|
||||
/*! Checks if the given \a address is an Ipv6 address.
|
||||
\return false if \a address is NULL, or with family different from AF_INET
|
||||
true if it has AF_INET address family
|
||||
*/
|
||||
static bool
|
||||
ipv6_is_same_family(const sockaddr *address)
|
||||
{
|
||||
if (address == NULL)
|
||||
return false;
|
||||
|
||||
return address->sa_family == AF_INET6;
|
||||
}
|
||||
|
||||
|
||||
/*! 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
|
||||
ipv6_equal_addresses(const sockaddr *a, const sockaddr *b)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
return true;
|
||||
if (a != NULL && b == NULL)
|
||||
return ipv6_is_empty_address(a, false);
|
||||
if (a == NULL && b != NULL)
|
||||
return ipv6_is_empty_address(b, false);
|
||||
|
||||
const sockaddr_in6 *i6a = (const sockaddr_in6 *)a;
|
||||
const sockaddr_in6 *i6b = (const sockaddr_in6 *)b;
|
||||
return !memcmp(&i6a->sin6_addr, &i6b->sin6_addr, sizeof(in6_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
|
||||
ipv6_equal_ports(const sockaddr *a, const sockaddr *b)
|
||||
{
|
||||
uint16 portA = a ? ((sockaddr_in6 *)a)->sin6_port : 0;
|
||||
uint16 portB = b ? ((sockaddr_in6 *)b)->sin6_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
|
||||
ipv6_equal_addresses_and_ports(const sockaddr *a, const sockaddr *b)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
return true;
|
||||
if (a != NULL && b == NULL)
|
||||
return ipv6_is_empty_address(a, true);
|
||||
if (a == NULL && b != NULL)
|
||||
return ipv6_is_empty_address(b, true);
|
||||
|
||||
const sockaddr_in6 *i6a = (const sockaddr_in6 *)a;
|
||||
const sockaddr_in6 *i6b = (const sockaddr_in6 *)b;
|
||||
return i6a->sin6_port == i6b->sin6_port
|
||||
&& !memcmp(&i6a->sin6_addr, &i6b->sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
|
||||
|
||||
/*! 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
|
||||
ipv6_equal_masked_addresses(const sockaddr *a, const sockaddr *b,
|
||||
const sockaddr *mask)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
return true;
|
||||
|
||||
const in6_addr *i6a;
|
||||
if (a == NULL)
|
||||
i6a = &in6addr_any;
|
||||
else
|
||||
i6a = &((const sockaddr_in6*)a)->sin6_addr;
|
||||
|
||||
const in6_addr *i6b;
|
||||
if (b == NULL)
|
||||
i6b = &in6addr_any;
|
||||
else
|
||||
i6b = &((const sockaddr_in6*)b)->sin6_addr;
|
||||
|
||||
if (!mask)
|
||||
return !memcmp(i6a, i6b, sizeof(in6_addr));
|
||||
|
||||
const uint8 *pmask = ((const sockaddr_in6 *)mask)->sin6_addr.s6_addr;
|
||||
for (uint8 i = 0; i < sizeof(in6_addr); ++i) {
|
||||
if (pmask[i] != 0xff) {
|
||||
return (i6a->s6_addr[i] & pmask[i])
|
||||
== (i6b->s6_addr[i] & pmask[i]);
|
||||
}
|
||||
|
||||
if (i6a->s6_addr[i] != i6b->s6_addr[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*! 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
|
||||
ipv6_first_mask_bit(const sockaddr *_mask)
|
||||
{
|
||||
if (_mask == NULL)
|
||||
return 0;
|
||||
|
||||
const uint8 *pmask = ((const sockaddr_in6 *)_mask)->sin6_addr.s6_addr;
|
||||
for (uint8 i = 0; i < sizeof(in6_addr); ++i) {
|
||||
if (pmask[i] == 0xff)
|
||||
continue;
|
||||
|
||||
for (uint8 bit = 0; bit < 8; bit++) {
|
||||
if (pmask[i] & (1 << bit))
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
return 128;
|
||||
}
|
||||
|
||||
|
||||
/*! 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
|
||||
ipv6_check_mask(const sockaddr *_mask)
|
||||
{
|
||||
if (_mask == NULL)
|
||||
return true;
|
||||
|
||||
bool zero = false;
|
||||
const uint8 *pmask = ((const sockaddr_in6 *)_mask)->sin6_addr.s6_addr;
|
||||
for (uint8 i = 0; i < sizeof(in6_addr); ++i) {
|
||||
if (pmask[i] == 0xff) {
|
||||
if (zero)
|
||||
return false;
|
||||
} else if (pmask[i] == 0) {
|
||||
zero = true;
|
||||
} else {
|
||||
for (int8 bit = 7; bit > 0; bit--) {
|
||||
if (pmask[i] & (1 << bit)) {
|
||||
if (zero)
|
||||
return false;
|
||||
} else {
|
||||
zero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*! Creates a buffer for the given \a address and prints the address into
|
||||
it (hexadecimal representation in network 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,
|
||||
or does not have enogh space
|
||||
*/
|
||||
static status_t
|
||||
ipv6_print_address_buffer(const sockaddr *_address, char *buffer,
|
||||
size_t bufferSize, bool printPort)
|
||||
{
|
||||
const sockaddr_in6 *address = (const sockaddr_in6 *)_address;
|
||||
|
||||
if (buffer == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (address == NULL) {
|
||||
if (bufferSize < sizeof("<none>"))
|
||||
return B_NO_MEMORY;
|
||||
strcpy(buffer, "<none>");
|
||||
} else {
|
||||
if (printPort && bufferSize > 0) {
|
||||
*buffer = '[';
|
||||
buffer++;
|
||||
bufferSize--;
|
||||
}
|
||||
|
||||
if (!ip6_sprintf(&address->sin6_addr, buffer, bufferSize))
|
||||
return B_NO_MEMORY;
|
||||
|
||||
if (printPort) {
|
||||
char port[7];
|
||||
sprintf(port, "]:%d", ntohs(address->sin6_port));
|
||||
if (bufferSize - strlen(buffer) < strlen(port) + 1)
|
||||
return B_NO_MEMORY;
|
||||
strcat(buffer, port);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ipv6_print_address(const sockaddr *_address, char **_buffer, bool printPort)
|
||||
{
|
||||
if (_buffer == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
char tmp[64];
|
||||
ipv6_print_address_buffer(_address, tmp, sizeof(tmp), printPort);
|
||||
|
||||
*_buffer = strdup(tmp);
|
||||
if (*_buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Determines the port of the given \a address.
|
||||
\return uint16 representing the port-nr
|
||||
*/
|
||||
static uint16
|
||||
ipv6_get_port(const sockaddr *address)
|
||||
{
|
||||
if (address == NULL || address->sa_len == 0)
|
||||
return 0;
|
||||
|
||||
return ((sockaddr_in6 *)address)->sin6_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 or has not been initialized
|
||||
*/
|
||||
static status_t
|
||||
ipv6_set_port(sockaddr *address, uint16 port)
|
||||
{
|
||||
if (address == NULL || address->sa_len == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
((sockaddr_in6 *)address)->sin6_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 or if the
|
||||
address given in from has not been initialized
|
||||
\return B_MISMATCHED_VALUES if from is not of family AF_INET6
|
||||
*/
|
||||
static status_t
|
||||
ipv6_set_to(sockaddr *address, const sockaddr *from)
|
||||
{
|
||||
if (address == NULL || from == NULL || from->sa_len == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (from->sa_family != AF_INET6)
|
||||
return B_MISMATCHED_VALUES;
|
||||
|
||||
memcpy(address, from, sizeof(sockaddr_in6));
|
||||
address->sa_len = sizeof(sockaddr_in6);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Updates missing parts in \a address with the values in \a from.
|
||||
\return B_OK if \a address has been updated from \a from
|
||||
\return B_BAD_VALUE if either \a address or \a from is NULL or if the
|
||||
address given in from has not been initialized
|
||||
\return B_MISMATCHED_VALUES if from is not of family AF_INET6
|
||||
*/
|
||||
static status_t
|
||||
ipv6_update_to(sockaddr *_address, const sockaddr *_from)
|
||||
{
|
||||
sockaddr_in6 *address = (sockaddr_in6 *)_address;
|
||||
const sockaddr_in6 *from = (const sockaddr_in6 *)_from;
|
||||
|
||||
if (address == NULL || from == NULL || from->sin6_len == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (from->sin6_family != AF_INET6)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
address->sin6_family = AF_INET6;
|
||||
address->sin6_len = sizeof(sockaddr_in6);
|
||||
|
||||
if (address->sin6_port == 0)
|
||||
address->sin6_port = from->sin6_port;
|
||||
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&address->sin6_addr)) {
|
||||
memcpy(address->sin6_addr.s6_addr, from->sin6_addr.s6_addr,
|
||||
sizeof(in6_addr));
|
||||
}
|
||||
|
||||
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
|
||||
ipv6_set_to_empty_address(sockaddr *address)
|
||||
{
|
||||
if (address == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
memset(address, 0, sizeof(sockaddr_in6));
|
||||
address->sa_len = sizeof(sockaddr_in6);
|
||||
address->sa_family = AF_INET6;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ipv6_set_to_defaults(sockaddr *_defaultMask, sockaddr *_defaultBroadcast,
|
||||
sockaddr *_address, sockaddr *_mask)
|
||||
{
|
||||
sockaddr_in6 *defaultMask = (sockaddr_in6 *)_defaultMask;
|
||||
sockaddr_in6 *address = (sockaddr_in6 *)_address;
|
||||
sockaddr_in6 *mask = (sockaddr_in6 *)_mask;
|
||||
|
||||
if (address == NULL || defaultMask == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
defaultMask->sin6_len = sizeof(sockaddr_in);
|
||||
defaultMask->sin6_family = AF_INET6;
|
||||
defaultMask->sin6_port = 0;
|
||||
if (mask != NULL) {
|
||||
memcpy(defaultMask->sin6_addr.s6_addr,
|
||||
mask->sin6_addr.s6_addr, sizeof(in6_addr));
|
||||
} else {
|
||||
// use /128 as the default mask
|
||||
memset(defaultMask->sin6_addr.s6_addr, 0xff, sizeof(in6_addr));
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Computes a hash-value of the given addresses \a ourAddress
|
||||
and \a peerAddress.
|
||||
\return uint32 representing the hash-value
|
||||
*/
|
||||
static uint32
|
||||
ipv6_hash_address_pair(const sockaddr *ourAddress, const sockaddr *peerAddress)
|
||||
{
|
||||
uint32 result = 0;
|
||||
if (ourAddress) {
|
||||
const sockaddr_in6 *our = (const sockaddr_in6 *)ourAddress;
|
||||
uint32 port = our->sin6_port;
|
||||
|
||||
result = jenkins_hashword((const uint32*)&our->sin6_addr,
|
||||
sizeof(in6_addr) / sizeof(uint32), result);
|
||||
result = jenkins_hashword(&port, 1, result);
|
||||
}
|
||||
if (peerAddress) {
|
||||
const sockaddr_in6 *peer = (const sockaddr_in6 *)peerAddress;
|
||||
uint32 port = peer->sin6_port;
|
||||
|
||||
result = jenkins_hashword((const uint32*)&peer->sin6_addr,
|
||||
sizeof(in6_addr) / sizeof(uint32), result);
|
||||
result = jenkins_hashword(&port, 1, result);
|
||||
}
|
||||
|
||||
// TODO: also use sin6_flowinfo and sin6_scope_id?
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*! 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 or if
|
||||
the given address is not initialized
|
||||
*/
|
||||
static status_t
|
||||
ipv6_checksum_address(struct Checksum *checksum, const sockaddr *address)
|
||||
{
|
||||
if (checksum == NULL || address == NULL || address->sa_len == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
in6_addr &a = ((sockaddr_in6 *)address)->sin6_addr;
|
||||
for (uint32 i = 0; i < sizeof(in6_addr); i++)
|
||||
(*checksum) << a.s6_addr[i];
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ipv6_get_loopback_address(sockaddr *_address)
|
||||
{
|
||||
sockaddr_in6 *address = (sockaddr_in6 *)_address;
|
||||
memset(address, 0, sizeof(sockaddr_in6));
|
||||
address->sin6_len = sizeof(sockaddr_in6);
|
||||
address->sin6_family = AF_INET6;
|
||||
memcpy(&address->sin6_addr, &in6addr_loopback, sizeof(in6_addr));
|
||||
}
|
||||
|
||||
|
||||
net_address_module_info gIPv6AddressModule = {
|
||||
{
|
||||
NULL,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
false, // has_broadcast_address
|
||||
ipv6_copy_address,
|
||||
ipv6_mask_address,
|
||||
ipv6_equal_addresses,
|
||||
ipv6_equal_ports,
|
||||
ipv6_equal_addresses_and_ports,
|
||||
ipv6_equal_masked_addresses,
|
||||
ipv6_is_empty_address,
|
||||
ipv6_is_same_family,
|
||||
ipv6_first_mask_bit,
|
||||
ipv6_check_mask,
|
||||
ipv6_print_address,
|
||||
ipv6_print_address_buffer,
|
||||
ipv6_get_port,
|
||||
ipv6_set_port,
|
||||
ipv6_set_to,
|
||||
ipv6_set_to_empty_address,
|
||||
ipv6_set_to_defaults,
|
||||
ipv6_update_to,
|
||||
ipv6_hash_address_pair,
|
||||
ipv6_checksum_address,
|
||||
ipv6_get_loopback_address
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef IPV6_ADDRESS_H
|
||||
#define IPV6_ADDRESS_H
|
||||
|
||||
|
||||
#include <netinet6/in6.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
extern struct net_address_module_info gIPv6AddressModule;
|
||||
|
||||
|
||||
#define NET_IPV6_MODULE_NAME "network/protocols/ipv6/v1"
|
||||
|
||||
|
||||
static inline bool
|
||||
operator==(const in6_addr &a1, const in6_addr &a2)
|
||||
{
|
||||
// TODO: optimize
|
||||
return !memcmp(&a1, &a2, sizeof(in6_addr));
|
||||
}
|
||||
|
||||
|
||||
#endif // IPV6_ADDRESS_H
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Copyright (c) 1996-1999 by Internet Software Consortium.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <net_datalink.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <KernelExport.h>
|
||||
#include <NetUtilities.h>
|
||||
|
||||
#include <memory.h>
|
||||
#include <netinet6/in6.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ipv6_utils.h"
|
||||
|
||||
|
||||
#define NS_IN6ADDRSZ 16
|
||||
#define NS_INT16SZ 2
|
||||
|
||||
#define SPRINTF(x) ((size_t)sprintf x)
|
||||
|
||||
|
||||
/*! Convert IPv6 binary address into presentation (printable) format.
|
||||
Author: Paul Vixie, 1996.
|
||||
\return pointer to dst string if address as been printed
|
||||
\return NULL if the buffer is too short
|
||||
*/
|
||||
const char *
|
||||
ip6_sprintf(const in6_addr *srcaddr, char *dst, size_t size)
|
||||
{
|
||||
/*
|
||||
* Note that int32_t and int16_t need only be "at least" large enough
|
||||
* to contain a value of the specified size. On some systems, like
|
||||
* Crays, there is no such thing as an integer variable with 16 bits.
|
||||
* Keep this in mind if you think this function should have been coded
|
||||
* to use pointer overlays. All the world's not a VAX.
|
||||
*/
|
||||
char tmp[INET6_ADDRSTRLEN], *tp;
|
||||
struct { int base, len; } best, cur;
|
||||
uint16 words[NS_IN6ADDRSZ / NS_INT16SZ];
|
||||
int i;
|
||||
const uint8 *src = srcaddr->s6_addr;
|
||||
|
||||
/*
|
||||
* Preprocess:
|
||||
* Copy the input (bytewise) array into a wordwise array.
|
||||
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
||||
*/
|
||||
memset(words, '\0', sizeof words);
|
||||
for (i = 0; i < NS_IN6ADDRSZ; i++)
|
||||
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
|
||||
best.base = -1;
|
||||
best.len = 0;
|
||||
cur.base = -1;
|
||||
cur.len = 0;
|
||||
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
|
||||
if (words[i] == 0) {
|
||||
if (cur.base == -1)
|
||||
cur.base = i, cur.len = 1;
|
||||
else
|
||||
cur.len++;
|
||||
} else {
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
cur.base = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
}
|
||||
if (best.base != -1 && best.len < 2)
|
||||
best.base = -1;
|
||||
|
||||
/*
|
||||
* Format the result.
|
||||
*/
|
||||
tp = tmp;
|
||||
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
|
||||
/* Are we inside the best run of 0x00's? */
|
||||
if (best.base != -1 && i >= best.base &&
|
||||
i < (best.base + best.len)) {
|
||||
if (i == best.base)
|
||||
*tp++ = ':';
|
||||
continue;
|
||||
}
|
||||
/* Are we following an initial run of 0x00s or any real hex? */
|
||||
if (i != 0)
|
||||
*tp++ = ':';
|
||||
/* Is this address an encapsulated IPv4? */
|
||||
#if 0
|
||||
if (i == 6 && best.base == 0 && (best.len == 6 ||
|
||||
(best.len == 7 && words[7] != 0x0001) ||
|
||||
(best.len == 5 && words[5] == 0xffff))) {
|
||||
if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
|
||||
return (NULL);
|
||||
tp += strlen(tp);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
tp += SPRINTF((tp, "%x", words[i]));
|
||||
}
|
||||
/* Was it a trailing run of 0x00's? */
|
||||
if (best.base != -1 && (best.base + best.len) ==
|
||||
(NS_IN6ADDRSZ / NS_INT16SZ))
|
||||
*tp++ = ':';
|
||||
*tp++ = '\0';
|
||||
|
||||
/*
|
||||
* Check for overflow, copy, and we're done.
|
||||
*/
|
||||
if ((size_t)(tp - tmp) > size)
|
||||
return NULL;
|
||||
|
||||
strcpy(dst, tmp);
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Atis Elsts, the.kfx@gmail.com
|
||||
*/
|
||||
#ifndef IPV6_UTILS_H
|
||||
#define IPV6_UTILS_H
|
||||
|
||||
|
||||
#include <netinet6/in6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net_stack.h>
|
||||
|
||||
|
||||
const char *ip6_sprintf(const in6_addr *addr, char *dst,
|
||||
size_t size = INET6_ADDRSTRLEN);
|
||||
|
||||
|
||||
static inline uint16
|
||||
compute_checksum(uint8* _buffer, size_t length)
|
||||
{
|
||||
uint16* buffer = (uint16*)_buffer;
|
||||
uint32 sum = 0;
|
||||
|
||||
while (length >= 2) {
|
||||
sum += *buffer++;
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
static inline uint16
|
||||
ipv6_checksum(const struct in6_addr* source,
|
||||
const struct in6_addr* destination,
|
||||
uint16 length, uint16 protocol,
|
||||
uint16 checksum)
|
||||
{
|
||||
uint32 sum = checksum;
|
||||
|
||||
length = htons(length);
|
||||
protocol = htons(protocol);
|
||||
|
||||
sum += compute_checksum((uint8*)source, sizeof(in6_addr));
|
||||
sum += compute_checksum((uint8*)destination, sizeof(in6_addr));
|
||||
sum += compute_checksum((uint8*)&length, sizeof(uint16));
|
||||
sum += compute_checksum((uint8*)&protocol, sizeof(uint16));
|
||||
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return ~(uint16)sum;
|
||||
}
|
||||
|
||||
|
||||
#endif // IPV6_UTILS_H
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Taken from http://burtleburtle.net/bob/c/lookup3.c
|
||||
*/
|
||||
#ifndef LIBKERN_JENKINS_H
|
||||
#define LIBKERN_JENKINS_H
|
||||
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
|
||||
These are functions for producing 32-bit hashes for hash table lookup.
|
||||
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
are externally useful functions. Routines to test the hash are included
|
||||
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
the public domain. It has no warranty.
|
||||
|
||||
You probably want to use hashlittle(). hashlittle() and hashbig()
|
||||
hash byte arrays. hashlittle() is is faster than hashbig() on
|
||||
little-endian machines. Intel and AMD are little-endian machines.
|
||||
On second thought, you probably want hashlittle2(), which is identical to
|
||||
hashlittle() except it returns two 32-bit hashes for the price of one.
|
||||
You could implement hashbig2() if you wanted but I haven't bothered here.
|
||||
|
||||
If you want to find a hash of, say, exactly 7 integers, do
|
||||
a = i1; b = i2; c = i3;
|
||||
mix(a,b,c);
|
||||
a += i4; b += i5; c += i6;
|
||||
mix(a,b,c);
|
||||
a += i7;
|
||||
final(a,b,c);
|
||||
then use c as the hash value. If you have a variable length array of
|
||||
4-byte integers to hash, use hashword(). If you have a byte array (like
|
||||
a character string), use hashlittle(). If you have several byte arrays, or
|
||||
a mix of things, see the comments above hashlittle().
|
||||
|
||||
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||
then mix those integers. This is fast (you can do a lot more thorough
|
||||
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
|
||||
This is reversible, so any information in (a,b,c) before mix() is
|
||||
still in (a,b,c) after mix().
|
||||
|
||||
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||
mix() in reverse, there are at least 32 bits of the output that
|
||||
are sometimes the same for one pair and different for another pair.
|
||||
This was tested for:
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||
satisfy this are
|
||||
4 6 8 16 19 4
|
||||
9 15 3 18 27 15
|
||||
14 9 3 7 17 3
|
||||
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
the operations, constants, and arrangements of the variables.
|
||||
|
||||
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
avalanche in c.
|
||||
|
||||
This allows some parallelism. Read-after-writes are good at doubling
|
||||
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
direction as the goal of parallelism. I did what I could. Rotates
|
||||
seem to cost as much as shifts on every machine I could lay my hands
|
||||
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
rotates.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= rot(c, 4); c += b; \
|
||||
b -= a; b ^= rot(a, 6); a += c; \
|
||||
c -= b; c ^= rot(b, 8); b += a; \
|
||||
a -= c; a ^= rot(c,16); c += b; \
|
||||
b -= a; b ^= rot(a,19); a += c; \
|
||||
c -= b; c ^= rot(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
|
||||
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
produce values of c that look totally different. This was tested for
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
These constants passed:
|
||||
14 11 25 16 4 14 24
|
||||
12 14 25 16 4 14 24
|
||||
and these came close:
|
||||
4 8 15 26 3 22 24
|
||||
10 8 15 26 3 22 24
|
||||
11 8 15 26 3 22 24
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define final(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= rot(b,14); \
|
||||
a ^= c; a -= rot(c,11); \
|
||||
b ^= a; b -= rot(a,25); \
|
||||
c ^= b; c -= rot(b,16); \
|
||||
a ^= c; a -= rot(c,4); \
|
||||
b ^= a; b -= rot(a,14); \
|
||||
c ^= b; c -= rot(b,24); \
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
This works on all machines. To be useful, it requires
|
||||
-- that the key be an array of uint32's, and
|
||||
-- that the length be the number of uint32's in the key
|
||||
|
||||
The function hashword() is identical to hashlittle() on little-endian
|
||||
machines, and identical to hashbig() on big-endian machines,
|
||||
except that the length has to be measured in uint32s rather than in
|
||||
bytes. hashlittle() is more complicated than hashword() only because
|
||||
hashlittle() has to dance around fitting the key bytes into registers.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
static uint32
|
||||
jenkins_hashword(const uint32 *k, /* the key, an array of uint32 values */
|
||||
size_t length, /* the length of the key, in uint32s */
|
||||
uint32 initval) /* the previous hash, or an arbitrary value */
|
||||
{
|
||||
uint32 a,b,c;
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + (((uint32)length)<<2) + initval;
|
||||
|
||||
/*------------------------------------------------- handle most of the key */
|
||||
while (length > 3)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 3;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*------------------------------------------- handle the last 3 uint32's */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
case 3 : c+=k[2];
|
||||
case 2 : b+=k[1];
|
||||
case 1 : a+=k[0];
|
||||
final(a,b,c);
|
||||
case 0: /* case 0: nothing left to add */
|
||||
break;
|
||||
}
|
||||
/*------------------------------------------------------ report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Atis Elsts, the.kfx@gmail.com
|
||||
*/
|
||||
|
||||
|
||||
#include "ipv6_address.h"
|
||||
#include "multicast.h"
|
||||
|
||||
#include <net_buffer.h>
|
||||
|
||||
#include <netinet6/in6.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
|
||||
template<typename Addressing>
|
||||
MulticastGroupInterface<Addressing>::MulticastGroupInterface(Filter *parent,
|
||||
const AddressType &address, net_interface *interface)
|
||||
: fParent(parent), fMulticastAddress(address), fInterface(interface)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing>
|
||||
MulticastGroupInterface<Addressing>::~MulticastGroupInterface()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastGroupInterface<Addressing>::Add()
|
||||
{
|
||||
if (fFilterMode == kInclude && !fAddresses.IsEmpty())
|
||||
return EINVAL;
|
||||
|
||||
fFilterMode = kExclude;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastGroupInterface<Addressing>::Drop()
|
||||
{
|
||||
fAddresses.Clear();
|
||||
fFilterMode = kInclude;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastGroupInterface<Addressing>::BlockSource(
|
||||
const AddressType &sourceAddress)
|
||||
{
|
||||
if (fFilterMode != kExclude)
|
||||
return EINVAL;
|
||||
|
||||
fAddresses.Add(sourceAddress);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastGroupInterface<Addressing>::UnblockSource(
|
||||
const AddressType &sourceAddress)
|
||||
{
|
||||
if (fFilterMode != kExclude)
|
||||
return EINVAL;
|
||||
|
||||
if (!fAddresses.Has(sourceAddress))
|
||||
return EADDRNOTAVAIL;
|
||||
|
||||
fAddresses.Add(sourceAddress);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastGroupInterface<Addressing>::AddSSM(const AddressType &sourceAddress)
|
||||
{
|
||||
if (fFilterMode == kExclude)
|
||||
return EINVAL;
|
||||
|
||||
fAddresses.Add(sourceAddress);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastGroupInterface<Addressing>::DropSSM(const AddressType &sourceAddress)
|
||||
{
|
||||
if (fFilterMode == kExclude)
|
||||
return EINVAL;
|
||||
|
||||
if (!fAddresses.Has(sourceAddress))
|
||||
return EADDRNOTAVAIL;
|
||||
|
||||
fAddresses.Add(sourceAddress);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> bool
|
||||
MulticastGroupInterface<Addressing>::IsEmpty() const
|
||||
{
|
||||
return fFilterMode == kInclude && fAddresses.IsEmpty();
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> void
|
||||
MulticastGroupInterface<Addressing>::Clear()
|
||||
{
|
||||
if (IsEmpty())
|
||||
return;
|
||||
|
||||
fFilterMode = kInclude;
|
||||
fAddresses.Clear();
|
||||
Addressing::LeaveGroup(this);
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> bool
|
||||
MulticastGroupInterface<Addressing>::FilterAccepts(net_buffer *buffer) const
|
||||
{
|
||||
bool has = fAddresses.Has(Addressing::AddressFromSockAddr(
|
||||
buffer->source));
|
||||
|
||||
return (has && fFilterMode == kInclude)
|
||||
|| (!has && fFilterMode == kExclude);
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing>
|
||||
MulticastFilter<Addressing>::MulticastFilter(ProtocolType *socket)
|
||||
: fParent(socket), fStates()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing>
|
||||
MulticastFilter<Addressing>::~MulticastFilter()
|
||||
{
|
||||
while (true) {
|
||||
typename States::Iterator iterator = fStates.GetIterator();
|
||||
if (!iterator.HasNext())
|
||||
return;
|
||||
|
||||
GroupInterface *state = iterator.Next();
|
||||
state->Clear();
|
||||
_ReturnState(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> status_t
|
||||
MulticastFilter<Addressing>::GetState(const AddressType &groupAddress,
|
||||
net_interface *interface, GroupInterface* &state, bool create)
|
||||
{
|
||||
state = fStates.Lookup(std::make_pair(&groupAddress, interface->index));
|
||||
|
||||
if (state == NULL && create) {
|
||||
state = new (nothrow) GroupInterface(this, groupAddress, interface);
|
||||
if (state == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status = fStates.Insert(state);
|
||||
if (status < B_OK) {
|
||||
delete state;
|
||||
return status;
|
||||
}
|
||||
|
||||
status = Addressing::JoinGroup(state);
|
||||
if (status < B_OK) {
|
||||
fStates.Remove(state);
|
||||
delete state;
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> void
|
||||
MulticastFilter<Addressing>::ReturnState(GroupInterface *state)
|
||||
{
|
||||
if (state->IsEmpty())
|
||||
_ReturnState(state);
|
||||
}
|
||||
|
||||
|
||||
template<typename Addressing> void
|
||||
MulticastFilter<Addressing>::_ReturnState(GroupInterface *state)
|
||||
{
|
||||
fStates.Remove(state);
|
||||
delete state;
|
||||
}
|
||||
|
||||
// IPv6 explicit template instantiation
|
||||
template class MulticastFilter<IPv6Multicast>;
|
||||
template class MulticastGroupInterface<IPv6Multicast>;
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Atis Elsts, the.kfx@gmail.com
|
||||
*/
|
||||
#ifndef _IPV6_MULTICAST_H_
|
||||
#define _IPV6_MULTICAST_H_
|
||||
|
||||
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
#include <net_datalink.h>
|
||||
|
||||
#include <netinet6/in6.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "jenkins.h"
|
||||
|
||||
|
||||
struct net_buffer;
|
||||
struct net_protocol;
|
||||
|
||||
|
||||
template<typename Addressing> class MulticastFilter;
|
||||
template<typename Addressing> class MulticastGroupInterface;
|
||||
|
||||
|
||||
struct IPv6Multicast {
|
||||
typedef struct in6_addr AddressType;
|
||||
typedef struct ipv6_protocol ProtocolType;
|
||||
typedef MulticastGroupInterface<IPv6Multicast> GroupInterface;
|
||||
|
||||
static status_t JoinGroup(GroupInterface *);
|
||||
static status_t LeaveGroup(GroupInterface *);
|
||||
|
||||
static const in6_addr &AddressFromSockAddr(const sockaddr *sockaddr)
|
||||
{ return ((const sockaddr_in6 *)sockaddr)->sin6_addr; }
|
||||
static size_t HashAddress(const in6_addr &address)
|
||||
{ return jenkins_hashword((const uint32*)&address,
|
||||
sizeof(in6_addr) / sizeof(uint32), 0); }
|
||||
};
|
||||
|
||||
|
||||
template<typename AddressType>
|
||||
class AddressSet {
|
||||
struct ContainedAddress : DoublyLinkedListLinkImpl<ContainedAddress> {
|
||||
AddressType address;
|
||||
};
|
||||
|
||||
typedef DoublyLinkedList<ContainedAddress> AddressList;
|
||||
|
||||
public:
|
||||
AddressSet()
|
||||
: fCount(0) {}
|
||||
|
||||
~AddressSet() { Clear(); }
|
||||
|
||||
status_t Add(const AddressType &address)
|
||||
{
|
||||
if (Has(address))
|
||||
return B_OK;
|
||||
|
||||
ContainedAddress *container = new ContainedAddress();
|
||||
if (container == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
container->address = address;
|
||||
fAddresses.Add(container);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void Remove(const AddressType &address)
|
||||
{
|
||||
ContainedAddress *container = _Get(address);
|
||||
if (container == NULL)
|
||||
return;
|
||||
|
||||
fAddresses.Remove(container);
|
||||
delete container;
|
||||
}
|
||||
|
||||
bool Has(const AddressType &address) const
|
||||
{
|
||||
return _Get(address) != NULL;
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return fAddresses.IsEmpty(); }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
while (!fAddresses.IsEmpty())
|
||||
Remove(fAddresses.Head()->address);
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(const AddressList &addresses)
|
||||
: fBaseIterator(addresses.GetIterator()) {}
|
||||
|
||||
bool HasNext() const { return fBaseIterator.HasNext(); }
|
||||
AddressType &Next() { return fBaseIterator.Next()->address; }
|
||||
|
||||
private:
|
||||
typename AddressList::ConstIterator fBaseIterator;
|
||||
};
|
||||
|
||||
Iterator GetIterator() const { return Iterator(fAddresses); }
|
||||
|
||||
private:
|
||||
ContainedAddress *_Get(const AddressType &address) const
|
||||
{
|
||||
typename AddressList::ConstIterator it = fAddresses.GetIterator();
|
||||
while (it.HasNext()) {
|
||||
ContainedAddress *container = it.Next();
|
||||
if (container->address == address)
|
||||
return container;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AddressList fAddresses;
|
||||
int fCount;
|
||||
};
|
||||
|
||||
|
||||
template<typename Addressing>
|
||||
class MulticastGroupInterface {
|
||||
public:
|
||||
typedef MulticastGroupInterface<Addressing> ThisType;
|
||||
typedef typename Addressing::AddressType AddressType;
|
||||
typedef MulticastFilter<Addressing> Filter;
|
||||
typedef ::AddressSet<AddressType> AddressSet;
|
||||
|
||||
enum FilterMode {
|
||||
kInclude,
|
||||
kExclude
|
||||
};
|
||||
|
||||
MulticastGroupInterface(Filter *parent, const AddressType &address,
|
||||
net_interface *interface);
|
||||
~MulticastGroupInterface();
|
||||
|
||||
Filter *Parent() const { return fParent; }
|
||||
|
||||
const AddressType &Address() const { return fMulticastAddress; }
|
||||
net_interface *Interface() const { return fInterface; }
|
||||
|
||||
status_t Add();
|
||||
status_t Drop();
|
||||
status_t BlockSource(const AddressType &sourceAddress);
|
||||
status_t UnblockSource(const AddressType &sourceAddress);
|
||||
status_t AddSSM(const AddressType &sourceAddress);
|
||||
status_t DropSSM(const AddressType &sourceAddress);
|
||||
|
||||
bool IsEmpty() const;
|
||||
void Clear();
|
||||
|
||||
FilterMode Mode() const { return fFilterMode; }
|
||||
const AddressSet &Sources() const { return fAddresses; }
|
||||
|
||||
bool FilterAccepts(net_buffer *buffer) const;
|
||||
|
||||
struct HashDefinition {
|
||||
typedef std::pair<const AddressType *, uint32> KeyType;
|
||||
typedef ThisType ValueType;
|
||||
|
||||
size_t HashKey(const KeyType &key) const
|
||||
{
|
||||
size_t result = 0;
|
||||
result = jenkins_hashword((const uint32*)&key.first,
|
||||
sizeof(in6_addr) / sizeof(uint32), result);
|
||||
result = jenkins_hashword(&key.second, 1, result);
|
||||
return result;
|
||||
}
|
||||
size_t Hash(ValueType *value) const
|
||||
{ return HashKey(std::make_pair(&value->Address(),
|
||||
value->Interface()->index)); }
|
||||
bool Compare(const KeyType &key, ValueType *value) const
|
||||
{ return value->Interface()->index == key.second
|
||||
&& value->Address() == *key.first; }
|
||||
MulticastGroupInterface*& GetLink(ValueType *value) const
|
||||
{ return value->HashLink(); }
|
||||
};
|
||||
|
||||
MulticastGroupInterface*& HashLink() { return fLink; }
|
||||
|
||||
private:
|
||||
// for g++ 2.95
|
||||
friend class HashDefinition;
|
||||
|
||||
Filter *fParent;
|
||||
AddressType fMulticastAddress;
|
||||
net_interface *fInterface;
|
||||
FilterMode fFilterMode;
|
||||
AddressSet fAddresses;
|
||||
MulticastGroupInterface* fLink;
|
||||
};
|
||||
|
||||
|
||||
template<typename Addressing>
|
||||
class MulticastFilter {
|
||||
public:
|
||||
typedef typename Addressing::AddressType AddressType;
|
||||
typedef typename Addressing::ProtocolType ProtocolType;
|
||||
typedef MulticastGroupInterface<Addressing> GroupInterface;
|
||||
|
||||
MulticastFilter(ProtocolType *parent);
|
||||
~MulticastFilter();
|
||||
|
||||
ProtocolType *Socket() const { return fParent; }
|
||||
|
||||
status_t GetState(const AddressType &groupAddress,
|
||||
net_interface *interface, GroupInterface* &state, bool create);
|
||||
void ReturnState(GroupInterface *state);
|
||||
|
||||
private:
|
||||
typedef typename GroupInterface::HashDefinition HashDefinition;
|
||||
typedef BOpenHashTable<HashDefinition> States;
|
||||
|
||||
void _ReturnState(GroupInterface *state);
|
||||
|
||||
ProtocolType *fParent;
|
||||
States fStates;
|
||||
};
|
||||
|
||||
|
||||
#endif // _IPV6_MULTICAST_H_
|
||||
@@ -413,6 +413,7 @@ net_address_module_info gL2cap4AddressModule = {
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
true, // has_broadcast_address
|
||||
l2cap_copy_address,
|
||||
l2cap_mask_address,
|
||||
l2cap_equal_addresses,
|
||||
|
||||
@@ -1207,17 +1207,34 @@ init_udp()
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
status = gStackModule->register_domain_protocols(AF_INET6, SOCK_DGRAM, IPPROTO_IP,
|
||||
"network/protocols/udp/v1",
|
||||
"network/protocols/ipv6/v1",
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
|
||||
status = gStackModule->register_domain_protocols(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
"network/protocols/udp/v1",
|
||||
"network/protocols/ipv4/v1",
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
status = gStackModule->register_domain_protocols(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
|
||||
"network/protocols/udp/v1",
|
||||
"network/protocols/ipv6/v1",
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
|
||||
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_UDP,
|
||||
"network/protocols/udp/v1");
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
status = gStackModule->register_domain_receiving_protocol(AF_INET6, IPPROTO_UDP,
|
||||
"network/protocols/udp/v1");
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
|
||||
add_debugger_command("udp_endpoints", UdpEndpointManager::DumpEndpoints,
|
||||
"lists all open UDP endpoints");
|
||||
@@ -1225,6 +1242,7 @@ init_udp()
|
||||
return B_OK;
|
||||
|
||||
err1:
|
||||
// TODO: shouldn't unregister the protocols here?
|
||||
delete sUdpEndpointManager;
|
||||
|
||||
TRACE_EPM("init_udp() fails with %lx (%s)", status, strerror(status));
|
||||
|
||||
@@ -290,7 +290,7 @@ net_address_module_info gAddressModule = {
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
true, // has_broadcast_address
|
||||
unix_copy_address,
|
||||
unix_mask_address,
|
||||
unix_equal_addresses,
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <netinet6/in6.h> // TODO
|
||||
|
||||
|
||||
struct datalink_protocol : net_protocol {
|
||||
struct net_domain_private* domain;
|
||||
@@ -134,6 +136,21 @@ remove_default_routes(net_interface_private* interface, int32 option)
|
||||
route.flags = RTF_LOCAL | RTF_HOST;
|
||||
remove_route(interface->domain, &route);
|
||||
}
|
||||
|
||||
// for IPv6 remove multicast route (ff00::/8)
|
||||
// TODO: move this code
|
||||
if (interface->address->sa_family == AF_INET6) {
|
||||
sockaddr_in6 address;
|
||||
memset(&address, 0, sizeof(sockaddr_in6));
|
||||
address.sin6_family = AF_INET6;
|
||||
address.sin6_len = sizeof(sockaddr_in6);
|
||||
address.sin6_addr.s6_addr[0] = 0xff;
|
||||
|
||||
route.destination = (sockaddr*)&address;
|
||||
route.mask = (sockaddr*)&address;
|
||||
route.flags = 0;
|
||||
remove_route(interface->domain, &route);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +177,21 @@ add_default_routes(net_interface_private* interface, int32 option)
|
||||
route.flags = RTF_LOCAL | RTF_HOST;
|
||||
add_route(interface->domain, &route);
|
||||
}
|
||||
|
||||
// for IPv6 add multicast route (ff00::/8)
|
||||
// TODO: move this code
|
||||
if (interface->address->sa_family == AF_INET6) {
|
||||
sockaddr_in6 address;
|
||||
memset(&address, 0, sizeof(sockaddr_in6));
|
||||
address.sin6_family = AF_INET6;
|
||||
address.sin6_len = sizeof(sockaddr_in6);
|
||||
address.sin6_addr.s6_addr[0] = 0xff;
|
||||
|
||||
route.destination = (sockaddr*)&address;
|
||||
route.mask = (sockaddr*)&address;
|
||||
route.flags = 0;
|
||||
add_route(interface->domain, &route);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,6 +215,14 @@ reallocate_address(sockaddr** _address, uint32 size)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_address(sockaddr** _address)
|
||||
{
|
||||
free(*_address);
|
||||
*_address = NULL;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
datalink_control_interface(net_domain_private* domain, int32 option,
|
||||
void* value, size_t* _length, size_t expected, bool getByName)
|
||||
@@ -386,7 +426,8 @@ datalink_send_datagram(net_protocol* protocol, net_domain* domain,
|
||||
|
||||
net_route* route = NULL;
|
||||
status_t status;
|
||||
if (protocol != NULL && protocol->socket->bound_to_device > 0) {
|
||||
if (protocol != NULL && protocol->socket != NULL
|
||||
&& protocol->socket->bound_to_device > 0) {
|
||||
status = get_device_route(domain, protocol->socket->bound_to_device,
|
||||
&route);
|
||||
} else
|
||||
@@ -709,8 +750,15 @@ interface_protocol_control(net_datalink_protocol* _protocol, int32 option,
|
||||
} else
|
||||
oldNetmask = address;
|
||||
|
||||
sockaddr* broadcast = reallocate_address(
|
||||
&interface->destination, request.ifr_addr.sa_len);
|
||||
// reset the broadcast address if the address family has such
|
||||
sockaddr* broadcast;
|
||||
if (interface->domain->address_module->has_broadcast_address) {
|
||||
broadcast = reallocate_address(&interface->destination,
|
||||
request.ifr_addr.sa_len);
|
||||
} else {
|
||||
broadcast = NULL;
|
||||
free_address(&interface->destination);
|
||||
}
|
||||
|
||||
interface->domain->address_module->set_to_defaults(
|
||||
netmask, broadcast, interface->address, oldNetmask);
|
||||
@@ -923,6 +971,7 @@ net_datalink_protocol_module_info gDatalinkInterfaceProtocolModule = {
|
||||
interface_protocol_init,
|
||||
interface_protocol_uninit,
|
||||
interface_protocol_send_data,
|
||||
NULL, // receive_data
|
||||
interface_protocol_up,
|
||||
interface_protocol_down,
|
||||
interface_protocol_control,
|
||||
|
||||
@@ -1038,6 +1038,7 @@ copy_metadata(net_buffer* destination, const net_buffer* source)
|
||||
destination->interface = source->interface;
|
||||
destination->offset = source->offset;
|
||||
destination->protocol = source->protocol;
|
||||
destination->hoplimit = source->hoplimit;
|
||||
destination->type = source->type;
|
||||
}
|
||||
|
||||
|
||||
@@ -813,11 +813,17 @@ init_stack()
|
||||
// TODO: for now!
|
||||
register_domain_datalink_protocols(AF_INET, IFT_LOOP,
|
||||
"network/datalink_protocols/loopback_frame/v1", NULL);
|
||||
register_domain_datalink_protocols(AF_INET6, IFT_LOOP,
|
||||
"network/datalink_protocols/loopback_frame/v1", NULL);
|
||||
register_domain_datalink_protocols(AF_INET, IFT_ETHER,
|
||||
"network/datalink_protocols/ipv4_datagram/v1",
|
||||
"network/datalink_protocols/arp/v1",
|
||||
"network/datalink_protocols/ethernet_frame/v1",
|
||||
NULL);
|
||||
register_domain_datalink_protocols(AF_INET6, IFT_ETHER,
|
||||
"network/datalink_protocols/ipv6_datagram/v1",
|
||||
"network/datalink_protocols/ethernet_frame/v1",
|
||||
NULL);
|
||||
|
||||
return B_OK;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user