Author: anevilyak Date: 2011-04-29 00:07:39 +0200 (Fri, 29 Apr 2011) New Revision: 41291 Changeset: https://dev.haiku-os.org/changeset/41291 Modified: haiku/trunk/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp haiku/trunk/src/servers/net/AutoconfigLooper.cpp haiku/trunk/src/servers/net/AutoconfigLooper.h haiku/trunk/src/servers/net/Jamfile haiku/trunk/src/servers/net/NetServer.cpp Log: 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. Modified: haiku/trunk/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp 2011-04-28 19:28:02 UTC (rev 41290) +++ haiku/trunk/src/add-ons/kernel/network/protocols/ipv6/ipv6.cpp 2011-04-28 22:07:39 UTC (rev 41291) @@ -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 @@ 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 @@ }; -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_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 @@ 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 @@ 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 @@ } +// #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 @@ 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,7 +504,192 @@ } +/*! 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 @@ 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 @@ 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 @@ 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,10 +1465,23 @@ 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 @@ 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)) { Modified: haiku/trunk/src/servers/net/AutoconfigLooper.cpp =================================================================== --- haiku/trunk/src/servers/net/AutoconfigLooper.cpp 2011-04-28 19:28:02 UTC (rev 41290) +++ haiku/trunk/src/servers/net/AutoconfigLooper.cpp 2011-04-28 22:07:39 UTC (rev 41291) @@ -33,6 +33,7 @@ fDevice(device), fCurrentClient(NULL) { + memset(fCurrentMac, 0, sizeof(fCurrentMac)); BMessage ready(kMsgReadyToRun); PostMessage(&ready); } @@ -56,7 +57,7 @@ void -AutoconfigLooper::_Configure() +AutoconfigLooper::_ConfigureIPv4() { // start with DHCP @@ -119,11 +120,157 @@ } +#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 @@ 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: Modified: haiku/trunk/src/servers/net/AutoconfigLooper.h =================================================================== --- haiku/trunk/src/servers/net/AutoconfigLooper.h 2011-04-28 19:28:02 UTC (rev 41290) +++ haiku/trunk/src/servers/net/AutoconfigLooper.h 2011-04-28 22:07:39 UTC (rev 41291) @@ -12,6 +12,7 @@ #include <Looper.h> #include <Messenger.h> #include <String.h> +#include <netinet6/in6.h> class AutoconfigClient; @@ -27,12 +28,16 @@ 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 Modified: haiku/trunk/src/servers/net/Jamfile =================================================================== --- haiku/trunk/src/servers/net/Jamfile 2011-04-28 19:28:02 UTC (rev 41290) +++ haiku/trunk/src/servers/net/Jamfile 2011-04-28 22:07:39 UTC (rev 41291) @@ -10,6 +10,11 @@ #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 : Modified: haiku/trunk/src/servers/net/NetServer.cpp =================================================================== --- haiku/trunk/src/servers/net/NetServer.cpp 2011-04-28 19:28:02 UTC (rev 41290) +++ haiku/trunk/src/servers/net/NetServer.cpp 2011-04-28 22:07:39 UTC (rev 41291) @@ -102,6 +102,13 @@ }; +// 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 @@ } +#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 -