xref: /haiku/src/add-ons/kernel/network/protocols/icmp/icmp.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include <net_datalink.h>
11 #include <net_protocol.h>
12 #include <net_stack.h>
13 #include <NetBufferUtilities.h>
14 
15 #include <KernelExport.h>
16 #include <util/list.h>
17 
18 #include <netinet/in.h>
19 #include <new>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 //#define TRACE_ICMP
24 #ifdef TRACE_ICMP
25 #	define TRACE(x) dprintf x
26 #else
27 #	define TRACE(x) ;
28 #endif
29 
30 
31 struct icmp_header {
32 	uint8	type;
33 	uint8	code;
34 	uint16	checksum;
35 	union {
36 		struct {
37 			uint16	id;
38 			uint16	sequence;
39 		} echo;
40 		struct {
41 			in_addr_t gateway;
42 		} redirect;
43 		struct {
44 			uint16	_reserved;
45 			uint16	next_mtu;
46 		} path_mtu;
47 		uint32 zero;
48 	};
49 };
50 
51 typedef NetBufferField<uint16, offsetof(icmp_header, checksum)> ICMPChecksumField;
52 
53 #define ICMP_TYPE_ECHO_REPLY	0
54 #define ICMP_TYPE_UNREACH		3
55 #define ICMP_TYPE_REDIRECT		5
56 #define ICMP_TYPE_ECHO_REQUEST	8
57 
58 // type unreach codes
59 #define ICMP_CODE_UNREACH_NEED_FRAGMENT	4	// this is used for path MTU discovery
60 
61 struct icmp_protocol : net_protocol {
62 };
63 
64 
65 net_buffer_module_info *gBufferModule;
66 static net_stack_module_info *sStackModule;
67 
68 
69 net_protocol *
70 icmp_init_protocol(net_socket *socket)
71 {
72 	icmp_protocol *protocol = new (std::nothrow) icmp_protocol;
73 	if (protocol == NULL)
74 		return NULL;
75 
76 	return protocol;
77 }
78 
79 
80 status_t
81 icmp_uninit_protocol(net_protocol *protocol)
82 {
83 	delete protocol;
84 	return B_OK;
85 }
86 
87 
88 status_t
89 icmp_open(net_protocol *protocol)
90 {
91 	return B_OK;
92 }
93 
94 
95 status_t
96 icmp_close(net_protocol *protocol)
97 {
98 	return B_OK;
99 }
100 
101 
102 status_t
103 icmp_free(net_protocol *protocol)
104 {
105 	return B_OK;
106 }
107 
108 
109 status_t
110 icmp_connect(net_protocol *protocol, const struct sockaddr *address)
111 {
112 	return B_ERROR;
113 }
114 
115 
116 status_t
117 icmp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
118 {
119 	return EOPNOTSUPP;
120 }
121 
122 
123 status_t
124 icmp_control(net_protocol *protocol, int level, int option, void *value,
125 	size_t *_length)
126 {
127 	return protocol->next->module->control(protocol->next, level, option,
128 		value, _length);
129 }
130 
131 
132 status_t
133 icmp_getsockopt(net_protocol *protocol, int level, int option,
134 	void *value, int *length)
135 {
136 	return protocol->next->module->getsockopt(protocol->next, level, option,
137 		value, length);
138 }
139 
140 
141 status_t
142 icmp_setsockopt(net_protocol *protocol, int level, int option,
143 	const void *value, int length)
144 {
145 	return protocol->next->module->setsockopt(protocol->next, level, option,
146 		value, length);
147 }
148 
149 
150 status_t
151 icmp_bind(net_protocol *protocol, const struct sockaddr *address)
152 {
153 	return B_ERROR;
154 }
155 
156 
157 status_t
158 icmp_unbind(net_protocol *protocol, struct sockaddr *address)
159 {
160 	return B_ERROR;
161 }
162 
163 
164 status_t
165 icmp_listen(net_protocol *protocol, int count)
166 {
167 	return EOPNOTSUPP;
168 }
169 
170 
171 status_t
172 icmp_shutdown(net_protocol *protocol, int direction)
173 {
174 	return EOPNOTSUPP;
175 }
176 
177 
178 status_t
179 icmp_send_data(net_protocol *protocol, net_buffer *buffer)
180 {
181 	return protocol->next->module->send_data(protocol->next, buffer);
182 }
183 
184 
185 status_t
186 icmp_send_routed_data(net_protocol *protocol, struct net_route *route,
187 	net_buffer *buffer)
188 {
189 	return protocol->next->module->send_routed_data(protocol->next, route, buffer);
190 }
191 
192 
193 ssize_t
194 icmp_send_avail(net_protocol *protocol)
195 {
196 	return B_ERROR;
197 }
198 
199 
200 status_t
201 icmp_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
202 	net_buffer **_buffer)
203 {
204 	return B_ERROR;
205 }
206 
207 
208 ssize_t
209 icmp_read_avail(net_protocol *protocol)
210 {
211 	return B_ERROR;
212 }
213 
214 
215 struct net_domain *
216 icmp_get_domain(net_protocol *protocol)
217 {
218 	return protocol->next->module->get_domain(protocol->next);
219 }
220 
221 
222 size_t
223 icmp_get_mtu(net_protocol *protocol, const struct sockaddr *address)
224 {
225 	return protocol->next->module->get_mtu(protocol->next, address);
226 }
227 
228 
229 status_t
230 icmp_receive_data(net_buffer *buffer)
231 {
232 	TRACE(("ICMP received some data, buffer length %lu\n", buffer->size));
233 
234 	NetBufferHeaderReader<icmp_header> bufferHeader(buffer);
235 	if (bufferHeader.Status() < B_OK)
236 		return bufferHeader.Status();
237 
238 	icmp_header &header = bufferHeader.Data();
239 
240 	TRACE(("  got type %u, code %u, checksum %u\n", header.type, header.code,
241 		ntohs(header.checksum)));
242 	TRACE(("  computed checksum: %ld\n", gBufferModule->checksum(buffer, 0, buffer->size, true)));
243 
244 	if (gBufferModule->checksum(buffer, 0, buffer->size, true) != 0)
245 		return B_BAD_DATA;
246 
247 	switch (header.type) {
248 		case ICMP_TYPE_ECHO_REPLY:
249 			break;
250 
251 		case ICMP_TYPE_ECHO_REQUEST:
252 		{
253 			net_domain *domain;
254 			if (buffer->interface != NULL) {
255 				domain = buffer->interface->domain;
256 
257 				// We only reply to echo requests of our local interface; we
258 				// don't reply to broadcast requests
259 				if (!domain->address_module->equal_addresses(
260 						buffer->interface->address, buffer->destination))
261 					break;
262 			} else
263 				domain = sStackModule->get_domain(buffer->source->sa_family);
264 
265 			if (domain == NULL || domain->module == NULL)
266 				break;
267 
268 			net_buffer *reply = gBufferModule->duplicate(buffer);
269 			if (reply == NULL)
270 				return B_NO_MEMORY;
271 
272 			gBufferModule->swap_addresses(reply);
273 
274 			// There already is an ICMP header, and we'll reuse it
275 			NetBufferHeaderReader<icmp_header> header(reply);
276 
277 			header->type = ICMP_TYPE_ECHO_REPLY;
278 			header->code = 0;
279 			header->checksum = 0;
280 
281 			header.Sync();
282 
283 			*ICMPChecksumField(reply) = gBufferModule->checksum(reply, 0,
284 					reply->size, true);
285 
286 			status_t status = domain->module->send_data(NULL, reply);
287 			if (status < B_OK) {
288 				gBufferModule->free(reply);
289 				return status;
290 			}
291 		}
292 	}
293 
294 	gBufferModule->free(buffer);
295 	return B_OK;
296 }
297 
298 
299 status_t
300 icmp_error(uint32 code, net_buffer *data)
301 {
302 	return B_ERROR;
303 }
304 
305 
306 status_t
307 icmp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
308 	void *errorData)
309 {
310 	return B_ERROR;
311 }
312 
313 
314 //	#pragma mark -
315 
316 
317 static status_t
318 icmp_std_ops(int32 op, ...)
319 {
320 	switch (op) {
321 		case B_MODULE_INIT:
322 		{
323 			sStackModule->register_domain_protocols(AF_INET, SOCK_DGRAM, IPPROTO_ICMP,
324 				"network/protocols/icmp/v1",
325 				"network/protocols/ipv4/v1",
326 				NULL);
327 
328 			sStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_ICMP,
329 				"network/protocols/icmp/v1");
330 			return B_OK;
331 		}
332 
333 		case B_MODULE_UNINIT:
334 			return B_OK;
335 
336 		default:
337 			return B_ERROR;
338 	}
339 }
340 
341 
342 net_protocol_module_info sICMPModule = {
343 	{
344 		"network/protocols/icmp/v1",
345 		0,
346 		icmp_std_ops
347 	},
348 	NET_PROTOCOL_ATOMIC_MESSAGES,
349 
350 	icmp_init_protocol,
351 	icmp_uninit_protocol,
352 	icmp_open,
353 	icmp_close,
354 	icmp_free,
355 	icmp_connect,
356 	icmp_accept,
357 	icmp_control,
358 	icmp_getsockopt,
359 	icmp_setsockopt,
360 	icmp_bind,
361 	icmp_unbind,
362 	icmp_listen,
363 	icmp_shutdown,
364 	icmp_send_data,
365 	icmp_send_routed_data,
366 	icmp_send_avail,
367 	icmp_read_data,
368 	icmp_read_avail,
369 	icmp_get_domain,
370 	icmp_get_mtu,
371 	icmp_receive_data,
372 	NULL,
373 	icmp_error,
374 	icmp_error_reply,
375 };
376 
377 module_dependency module_dependencies[] = {
378 	{NET_STACK_MODULE_NAME, (module_info **)&sStackModule},
379 	{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
380 	{}
381 };
382 
383 module_info *modules[] = {
384 	(module_info *)&sICMPModule,
385 	NULL
386 };
387