Patch by Atis Elsts from GSoC 2010 that was overlooked.
- Adds IPv6 fragmentation support and some partial work to enable configuration of IPv6 in net_server. Not currently in the build. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41291 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
a0bc939256
commit
8a9d5e34fe
@ -24,6 +24,7 @@
|
|||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
#include <util/AutoLock.h>
|
#include <util/AutoLock.h>
|
||||||
#include <util/list.h>
|
#include <util/list.h>
|
||||||
|
#include <util/khash.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <util/MultiHashTable.h>
|
#include <util/MultiHashTable.h>
|
||||||
|
|
||||||
@ -50,6 +51,12 @@
|
|||||||
#endif
|
#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 IPv6Header {
|
||||||
struct ip6_hdr header;
|
struct ip6_hdr header;
|
||||||
|
|
||||||
@ -58,9 +65,53 @@ struct IPv6Header {
|
|||||||
uint16 PayloadLength() const { return ntohs(header.ip6_plen); }
|
uint16 PayloadLength() const { return ntohs(header.ip6_plen); }
|
||||||
const in6_addr& Dst() const { return header.ip6_dst; }
|
const in6_addr& Dst() const { return header.ip6_dst; }
|
||||||
const in6_addr& Src() const { return header.ip6_src; }
|
const in6_addr& Src() const { return header.ip6_src; }
|
||||||
uint16 GetTransportHeaderOffset(net_buffer* buffer) const;
|
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;
|
||||||
|
uint32 id;
|
||||||
|
// using u32 for the field allows to feed the whole structure in hash func.
|
||||||
|
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; }
|
||||||
|
|
||||||
|
static uint32 Hash(void* _packet, const void* _key,
|
||||||
|
uint32 range);
|
||||||
|
static int Compare(void* _packet, const void* _key);
|
||||||
|
static int32 NextOffset()
|
||||||
|
{ return offsetof(FragmentPacket, fNext); }
|
||||||
|
static void StaleTimer(struct net_timer* timer, void* data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FragmentPacket* fNext;
|
||||||
|
struct ipv6_packet_key fKey;
|
||||||
|
bool fReceivedLastFragment;
|
||||||
|
int32 fBytesLeft;
|
||||||
|
FragmentList fFragments;
|
||||||
|
net_timer fTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class RawSocket
|
class RawSocket
|
||||||
: public DoublyLinkedListLinkImpl<RawSocket>, public DatagramSocket<> {
|
: public DoublyLinkedListLinkImpl<RawSocket>, public DatagramSocket<> {
|
||||||
public:
|
public:
|
||||||
@ -110,12 +161,12 @@ struct ipv6_protocol : net_protocol {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static const int kDefaultTTL = 254;
|
static const int kDefaultTTL = IPV6_DEFHLIM;
|
||||||
static const int kDefaultMulticastTTL = 1;
|
static const int kDefaultMulticastTTL = 1;
|
||||||
|
|
||||||
|
|
||||||
extern net_protocol_module_info gIPv6Module;
|
extern net_protocol_module_info gIPv6Module;
|
||||||
// we need this in ipv6_std_ops() for registering the AF_INET domain
|
// we need this in ipv6_std_ops() for registering the AF_INET6 domain
|
||||||
|
|
||||||
net_stack_module_info* gStackModule;
|
net_stack_module_info* gStackModule;
|
||||||
net_buffer_module_info* gBufferModule;
|
net_buffer_module_info* gBufferModule;
|
||||||
@ -125,6 +176,9 @@ static net_datalink_module_info* sDatalinkModule;
|
|||||||
static net_socket_module_info* sSocketModule;
|
static net_socket_module_info* sSocketModule;
|
||||||
static RawSocketList sRawSockets;
|
static RawSocketList sRawSockets;
|
||||||
static mutex sRawSocketsLock;
|
static mutex sRawSocketsLock;
|
||||||
|
static mutex sFragmentLock;
|
||||||
|
static hash_table* sFragmentHash;
|
||||||
|
static int32 sFragmentID;
|
||||||
static mutex sMulticastGroupsLock;
|
static mutex sMulticastGroupsLock;
|
||||||
|
|
||||||
typedef MultiHashTable<MulticastStateHash> MulticastState;
|
typedef MultiHashTable<MulticastStateHash> MulticastState;
|
||||||
@ -135,18 +189,19 @@ static mutex sReceivingProtocolLock;
|
|||||||
|
|
||||||
|
|
||||||
uint16
|
uint16
|
||||||
IPv6Header::GetTransportHeaderOffset(net_buffer* buffer) const
|
IPv6Header::GetHeaderOffset(net_buffer* buffer, uint32 headerCode) const
|
||||||
{
|
{
|
||||||
uint16 offset = sizeof(struct ip6_hdr);
|
uint16 offset = sizeof(struct ip6_hdr);
|
||||||
uint8 next = header.ip6_nxt;
|
uint8 next = header.ip6_nxt;
|
||||||
|
|
||||||
// these are the extension headers that might be supported one day
|
// these are the extension headers that might be supported one day
|
||||||
while (next == IPPROTO_HOPOPTS
|
while (next != headerCode
|
||||||
|| next == IPPROTO_ROUTING
|
&& (next == IPPROTO_HOPOPTS
|
||||||
|| next == IPPROTO_FRAGMENT
|
|| next == IPPROTO_ROUTING
|
||||||
|| next == IPPROTO_ESP
|
|| next == IPPROTO_FRAGMENT
|
||||||
|| next == IPPROTO_AH
|
|| next == IPPROTO_ESP
|
||||||
|| next == IPPROTO_DSTOPTS) {
|
|| next == IPPROTO_AH
|
||||||
|
|| next == IPPROTO_DSTOPTS)) {
|
||||||
struct ip6_ext extensionHeader;
|
struct ip6_ext extensionHeader;
|
||||||
status_t status = gBufferModule->read(buffer, offset,
|
status_t status = gBufferModule->read(buffer, offset,
|
||||||
&extensionHeader, sizeof(ip6_ext));
|
&extensionHeader, sizeof(ip6_ext));
|
||||||
@ -157,6 +212,17 @@ IPv6Header::GetTransportHeaderOffset(net_buffer* buffer) const
|
|||||||
offset += extensionHeader.ip6e_len;
|
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;
|
buffer->protocol = next;
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
@ -169,6 +235,244 @@ RawSocket::RawSocket(net_socket* socket)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
FragmentPacket::FragmentPacket(const ipv6_packet_key &key)
|
||||||
|
:
|
||||||
|
fKey(key),
|
||||||
|
fReceivedLastFragment(false),
|
||||||
|
fBytesLeft(IPV6_MAXPACKET)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
FragmentPacket::Compare(void* _packet, const void* _key)
|
||||||
|
{
|
||||||
|
const ipv6_packet_key* key = (ipv6_packet_key*)_key;
|
||||||
|
ipv6_packet_key* packetKey = &((FragmentPacket*)_packet)->fKey;
|
||||||
|
|
||||||
|
return memcmp(packetKey, key, sizeof(ipv6_packet_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32
|
||||||
|
FragmentPacket::Hash(void* _packet, const void* _key, uint32 range)
|
||||||
|
{
|
||||||
|
const struct ipv6_packet_key* key = (struct ipv6_packet_key*)_key;
|
||||||
|
FragmentPacket* packet = (FragmentPacket*)_packet;
|
||||||
|
if (packet != NULL)
|
||||||
|
key = &packet->fKey;
|
||||||
|
|
||||||
|
return jenkins_hashword((const uint32*)key,
|
||||||
|
sizeof(ipv6_packet_key) / sizeof(uint32), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
FragmentPacket::StaleTimer(struct net_timer* timer, void* data)
|
||||||
|
{
|
||||||
|
FragmentPacket* packet = (FragmentPacket*)data;
|
||||||
|
TRACE("Assembling FragmentPacket %p timed out!", packet);
|
||||||
|
|
||||||
|
MutexLocker locker(&sFragmentLock);
|
||||||
|
hash_remove(sFragmentHash, 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
|
size_t
|
||||||
MulticastStateHash::HashKey(const KeyType &key) const
|
MulticastStateHash::HashKey(const KeyType &key) const
|
||||||
{
|
{
|
||||||
@ -191,7 +495,7 @@ dump_ipv6_header(IPv6Header &header)
|
|||||||
dprintf(" version: %d\n", header.ProtocolVersion() >> 4);
|
dprintf(" version: %d\n", header.ProtocolVersion() >> 4);
|
||||||
dprintf(" service_type: %d\n", header.ServiceType());
|
dprintf(" service_type: %d\n", header.ServiceType());
|
||||||
dprintf(" payload_length: %d\n", header.PayloadLength());
|
dprintf(" payload_length: %d\n", header.PayloadLength());
|
||||||
dprintf(" next_header: %d\n", header.header.ip6_nxt);
|
dprintf(" next_header: %d\n", header.NextHeader());
|
||||||
dprintf(" hop_limit: %d\n", header.header.ip6_hops);
|
dprintf(" hop_limit: %d\n", header.header.ip6_hops);
|
||||||
dprintf(" source: %s\n", ip6_sprintf(&header.header.ip6_src, addrbuf));
|
dprintf(" source: %s\n", ip6_sprintf(&header.header.ip6_src, addrbuf));
|
||||||
dprintf(" destination: %s\n",
|
dprintf(" destination: %s\n",
|
||||||
@ -200,6 +504,191 @@ dump_ipv6_header(IPv6Header &header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! 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 = (FragmentPacket*)hash_lookup(sFragmentHash, &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 = hash_insert(sFragmentHash, 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()) {
|
||||||
|
hash_remove(sFragmentHash, 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(%lu bytes, mtu %lu)", 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;
|
||||||
|
|
||||||
|
uint8 data[bytesLeft];
|
||||||
|
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 %ld, bytesLeft %ld", 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 %ld bytes (%ld 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
|
static status_t
|
||||||
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
|
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
|
||||||
bool deliverToRaw, net_interface *interface)
|
bool deliverToRaw, net_interface *interface)
|
||||||
@ -811,8 +1300,8 @@ ipv6_send_routed_data(net_protocol* _protocol, struct net_route* route,
|
|||||||
|
|
||||||
uint32 mtu = route->mtu ? route->mtu : interface->mtu;
|
uint32 mtu = route->mtu ? route->mtu : interface->mtu;
|
||||||
if (buffer->size > mtu) {
|
if (buffer->size > mtu) {
|
||||||
// TODO: we need to fragment the packet
|
// we need to fragment the packet
|
||||||
return EMSGSIZE;
|
return send_fragments(protocol, route, buffer, mtu);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sDatalinkModule->send_routed_data(route, buffer);
|
return sDatalinkModule->send_routed_data(route, buffer);
|
||||||
@ -951,7 +1440,7 @@ ipv6_receive_data(net_buffer* buffer)
|
|||||||
buffer->destination, &buffer->interface_address)) {
|
buffer->destination, &buffer->interface_address)) {
|
||||||
char srcbuf[INET6_ADDRSTRLEN];
|
char srcbuf[INET6_ADDRSTRLEN];
|
||||||
char dstbuf[INET6_ADDRSTRLEN];
|
char dstbuf[INET6_ADDRSTRLEN];
|
||||||
TRACE(" ipv4_receive_data(): packet was not for us %s -> %s",
|
TRACE(" ipv6_receive_data(): packet was not for us %s -> %s",
|
||||||
ip6_sprintf(&header.Src(), srcbuf),
|
ip6_sprintf(&header.Src(), srcbuf),
|
||||||
ip6_sprintf(&header.Dst(), dstbuf));
|
ip6_sprintf(&header.Dst(), dstbuf));
|
||||||
|
|
||||||
@ -968,7 +1457,7 @@ ipv6_receive_data(net_buffer* buffer)
|
|||||||
memcpy(buffer->destination, &destination, sizeof(sockaddr_in6));
|
memcpy(buffer->destination, &destination, sizeof(sockaddr_in6));
|
||||||
|
|
||||||
// get the transport protocol and transport header offset
|
// get the transport protocol and transport header offset
|
||||||
uint16 transportHeaderOffset = header.GetTransportHeaderOffset(buffer);
|
uint16 transportHeaderOffset = header.GetHeaderOffset(buffer);
|
||||||
uint8 protocol = buffer->protocol;
|
uint8 protocol = buffer->protocol;
|
||||||
|
|
||||||
// remove any trailing/padding data
|
// remove any trailing/padding data
|
||||||
@ -976,9 +1465,22 @@ ipv6_receive_data(net_buffer* buffer)
|
|||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
//
|
// check for fragmentation
|
||||||
// TODO: 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
|
// tell the buffer to preserve removed ipv6 header - may need it later
|
||||||
gBufferModule->store_header(buffer);
|
gBufferModule->store_header(buffer);
|
||||||
@ -1047,8 +1549,8 @@ ipv6_process_ancillary_data_no_container(net_protocol* _protocol,
|
|||||||
if (msgControlLen < CMSG_SPACE(sizeof(int)))
|
if (msgControlLen < CMSG_SPACE(sizeof(int)))
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
// '255' is the default value to use when extracting the real one fails
|
// use some default value (64 at the moment) when extracting the real one fails
|
||||||
int hopLimit = 255;
|
int hopLimit = IPV6_DEFHLIM;
|
||||||
|
|
||||||
if (gBufferModule->stored_header_length(buffer)
|
if (gBufferModule->stored_header_length(buffer)
|
||||||
>= (int)sizeof(ip6_hdr)) {
|
>= (int)sizeof(ip6_hdr)) {
|
||||||
|
@ -33,6 +33,7 @@ AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device)
|
|||||||
fDevice(device),
|
fDevice(device),
|
||||||
fCurrentClient(NULL)
|
fCurrentClient(NULL)
|
||||||
{
|
{
|
||||||
|
memset(fCurrentMac, 0, sizeof(fCurrentMac));
|
||||||
BMessage ready(kMsgReadyToRun);
|
BMessage ready(kMsgReadyToRun);
|
||||||
PostMessage(&ready);
|
PostMessage(&ready);
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ AutoconfigLooper::_RemoveClient()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
AutoconfigLooper::_Configure()
|
AutoconfigLooper::_ConfigureIPv4()
|
||||||
{
|
{
|
||||||
// start with DHCP
|
// start with DHCP
|
||||||
|
|
||||||
@ -119,11 +120,157 @@ AutoconfigLooper::_Configure()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef INET6
|
||||||
|
static in6_addr
|
||||||
|
BuildIPv6LinkLocalAddress(uint8 mac[6])
|
||||||
|
{
|
||||||
|
in6_addr result;
|
||||||
|
|
||||||
|
result.s6_addr[0] = 0xfe;
|
||||||
|
result.s6_addr[1] = 0x80;
|
||||||
|
result.s6_addr[2] = 0;
|
||||||
|
result.s6_addr[3] = 0;
|
||||||
|
result.s6_addr[4] = 0;
|
||||||
|
result.s6_addr[5] = 0;
|
||||||
|
result.s6_addr[6] = 0;
|
||||||
|
result.s6_addr[7] = 0;
|
||||||
|
|
||||||
|
result.s6_addr[8] = mac[0] ^ 0x02;
|
||||||
|
result.s6_addr[9] = mac[1];
|
||||||
|
result.s6_addr[10] = mac[2];
|
||||||
|
result.s6_addr[11] = 0xff;
|
||||||
|
result.s6_addr[12] = 0xfe;
|
||||||
|
result.s6_addr[13] = mac[3];
|
||||||
|
result.s6_addr[14] = mac[4];
|
||||||
|
result.s6_addr[15] = mac[5];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AutoconfigLooper::_ConfigureIPv6LinkLocal(bool add)
|
||||||
|
{
|
||||||
|
// do not touch the loopback device
|
||||||
|
if (!strncmp(fDevice.String(), "loop", 4))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ifreq request;
|
||||||
|
if (!prepare_request(request, fDevice.String()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int socket = ::socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
if (socket < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set IFF_CONFIGURING flag on interface
|
||||||
|
if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) {
|
||||||
|
request.ifr_flags |= IFF_CONFIGURING;
|
||||||
|
ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 mac[6];
|
||||||
|
memcpy(mac, fCurrentMac, 6);
|
||||||
|
if (add == true) {
|
||||||
|
if (get_mac_address(fDevice.String(), mac) != B_OK)
|
||||||
|
add = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add == true) {
|
||||||
|
in6_addr inetAddress = BuildIPv6LinkLocalAddress(mac);
|
||||||
|
if (_AddIPv6LinkLocal(socket, inetAddress) != true)
|
||||||
|
add = false;
|
||||||
|
|
||||||
|
// save the MAC address for later usage
|
||||||
|
memcpy(fCurrentMac, mac, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add == false) {
|
||||||
|
static const uint8 zeroMac[6] = {0};
|
||||||
|
if (memcmp(fCurrentMac, zeroMac, 6)) {
|
||||||
|
in6_addr inetAddress = BuildIPv6LinkLocalAddress(fCurrentMac);
|
||||||
|
_RemoveIPv6LinkLocal(socket, inetAddress);
|
||||||
|
// reset the stored MAC address
|
||||||
|
memcpy(fCurrentMac, zeroMac, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0
|
||||||
|
&& (request.ifr_flags & IFF_CONFIGURING) == 0) {
|
||||||
|
// Someone else configured the interface in the mean time
|
||||||
|
close(socket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
AutoconfigLooper::_AddIPv6LinkLocal(int socket, const in6_addr &address)
|
||||||
|
{
|
||||||
|
struct ifreq request;
|
||||||
|
if (!prepare_request(request, fDevice.String()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ifaliasreq aliasRequest;
|
||||||
|
memset(&aliasRequest, 0, sizeof(ifaliasreq));
|
||||||
|
strlcpy(aliasRequest.ifra_name, fDevice.String(), IF_NAMESIZE);
|
||||||
|
aliasRequest.ifra_addr.ss_len = sizeof(sockaddr_in6);
|
||||||
|
aliasRequest.ifra_addr.ss_family = AF_INET6;
|
||||||
|
|
||||||
|
if (ioctl(socket, SIOCAIFADDR, &aliasRequest, sizeof(ifaliasreq)) < 0) {
|
||||||
|
if (errno != B_NAME_IN_USE)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in6* socketAddress = (sockaddr_in6*)&request.ifr_addr;
|
||||||
|
socketAddress->sin6_len = sizeof(sockaddr_in6);
|
||||||
|
socketAddress->sin6_family = AF_INET6;
|
||||||
|
|
||||||
|
// address
|
||||||
|
memcpy(&socketAddress->sin6_addr, &address, sizeof(in6_addr));
|
||||||
|
if (ioctl(socket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// mask (/64)
|
||||||
|
memset(socketAddress->sin6_addr.s6_addr, 0xff, 8);
|
||||||
|
memset(socketAddress->sin6_addr.s6_addr + 8, 0, 8);
|
||||||
|
|
||||||
|
if (ioctl(socket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AutoconfigLooper::_RemoveIPv6LinkLocal(int socket, const in6_addr &address)
|
||||||
|
{
|
||||||
|
struct ifreq request;
|
||||||
|
if (!prepare_request(request, fDevice.String()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sockaddr_in6* socketAddress = (sockaddr_in6*)&request.ifr_addr;
|
||||||
|
socketAddress->sin6_len = sizeof(sockaddr_in6);
|
||||||
|
socketAddress->sin6_family = AF_INET6;
|
||||||
|
|
||||||
|
// address
|
||||||
|
memcpy(&socketAddress->sin6_addr, &address, sizeof(in6_addr));
|
||||||
|
if (ioctl(socket, SIOCDIFADDR, &request, sizeof(struct ifreq)) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // INET6
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
AutoconfigLooper::_ReadyToRun()
|
AutoconfigLooper::_ReadyToRun()
|
||||||
{
|
{
|
||||||
start_watching_network(B_WATCH_NETWORK_LINK_CHANGES, this);
|
start_watching_network(B_WATCH_NETWORK_LINK_CHANGES, this);
|
||||||
_Configure();
|
_ConfigureIPv4();
|
||||||
|
#ifdef INET6
|
||||||
|
_ConfigureIPv6LinkLocal(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -148,8 +295,11 @@ AutoconfigLooper::MessageReceived(BMessage* message)
|
|||||||
|
|
||||||
if ((media & IFM_ACTIVE) != 0) {
|
if ((media & IFM_ACTIVE) != 0) {
|
||||||
// Reconfigure the interface when we have a link again
|
// Reconfigure the interface when we have a link again
|
||||||
_Configure();
|
_ConfigureIPv4();
|
||||||
}
|
}
|
||||||
|
#ifdef INET6
|
||||||
|
_ConfigureIPv6LinkLocal((media & IFM_ACTIVE) != 0);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <Looper.h>
|
#include <Looper.h>
|
||||||
#include <Messenger.h>
|
#include <Messenger.h>
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
#include <netinet6/in6.h>
|
||||||
|
|
||||||
class AutoconfigClient;
|
class AutoconfigClient;
|
||||||
|
|
||||||
@ -27,12 +28,16 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void _RemoveClient();
|
void _RemoveClient();
|
||||||
void _Configure();
|
void _ConfigureIPv4();
|
||||||
|
void _ConfigureIPv6LinkLocal(bool add);
|
||||||
|
bool _AddIPv6LinkLocal(int socket, const in6_addr &);
|
||||||
|
void _RemoveIPv6LinkLocal(int socket, const in6_addr &);
|
||||||
void _ReadyToRun();
|
void _ReadyToRun();
|
||||||
|
|
||||||
BMessenger fTarget;
|
BMessenger fTarget;
|
||||||
BString fDevice;
|
BString fDevice;
|
||||||
AutoconfigClient* fCurrentClient;
|
AutoconfigClient* fCurrentClient;
|
||||||
|
uint8 fCurrentMac[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUTOCONFIG_LOOPER_H
|
#endif // AUTOCONFIG_LOOPER_H
|
||||||
|
@ -10,6 +10,11 @@ UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_wlan ] : true ;
|
|||||||
#UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel network ppp shared libkernelppp headers ] ;
|
#UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel network ppp shared libkernelppp headers ] ;
|
||||||
#UseHeaders [ FDirName $(HAIKU_TOP) src tests kits net DialUpPreflet ] ;
|
#UseHeaders [ FDirName $(HAIKU_TOP) src tests kits net DialUpPreflet ] ;
|
||||||
|
|
||||||
|
#local defines = [ FDefines INET6=1 ] ;
|
||||||
|
|
||||||
|
SubDirCcFlags $(defines) ;
|
||||||
|
SubDirC++Flags $(defines) ;
|
||||||
|
|
||||||
AddResources net_server : net_server.rdef ;
|
AddResources net_server : net_server.rdef ;
|
||||||
|
|
||||||
Server net_server :
|
Server net_server :
|
||||||
|
@ -102,6 +102,13 @@ struct address_family {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// AF_INET6 family
|
||||||
|
#if INET6
|
||||||
|
static bool inet6_parse_address(const char* string, sockaddr* address);
|
||||||
|
static void inet6_set_any_address(sockaddr* address);
|
||||||
|
static void inet6_set_port(sockaddr* address, int32 port);
|
||||||
|
#endif
|
||||||
|
|
||||||
static const address_family kFamilies[] = {
|
static const address_family kFamilies[] = {
|
||||||
{
|
{
|
||||||
AF_INET,
|
AF_INET,
|
||||||
@ -215,6 +222,44 @@ parse_address(int32& family, const char* argument, BNetworkAddress& address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if INET6
|
||||||
|
static bool
|
||||||
|
inet6_parse_address(const char* string, sockaddr* _address)
|
||||||
|
{
|
||||||
|
sockaddr_in6& address = *(sockaddr_in6*)_address;
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET6, string, &address.sin6_addr) != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
address.sin6_family = AF_INET6;
|
||||||
|
address.sin6_len = sizeof(sockaddr_in6);
|
||||||
|
address.sin6_port = 0;
|
||||||
|
address.sin6_flowinfo = 0;
|
||||||
|
address.sin6_scope_id = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
inet6_set_any_address(sockaddr* _address)
|
||||||
|
{
|
||||||
|
sockaddr_in6& address = *(sockaddr_in6*)_address;
|
||||||
|
memset(&address, 0, sizeof(sockaddr_in6));
|
||||||
|
address.sin6_family = AF_INET6;
|
||||||
|
address.sin6_len = sizeof(struct sockaddr_in6);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
inet6_set_port(sockaddr* _address, int32 port)
|
||||||
|
{
|
||||||
|
sockaddr_in6& address = *(sockaddr_in6*)_address;
|
||||||
|
address.sin6_port = port;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user