1/*
2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Atis Elsts, the.kfx@gmail.com
7 */
8
9
10#include <net_datalink.h>
11#include <net_datalink_protocol.h>
12#include <net_device.h>
13#include <net_stack.h>
14#include <net_protocol.h>
15#include <NetBufferUtilities.h>
16
17#include <generic_syscall.h>
18#include <util/atomic.h>
19#include <util/AutoLock.h>
20#include <util/DoublyLinkedList.h>
21#include <util/OpenHashTable.h>
22#include <KernelExport.h>
23
24#include <netinet/in.h>
25#include <netinet6/in6.h>
26#include <netinet/icmp6.h>
27#include <net/ethernet.h>
28#include <net/if.h>
29#include <net/if_types.h>
30#include <net/if_dl.h>
31#include <sys/sockio.h>
32#include <new>
33
34#include <ipv6/jenkins.h>
35#include <ipv6/ipv6_address.h>
36#include "ndp.h"
37
38
39//#define TRACE_NDP
40#ifdef TRACE_NDP
41#	define TRACE(x) dprintf x
42#else
43#	define TRACE(x) ;
44#endif
45
46
47struct ipv6_datalink_protocol : net_datalink_protocol {
48	sockaddr_dl	hardware_address;
49	in6_addr	local_address;
50};
51
52
53static void ndp_timer(struct net_timer* timer, void* data);
54
55
56net_buffer_module_info* gBufferModule;
57static net_stack_module_info* sStackModule;
58static net_datalink_module_info* sDatalinkModule;
59static net_protocol_module_info* sIPv6Module;
60static net_protocol* sIPv6Protocol;
61static mutex sCacheLock;
62static const net_buffer* kDeletedBuffer = (net_buffer*)~0;
63
64// needed for IN6_IS_ADDR_UNSPECIFIED() macro
65const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
66
67//	#pragma mark -
68
69
70struct neighbor_discovery_header {
71	uint8		icmp6_type;
72	uint8		icmp6_code;
73	uint16		icmp6_checksum;
74	uint32		flags;
75	in6_addr	target_address;
76
77	// This part is specific for Ethernet;
78	// also, theoretically there could be more than one option.
79	uint8		option_type;
80	uint8		option_length;
81	uint8		link_address[ETHER_ADDRESS_LENGTH];
82} _PACKED;
83
84struct router_advertisement_header {
85	uint8		icmp6_type;
86	uint8		icmp6_code;
87	uint16		icmp6_checksum;
88	uint8		hop_limit;
89	uint8		flags;
90	uint16		router_lifetime;
91	uint32		reachable_time;
92	uint32		retransmit_timer;
93	uint8		options[0];
94} _PACKED;
95
96
97struct ndp_entry {
98	ndp_entry*	next;
99	in6_addr	protocol_address;
100	sockaddr_dl	hardware_address;
101	uint32		flags;
102	net_buffer*	request_buffer;
103	net_timer	timer;
104	uint32		timer_state;
105	bigtime_t	timestamp;
106	net_datalink_protocol* protocol;
107
108	typedef DoublyLinkedListCLink<net_buffer> NetBufferLink;
109	typedef DoublyLinkedList<net_buffer, NetBufferLink> BufferList;
110
111	BufferList  queue;
112
113	static ndp_entry* Lookup(const in6_addr& protocolAddress);
114	static ndp_entry* Add(const in6_addr& protocolAddress,
115		sockaddr_dl* hardwareAddress, uint32 flags);
116
117	~ndp_entry();
118
119	void ClearQueue();
120	void MarkFailed();
121	void MarkValid();
122	void ScheduleRemoval();
123};
124
125
126struct ndpHash {
127	typedef in6_addr KeyType;
128	typedef ndp_entry ValueType;
129
130	size_t HashKey(in6_addr key) const
131	{
132		return jenkins_hashword((const uint32*)&(key),
133			sizeof(in6_addr) / sizeof(uint32), 0);
134	}
135
136	size_t Hash(ndp_entry* value) const
137	{
138		return HashKey(value->protocol_address);
139	}
140
141	bool Compare(in6_addr key, ndp_entry* value) const
142	{
143		return value->protocol_address == key;
144	}
145
146	ndp_entry*& GetLink(ndp_entry* value) const
147	{
148		return value->next;
149	}
150};
151
152
153typedef BOpenHashTable<ndpHash> AddressCache;
154static AddressCache* sCache;
155
156
157#define NDP_FLAG_LOCAL		0x01
158#define NDP_FLAG_REJECT		0x02
159#define NDP_FLAG_PERMANENT	0x04
160#define NDP_FLAG_PUBLISH	0x08
161#define NDP_FLAG_VALID		0x10
162
163#define NDP_FLAG_REMOVED			0x00010000
164#define NDP_PUBLIC_FLAG_MASK		0x0000ffff
165
166#define NDP_NO_STATE				0
167#define NDP_STATE_REQUEST			1
168#define NDP_STATE_LAST_REQUEST		5
169#define NDP_STATE_REQUEST_FAILED	6
170#define NDP_STATE_REMOVE_FAILED		7
171#define NDP_STATE_STALE				8
172
173#define NDP_STALE_TIMEOUT	30 * 60000000LL		// 30 minutes
174#define NDP_REJECT_TIMEOUT	20000000LL			// 20 seconds
175#define NDP_REQUEST_TIMEOUT	1000000LL			// 1 second
176
177
178//	#pragma mark -
179
180
181static void
182ipv6_to_ether_multicast(sockaddr_dl* destination, const sockaddr_in6* source)
183{
184	// To send an IPv6 multicast packet over Ethernet,
185	// take the last 32 bits of the destination IPv6 address,
186	// prepend 33-33- and use that as the destination Ethernet address.
187
188	destination->sdl_len = sizeof(sockaddr_dl);
189	destination->sdl_family = AF_LINK;
190	destination->sdl_index = 0;
191	destination->sdl_type = IFT_ETHER;
192	destination->sdl_e_type = htons(ETHER_TYPE_IPV6);
193	destination->sdl_nlen = destination->sdl_slen = 0;
194	destination->sdl_alen = ETHER_ADDRESS_LENGTH;
195
196	destination->sdl_data[0] = 0x33;
197	destination->sdl_data[1] = 0x33;
198	memcpy(&destination->sdl_data[2], &source->sin6_addr.s6_addr[12], 4);
199}
200
201
202static inline sockaddr*
203ipv6_to_sockaddr(sockaddr_in6* target, const in6_addr& address)
204{
205	target->sin6_family = AF_INET6;
206	target->sin6_len = sizeof(sockaddr_in6);
207	target->sin6_port = 0;
208	target->sin6_flowinfo = 0;
209	target->sin6_scope_id = 0;
210	memcpy(target->sin6_addr.s6_addr, address.s6_addr, sizeof(in6_addr));
211	return (sockaddr*)target;
212}
213
214
215static inline sockaddr*
216ipv6_to_solicited_multicast(sockaddr_in6* target, const in6_addr& address)
217{
218	// The solicited-node multicast address for a given unicast address
219	// is constructed by taking the last three octets of the unicast address
220	// and prepending FF02::1:FF00:0000/104.
221
222	target->sin6_family = AF_INET6;
223	target->sin6_len = sizeof(sockaddr_in6);
224	target->sin6_port = 0;
225	target->sin6_flowinfo = 0;
226	target->sin6_scope_id = 0;
227
228	uint8* targetIPv6 = target->sin6_addr.s6_addr;
229	memset(targetIPv6, 0, sizeof(in6_addr));
230	targetIPv6[0] = 0xff;
231	targetIPv6[1] = 0x02;
232	targetIPv6[11] = 0x01;
233	targetIPv6[12] = 0xff;
234 	memcpy(&targetIPv6[13], &address.s6_addr[13], 3);
235
236	return (sockaddr*)target;
237}
238
239
240//	#pragma mark -
241
242
243static net_buffer*
244get_request_buffer(ndp_entry* entry)
245{
246	net_buffer* buffer = entry->request_buffer;
247	if (buffer == NULL || buffer == kDeletedBuffer)
248		return NULL;
249
250	buffer = atomic_pointer_test_and_set(&entry->request_buffer,
251		(net_buffer*)NULL, buffer);
252	if (buffer == kDeletedBuffer)
253		return NULL;
254
255	return buffer;
256}
257
258
259static void
260put_request_buffer(ndp_entry* entry, net_buffer* buffer)
261{
262	net_buffer* requestBuffer = atomic_pointer_test_and_set(
263		&entry->request_buffer, buffer, (net_buffer*)NULL);
264	if (requestBuffer != NULL) {
265		// someone else took over ownership of the request buffer
266		gBufferModule->free(buffer);
267	}
268}
269
270
271static void
272delete_request_buffer(ndp_entry* entry)
273{
274	net_buffer* buffer = atomic_pointer_get_and_set(&entry->request_buffer,
275		kDeletedBuffer);
276	if (buffer != NULL && buffer != kDeletedBuffer)
277		gBufferModule->free(buffer);
278}
279
280
281ndp_entry*
282ndp_entry::Lookup(const in6_addr& address)
283{
284	return sCache->Lookup(address);
285}
286
287
288ndp_entry*
289ndp_entry::Add(const in6_addr& protocolAddress, sockaddr_dl* hardwareAddress,
290	uint32 flags)
291{
292	ASSERT_LOCKED_MUTEX(&sCacheLock);
293
294	ndp_entry* entry = new (std::nothrow) ndp_entry;
295	if (entry == NULL)
296		return NULL;
297
298	entry->protocol_address = protocolAddress;
299	entry->flags = flags;
300	entry->timestamp = system_time();
301	entry->protocol = NULL;
302	entry->request_buffer = NULL;
303	entry->timer_state = NDP_NO_STATE;
304	sStackModule->init_timer(&entry->timer, ndp_timer, entry);
305
306	if (hardwareAddress != NULL) {
307		// this entry is already resolved
308		entry->hardware_address = *hardwareAddress;
309		entry->hardware_address.sdl_e_type = htons(ETHER_TYPE_IPV6);
310	} else {
311		// this entry still needs to be resolved
312		entry->hardware_address.sdl_alen = 0;
313	}
314	if (entry->hardware_address.sdl_len != sizeof(sockaddr_dl)) {
315		// explicitly set correct length in case our caller hasn't...
316		entry->hardware_address.sdl_len = sizeof(sockaddr_dl);
317	}
318
319	if (sCache->Insert(entry) != B_OK) {
320		// We can delete the entry here with the sCacheLock held, since it's
321		// guaranteed there are no timers pending.
322		delete entry;
323		return NULL;
324	}
325
326	return entry;
327}
328
329
330ndp_entry::~ndp_entry()
331{
332	// make sure there is no active timer left for us
333	sStackModule->cancel_timer(&timer);
334	sStackModule->wait_for_timer(&timer);
335
336	ClearQueue();
337}
338
339
340void
341ndp_entry::ClearQueue()
342{
343	BufferList::Iterator iterator = queue.GetIterator();
344	while (iterator.HasNext()) {
345		net_buffer* buffer = iterator.Next();
346		iterator.Remove();
347		gBufferModule->free(buffer);
348	}
349}
350
351
352void
353ndp_entry::MarkFailed()
354{
355	TRACE(("NDP entry %p Marked as FAILED\n", this));
356
357	flags = (flags & ~NDP_FLAG_VALID) | NDP_FLAG_REJECT;
358	ClearQueue();
359}
360
361
362void
363ndp_entry::MarkValid()
364{
365	TRACE(("NDP entry %p Marked as VALID\n", this));
366
367	flags = (flags & ~NDP_FLAG_REJECT) | NDP_FLAG_VALID;
368
369	BufferList::Iterator iterator = queue.GetIterator();
370	while (iterator.HasNext()) {
371		net_buffer* buffer = iterator.Next();
372		iterator.Remove();
373
374		TRACE(("  NDP Dequeing packet %p...\n", buffer));
375
376		memcpy(buffer->destination, &hardware_address,
377			hardware_address.sdl_len);
378		protocol->next->module->send_data(protocol->next, buffer);
379	}
380}
381
382
383void
384ndp_entry::ScheduleRemoval()
385{
386	// schedule a timer to remove this entry
387	timer_state = NDP_STATE_REMOVE_FAILED;
388	sStackModule->set_timer(&timer, 0);
389}
390
391
392//	#pragma mark -
393
394
395static status_t
396ndp_init()
397{
398	sIPv6Protocol = sIPv6Module->init_protocol(NULL);
399	if (sIPv6Protocol == NULL)
400		return B_NO_MEMORY;
401	sIPv6Protocol->module = sIPv6Module;
402	sIPv6Protocol->socket = NULL;
403	sIPv6Protocol->next = NULL;
404
405	int value = 255;
406	sIPv6Module->setsockopt(sIPv6Protocol, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
407		&value, sizeof(value));
408	sIPv6Module->setsockopt(sIPv6Protocol, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
409		&value, sizeof(value));
410
411	mutex_init(&sCacheLock, "ndp cache");
412
413	sCache = new(std::nothrow) AddressCache();
414	if (sCache == NULL || sCache->Init(64) != B_OK) {
415		mutex_destroy(&sCacheLock);
416		return B_NO_MEMORY;
417	}
418
419	return B_OK;
420}
421
422
423static status_t
424ndp_uninit()
425{
426	if (sIPv6Protocol)
427		sIPv6Module->uninit_protocol(sIPv6Protocol);
428
429	return B_OK;
430}
431
432
433//	#pragma mark -
434
435
436/*!	Updates the entry determined by \a protocolAddress with the specified
437	\a hardwareAddress.
438	If such an entry does not exist yet, a new entry is added. If you try
439	to update a local existing entry but didn't ask for it (by setting
440	\a flags to NDP_FLAG_LOCAL), an error is returned.
441
442	This function does not lock the cache - you have to do it yourself
443	before calling it.
444*/
445status_t
446ndp_update_entry(const in6_addr& protocolAddress, sockaddr_dl* hardwareAddress,
447	uint32 flags, ndp_entry** _entry = NULL)
448{
449	ASSERT_LOCKED_MUTEX(&sCacheLock);
450
451	ndp_entry* entry = ndp_entry::Lookup(protocolAddress);
452	if (entry != NULL) {
453		// We disallow updating of entries that had been resolved before,
454		// but to a different address (only for those that belong to a
455		// specific address - redefining INADDR_ANY is always allowed).
456		// Right now, you have to manually purge the NDP entries (or wait some
457		// time) to let us switch to the new address.
458		if (!IN6_IS_ADDR_UNSPECIFIED(&protocolAddress)
459			&& entry->hardware_address.sdl_alen != 0
460			&& memcmp(LLADDR(&entry->hardware_address),
461				LLADDR(hardwareAddress), ETHER_ADDRESS_LENGTH)) {
462			// TODO: also printf the address
463			dprintf("NDP host updated with different hardware address "
464				"%02x:%02x:%02x:%02x:%02x:%02x.\n",
465				hardwareAddress->sdl_data[0], hardwareAddress->sdl_data[1],
466				hardwareAddress->sdl_data[2], hardwareAddress->sdl_data[3],
467				hardwareAddress->sdl_data[4], hardwareAddress->sdl_data[5]);
468			return B_ERROR;
469		}
470
471		entry->hardware_address = *hardwareAddress;
472		entry->timestamp = system_time();
473	} else {
474		entry = ndp_entry::Add(protocolAddress, hardwareAddress, flags);
475		if (entry == NULL)
476			return B_NO_MEMORY;
477	}
478
479	delete_request_buffer(entry);
480
481	if ((entry->flags & NDP_FLAG_PERMANENT) == 0) {
482		// (re)start the stale timer
483		entry->timer_state = NDP_STATE_STALE;
484		sStackModule->set_timer(&entry->timer, NDP_STALE_TIMEOUT);
485	}
486
487	if ((entry->flags & NDP_FLAG_REJECT) != 0)
488		entry->MarkFailed();
489	else
490		entry->MarkValid();
491
492	if (_entry)
493		*_entry = entry;
494
495	return B_OK;
496}
497
498
499static void
500ndp_remove_local_entry(ipv6_datalink_protocol* protocol, const sockaddr* local,
501	bool updateLocalAddress)
502{
503	in6_addr inetAddress;
504
505	if (local == NULL) {
506		// interface has not yet been set
507		memset(&inetAddress, 0, sizeof(in6_addr));
508	} else {
509		memcpy(&inetAddress, &((sockaddr_in6*)local)->sin6_addr,
510			sizeof(in6_addr));
511
512		// leave the NS multicast address
513		sockaddr_in6 multicast;
514		ipv6_to_solicited_multicast(&multicast, inetAddress);
515
516		struct ipv6_mreq mreq;
517		memcpy(&mreq.ipv6mr_multiaddr, &multicast.sin6_addr, sizeof(in6_addr));
518		mreq.ipv6mr_interface = protocol->interface->index;
519
520		if (sIPv6Protocol != NULL) {
521			sIPv6Module->setsockopt(sIPv6Protocol, IPPROTO_IPV6,
522				IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
523		}
524	}
525
526	// TRACE(("%s(): address %s\n", __FUNCTION__, inet6_to_string(inetAddress)));
527
528	MutexLocker locker(sCacheLock);
529
530	ndp_entry* entry = ndp_entry::Lookup(inetAddress);
531	if (entry != NULL) {
532		sCache->Remove(entry);
533		entry->flags |= NDP_FLAG_REMOVED;
534	}
535
536	if (updateLocalAddress && protocol->local_address == inetAddress) {
537		// find new local sender address
538		memset(&protocol->local_address, 0, sizeof(in6_addr));
539
540		net_interface_address* address = NULL;
541		while (sDatalinkModule->get_next_interface_address(protocol->interface,
542				&address)) {
543			if (address->local == NULL || address->local->sa_family != AF_INET6)
544				continue;
545
546			memcpy(&protocol->local_address,
547				&((sockaddr_in6*)address->local)->sin6_addr, sizeof(in6_addr));
548		}
549	}
550
551	locker.Unlock();
552	delete entry;
553}
554
555
556/*!	Removes all entries belonging to the local interface of the \a procotol
557	given.
558*/
559static void
560ndp_remove_local(ipv6_datalink_protocol* protocol)
561{
562	net_interface_address* address = NULL;
563	while (sDatalinkModule->get_next_interface_address(protocol->interface,
564			&address)) {
565		if (address->local == NULL || address->local->sa_family != AF_INET6)
566			continue;
567
568		ndp_remove_local_entry(protocol, address->local, false);
569	}
570}
571
572
573static status_t
574ndp_set_local_entry(ipv6_datalink_protocol* protocol, const sockaddr* local)
575{
576	MutexLocker locker(sCacheLock);
577
578	net_interface* interface = protocol->interface;
579	in6_addr inetAddress;
580
581	if (local == NULL) {
582		// interface has not yet been set
583		memset(&inetAddress, 0, sizeof(in6_addr));
584	} else {
585		memcpy(&inetAddress,
586			&((sockaddr_in6*)local)->sin6_addr,
587			sizeof(in6_addr));
588
589		// join multicast address for listening to NS packets
590		sockaddr_in6 multicast;
591		ipv6_to_solicited_multicast(&multicast, inetAddress);
592
593		struct ipv6_mreq mreq;
594		memcpy(&mreq.ipv6mr_multiaddr, &multicast.sin6_addr, sizeof(in6_addr));
595		mreq.ipv6mr_interface = protocol->interface->index;
596
597		if (sIPv6Protocol != NULL) {
598			sIPv6Module->setsockopt(sIPv6Protocol, IPPROTO_IPV6,
599				IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
600		}
601	}
602
603	// TRACE(("%s(): address %s\n", __FUNCTION__, inet6_to_string(inetAddress)));
604
605	if (IN6_IS_ADDR_UNSPECIFIED(&protocol->local_address))
606		memcpy(&protocol->local_address, &inetAddress, sizeof(in6_addr));
607
608	sockaddr_dl address;
609	address.sdl_len = sizeof(sockaddr_dl);
610	address.sdl_family = AF_LINK;
611	address.sdl_type = IFT_ETHER;
612	address.sdl_e_type = htons(ETHER_TYPE_IPV6);
613	address.sdl_nlen = 0;
614	address.sdl_slen = 0;
615	address.sdl_alen = interface->device->address.length;
616	memcpy(LLADDR(&address), interface->device->address.data, address.sdl_alen);
617
618	memcpy(&protocol->hardware_address, &address, sizeof(sockaddr_dl));
619		// cache the address in our protocol
620
621	ndp_entry* entry;
622	status_t status = ndp_update_entry(inetAddress, &address,
623		NDP_FLAG_LOCAL | NDP_FLAG_PERMANENT, &entry);
624	if (status == B_OK)
625		entry->protocol = protocol;
626
627	return status;
628}
629
630
631/*!	Creates permanent local entries for all addresses of the interface belonging
632	to this protocol.
633	Returns an error if no entry could be added.
634*/
635static status_t
636ndp_update_local(ipv6_datalink_protocol* protocol)
637{
638	memset(&protocol->local_address, 0, sizeof(in6_addr));
639
640	ssize_t count = 0;
641
642	net_interface_address* address = NULL;
643	while (sDatalinkModule->get_next_interface_address(protocol->interface,
644			&address)) {
645		if (address->local == NULL || address->local->sa_family != AF_INET6)
646			continue;
647
648		if (ndp_set_local_entry(protocol, address->local) == B_OK) {
649			count++;
650		}
651	}
652
653	if (count == 0)
654		return ndp_set_local_entry(protocol, NULL);
655
656	return B_OK;
657}
658
659
660static status_t
661ndp_receive_solicitation(net_buffer* buffer, bool* reuseBuffer)
662{
663	*reuseBuffer = false;
664
665	NetBufferHeaderReader<neighbor_discovery_header> bufferHeader(buffer);
666	if (bufferHeader.Status() < B_OK)
667		return bufferHeader.Status();
668
669	neighbor_discovery_header& header = bufferHeader.Data();
670	if (header.option_type != ND_OPT_SOURCE_LINKADDR
671		|| header.option_length != 1)
672		return B_OK;
673
674	{
675		MutexLocker locker(sCacheLock);
676
677		// remember the address of the sender as we might need it later
678		sockaddr_dl hardwareAddress;
679		hardwareAddress.sdl_len = sizeof(sockaddr_dl);
680		hardwareAddress.sdl_family = AF_LINK;
681		hardwareAddress.sdl_index = 0;
682		hardwareAddress.sdl_type = IFT_ETHER;
683		hardwareAddress.sdl_e_type = htons(ETHER_TYPE_IPV6);
684		hardwareAddress.sdl_nlen = hardwareAddress.sdl_slen = 0;
685		hardwareAddress.sdl_alen = ETHER_ADDRESS_LENGTH;
686		memcpy(LLADDR(&hardwareAddress), header.link_address,
687			ETHER_ADDRESS_LENGTH);
688
689		ndp_update_entry(header.target_address, &hardwareAddress, 0);
690
691		// check if this request is for us
692
693		ndp_entry* entry = ndp_entry::Lookup(header.target_address);
694		if (entry == NULL
695			|| (entry->flags & (NDP_FLAG_LOCAL | NDP_FLAG_PUBLISH)) == 0) {
696			// We're not the one to answer this request
697			// TODO: instead of letting the other's request time-out, can we
698			//	reply failure somehow?
699			TRACE(("  not for us\n"));
700			return B_ERROR;
701		}
702
703		// send a reply (by reusing the buffer we got)
704		gBufferModule->trim(buffer, sizeof(neighbor_discovery_header));
705
706		header.icmp6_type = ND_NEIGHBOR_ADVERT;
707		header.icmp6_code = 0;
708		header.icmp6_checksum = 0;
709		header.flags = ND_NA_FLAG_SOLICITED;
710		header.option_type = ND_OPT_TARGET_LINKADDR;
711		memcpy(&header.link_address, LLADDR(&entry->hardware_address),
712			ETHER_ADDRESS_LENGTH);
713		bufferHeader.Sync();
714	}
715
716	// fix source and destination address
717	sockaddr_in6* source = (sockaddr_in6*)buffer->source;
718	sockaddr_in6* destination = (sockaddr_in6*)buffer->destination;
719	memcpy(&destination->sin6_addr, &source->sin6_addr, sizeof(in6_addr));
720	memcpy(&source->sin6_addr, &header.target_address, sizeof(in6_addr));
721
722	buffer->flags = 0;
723		// make sure this won't be a broadcast message
724
725	if (sIPv6Protocol == NULL)
726		return B_ERROR;
727
728	*reuseBuffer = true;
729
730	// send the ICMPv6 packet out
731	TRACE(("Sending Neighbor Advertisement\n"));
732	return sIPv6Module->send_data(sIPv6Protocol, buffer);
733}
734
735
736static void
737ndp_receive_advertisement(net_buffer* buffer)
738{
739	// TODO: also process unsolicited advertisments?
740	if ((buffer->flags & MSG_MCAST) != 0)
741		return;
742
743	NetBufferHeaderReader<neighbor_discovery_header> bufferHeader(buffer);
744	if (bufferHeader.Status() < B_OK)
745		return;
746
747	neighbor_discovery_header& header = bufferHeader.Data();
748	if (header.option_type != ND_OPT_TARGET_LINKADDR
749		|| header.option_length != 1) {
750		return;
751	}
752
753	sockaddr_dl hardwareAddress;
754	hardwareAddress.sdl_len = sizeof(sockaddr_dl);
755	hardwareAddress.sdl_family = AF_LINK;
756	hardwareAddress.sdl_index = 0;
757	hardwareAddress.sdl_type = IFT_ETHER;
758	hardwareAddress.sdl_e_type = htons(ETHER_TYPE_IPV6);
759	hardwareAddress.sdl_nlen = hardwareAddress.sdl_slen = 0;
760	hardwareAddress.sdl_alen = ETHER_ADDRESS_LENGTH;
761	memcpy(LLADDR(&hardwareAddress), header.link_address, ETHER_ADDRESS_LENGTH);
762
763	MutexLocker locker(sCacheLock);
764	// TODO: take in account ND_NA_FLAGs
765	ndp_update_entry(header.target_address, &hardwareAddress, 0);
766}
767
768
769static void
770ndp_receive_router_advertisement(net_buffer* buffer)
771{
772	NetBufferHeaderReader<router_advertisement_header> bufferHeader(buffer);
773	if (bufferHeader.Status() < B_OK)
774		return;
775
776	// TODO: check for validity
777
778	// TODO: parse the options
779}
780
781
782static status_t
783ndp_receive_data(net_buffer* buffer)
784{
785	TRACE("ndp_receive_data\n");
786
787	NetBufferHeaderReader<icmp6_hdr> icmp6Header(buffer);
788	if (icmp6Header.Status() < B_OK)
789		return icmp6Header.Status();
790
791	bool reuseBuffer = false;
792
793	switch (icmp6Header->icmp6_type) {
794		case ND_NEIGHBOR_SOLICIT:
795			TRACE(("  received Neighbor Solicitation\n"));
796			ndp_receive_solicitation(buffer, &reuseBuffer);
797			break;
798
799		case ND_NEIGHBOR_ADVERT:
800			TRACE(("  received Neighbor Advertisement\n"));
801			ndp_receive_advertisement(buffer);
802			break;
803
804		case ND_ROUTER_ADVERT:
805			TRACE(("  received Router Advertisement\n"));
806			ndp_receive_router_advertisement(buffer);
807			break;
808	}
809
810	if (reuseBuffer == false)
811		gBufferModule->free(buffer);
812	return B_OK;
813}
814
815
816static void
817ndp_timer(struct net_timer* timer, void* data)
818{
819	ndp_entry* entry = (ndp_entry*)data;
820	TRACE(("NDP timer %" B_PRId32 ", entry %p!\n", entry->timer_state, entry));
821
822	switch (entry->timer_state) {
823		case NDP_NO_STATE:
824			// who are you kidding?
825			break;
826
827		case NDP_STATE_REQUEST_FAILED:
828			// Requesting the NDP entry failed, we keep it around for a while,
829			// though, so that we won't try to request the same address again
830			// too soon.
831			TRACE(("  requesting NDP entry %p failed!\n", entry));
832			entry->timer_state = NDP_STATE_REMOVE_FAILED;
833			entry->MarkFailed();
834			sStackModule->set_timer(&entry->timer, NDP_REJECT_TIMEOUT);
835			break;
836
837		case NDP_STATE_REMOVE_FAILED:
838		case NDP_STATE_STALE:
839			// the entry has aged so much that we're going to remove it
840			TRACE(("  remove NDP entry %p!\n", entry));
841
842			mutex_lock(&sCacheLock);
843			if ((entry->flags & NDP_FLAG_REMOVED) != 0) {
844				// The entry has already been removed, and is about to be deleted
845				mutex_unlock(&sCacheLock);
846				break;
847			}
848
849			sCache->Remove(entry);
850			mutex_unlock(&sCacheLock);
851
852			delete entry;
853			break;
854
855		default:
856		{
857			if (entry->timer_state > NDP_STATE_LAST_REQUEST)
858				break;
859
860			TRACE(("  send request for NDP entry %p!\n", entry));
861
862			net_buffer* request = get_request_buffer(entry);
863			if (request == NULL)
864				break;
865
866			if (entry->timer_state < NDP_STATE_LAST_REQUEST) {
867				// we'll still need our buffer, so in order to prevent it being
868				// freed by a successful send, we need to clone it
869				net_buffer* clone = gBufferModule->clone(request, true);
870				if (clone == NULL) {
871					// cloning failed - that means we won't be able to send as
872					// many requests as originally planned
873					entry->timer_state = NDP_STATE_LAST_REQUEST;
874				} else {
875					put_request_buffer(entry, request);
876					request = clone;
877				}
878			}
879
880			if (sIPv6Protocol == NULL)
881				break;
882
883			// we're trying to resolve the address, so keep sending requests
884			status_t status = sIPv6Module->send_data(sIPv6Protocol, request);
885			if (status < B_OK)
886				gBufferModule->free(request);
887
888			entry->timer_state++;
889			sStackModule->set_timer(&entry->timer, NDP_REQUEST_TIMEOUT);
890			break;
891		}
892	}
893}
894
895
896static status_t
897ndp_start_resolve(ipv6_datalink_protocol* protocol, const in6_addr& address,
898	ndp_entry** _entry)
899{
900	ASSERT_LOCKED_MUTEX(&sCacheLock);
901
902	// create an unresolved entry as a placeholder
903	ndp_entry* entry = ndp_entry::Add(address, NULL, 0);
904	if (entry == NULL)
905		return B_NO_MEMORY;
906
907	// prepare NDP request
908
909	net_buffer* buffer = entry->request_buffer = gBufferModule->create(256);
910	if (entry->request_buffer == NULL) {
911		entry->ScheduleRemoval();
912		return B_NO_MEMORY;
913	}
914
915	NetBufferPrepend<neighbor_discovery_header> header(buffer);
916	status_t status = header.Status();
917	if (status < B_OK) {
918		entry->ScheduleRemoval();
919		return status;
920	}
921
922	net_interface* interface = protocol->interface;
923	net_device* device = interface->device;
924
925	// prepare source and target addresses
926
927	sockaddr_in6* source = (sockaddr_in6*)buffer->source;
928	ipv6_to_sockaddr(source, protocol->local_address);
929	// protocol->local_address
930
931	sockaddr_in6* destination = (sockaddr_in6*)buffer->destination;
932	ipv6_to_solicited_multicast(destination, address);
933
934	buffer->protocol = IPPROTO_ICMPV6;
935
936	// prepare Neighbor Solicitation header
937
938	header->icmp6_type = ND_NEIGHBOR_SOLICIT;
939	header->icmp6_code = 0;
940	header->icmp6_checksum = 0;
941	header->flags = 0;
942	memcpy(&header->target_address, &address, sizeof(in6_addr));
943	header->option_type = ND_OPT_SOURCE_LINKADDR;
944	header->option_length = (sizeof(nd_opt_hdr) + ETHER_ADDRESS_LENGTH) >> 3;
945	memcpy(&header->link_address, device->address.data, ETHER_ADDRESS_LENGTH);
946	header.Sync();
947
948	if (sIPv6Protocol == NULL) {
949		entry->ScheduleRemoval();
950		return B_NO_MEMORY;
951	}
952
953	// this does not work, because multicast for now is only looped back!
954#if FIXME
955	// hack: set to use the correct interface by setting socket option
956	sIPv6Module->setsockopt(sIPv6Protocol, IPPROTO_IPV6, IPV6_MULTICAST_IF,
957		&source->sin6_addr, sizeof(in6_addr));
958#endif
959
960	net_buffer* clone = gBufferModule->clone(buffer, true);
961	if (clone == NULL) {
962		entry->ScheduleRemoval();
963		return B_NO_MEMORY;
964	}
965
966	// send the ICMPv6 packet out
967	TRACE(("Sending Neighbor Solicitation\n"));
968	status = sIPv6Module->send_data(sIPv6Protocol, clone);
969	if (status < B_OK) {
970		entry->ScheduleRemoval();
971		return status;
972	}
973
974	entry->protocol = protocol;
975	entry->timer_state = NDP_STATE_REQUEST;
976	sStackModule->set_timer(&entry->timer, 0);
977		// start request timer
978
979	*_entry = entry;
980	return B_OK;
981}
982
983
984//	#pragma mark -
985
986
987static status_t
988ipv6_datalink_init(net_interface* interface, net_domain* domain,
989	net_datalink_protocol** _protocol)
990{
991	if (domain->family != AF_INET6)
992		return B_BAD_TYPE;
993
994	status_t status = sStackModule->register_domain_device_handler(
995		interface->device, B_NET_FRAME_TYPE(IFT_ETHER, ETHER_TYPE_IPV6), domain);
996	if (status != B_OK)
997		return status;
998
999	ipv6_datalink_protocol* protocol = new(std::nothrow) ipv6_datalink_protocol;
1000	if (protocol == NULL)
1001		return B_NO_MEMORY;
1002
1003	memset(&protocol->hardware_address, 0, sizeof(sockaddr_dl));
1004	memset(&protocol->local_address, 0, sizeof(in6_addr));
1005	*_protocol = protocol;
1006	return B_OK;
1007}
1008
1009
1010static status_t
1011ipv6_datalink_uninit(net_datalink_protocol* protocol)
1012{
1013	sStackModule->unregister_device_handler(protocol->interface->device,
1014		B_NET_FRAME_TYPE(IFT_ETHER, ETHER_TYPE_IPV6));
1015
1016	delete protocol;
1017 	return B_OK;
1018}
1019
1020
1021static status_t
1022ipv6_datalink_send_data(net_datalink_protocol* _protocol, net_buffer* buffer)
1023{
1024	ipv6_datalink_protocol* protocol = (ipv6_datalink_protocol*)_protocol;
1025
1026	memcpy(buffer->source, &protocol->hardware_address,
1027		protocol->hardware_address.sdl_len);
1028
1029	if ((buffer->flags & MSG_MCAST) != 0) {
1030		sockaddr_dl multicastDestination;
1031		ipv6_to_ether_multicast(&multicastDestination,
1032			(sockaddr_in6*)buffer->destination);
1033		memcpy(buffer->destination, &multicastDestination,
1034			sizeof(sockaddr_dl));
1035	} else {
1036		MutexLocker locker(sCacheLock);
1037
1038		// Lookup destination (we may need to wait for this)
1039		ndp_entry* entry = ndp_entry::Lookup(
1040			((struct sockaddr_in6*)buffer->destination)->sin6_addr);
1041		if (entry == NULL) {
1042			status_t status = ndp_start_resolve(protocol,
1043				((struct sockaddr_in6*)buffer->destination)->sin6_addr, &entry);
1044			if (status < B_OK)
1045				return status;
1046		}
1047
1048		if ((entry->flags & NDP_FLAG_REJECT) != 0)
1049			return EHOSTUNREACH;
1050		if (!(entry->flags & NDP_FLAG_VALID)) {
1051			// entry is still being resolved.
1052			TRACE(("NDP Queuing packet %p, entry still being resolved.\n",
1053					buffer));
1054			entry->queue.Add(buffer);
1055			return B_OK;
1056		}
1057
1058		memcpy(buffer->destination, &entry->hardware_address,
1059			entry->hardware_address.sdl_len);
1060	}
1061
1062	return protocol->next->module->send_data(protocol->next, buffer);
1063}
1064
1065
1066
1067static status_t
1068ipv6_datalink_up(net_datalink_protocol* _protocol)
1069{
1070	ipv6_datalink_protocol* protocol = (ipv6_datalink_protocol*)_protocol;
1071	status_t status = protocol->next->module->interface_up(protocol->next);
1072	if (status != B_OK)
1073		return status;
1074
1075	// cache this device's address for later use
1076
1077	status = ndp_update_local(protocol);
1078	if (status != B_OK) {
1079		protocol->next->module->interface_down(protocol->next);
1080		return status;
1081	}
1082
1083	return B_OK;
1084}
1085
1086
1087static void
1088ipv6_datalink_down(net_datalink_protocol *protocol)
1089{
1090	// remove local NDP entries from the cache
1091	ndp_remove_local((ipv6_datalink_protocol*)protocol);
1092
1093	protocol->next->module->interface_down(protocol->next);
1094}
1095
1096
1097status_t
1098ipv6_datalink_change_address(net_datalink_protocol* _protocol,
1099	net_interface_address* address, int32 option,
1100	const struct sockaddr* oldAddress, const struct sockaddr* newAddress)
1101{
1102	ipv6_datalink_protocol* protocol = (ipv6_datalink_protocol*)_protocol;
1103	switch (option) {
1104		case SIOCSIFADDR:
1105		case SIOCAIFADDR:
1106		case SIOCDIFADDR:
1107			// Those are the options we handle
1108			if ((protocol->interface->flags & IFF_UP) != 0) {
1109				// Update NDP entry for the local address
1110
1111				if (newAddress != NULL && newAddress->sa_family == AF_INET6) {
1112					status_t status = ndp_set_local_entry(protocol, newAddress);
1113					if (status != B_OK)
1114						return status;
1115
1116					// add IPv6 multicast route (ff00::/8)
1117					sockaddr_in6 socketAddress;
1118					memset(&socketAddress, 0, sizeof(sockaddr_in6));
1119					socketAddress.sin6_family = AF_INET6;
1120					socketAddress.sin6_len = sizeof(sockaddr_in6);
1121					socketAddress.sin6_addr.s6_addr[0] = 0xff;
1122
1123					net_route route;
1124					memset(&route, 0, sizeof(net_route));
1125					route.destination = (sockaddr*)&socketAddress;
1126					route.mask = (sockaddr*)&socketAddress;
1127					route.flags = 0;
1128					sDatalinkModule->add_route(address->domain, &route);
1129				}
1130
1131				if (oldAddress != NULL && oldAddress->sa_family == AF_INET6) {
1132					ndp_remove_local_entry(protocol, oldAddress, true);
1133
1134					// remove IPv6 multicast route (ff00::/8)
1135					sockaddr_in6 socketAddress;
1136					memset(&socketAddress, 0, sizeof(sockaddr_in6));
1137					socketAddress.sin6_family = AF_INET6;
1138					socketAddress.sin6_len = sizeof(sockaddr_in6);
1139					socketAddress.sin6_addr.s6_addr[0] = 0xff;
1140
1141					net_route route;
1142					memset(&route, 0, sizeof(net_route));
1143					route.destination = (sockaddr*)&socketAddress;
1144					route.mask = (sockaddr*)&socketAddress;
1145					route.flags = 0;
1146					sDatalinkModule->remove_route(address->domain, &route);
1147				}
1148			}
1149			break;
1150
1151		default:
1152			break;
1153	}
1154
1155	return protocol->next->module->change_address(protocol->next, address,
1156		option, oldAddress, newAddress);
1157}
1158
1159
1160static status_t
1161ipv6_datalink_control(net_datalink_protocol* protocol, int32 op, void* argument,
1162	size_t length)
1163{
1164	return protocol->next->module->control(protocol->next, op, argument,
1165		length);
1166}
1167
1168
1169static status_t
1170ipv6_datalink_join_multicast(net_datalink_protocol* protocol,
1171	const sockaddr* address)
1172{
1173	if (address->sa_family != AF_INET6)
1174		return EINVAL;
1175
1176	sockaddr_dl multicastAddress;
1177	ipv6_to_ether_multicast(&multicastAddress, (const sockaddr_in6*)address);
1178
1179	return protocol->next->module->join_multicast(protocol->next,
1180		(sockaddr*)&multicastAddress);
1181}
1182
1183
1184static status_t
1185ipv6_datalink_leave_multicast(net_datalink_protocol* protocol,
1186	const sockaddr* address)
1187{
1188	if (address->sa_family != AF_INET6)
1189		return EINVAL;
1190
1191	sockaddr_dl multicastAddress;
1192	ipv6_to_ether_multicast(&multicastAddress, (const sockaddr_in6*)address);
1193
1194	return protocol->next->module->leave_multicast(protocol->next,
1195		(sockaddr*)&multicastAddress);
1196}
1197
1198
1199static status_t
1200ipv6_datalink_std_ops(int32 op, ...)
1201{
1202	switch (op) {
1203	case B_MODULE_INIT:
1204		return ndp_init();
1205
1206	case B_MODULE_UNINIT:
1207		return ndp_uninit();
1208	}
1209
1210	return B_ERROR;
1211}
1212
1213
1214net_datalink_protocol_module_info gIPv6DataLinkModule = {
1215	{
1216		"network/datalink_protocols/ipv6_datagram/v1",
1217		0,
1218		ipv6_datalink_std_ops
1219	},
1220	ipv6_datalink_init,
1221	ipv6_datalink_uninit,
1222	ipv6_datalink_send_data,
1223	ipv6_datalink_up,
1224	ipv6_datalink_down,
1225	ipv6_datalink_change_address,
1226	ipv6_datalink_control,
1227	ipv6_datalink_join_multicast,
1228	ipv6_datalink_leave_multicast,
1229};
1230
1231net_ndp_module_info gIPv6NDPModule = {
1232	{
1233		"network/datalink_protocols/ipv6_datagram/ndp/v1",
1234		0,
1235		NULL
1236	},
1237	ndp_receive_data
1238};
1239
1240module_dependency module_dependencies[] = {
1241	{NET_STACK_MODULE_NAME, (module_info**)&sStackModule},
1242	{NET_DATALINK_MODULE_NAME, (module_info**)&sDatalinkModule},
1243	{NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule},
1244	{"network/protocols/ipv6/v1", (module_info**)&sIPv6Module},
1245	{}
1246};
1247
1248module_info* modules[] = {
1249	(module_info*)&gIPv6DataLinkModule,
1250	(module_info*)&gIPv6NDPModule,
1251	NULL
1252};
1253