1/*
2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <net_datalink.h>
8#include <net_protocol.h>
9#include <net_stack.h>
10#include <net_datalink_protocol.h>
11#include <NetUtilities.h>
12#include <NetBufferUtilities.h>
13
14#include <KernelExport.h>
15#include <util/list.h>
16
17#include <netinet/icmp6.h>
18#include <netinet/in.h>
19#include <new>
20#include <stdlib.h>
21#include <string.h>
22
23#include <ipv6_datagram/ndp.h>
24
25
26#define TRACE_ICMP6
27#ifdef TRACE_ICMP6
28#	define TRACE(x) dprintf x
29#else
30#	define TRACE(x) ;
31#endif
32
33
34typedef NetBufferField<uint16, offsetof(icmp6_hdr, icmp6_cksum)> ICMP6ChecksumField;
35
36
37net_buffer_module_info *gBufferModule;
38static net_stack_module_info *sStackModule;
39static net_ndp_module_info *sIPv6NDPModule;
40
41
42net_protocol *
43icmp6_init_protocol(net_socket *socket)
44{
45	net_protocol *protocol = new (std::nothrow) net_protocol;
46	if (protocol == NULL)
47		return NULL;
48
49	return protocol;
50}
51
52
53status_t
54icmp6_uninit_protocol(net_protocol *protocol)
55{
56	delete protocol;
57	return B_OK;
58}
59
60
61status_t
62icmp6_open(net_protocol *protocol)
63{
64	return B_OK;
65}
66
67
68status_t
69icmp6_close(net_protocol *protocol)
70{
71	return B_OK;
72}
73
74
75status_t
76icmp6_free(net_protocol *protocol)
77{
78	return B_OK;
79}
80
81
82status_t
83icmp6_connect(net_protocol *protocol, const struct sockaddr *address)
84{
85	return B_ERROR;
86}
87
88
89status_t
90icmp6_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
91{
92	return EOPNOTSUPP;
93}
94
95
96status_t
97icmp6_control(net_protocol *protocol, int level, int option, void *value,
98	size_t *_length)
99{
100	return protocol->next->module->control(protocol->next, level, option,
101		value, _length);
102}
103
104
105status_t
106icmp6_getsockopt(net_protocol *protocol, int level, int option,
107	void *value, int *length)
108{
109	return protocol->next->module->getsockopt(protocol->next, level, option,
110		value, length);
111}
112
113
114status_t
115icmp6_setsockopt(net_protocol *protocol, int level, int option,
116	const void *value, int length)
117{
118	return protocol->next->module->setsockopt(protocol->next, level, option,
119		value, length);
120}
121
122
123status_t
124icmp6_bind(net_protocol *protocol, const struct sockaddr *address)
125{
126	return B_ERROR;
127}
128
129
130status_t
131icmp6_unbind(net_protocol *protocol, struct sockaddr *address)
132{
133	return B_ERROR;
134}
135
136
137status_t
138icmp6_listen(net_protocol *protocol, int count)
139{
140	return EOPNOTSUPP;
141}
142
143
144status_t
145icmp6_shutdown(net_protocol *protocol, int direction)
146{
147	return EOPNOTSUPP;
148}
149
150
151status_t
152icmp6_send_data(net_protocol *protocol, net_buffer *buffer)
153{
154	return protocol->next->module->send_data(protocol->next, buffer);
155}
156
157
158status_t
159icmp6_send_routed_data(net_protocol *protocol, struct net_route *route,
160	net_buffer *buffer)
161{
162	return protocol->next->module->send_routed_data(protocol->next, route, buffer);
163}
164
165
166ssize_t
167icmp6_send_avail(net_protocol *protocol)
168{
169	return B_ERROR;
170}
171
172
173status_t
174icmp6_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
175	net_buffer **_buffer)
176{
177	return B_ERROR;
178}
179
180
181ssize_t
182icmp6_read_avail(net_protocol *protocol)
183{
184	return B_ERROR;
185}
186
187
188struct net_domain *
189icmp6_get_domain(net_protocol *protocol)
190{
191	return protocol->next->module->get_domain(protocol->next);
192}
193
194
195size_t
196icmp6_get_mtu(net_protocol *protocol, const struct sockaddr *address)
197{
198	return protocol->next->module->get_mtu(protocol->next, address);
199}
200
201
202static net_domain*
203get_domain(struct net_buffer* buffer)
204{
205	net_domain* domain;
206	if (buffer->interface_address != NULL)
207		domain = buffer->interface_address->domain;
208	else
209		domain = sStackModule->get_domain(buffer->source->sa_family);
210
211	if (domain == NULL || domain->module == NULL)
212		return NULL;
213
214	return domain;
215}
216
217
218status_t
219icmp6_receive_data(net_buffer *buffer)
220{
221	TRACE(("ICMPv6 received some data, buffer length %" B_PRIu32 "\n",
222		buffer->size));
223
224	net_domain* domain = get_domain(buffer);
225	if (domain == NULL)
226		return B_ERROR;
227
228	NetBufferHeaderReader<icmp6_hdr> bufferHeader(buffer);
229	if (bufferHeader.Status() < B_OK)
230		return bufferHeader.Status();
231
232	icmp6_hdr &header = bufferHeader.Data();
233
234	TRACE(("  got type %u, code %u, checksum 0x%x\n", header.icmp6_type,
235			header.icmp6_code, header.icmp6_cksum));
236
237	net_address_module_info* addressModule = domain->address_module;
238
239	// compute and check the checksum
240 	if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer,
241 			IPPROTO_ICMPV6) != 0)
242 		return B_BAD_DATA;
243
244	switch (header.icmp6_type) {
245		case ICMP6_ECHO_REPLY:
246			break;
247
248		case ICMP6_ECHO_REQUEST:
249		{
250			if (buffer->interface_address != NULL) {
251				// We only reply to echo requests of our local interface; we
252				// don't reply to broadcast requests
253				if (!domain->address_module->equal_addresses(
254						buffer->interface_address->local, buffer->destination))
255					break;
256			}
257
258			net_buffer *reply = gBufferModule->duplicate(buffer);
259			if (reply == NULL)
260				return B_NO_MEMORY;
261
262			gBufferModule->swap_addresses(reply);
263
264			// There already is an ICMP header, and we'll reuse it
265			NetBufferHeaderReader<icmp6_hdr> header(reply);
266
267			header->icmp6_type = ICMP6_ECHO_REPLY;
268			header->icmp6_code = 0;
269			header->icmp6_cksum = 0;
270
271			header.Sync();
272
273			*ICMP6ChecksumField(reply) = Checksum::PseudoHeader(addressModule,
274				gBufferModule, buffer, IPPROTO_ICMPV6);
275
276			status_t status = domain->module->send_data(NULL, reply);
277			if (status < B_OK) {
278				gBufferModule->free(reply);
279				return status;
280			}
281		}
282
283		default:
284			// unrecognized messages go to neighbor discovery protocol handler
285			return sIPv6NDPModule->receive_data(buffer);
286	}
287
288	gBufferModule->free(buffer);
289	return B_OK;
290}
291
292
293status_t
294icmp6_deliver_data(net_protocol *protocol, net_buffer *buffer)
295{
296	// TODO: does this look OK?
297	return icmp6_receive_data(buffer);
298}
299
300
301status_t
302icmp6_error_received(net_error code, net_buffer* data)
303{
304 	return B_ERROR;
305}
306
307
308status_t
309icmp6_error_reply(net_protocol* protocol, net_buffer* buffer, net_error error,
310	net_error_data* errorData)
311{
312	return B_ERROR;
313}
314
315
316//	#pragma mark -
317
318
319static status_t
320icmp6_init()
321{
322	sStackModule->register_domain_protocols(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6,
323		"network/protocols/icmp6/v1",
324		"network/protocols/ipv6/v1",
325		NULL);
326
327	sStackModule->register_domain_receiving_protocol(AF_INET6, IPPROTO_ICMPV6,
328		"network/protocols/icmp6/v1");
329
330	return B_OK;
331}
332
333
334static status_t
335icmp6_std_ops(int32 op, ...)
336{
337	switch (op) {
338		case B_MODULE_INIT:
339			return icmp6_init();
340
341		case B_MODULE_UNINIT:
342			return B_OK;
343
344		default:
345			return B_ERROR;
346	}
347}
348
349
350net_protocol_module_info sICMP6Module = {
351	{
352		"network/protocols/icmp6/v1",
353		0,
354		icmp6_std_ops
355	},
356	NET_PROTOCOL_ATOMIC_MESSAGES,
357
358	icmp6_init_protocol,
359	icmp6_uninit_protocol,
360	icmp6_open,
361	icmp6_close,
362	icmp6_free,
363	icmp6_connect,
364	icmp6_accept,
365	icmp6_control,
366	icmp6_getsockopt,
367	icmp6_setsockopt,
368	icmp6_bind,
369	icmp6_unbind,
370	icmp6_listen,
371	icmp6_shutdown,
372	icmp6_send_data,
373	icmp6_send_routed_data,
374	icmp6_send_avail,
375	icmp6_read_data,
376	icmp6_read_avail,
377	icmp6_get_domain,
378	icmp6_get_mtu,
379	icmp6_receive_data,
380	icmp6_deliver_data,
381	icmp6_error_received,
382	icmp6_error_reply,
383	NULL,		// add_ancillary_data()
384	NULL,		// process_ancillary_data()
385	NULL,		// process_ancillary_data_no_container()
386	NULL,		// send_data_no_buffer()
387	NULL		// read_data_no_buffer()
388};
389
390module_dependency module_dependencies[] = {
391	{NET_STACK_MODULE_NAME, (module_info **)&sStackModule},
392	{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
393	{"network/datalink_protocols/ipv6_datagram/ndp/v1",
394		(module_info **)&sIPv6NDPModule},
395	{}
396};
397
398module_info *modules[] = {
399	(module_info *)&sICMP6Module,
400	NULL
401};
402