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:
Axel Dörfler
2010-07-19 16:55:55 +00:00
parent 0e23b87fe4
commit 8d1485fa06
30 changed files with 4307 additions and 10 deletions
+1
View File
@@ -36,6 +36,7 @@ typedef struct net_buffer {
uint32 flags;
uint32 size;
uint8 protocol;
uint8 hoplimit;
} net_buffer;
struct ancillary_data_container;
+1
View File
@@ -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,
+52 -3
View File
@@ -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;