xref: /haiku/src/add-ons/kernel/network/protocols/icmp/icmp.cpp (revision c7509fce9db782326f159843f1b028b5f5dcb1d2)
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 static net_stack_module_info *sStackModule;
66 struct net_buffer_module_info *gBufferModule;
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_bind(net_protocol *protocol, struct sockaddr *address)
134 {
135 	return B_ERROR;
136 }
137 
138 
139 status_t
140 icmp_unbind(net_protocol *protocol, struct sockaddr *address)
141 {
142 	return B_ERROR;
143 }
144 
145 
146 status_t
147 icmp_listen(net_protocol *protocol, int count)
148 {
149 	return EOPNOTSUPP;
150 }
151 
152 
153 status_t
154 icmp_shutdown(net_protocol *protocol, int direction)
155 {
156 	return EOPNOTSUPP;
157 }
158 
159 
160 status_t
161 icmp_send_data(net_protocol *protocol, net_buffer *buffer)
162 {
163 	return protocol->next->module->send_data(protocol->next, buffer);
164 }
165 
166 
167 status_t
168 icmp_send_routed_data(net_protocol *protocol, struct net_route *route,
169 	net_buffer *buffer)
170 {
171 	return protocol->next->module->send_routed_data(protocol->next, route, buffer);
172 }
173 
174 
175 ssize_t
176 icmp_send_avail(net_protocol *protocol)
177 {
178 	return B_ERROR;
179 }
180 
181 
182 status_t
183 icmp_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
184 	net_buffer **_buffer)
185 {
186 	return B_ERROR;
187 }
188 
189 
190 ssize_t
191 icmp_read_avail(net_protocol *protocol)
192 {
193 	return B_ERROR;
194 }
195 
196 
197 struct net_domain *
198 icmp_get_domain(net_protocol *protocol)
199 {
200 	return protocol->next->module->get_domain(protocol->next);
201 }
202 
203 
204 size_t
205 icmp_get_mtu(net_protocol *protocol, const struct sockaddr *address)
206 {
207 	return protocol->next->module->get_mtu(protocol->next, address);
208 }
209 
210 
211 status_t
212 icmp_receive_data(net_buffer *buffer)
213 {
214 	TRACE(("ICMP received some data, buffer length %lu\n", buffer->size));
215 
216 	NetBufferHeaderReader<icmp_header> bufferHeader(buffer);
217 	if (bufferHeader.Status() < B_OK)
218 		return bufferHeader.Status();
219 
220 	icmp_header &header = bufferHeader.Data();
221 
222 	TRACE(("  got type %u, code %u, checksum %u\n", header.type, header.code,
223 		ntohs(header.checksum)));
224 	TRACE(("  computed checksum: %ld\n", gBufferModule->checksum(buffer, 0, buffer->size, true)));
225 
226 	if (gBufferModule->checksum(buffer, 0, buffer->size, true) != 0)
227 		return B_BAD_DATA;
228 
229 	switch (header.type) {
230 		case ICMP_TYPE_ECHO_REPLY:
231 			break;
232 
233 		case ICMP_TYPE_ECHO_REQUEST:
234 		{
235 			net_domain *domain;
236 			if (buffer->interface != NULL)
237 				domain = buffer->interface->domain;
238 			else
239 				domain = sStackModule->get_domain(buffer->source.ss_family);
240 			if (domain == NULL || domain->module == NULL)
241 				break;
242 
243 			net_buffer *reply = gBufferModule->duplicate(buffer);
244 			if (reply == NULL)
245 				return B_NO_MEMORY;
246 
247 			// switch source/destination address
248 			memcpy(&reply->source, &buffer->destination, buffer->destination.ss_len);
249 			memcpy(&reply->destination, &buffer->source, buffer->source.ss_len);
250 
251 			// There already is an ICMP header, and we'll reuse it
252 			NetBufferHeaderReader<icmp_header> header(reply);
253 
254 			header->type = ICMP_TYPE_ECHO_REPLY;
255 			header->code = 0;
256 			header->checksum = 0;
257 
258 			header.Sync();
259 
260 			*ICMPChecksumField(reply) = gBufferModule->checksum(reply, 0,
261 					reply->size, true);
262 
263 			status_t status = domain->module->send_data(NULL, reply);
264 			if (status < B_OK) {
265 				gBufferModule->free(reply);
266 				return status;
267 			}
268 		}
269 	}
270 
271 	gBufferModule->free(buffer);
272 	return B_OK;
273 }
274 
275 
276 status_t
277 icmp_error(uint32 code, net_buffer *data)
278 {
279 	return B_ERROR;
280 }
281 
282 
283 status_t
284 icmp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
285 	void *errorData)
286 {
287 	return B_ERROR;
288 }
289 
290 
291 //	#pragma mark -
292 
293 
294 static status_t
295 icmp_std_ops(int32 op, ...)
296 {
297 	switch (op) {
298 		case B_MODULE_INIT:
299 		{
300 			status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule);
301 			if (status < B_OK)
302 				return status;
303 			status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
304 			if (status < B_OK) {
305 				put_module(NET_STACK_MODULE_NAME);
306 				return status;
307 			}
308 
309 			sStackModule->register_domain_protocols(AF_INET, SOCK_DGRAM, IPPROTO_ICMP,
310 				"network/protocols/icmp/v1",
311 				"network/protocols/ipv4/v1",
312 				NULL);
313 
314 			sStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_ICMP,
315 				"network/protocols/icmp/v1");
316 			return B_OK;
317 		}
318 
319 		case B_MODULE_UNINIT:
320 			put_module(NET_BUFFER_MODULE_NAME);
321 			put_module(NET_STACK_MODULE_NAME);
322 			return B_OK;
323 
324 		default:
325 			return B_ERROR;
326 	}
327 }
328 
329 
330 net_protocol_module_info sICMPModule = {
331 	{
332 		"network/protocols/icmp/v1",
333 		0,
334 		icmp_std_ops
335 	},
336 	icmp_init_protocol,
337 	icmp_uninit_protocol,
338 	icmp_open,
339 	icmp_close,
340 	icmp_free,
341 	icmp_connect,
342 	icmp_accept,
343 	icmp_control,
344 	icmp_bind,
345 	icmp_unbind,
346 	icmp_listen,
347 	icmp_shutdown,
348 	icmp_send_data,
349 	icmp_send_routed_data,
350 	icmp_send_avail,
351 	icmp_read_data,
352 	icmp_read_avail,
353 	icmp_get_domain,
354 	icmp_get_mtu,
355 	icmp_receive_data,
356 	icmp_error,
357 	icmp_error_reply,
358 };
359 
360 module_info *modules[] = {
361 	(module_info *)&sICMPModule,
362 	NULL
363 };
364