/*
* Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Atis Elsts, the.kfx@gmail.com
*/
#include "ipv6_address.h"
#include "ipv6_utils.h"
#include "multicast.h"
#include <net_datalink.h>
#include <net_datalink_protocol.h>
#include <net_device.h>
#include <net_protocol.h>
#include <net_stack.h>
#include <NetBufferUtilities.h>
#include <ProtocolUtilities.h>
#include <ByteOrder.h>
#include <KernelExport.h>
#include <StackOrHeapArray.h>
#include <util/AutoLock.h>
#include <util/list.h>
#include <util/DoublyLinkedList.h>
#include <util/MultiHashTable.h>
#include <netinet6/in6.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <new>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <utility>
//#define TRACE_IPV6
#ifdef TRACE_IPV6
#define TRACE(format, args...) \
dprintf("IPv6 [%" B_PRIdBIGTIME "] " format "\n", system_time(), ##args)
#define TRACE_SK(protocol, format, args...) \
dprintf("IPv6 [%" B_PRIdBIGTIME "] %p " format "\n", system_time(), \
protocol, ##args)
#else
#define TRACE(args...)
#define TRACE_SK(args...)
#endif
#define MAX_HASH_FRAGMENTS 64
// slots in the fragment packet's hash
#define FRAGMENT_TIMEOUT 60000000LL
// discard fragment after 60 seconds [RFC 2460]
struct IPv6Header {
struct ip6_hdr header;
uint8 ProtocolVersion() const { return header.ip6_vfc & IPV6_VERSION_MASK; }
uint8 ServiceType() const { return ntohl(header.ip6_flow) >> 20;}
uint16 PayloadLength() const { return ntohs(header.ip6_plen); }
const in6_addr& Dst() const { return header.ip6_dst; }
const in6_addr& Src() const { return header.ip6_src; }
uint8 NextHeader() const { return header.ip6_nxt; }
uint16 GetHeaderOffset(net_buffer* buffer, uint32 headerCode = ~0u) const;
};
typedef DoublyLinkedList<struct net_buffer,
DoublyLinkedListCLink<struct net_buffer> > FragmentList;
// TODO: make common fragmentation interface for both address families
struct ipv6_packet_key {
in6_addr source;
in6_addr destination;
// We use uint32 here due to the hash function
uint32 id;
uint32 protocol;
};
class FragmentPacket {
public:
FragmentPacket(const ipv6_packet_key& key);
~FragmentPacket();
status_t AddFragment(uint16 start, uint16 end,
net_buffer* buffer, bool lastFragment);
status_t Reassemble(net_buffer* to);
bool IsComplete() const
{ return fReceivedLastFragment
&& fBytesLeft == 0; }
const ipv6_packet_key& Key() const { return fKey; }
FragmentPacket*& HashTableLink() { return fNext; }
static void StaleTimer(struct net_timer* timer, void* data);
private:
FragmentPacket* fNext;
struct ipv6_packet_key fKey;
uint32 fIndex;
int32 fBytesLeft;
FragmentList fFragments;
net_timer fTimer;
bool fReceivedLastFragment;
};
struct FragmentHashDefinition {
typedef ipv6_packet_key KeyType;
typedef FragmentPacket ValueType;
size_t HashKey(const KeyType& key) const
{
return jenkins_hashword((const uint32*)&key,
sizeof(ipv6_packet_key) / sizeof(uint32), 0);
}
size_t Hash(ValueType* value) const
{
return HashKey(value->Key());
}
bool Compare(const KeyType& key, ValueType* value) const
{
const ipv6_packet_key& packetKey = value->Key();
return packetKey.id == key.id
&& packetKey.source == key.source
&& packetKey.destination == key.destination
&& packetKey.protocol == key.protocol;
}
ValueType*& GetLink(ValueType* value) const
{
return value->HashTableLink();
}
};
typedef BOpenHashTable<FragmentHashDefinition, false, true> FragmentTable;
class RawSocket
: public DoublyLinkedListLinkImpl<RawSocket>, public DatagramSocket<> {
public:
RawSocket(net_socket* socket);
};
typedef DoublyLinkedList<RawSocket> RawSocketList;
typedef MulticastGroupInterface<IPv6Multicast> IPv6GroupInterface;
typedef MulticastFilter<IPv6Multicast> IPv6MulticastFilter;
struct MulticastStateHash {
typedef std::pair<const in6_addr*, uint32> KeyType;
typedef IPv6GroupInterface ValueType;
size_t HashKey(const KeyType &key) const;
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; }
bool CompareValues(ValueType* value1, ValueType* value2) const
{ return value1->Interface()->index == value2->Interface()->index
&& value1->Address() == value2->Address(); }
ValueType*& GetLink(ValueType* value) const { return value->MulticastGroupsHashLink(); }
};
struct ipv6_protocol : net_protocol {
ipv6_protocol()
:
raw(NULL),
multicast_filter(this)
{
}
~ipv6_protocol()
{
delete raw;
}
RawSocket *raw;
uint8 service_type;
uint8 time_to_live;
uint8 multicast_time_to_live;
uint8 receive_hoplimit;
uint8 receive_pktinfo;
struct sockaddr* multicast_address; // for IPV6_MULTICAST_IF
IPv6MulticastFilter multicast_filter;
};
static const int kDefaultTTL = IPV6_DEFHLIM;
static const int kDefaultMulticastTTL = 1;
extern net_protocol_module_info gIPv6Module;
// we need this in ipv6_std_ops() for registering the AF_INET6 domain
net_stack_module_info* gStackModule;
net_buffer_module_info* gBufferModule;
static struct net_domain* sDomain;
static net_datalink_module_info* sDatalinkModule;
static net_socket_module_info* sSocketModule;
static RawSocketList sRawSockets;
static mutex sRawSocketsLock;
static mutex sFragmentLock;
static FragmentTable sFragmentHash;
static int32 sFragmentID;
static mutex sMulticastGroupsLock;
typedef MultiHashTable<MulticastStateHash> MulticastState;
static MulticastState* sMulticastState;
static net_protocol_module_info* sReceivingProtocol[256];
static mutex sReceivingProtocolLock;
uint16
IPv6Header::GetHeaderOffset(net_buffer* buffer, uint32 headerCode) const
{
uint16 offset = sizeof(struct ip6_hdr);
uint8 next = header.ip6_nxt;
// these are the extension headers that might be supported one day
while (next != headerCode
&& (next == IPPROTO_HOPOPTS
|| next == IPPROTO_ROUTING
|| next == IPPROTO_FRAGMENT
|| next == IPPROTO_ESP
|| next == IPPROTO_AH
|| next == IPPROTO_DSTOPTS)) {
struct ip6_ext extensionHeader;
status_t status = gBufferModule->read(buffer, offset,
&extensionHeader, sizeof(ip6_ext));
if (status != B_OK)
break;
next = extensionHeader.ip6e_nxt;
offset += extensionHeader.ip6e_len;
}
// were we looking for a specific header?
if (headerCode != ~0u) {
if (next == headerCode) {
// found the specific header
return offset;
}
// return 0 if fragement header is not present
return 0;
}
// the general transport layer header case
buffer->protocol = next;
return offset;
}
RawSocket::RawSocket(net_socket* socket)
:
DatagramSocket<>("ipv6 raw socket", socket)
{
}
// #pragma mark -
FragmentPacket::FragmentPacket(const ipv6_packet_key &key)
:
fKey(key),
fBytesLeft(IPV6_MAXPACKET),
fReceivedLastFragment(false)
{
gStackModule->init_timer(&fTimer, FragmentPacket::StaleTimer, this);
}
FragmentPacket::~FragmentPacket()
{
// cancel the kill timer
gStackModule->set_timer(&fTimer, -1);
// delete all fragments
net_buffer* buffer;
while ((buffer = fFragments.RemoveHead()) != NULL) {
gBufferModule->free(buffer);
}
}
status_t
FragmentPacket::AddFragment(uint16 start, uint16 end, net_buffer* buffer,
bool lastFragment)
{
// restart the timer
gStackModule->set_timer(&fTimer, FRAGMENT_TIMEOUT);
if (start >= end) {
// invalid fragment
return B_BAD_DATA;
}
// Search for a position in the list to insert the fragment
FragmentList::ReverseIterator iterator = fFragments.GetReverseIterator();
net_buffer* previous = NULL;
net_buffer* next = NULL;
while ((previous = iterator.Next()) != NULL) {
if (previous->fragment.start <= start) {
// The new fragment can be inserted after this one
break;
}
next = previous;
}
// See if we already have the fragment's data
if (previous != NULL && previous->fragment.start <= start
&& previous->fragment.end >= end) {
// we do, so we can just drop this fragment
gBufferModule->free(buffer);
return B_OK;
}
fIndex = buffer->index;
// adopt the buffer's device index
TRACE(" previous: %p, next: %p", previous, next);
// If we have parts of the data already, truncate as needed
if (previous != NULL && previous->fragment.end > start) {
TRACE(" remove header %d bytes", previous->fragment.end - start);
gBufferModule->remove_header(buffer, previous->fragment.end - start);
start = previous->fragment.end;
}
if (next != NULL && next->fragment.start < end) {
TRACE(" remove trailer %d bytes", next->fragment.start - end);
gBufferModule->remove_trailer(buffer, next->fragment.start - end);
end = next->fragment.start;
}
// Now try if we can already merge the fragments together
// We will always keep the last buffer received, so that we can still
// report an error (in which case we're not responsible for freeing it)
if (previous != NULL && previous->fragment.end == start) {
fFragments.Remove(previous);
buffer->fragment.start = previous->fragment.start;
buffer->fragment.end = end;
status_t status = gBufferModule->merge(buffer, previous, false);
TRACE(" merge previous: %s", strerror(status));
if (status != B_OK) {
fFragments.Insert(next, previous);
return status;
}
fFragments.Insert(next, buffer);
// cut down existing hole
fBytesLeft -= end - start;
if (lastFragment && !fReceivedLastFragment) {
fReceivedLastFragment = true;
fBytesLeft -= IPV6_MAXPACKET - end;
}
TRACE(" hole length: %d", (int)fBytesLeft);
return B_OK;
} else if (next != NULL && next->fragment.start == end) {
net_buffer* afterNext = (net_buffer*)next->link.next;
fFragments.Remove(next);
buffer->fragment.start = start;
buffer->fragment.end = next->fragment.end;
status_t status = gBufferModule->merge(buffer, next, true);
TRACE(" merge next: %s", strerror(status));
if (status != B_OK) {
// Insert "next" at its previous position
fFragments.Insert(afterNext, next);
return status;
}
fFragments.Insert(afterNext, buffer);
// cut down existing hole
fBytesLeft -= end - start;
if (lastFragment && !fReceivedLastFragment) {
fReceivedLastFragment = true;
fBytesLeft -= IPV6_MAXPACKET - end;
}
TRACE(" hole length: %d", (int)fBytesLeft);
return B_OK;
}
// We couldn't merge the fragments, so we need to add it as is
TRACE(" new fragment: %p, bytes %d-%d", buffer, start, end);
buffer->fragment.start = start;
buffer->fragment.end = end;
fFragments.Insert(next, buffer);
// update length of the hole, if any
fBytesLeft -= end - start;
if (lastFragment && !fReceivedLastFragment) {
fReceivedLastFragment = true;
fBytesLeft -= IPV6_MAXPACKET - end;
}
TRACE(" hole length: %d", (int)fBytesLeft);
return B_OK;
}
/*! Reassembles the fragments to the specified buffer \a to.
This buffer must have been added via AddFragment() before.
*/
status_t
FragmentPacket::Reassemble(net_buffer* to)
{
if (!IsComplete())
return B_ERROR;
net_buffer* buffer = NULL;
net_buffer* fragment;
while ((fragment = fFragments.RemoveHead()) != NULL) {
if (buffer != NULL) {
status_t status;
if (to == fragment) {
status = gBufferModule->merge(fragment, buffer, false);
buffer = fragment;
} else
status = gBufferModule->merge(buffer, fragment, true);
if (status != B_OK)
return status;
} else
buffer = fragment;
}
if (buffer != to)
panic("ipv6 packet reassembly did not work correctly.");
to->index = fIndex;
// reset the buffer's device index
return B_OK;
}
/*static*/ void
FragmentPacket::StaleTimer(struct net_timer* timer, void* data)
{
FragmentPacket* packet = (FragmentPacket*)data;
TRACE("Assembling FragmentPacket %p timed out!", packet);
MutexLocker locker(&sFragmentLock);
sFragmentHash.Remove(packet);
locker.Unlock();
if (!packet->fFragments.IsEmpty()) {
// Send error: fragment reassembly time exceeded
sDomain->module->error_reply(NULL, packet->fFragments.First(),
B_NET_ERROR_REASSEMBLY_TIME_EXCEEDED, NULL);
}
delete packet;
}
// #pragma mark -
size_t
MulticastStateHash::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;
}
// #pragma mark -
static inline void
dump_ipv6_header(IPv6Header &header)
{
#ifdef TRACE_IPV6
char addrbuf[INET6_ADDRSTRLEN];
dprintf(" version: %d\n", header.ProtocolVersion() >> 4);
dprintf(" service_type: %d\n", header.ServiceType());
dprintf(" payload_length: %d\n", header.PayloadLength());
dprintf(" next_header: %d\n", header.NextHeader());
dprintf(" hop_limit: %d\n", header.header.ip6_hops);
dprintf(" source: %s\n", ip6_sprintf(&header.header.ip6_src, addrbuf));
dprintf(" destination: %s\n",
ip6_sprintf(&header.header.ip6_dst, addrbuf));
#endif
}
/*! Attempts to re-assemble fragmented packets.
\return B_OK if everything went well; if it could reassemble the packet,
\a _buffer will point to its buffer, otherwise, it will be \c NULL.
\return various error codes if something went wrong (mostly B_NO_MEMORY)
*/
static status_t
reassemble_fragments(const IPv6Header &header, net_buffer** _buffer,
uint16 offset)
{
net_buffer* buffer = *_buffer;
status_t status;
ip6_frag fragmentHeader;
status = gBufferModule->read(buffer, offset, &fragmentHeader,
sizeof(ip6_frag));
if (status != B_OK)
return status;
struct ipv6_packet_key key;
memcpy(&key.source, &header.Src(), sizeof(in6_addr));
memcpy(&key.destination, &header.Dst(), sizeof(in6_addr));
key.id = fragmentHeader.ip6f_ident;
key.protocol = fragmentHeader.ip6f_nxt;
// TODO: Make locking finer grained.
MutexLocker locker(&sFragmentLock);
FragmentPacket* packet = sFragmentHash.Lookup(key);
if (packet == NULL) {
// New fragment packet
packet = new (std::nothrow) FragmentPacket(key);
if (packet == NULL)
return B_NO_MEMORY;
// add packet to hash
status = sFragmentHash.Insert(packet);
if (status != B_OK) {
delete packet;
return status;
}
}
uint16 start = ntohs(fragmentHeader.ip6f_offlg & IP6F_OFF_MASK);
uint16 end = start + header.PayloadLength();
bool lastFragment = (fragmentHeader.ip6f_offlg & IP6F_MORE_FRAG) == 0;
TRACE(" Received IPv6 %sfragment of size %d, offset %d.",
lastFragment ? "last ": "", end - start, start);
// Remove header unless this is the first fragment
if (start != 0)
gBufferModule->remove_header(buffer, offset);
status = packet->AddFragment(start, end, buffer, lastFragment);
if (status != B_OK)
return status;
if (packet->IsComplete()) {
sFragmentHash.Remove(packet);
// no matter if reassembling succeeds, we won't need this packet
// anymore
status = packet->Reassemble(buffer);
delete packet;
// _buffer does not change
return status;
}
// This indicates that the packet is not yet complete
*_buffer = NULL;
return B_OK;
}
/*! Fragments the incoming buffer and send all fragments via the specified
\a route.
*/
static status_t
send_fragments(ipv6_protocol* protocol, struct net_route* route,
net_buffer* buffer, uint32 mtu)
{
TRACE_SK(protocol, "SendFragments(%" B_PRIu32 " bytes, mtu %" B_PRIu32 ")",
buffer->size, mtu);
NetBufferHeaderReader<IPv6Header> originalHeader(buffer);
if (originalHeader.Status() != B_OK)
return originalHeader.Status();
// TODO: currently FragHeader goes always as the last one, but in theory
// ext. headers like AuthHeader and DestOptions should go after it.
uint16 headersLength = originalHeader->GetHeaderOffset(buffer);
uint16 extensionHeadersLength = headersLength
- sizeof(ip6_hdr) + sizeof(ip6_frag);
uint32 bytesLeft = buffer->size - headersLength;
uint32 fragmentOffset = 0;
status_t status = B_OK;
// TODO: this is rather inefficient
net_buffer* headerBuffer = gBufferModule->clone(buffer, false);
if (headerBuffer == NULL)
return B_NO_MEMORY;
status = gBufferModule->remove_trailer(headerBuffer, bytesLeft);
if (status != B_OK)
return status;
BStackOrHeapArray<uint8, 128> data(bytesLeft);
if (!data.IsValid())
return B_NO_MEMORY;
status = gBufferModule->read(buffer, headersLength, data, bytesLeft);
if (status != B_OK)
return status;
// TODO (from ipv4): we need to make sure all header space is contiguous or
// use another construct.
NetBufferHeaderReader<IPv6Header> bufferHeader(headerBuffer);
// Adapt MTU to be a multiple of 8 (fragment offsets can only be specified
// this way)
mtu -= headersLength + sizeof(ip6_frag);
mtu &= ~7;
TRACE(" adjusted MTU to %" B_PRIu32 " bytesLeft %" B_PRIu32, mtu,
bytesLeft);
while (bytesLeft > 0) {
uint32 fragmentLength = min_c(bytesLeft, mtu);
bytesLeft -= fragmentLength;
bool lastFragment = bytesLeft == 0;
bufferHeader->header.ip6_nxt = IPPROTO_FRAGMENT;
bufferHeader->header.ip6_plen
= htons(fragmentLength + extensionHeadersLength);
bufferHeader.Sync();
ip6_frag fragmentHeader;
fragmentHeader.ip6f_nxt = originalHeader->NextHeader();
fragmentHeader.ip6f_reserved = 0;
fragmentHeader.ip6f_offlg = htons(fragmentOffset) & IP6F_OFF_MASK;
if (!lastFragment)
fragmentHeader.ip6f_offlg |= IP6F_MORE_FRAG;
fragmentHeader.ip6f_ident = htonl(atomic_add(&sFragmentID, 1));
TRACE(" send fragment of %" B_PRIu32 " bytes (%" B_PRIu32
" bytes left)", fragmentLength, bytesLeft);
net_buffer* fragmentBuffer;
if (!lastFragment)
fragmentBuffer = gBufferModule->clone(headerBuffer, false);
else
fragmentBuffer = buffer;
if (fragmentBuffer == NULL) {
status = B_NO_MEMORY;
break;
}
// copy data to fragment
do {
status = gBufferModule->append(
fragmentBuffer, &fragmentHeader, sizeof(ip6_frag));
if (status != B_OK)
break;
status = gBufferModule->append(
fragmentBuffer, &data[fragmentOffset], fragmentLength);
if (status != B_OK)
break;
// send fragment
status = sDatalinkModule->send_routed_data(route, fragmentBuffer);
} while (false);
if (lastFragment) {
// we don't own the last buffer, so we don't have to free it
break;
}
if (status != B_OK) {
gBufferModule->free(fragmentBuffer);
break;
}
fragmentOffset += fragmentLength;
}
gBufferModule->free(headerBuffer);
return status;
}
static status_t
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
bool deliverToRaw, net_interface *interface)
{
sockaddr_in6* multicastAddr = (sockaddr_in6*)buffer->destination;
MulticastState::ValueIterator it = sMulticastState->Lookup(std::make_pair(
&multicastAddr->sin6_addr, interface->index));
while (it.HasNext()) {
IPv6GroupInterface* state = it.Next();
ipv6_protocol* ipproto = state->Parent()->Socket();
if (deliverToRaw && ipproto->raw == NULL)
continue;
if (state->FilterAccepts(buffer)) {
// TODO: do as in IPv4 code
module->deliver_data(ipproto, buffer);
}
}
return B_OK;
}
static status_t
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
bool deliverToRaw)
{
if (module->deliver_data == NULL)
return B_OK;
MutexLocker _(sMulticastGroupsLock);
status_t status = B_OK;
if (buffer->interface_address != NULL) {
status = deliver_multicast(module, buffer, deliverToRaw,
buffer->interface_address->interface);
} else {
#if 0 // FIXME: multicast
net_domain_private* domain = (net_domain_private*)sDomain;
RecursiveLocker locker(domain->lock);
net_interface* interface = NULL;
while (true) {
interface = (net_interface*)list_get_next_item(
&domain->interfaces, interface);
if (interface == NULL)
break;
status = deliver_multicast(module, buffer, deliverToRaw, interface);
if (status < B_OK)
break;
}
#endif
}
return status;
}
static void
raw_receive_data(net_buffer* buffer)
{
MutexLocker locker(sRawSocketsLock);
if (sRawSockets.IsEmpty())
return;
TRACE("RawReceiveData(%i)", buffer->protocol);
if ((buffer->flags & MSG_MCAST) != 0) {
deliver_multicast(&gIPv6Module, buffer, true);
} else {
RawSocketList::Iterator iterator = sRawSockets.GetIterator();
while (iterator.HasNext()) {
RawSocket* raw = iterator.Next();
if (raw->Socket()->protocol == buffer->protocol)
raw->EnqueueClone(buffer);
}
}
}
static inline sockaddr*
fill_sockaddr_in6(sockaddr_in6* target, const in6_addr &address)
{
target->sin6_family = AF_INET6;
target->sin6_len = sizeof(sockaddr_in6);
target->sin6_port = 0;
target->sin6_flowinfo = 0;
memcpy(target->sin6_addr.s6_addr, address.s6_addr, sizeof(in6_addr));
target->sin6_scope_id = 0;
return (sockaddr*)target;
}
status_t
IPv6Multicast::JoinGroup(IPv6GroupInterface* state)
{
MutexLocker _(sMulticastGroupsLock);
sockaddr_in6 groupAddr;
status_t status = sDatalinkModule->join_multicast(state->Interface(),
sDomain, fill_sockaddr_in6(&groupAddr, state->Address()));
if (status != B_OK)
return status;
sMulticastState->Insert(state);
return B_OK;
}
status_t
IPv6Multicast::LeaveGroup(IPv6GroupInterface* state)
{
MutexLocker _(sMulticastGroupsLock);
sMulticastState->Remove(state);
sockaddr_in6 groupAddr;
return sDatalinkModule->leave_multicast(state->Interface(), sDomain,
fill_sockaddr_in6(&groupAddr, state->Address()));
}
static net_protocol_module_info*
receiving_protocol(uint8 protocol)
{
net_protocol_module_info* module = sReceivingProtocol[protocol];
if (module != NULL)
return module;
MutexLocker locker(sReceivingProtocolLock);
module = sReceivingProtocol[protocol];
if (module != NULL)
return module;
if (gStackModule->get_domain_receiving_protocol(sDomain, protocol,
&module) == B_OK)
sReceivingProtocol[protocol] = module;
return module;
}
static status_t
ipv6_delta_group(IPv6GroupInterface* group, int option,
net_interface* interface, const in6_addr* sourceAddr)
{
switch (option) {
case IPV6_JOIN_GROUP:
return group->Add();
case IPV6_LEAVE_GROUP:
return group->Drop();
}
return B_ERROR;
}
static status_t
ipv6_delta_membership(ipv6_protocol* protocol, int option,
net_interface* interface, const in6_addr* groupAddr,
const in6_addr* sourceAddr)
{
IPv6MulticastFilter &filter = protocol->multicast_filter;
IPv6GroupInterface* state = NULL;
status_t status = B_OK;
switch (option) {
// TODO: support more options
case IPV6_JOIN_GROUP:
status = filter.GetState(*groupAddr, interface, state, true);
break;
case IPV6_LEAVE_GROUP:
filter.GetState(*groupAddr, interface, state, false);
if (state == NULL)
return EADDRNOTAVAIL;
break;
}
if (status != B_OK)
return status;
status = ipv6_delta_group(state, option, interface, sourceAddr);
filter.ReturnState(state);
return status;
}
static status_t
ipv6_delta_membership(ipv6_protocol* protocol, int option,
uint32 interfaceIndex, in6_addr* groupAddr, in6_addr* sourceAddr)
{
net_interface* interface;
// TODO: can the interface be unspecified?
interface = sDatalinkModule->get_interface(sDomain, interfaceIndex);
if (interface == NULL)
return B_DEVICE_NOT_FOUND;
return ipv6_delta_membership(protocol, option, interface,
groupAddr, sourceAddr);
}
static status_t
get_int_option(void* target, size_t length, int value)
{
if (length != sizeof(int))
return B_BAD_VALUE;
return user_memcpy(target, &value, sizeof(int));
}
template<typename Type> static status_t
set_int_option(Type &target, const void* _value, size_t length)
{
int value;
if (length != sizeof(int))
return B_BAD_VALUE;
if (user_memcpy(&value, _value, sizeof(int)) != B_OK)
return B_BAD_ADDRESS;
target = value;
return B_OK;
}
// #pragma mark -
net_protocol*
ipv6_init_protocol(net_socket* socket)
{
ipv6_protocol* protocol = new (std::nothrow) ipv6_protocol();
if (protocol == NULL)
return NULL;
protocol->raw = NULL;
protocol->service_type = 0;
protocol->time_to_live = kDefaultTTL;
protocol->multicast_time_to_live = kDefaultMulticastTTL;
protocol->receive_hoplimit = 0;
protocol->receive_pktinfo = 0;
protocol->multicast_address = NULL;
return protocol;
}
status_t
ipv6_uninit_protocol(net_protocol* _protocol)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
delete protocol;
return B_OK;
}
/*! Since open() is only called on the top level protocol, when we get here
it means we are on a SOCK_RAW socket.
*/
status_t
ipv6_open(net_protocol* _protocol)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
RawSocket* raw = new (std::nothrow) RawSocket(protocol->socket);
if (raw == NULL)
return B_NO_MEMORY;
status_t status = raw->InitCheck();
if (status != B_OK) {
delete raw;
return status;
}
TRACE_SK(protocol, "Open()");
protocol->raw = raw;
MutexLocker locker(sRawSocketsLock);
sRawSockets.Add(raw);
return B_OK;
}
status_t
ipv6_close(net_protocol* _protocol)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
RawSocket* raw = protocol->raw;
if (raw == NULL)
return B_ERROR;
TRACE_SK(protocol, "Close()");
MutexLocker locker(sRawSocketsLock);
sRawSockets.Remove(raw);
delete raw;
protocol->raw = NULL;
return B_OK;
}
status_t
ipv6_free(net_protocol* protocol)
{
return B_OK;
}
status_t
ipv6_connect(net_protocol* protocol, const struct sockaddr* address)
{
return B_ERROR;
}
status_t
ipv6_accept(net_protocol* protocol, struct net_socket** _acceptedSocket)
{
return EOPNOTSUPP;
}
status_t
ipv6_control(net_protocol* _protocol, int level, int option, void* value,
size_t* _length)
{
if ((level & LEVEL_MASK) != IPPROTO_IPV6)
return sDatalinkModule->control(sDomain, option, value, _length);
return B_BAD_VALUE;
}
status_t
ipv6_getsockopt(net_protocol* _protocol, int level, int option, void* value,
int* _length)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
if (level == IPPROTO_IPV6) {
// TODO: support more of these options
if (option == IPV6_MULTICAST_HOPS) {
return get_int_option(value, *_length,
protocol->multicast_time_to_live);
}
if (option == IPV6_MULTICAST_LOOP)
return EOPNOTSUPP;
if (option == IPV6_UNICAST_HOPS)
return get_int_option(value, *_length, protocol->time_to_live);
if (option == IPV6_V6ONLY)
return EOPNOTSUPP;
if (option == IPV6_RECVPKTINFO)
return get_int_option(value, *_length, protocol->receive_pktinfo);
if (option == IPV6_RECVHOPLIMIT)
return get_int_option(value, *_length, protocol->receive_hoplimit);
if (option == IPV6_JOIN_GROUP
|| option == IPV6_LEAVE_GROUP)
return EOPNOTSUPP;
dprintf("IPv6::getsockopt(): get unknown option: %d\n", option);
return ENOPROTOOPT;
}
return sSocketModule->get_option(protocol->socket, level, option, value,
_length);
}
status_t
ipv6_setsockopt(net_protocol* _protocol, int level, int option,
const void* value, int length)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
if (level == IPPROTO_IPV6) {
// TODO: support more of these options
if (option == IPV6_MULTICAST_IF) {
if (length != sizeof(struct in6_addr))
return B_BAD_VALUE;
struct sockaddr_in6* address = new (std::nothrow) sockaddr_in6;
if (address == NULL)
return B_NO_MEMORY;
if (user_memcpy(&address->sin6_addr, value, sizeof(in6_addr))
!= B_OK) {
delete address;
return B_BAD_ADDRESS;
}
// Using the unspecifed address to remove the previous setting.
if (IN6_IS_ADDR_UNSPECIFIED(&address->sin6_addr)) {
delete address;
delete protocol->multicast_address;
protocol->multicast_address = NULL;
return B_OK;
}
struct net_interface* interface
= sDatalinkModule->get_interface_with_address(
(sockaddr*)address);
if (interface == NULL) {
delete address;
return EADDRNOTAVAIL;
}
delete protocol->multicast_address;
protocol->multicast_address = (struct sockaddr*)address;
sDatalinkModule->put_interface(interface);
return B_OK;
}
if (option == IPV6_MULTICAST_HOPS) {
return set_int_option(protocol->multicast_time_to_live,
value, length);
}
if (option == IPV6_MULTICAST_LOOP)
return EOPNOTSUPP;
if (option == IPV6_UNICAST_HOPS)
return set_int_option(protocol->time_to_live, value, length);
if (option == IPV6_V6ONLY)
return EOPNOTSUPP;
if (option == IPV6_RECVPKTINFO)
return set_int_option(protocol->receive_pktinfo, value, length);
if (option == IPV6_RECVHOPLIMIT)
return set_int_option(protocol->receive_hoplimit, value, length);
if (option == IPV6_JOIN_GROUP || option == IPV6_LEAVE_GROUP) {
ipv6_mreq mreq;
if (length != sizeof(ipv6_mreq))
return B_BAD_VALUE;
if (user_memcpy(&mreq, value, sizeof(ipv6_mreq)) != B_OK)
return B_BAD_ADDRESS;
return ipv6_delta_membership(protocol, option,
mreq.ipv6mr_interface, &mreq.ipv6mr_multiaddr, NULL);
}
dprintf("IPv6::setsockopt(): set unknown option: %d\n", option);
return ENOPROTOOPT;
}
return sSocketModule->set_option(protocol->socket, level, option,
value, length);
}
status_t
ipv6_bind(net_protocol* protocol, const sockaddr* _address)
{
if (_address->sa_family != AF_INET6)
return EAFNOSUPPORT;
const sockaddr_in6* address = (const sockaddr_in6*)_address;
// only INADDR_ANY and addresses of local interfaces are accepted:
if (IN6_IS_ADDR_UNSPECIFIED(&address->sin6_addr)
|| IN6_IS_ADDR_MULTICAST(&address->sin6_addr)
|| sDatalinkModule->is_local_address(sDomain, _address, NULL, NULL)) {
memcpy(&protocol->socket->address, address, sizeof(sockaddr_in6));
protocol->socket->address.ss_len = sizeof(sockaddr_in6);
// explicitly set length, as our callers can't be trusted to
// always provide the correct length!
return B_OK;
}
return B_ERROR;
// address is unknown on this host
}
status_t
ipv6_unbind(net_protocol* protocol, struct sockaddr* address)
{
// nothing to do here
return B_OK;
}
status_t
ipv6_listen(net_protocol* protocol, int count)
{
return EOPNOTSUPP;
}
status_t
ipv6_shutdown(net_protocol* protocol, int direction)
{
return EOPNOTSUPP;
}
static uint8
ip6_select_hoplimit(net_protocol* _protocol, net_buffer* buffer)
{
// TODO: the precedence should be as follows:
// 1. Hoplimit value specified via ioctl.
// 2. (If the outgoing interface is detected) the current
// hop limit of the interface specified by router advertisement.
// 3. The system default hoplimit.
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
const bool isMulticast = buffer->flags & MSG_MCAST;
if (protocol) {
return isMulticast ? protocol->multicast_time_to_live
: protocol->time_to_live;
}
return isMulticast ? kDefaultMulticastTTL : kDefaultTTL;
}
status_t
ipv6_send_routed_data(net_protocol* _protocol, struct net_route* route,
net_buffer* buffer)
{
if (route == NULL)
return B_BAD_VALUE;
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
net_interface* interface = route->interface_address->interface;
uint8 protocolNumber;
if (protocol != NULL && protocol->socket != NULL)
protocolNumber = protocol->socket->protocol;
else
protocolNumber = buffer->protocol;
TRACE_SK(protocol, "SendRoutedData(%p, %p [%" B_PRIu32 " bytes])", route,
buffer, buffer->size);
sockaddr_in6& source = *(sockaddr_in6*)buffer->source;
sockaddr_in6& destination = *(sockaddr_in6*)buffer->destination;
buffer->flags &= ~(MSG_BCAST | MSG_MCAST);
if (IN6_IS_ADDR_UNSPECIFIED(&destination.sin6_addr))
return EDESTADDRREQ;
if (IN6_IS_ADDR_MULTICAST(&destination.sin6_addr))
buffer->flags |= MSG_MCAST;
uint16 dataLength = buffer->size;
// Add IPv6 header
NetBufferPrepend<ip6_hdr> header(buffer);
if (header.Status() != B_OK)
return header.Status();
if (buffer->size > 0xffff)
return EMSGSIZE;
uint32 flowinfo = 0;
// TODO: fill in the flow id from somewhere
if (protocol) {
// fill in traffic class
flowinfo |= htonl(protocol->service_type << 20);
}
// set lower 28 bits
header->ip6_flow = htonl(flowinfo) & IPV6_FLOWINFO_MASK;
// set upper 4 bits
header->ip6_vfc |= IPV6_VERSION;
header->ip6_plen = htons(dataLength);
header->ip6_nxt = protocolNumber;
header->ip6_hlim = ip6_select_hoplimit(protocol, buffer);
memcpy(&header->ip6_src, &source.sin6_addr, sizeof(in6_addr));
memcpy(&header->ip6_dst, &destination.sin6_addr, sizeof(in6_addr));
header.Sync();
// write the checksum for ICMPv6 sockets
if (protocolNumber == IPPROTO_ICMPV6
&& dataLength >= sizeof(struct icmp6_hdr)) {
NetBufferField<uint16, sizeof(ip6_hdr)
+ offsetof(icmp6_hdr, icmp6_cksum)>
icmpChecksum(buffer);
// first make sure the existing checksum is zero
*icmpChecksum = 0;
icmpChecksum.Sync();
uint16 checksum = gBufferModule->checksum(buffer, sizeof(ip6_hdr),
buffer->size - sizeof(ip6_hdr), false);
checksum = ipv6_checksum(&header->ip6_src,
&header->ip6_dst, dataLength, protocolNumber,
checksum);
*icmpChecksum = checksum;
}
char addrbuf[INET6_ADDRSTRLEN];
ip6_sprintf(&destination.sin6_addr, addrbuf);
TRACE_SK(protocol, " SendRoutedData(): destination: %s", addrbuf);
uint32 mtu = route->mtu ? route->mtu : interface->mtu;
if (buffer->size > mtu) {
// we need to fragment the packet
return send_fragments(protocol, route, buffer, mtu);
}
return sDatalinkModule->send_routed_data(route, buffer);
}
status_t
ipv6_send_data(net_protocol* _protocol, net_buffer* buffer)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
TRACE_SK(protocol, "SendData(%p [%" B_PRIu32 " bytes])", buffer,
buffer->size);
sockaddr_in6* destination = (sockaddr_in6*)buffer->destination;
// handle IPV6_MULTICAST_IF
if (IN6_IS_ADDR_MULTICAST(&destination->sin6_addr)
&& protocol->multicast_address != NULL) {
net_interface_address* address = sDatalinkModule->get_interface_address(
protocol->multicast_address);
if (address == NULL || (address->interface->flags & IFF_UP) == 0) {
sDatalinkModule->put_interface_address(address);
return EADDRNOTAVAIL;
}
sDatalinkModule->put_interface_address(buffer->interface_address);
buffer->interface_address = address;
// the buffer takes over ownership of the address
net_route* route = sDatalinkModule->get_route(sDomain, address->local);
if (route == NULL)
return ENETUNREACH;
return sDatalinkModule->send_routed_data(route, buffer);
}
return sDatalinkModule->send_data(protocol, sDomain, buffer);
}
ssize_t
ipv6_send_avail(net_protocol* protocol)
{
return B_ERROR;
}
status_t
ipv6_read_data(net_protocol* _protocol, size_t numBytes, uint32 flags,
net_buffer** _buffer)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
RawSocket* raw = protocol->raw;
if (raw == NULL)
return B_ERROR;
TRACE_SK(protocol, "ReadData(%" B_PRIuSIZE ", 0x%" B_PRIu32 ")", numBytes,
flags);
return raw->Dequeue(flags, _buffer);
}
ssize_t
ipv6_read_avail(net_protocol* _protocol)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
RawSocket* raw = protocol->raw;
if (raw == NULL)
return B_ERROR;
return raw->AvailableData();
}
struct net_domain*
ipv6_get_domain(net_protocol* protocol)
{
return sDomain;
}
size_t
ipv6_get_mtu(net_protocol* protocol, const struct sockaddr* address)
{
net_route* route = sDatalinkModule->get_route(sDomain, address);
if (route == NULL)
return 0;
size_t mtu;
if (route->mtu != 0)
mtu = route->mtu;
else
mtu = route->interface_address->interface->mtu;
sDatalinkModule->put_route(sDomain, route);
// TODO: what about extension headers?
// this function probably shoud be changed in calling places, not here
return mtu - sizeof(ip6_hdr);
}
status_t
ipv6_receive_data(net_buffer* buffer)
{
TRACE("ReceiveData(%p [%" B_PRIu32 " bytes])", buffer, buffer->size);
NetBufferHeaderReader<IPv6Header> bufferHeader(buffer);
if (bufferHeader.Status() != B_OK)
return bufferHeader.Status();
IPv6Header &header = bufferHeader.Data();
// dump_ipv6_header(header);
if (header.ProtocolVersion() != IPV6_VERSION)
return B_BAD_TYPE;
uint16 packetLength = header.PayloadLength() + sizeof(ip6_hdr);
if (packetLength > buffer->size)
return B_BAD_DATA;
// lower layers notion of Broadcast or Multicast have no relevance to us
buffer->flags &= ~(MSG_BCAST | MSG_MCAST);
sockaddr_in6 destination;
fill_sockaddr_in6(&destination, header.Dst());
if (IN6_IS_ADDR_MULTICAST(&destination.sin6_addr)) {
buffer->flags |= MSG_MCAST;
} else {
uint32 matchedAddressType = 0;
// test if the packet is really for us
if (!sDatalinkModule->is_local_address(sDomain, (sockaddr*)&destination,
&buffer->interface_address, &matchedAddressType)
&& !sDatalinkModule->is_local_link_address(sDomain, true,
buffer->destination, &buffer->interface_address)) {
char srcbuf[INET6_ADDRSTRLEN];
char dstbuf[INET6_ADDRSTRLEN];
ip6_sprintf(&header.Src(), srcbuf);
ip6_sprintf(&header.Dst(), dstbuf);
TRACE(" ipv6_receive_data(): packet was not for us %s -> %s",
srcbuf, dstbuf);
// TODO: Send ICMPv6 error: Host unreachable
return B_ERROR;
}
// copy over special address types (MSG_BCAST or MSG_MCAST):
buffer->flags |= matchedAddressType;
}
// set net_buffer's source/destination address
fill_sockaddr_in6((struct sockaddr_in6*)buffer->source, header.Src());
memcpy(buffer->destination, &destination, sizeof(sockaddr_in6));
// get the transport protocol and transport header offset
uint16 transportHeaderOffset = header.GetHeaderOffset(buffer);
uint8 protocol = buffer->protocol;
// remove any trailing/padding data
status_t status = gBufferModule->trim(buffer, packetLength);
if (status != B_OK)
return status;
// check for fragmentation
uint16 fragmentHeaderOffset
= header.GetHeaderOffset(buffer, IPPROTO_FRAGMENT);
if (fragmentHeaderOffset != 0) {
// this is a fragment
TRACE(" ipv6_receive_data(): Found a Fragment!");
status = reassemble_fragments(header, &buffer, fragmentHeaderOffset);
TRACE(" ipv6_receive_data(): -> %s", strerror(status));
if (status != B_OK)
return status;
if (buffer == NULL) {
// buffer was put into fragment packet
TRACE(" ipv6_receive_data(): Not yet assembled.");
return B_OK;
}
}
// tell the buffer to preserve removed ipv6 header - may need it later
gBufferModule->store_header(buffer);
// remove ipv6 headers for now
gBufferModule->remove_header(buffer, transportHeaderOffset);
// deliver the data to raw sockets
raw_receive_data(buffer);
net_protocol_module_info* module = receiving_protocol(protocol);
if (module == NULL) {
// no handler for this packet
return EAFNOSUPPORT;
}
if ((buffer->flags & MSG_MCAST) != 0) {
// Unfortunately historical reasons dictate that the IP multicast
// model be a little different from the unicast one. We deliver
// this frame directly to all sockets registered with interest
// for this multicast group.
return deliver_multicast(module, buffer, false);
}
return module->receive_data(buffer);
}
status_t
ipv6_deliver_data(net_protocol* _protocol, net_buffer* buffer)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
if (protocol->raw == NULL)
return B_ERROR;
return protocol->raw->EnqueueClone(buffer);
}
status_t
ipv6_error_received(net_error error, net_buffer* data)
{
return B_ERROR;
}
status_t
ipv6_error_reply(net_protocol* protocol, net_buffer* cause, net_error error,
net_error_data* errorData)
{
return B_ERROR;
}
ssize_t
ipv6_process_ancillary_data_no_container(net_protocol* _protocol,
net_buffer* buffer, void* msgControl, size_t msgControlLen)
{
ipv6_protocol* protocol = (ipv6_protocol*)_protocol;
ssize_t bytesWritten = 0;
if (protocol->receive_hoplimit != 0) {
TRACE("receive_hoplimit");
if (msgControlLen < CMSG_SPACE(sizeof(int)))
return B_NO_MEMORY;
// use some default value (64 at the moment) when the real one fails
int hopLimit = IPV6_DEFHLIM;
if (gBufferModule->stored_header_length(buffer)
>= (int)sizeof(ip6_hdr)) {
IPv6Header header;
if (gBufferModule->restore_header(buffer, 0,
&header, sizeof(ip6_hdr)) == B_OK
&& header.ProtocolVersion() != IPV6_VERSION) {
// header is OK, take hoplimit from it
hopLimit = header.header.ip6_hlim;
}
}
cmsghdr* messageHeader = (cmsghdr*)((char*)msgControl + bytesWritten);
messageHeader->cmsg_len = CMSG_LEN(sizeof(int));
messageHeader->cmsg_level = IPPROTO_IPV6;
messageHeader->cmsg_type = IPV6_HOPLIMIT;
memcpy(CMSG_DATA(messageHeader), &hopLimit, sizeof(int));
bytesWritten += CMSG_SPACE(sizeof(int));
msgControlLen -= CMSG_SPACE(sizeof(int));
}
if (protocol->receive_pktinfo != 0) {
TRACE("receive_pktinfo");
if (msgControlLen < CMSG_SPACE(sizeof(struct in6_pktinfo)))
return B_NO_MEMORY;
cmsghdr* messageHeader = (cmsghdr*)((char*)msgControl + bytesWritten);
messageHeader->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
messageHeader->cmsg_level = IPPROTO_IPV6;
messageHeader->cmsg_type = IPV6_PKTINFO;
struct in6_pktinfo pi;
memcpy(&pi.ipi6_addr,
&((struct sockaddr_in6*)buffer->destination)->sin6_addr,
sizeof(struct in6_addr));
if (buffer->interface_address != NULL
&& buffer->interface_address->interface != NULL)
pi.ipi6_ifindex = buffer->interface_address->interface->index;
else
pi.ipi6_ifindex = 0;
memcpy(CMSG_DATA(messageHeader), &pi, sizeof(struct in6_pktinfo));
bytesWritten += CMSG_SPACE(sizeof(struct in6_pktinfo));
msgControlLen -= CMSG_SPACE(sizeof(struct in6_pktinfo));
}
return bytesWritten;
}
// #pragma mark -
status_t
init_ipv6()
{
mutex_init(&sRawSocketsLock, "raw sockets");
mutex_init(&sFragmentLock, "IPv4 Fragments");
mutex_init(&sMulticastGroupsLock, "IPv6 multicast groups");
mutex_init(&sReceivingProtocolLock, "IPv6 receiving protocols");
status_t status;
sMulticastState = new MulticastState();
if (sMulticastState == NULL) {
status = B_NO_MEMORY;
goto err1;
}
status = sMulticastState->Init();
if (status != B_OK)
goto err2;
new (&sFragmentHash) FragmentTable();
status = sFragmentHash.Init(256);
if (status != B_OK)
goto err3;
new (&sRawSockets) RawSocketList;
// static initializers do not work in the kernel,
// so we have to do it here, manually
// TODO: for modules, this shouldn't be required
status = gStackModule->register_domain_protocols(AF_INET6, SOCK_RAW, 0,
NET_IPV6_MODULE_NAME, NULL);
if (status != B_OK)
goto err3;
status = gStackModule->register_domain(AF_INET6, "internet6", &gIPv6Module,
&gIPv6AddressModule, &sDomain);
if (status != B_OK)
goto err3;
TRACE("init_ipv6: OK");
return B_OK;
err3:
sFragmentHash.~FragmentTable();
err2:
delete sMulticastState;
err1:
mutex_destroy(&sReceivingProtocolLock);
mutex_destroy(&sMulticastGroupsLock);
mutex_destroy(&sFragmentLock);
mutex_destroy(&sRawSocketsLock);
TRACE("init_ipv6: error %s", strerror(status));
return status;
}
status_t
uninit_ipv6()
{
mutex_lock(&sReceivingProtocolLock);
// put all the domain receiving protocols we gathered so far
for (uint32 i = 0; i < 256; i++) {
if (sReceivingProtocol[i] != NULL)
gStackModule->put_domain_receiving_protocol(sDomain, i);
}
sFragmentHash.~FragmentTable();
delete sMulticastState;
gStackModule->unregister_domain(sDomain);
mutex_unlock(&sReceivingProtocolLock);
mutex_destroy(&sMulticastGroupsLock);
mutex_destroy(&sFragmentLock);
mutex_destroy(&sRawSocketsLock);
mutex_destroy(&sReceivingProtocolLock);
return B_OK;
}
static status_t
ipv6_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
return init_ipv6();
case B_MODULE_UNINIT:
return uninit_ipv6();
default:
return B_ERROR;
}
}
net_protocol_module_info gIPv6Module = {
{
NET_IPV6_MODULE_NAME,
0,
ipv6_std_ops
},
NET_PROTOCOL_ATOMIC_MESSAGES,
ipv6_init_protocol,
ipv6_uninit_protocol,
ipv6_open,
ipv6_close,
ipv6_free,
ipv6_connect,
ipv6_accept,
ipv6_control,
ipv6_getsockopt,
ipv6_setsockopt,
ipv6_bind,
ipv6_unbind,
ipv6_listen,
ipv6_shutdown,
ipv6_send_data,
ipv6_send_routed_data,
ipv6_send_avail,
ipv6_read_data,
ipv6_read_avail,
ipv6_get_domain,
ipv6_get_mtu,
ipv6_receive_data,
ipv6_deliver_data,
ipv6_error_received,
ipv6_error_reply,
NULL, // add_ancillary_data()
NULL, // process_ancillary_data()
ipv6_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**)&gStackModule},
{NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule},
{NET_DATALINK_MODULE_NAME, (module_info**)&sDatalinkModule},
{NET_SOCKET_MODULE_NAME, (module_info**)&sSocketModule},
{}
};
module_info* modules[] = {
(module_info*)&gIPv6Module,
NULL
};
↑ V512 A call of the 'memcpy' function will lead to a buffer overflow.
↑ V512 A call of the 'memcpy' function will lead to a buffer overflow.