xref: /haiku/src/add-ons/kernel/network/protocols/icmp/icmp.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
1 /*
2  * Copyright 2006-2007, 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 			else
257 				domain = sStackModule->get_domain(buffer->source->sa_family);
258 			if (domain == NULL || domain->module == NULL)
259 				break;
260 
261 			net_buffer *reply = gBufferModule->duplicate(buffer);
262 			if (reply == NULL)
263 				return B_NO_MEMORY;
264 
265 			gBufferModule->swap_addresses(reply);
266 
267 			// There already is an ICMP header, and we'll reuse it
268 			NetBufferHeaderReader<icmp_header> header(reply);
269 
270 			header->type = ICMP_TYPE_ECHO_REPLY;
271 			header->code = 0;
272 			header->checksum = 0;
273 
274 			header.Sync();
275 
276 			*ICMPChecksumField(reply) = gBufferModule->checksum(reply, 0,
277 					reply->size, true);
278 
279 			status_t status = domain->module->send_data(NULL, reply);
280 			if (status < B_OK) {
281 				gBufferModule->free(reply);
282 				return status;
283 			}
284 		}
285 	}
286 
287 	gBufferModule->free(buffer);
288 	return B_OK;
289 }
290 
291 
292 status_t
293 icmp_error(uint32 code, net_buffer *data)
294 {
295 	return B_ERROR;
296 }
297 
298 
299 status_t
300 icmp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
301 	void *errorData)
302 {
303 	return B_ERROR;
304 }
305 
306 
307 //	#pragma mark -
308 
309 
310 static status_t
311 icmp_std_ops(int32 op, ...)
312 {
313 	switch (op) {
314 		case B_MODULE_INIT:
315 		{
316 			sStackModule->register_domain_protocols(AF_INET, SOCK_DGRAM, IPPROTO_ICMP,
317 				"network/protocols/icmp/v1",
318 				"network/protocols/ipv4/v1",
319 				NULL);
320 
321 			sStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_ICMP,
322 				"network/protocols/icmp/v1");
323 			return B_OK;
324 		}
325 
326 		case B_MODULE_UNINIT:
327 			return B_OK;
328 
329 		default:
330 			return B_ERROR;
331 	}
332 }
333 
334 
335 net_protocol_module_info sICMPModule = {
336 	{
337 		"network/protocols/icmp/v1",
338 		0,
339 		icmp_std_ops
340 	},
341 	icmp_init_protocol,
342 	icmp_uninit_protocol,
343 	icmp_open,
344 	icmp_close,
345 	icmp_free,
346 	icmp_connect,
347 	icmp_accept,
348 	icmp_control,
349 	icmp_getsockopt,
350 	icmp_setsockopt,
351 	icmp_bind,
352 	icmp_unbind,
353 	icmp_listen,
354 	icmp_shutdown,
355 	icmp_send_data,
356 	icmp_send_routed_data,
357 	icmp_send_avail,
358 	icmp_read_data,
359 	icmp_read_avail,
360 	icmp_get_domain,
361 	icmp_get_mtu,
362 	icmp_receive_data,
363 	NULL,
364 	icmp_error,
365 	icmp_error_reply,
366 };
367 
368 module_dependency module_dependencies[] = {
369 	{NET_STACK_MODULE_NAME, (module_info **)&sStackModule},
370 	{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
371 	{}
372 };
373 
374 module_info *modules[] = {
375 	(module_info *)&sICMPModule,
376 	NULL
377 };
378