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:
Rene Gollent 2011-04-28 22:07:39 +00:00
parent a0bc939256
commit 8a9d5e34fe
5 changed files with 731 additions and 24 deletions

View File

@ -24,6 +24,7 @@
#include <KernelExport.h>
#include <util/AutoLock.h>
#include <util/list.h>
#include <util/khash.h>
#include <util/DoublyLinkedList.h>
#include <util/MultiHashTable.h>
@ -50,6 +51,12 @@
#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;
@ -58,9 +65,53 @@ struct IPv6Header {
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; }
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
: public DoublyLinkedListLinkImpl<RawSocket>, public DatagramSocket<> {
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;
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_buffer_module_info* gBufferModule;
@ -125,6 +176,9 @@ static net_datalink_module_info* sDatalinkModule;
static net_socket_module_info* sSocketModule;
static RawSocketList sRawSockets;
static mutex sRawSocketsLock;
static mutex sFragmentLock;
static hash_table* sFragmentHash;
static int32 sFragmentID;
static mutex sMulticastGroupsLock;
typedef MultiHashTable<MulticastStateHash> MulticastState;
@ -135,18 +189,19 @@ static mutex sReceivingProtocolLock;
uint16
IPv6Header::GetTransportHeaderOffset(net_buffer* buffer) const
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 == IPPROTO_HOPOPTS
|| next == IPPROTO_ROUTING
|| next == IPPROTO_FRAGMENT
|| next == IPPROTO_ESP
|| next == IPPROTO_AH
|| next == IPPROTO_DSTOPTS) {
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));
@ -157,6 +212,17 @@ IPv6Header::GetTransportHeaderOffset(net_buffer* buffer) const
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;
}
@ -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
MulticastStateHash::HashKey(const KeyType &key) const
{
@ -191,7 +495,7 @@ dump_ipv6_header(IPv6Header &header)
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.header.ip6_nxt);
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",
@ -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
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
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;
if (buffer->size > mtu) {
// TODO: we need to fragment the packet
return EMSGSIZE;
// we need to fragment the packet
return send_fragments(protocol, route, buffer, mtu);
}
return sDatalinkModule->send_routed_data(route, buffer);
@ -951,7 +1440,7 @@ ipv6_receive_data(net_buffer* buffer)
buffer->destination, &buffer->interface_address)) {
char srcbuf[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.Dst(), dstbuf));
@ -968,7 +1457,7 @@ ipv6_receive_data(net_buffer* buffer)
memcpy(buffer->destination, &destination, sizeof(sockaddr_in6));
// get the transport protocol and transport header offset
uint16 transportHeaderOffset = header.GetTransportHeaderOffset(buffer);
uint16 transportHeaderOffset = header.GetHeaderOffset(buffer);
uint8 protocol = buffer->protocol;
// remove any trailing/padding data
@ -976,9 +1465,22 @@ ipv6_receive_data(net_buffer* buffer)
if (status != B_OK)
return status;
//
// TODO: check for fragmentation
//
// 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);
@ -1047,8 +1549,8 @@ ipv6_process_ancillary_data_no_container(net_protocol* _protocol,
if (msgControlLen < CMSG_SPACE(sizeof(int)))
return B_NO_MEMORY;
// '255' is the default value to use when extracting the real one fails
int hopLimit = 255;
// use some default value (64 at the moment) when extracting the real one fails
int hopLimit = IPV6_DEFHLIM;
if (gBufferModule->stored_header_length(buffer)
>= (int)sizeof(ip6_hdr)) {

View File

@ -33,6 +33,7 @@ AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device)
fDevice(device),
fCurrentClient(NULL)
{
memset(fCurrentMac, 0, sizeof(fCurrentMac));
BMessage ready(kMsgReadyToRun);
PostMessage(&ready);
}
@ -56,7 +57,7 @@ AutoconfigLooper::_RemoveClient()
void
AutoconfigLooper::_Configure()
AutoconfigLooper::_ConfigureIPv4()
{
// 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
AutoconfigLooper::_ReadyToRun()
{
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) {
// Reconfigure the interface when we have a link again
_Configure();
_ConfigureIPv4();
}
#ifdef INET6
_ConfigureIPv6LinkLocal((media & IFM_ACTIVE) != 0);
#endif
break;
default:

View File

@ -12,6 +12,7 @@
#include <Looper.h>
#include <Messenger.h>
#include <String.h>
#include <netinet6/in6.h>
class AutoconfigClient;
@ -27,12 +28,16 @@ public:
private:
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();
BMessenger fTarget;
BString fDevice;
AutoconfigClient* fCurrentClient;
uint8 fCurrentMac[6];
};
#endif // AUTOCONFIG_LOOPER_H

View File

@ -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 tests kits net DialUpPreflet ] ;
#local defines = [ FDefines INET6=1 ] ;
SubDirCcFlags $(defines) ;
SubDirC++Flags $(defines) ;
AddResources net_server : net_server.rdef ;
Server net_server :

View File

@ -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[] = {
{
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 -