xref: /haiku/src/add-ons/kernel/network/protocols/icmp/icmp.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
1 /*
2  * Copyright 2006, 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 
24 struct icmp_header {
25 	uint8	type;
26 	uint8	code;
27 	uint16	checksum;
28 	union {
29 		struct {
30 			uint16	id;
31 			uint16	sequence;
32 		} echo;
33 		struct {
34 			in_addr_t gateway;
35 		} redirect;
36 		struct {
37 			uint16	_reserved;
38 			uint16	next_mtu;
39 		} path_mtu;
40 		uint32 zero;
41 	};
42 };
43 
44 #define ICMP_TYPE_ECHO_REPLY	0
45 #define ICMP_TYPE_UNREACH		3
46 #define ICMP_TYPE_REDIRECT		5
47 #define ICMP_TYPE_ECHO_REQUEST	8
48 
49 // type unreach codes
50 #define ICMP_CODE_UNREACH_NEED_FRAGMENT	4	// this is used for path MTU discovery
51 
52 struct icmp_protocol : net_protocol {
53 };
54 
55 
56 static net_stack_module_info *sStackModule;
57 struct net_buffer_module_info *sBufferModule;
58 
59 
60 net_protocol *
61 icmp_init_protocol(net_socket *socket)
62 {
63 	icmp_protocol *protocol = new (std::nothrow) icmp_protocol;
64 	if (protocol == NULL)
65 		return NULL;
66 
67 	return protocol;
68 }
69 
70 
71 status_t
72 icmp_uninit_protocol(net_protocol *protocol)
73 {
74 	delete protocol;
75 	return B_OK;
76 }
77 
78 
79 status_t
80 icmp_open(net_protocol *protocol)
81 {
82 	return B_OK;
83 }
84 
85 
86 status_t
87 icmp_close(net_protocol *protocol)
88 {
89 	return B_OK;
90 }
91 
92 
93 status_t
94 icmp_free(net_protocol *protocol)
95 {
96 	return B_OK;
97 }
98 
99 
100 status_t
101 icmp_connect(net_protocol *protocol, const struct sockaddr *address)
102 {
103 	return B_ERROR;
104 }
105 
106 
107 status_t
108 icmp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
109 {
110 	return EOPNOTSUPP;
111 }
112 
113 
114 status_t
115 icmp_control(net_protocol *protocol, int level, int option, void *value,
116 	size_t *_length)
117 {
118 	return protocol->next->module->control(protocol->next, level, option,
119 		value, _length);
120 }
121 
122 
123 status_t
124 icmp_bind(net_protocol *protocol, struct sockaddr *address)
125 {
126 	return B_ERROR;
127 }
128 
129 
130 status_t
131 icmp_unbind(net_protocol *protocol, struct sockaddr *address)
132 {
133 	return B_ERROR;
134 }
135 
136 
137 status_t
138 icmp_listen(net_protocol *protocol, int count)
139 {
140 	return EOPNOTSUPP;
141 }
142 
143 
144 status_t
145 icmp_shutdown(net_protocol *protocol, int direction)
146 {
147 	return EOPNOTSUPP;
148 }
149 
150 
151 status_t
152 icmp_send_data(net_protocol *protocol, net_buffer *buffer)
153 {
154 	return protocol->next->module->send_data(protocol->next, buffer);
155 }
156 
157 
158 status_t
159 icmp_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 
166 ssize_t
167 icmp_send_avail(net_protocol *protocol)
168 {
169 	return B_ERROR;
170 }
171 
172 
173 status_t
174 icmp_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
175 	net_buffer **_buffer)
176 {
177 	return B_ERROR;
178 }
179 
180 
181 ssize_t
182 icmp_read_avail(net_protocol *protocol)
183 {
184 	return B_ERROR;
185 }
186 
187 
188 struct net_domain *
189 icmp_get_domain(net_protocol *protocol)
190 {
191 	return protocol->next->module->get_domain(protocol->next);
192 }
193 
194 
195 size_t
196 icmp_get_mtu(net_protocol *protocol, const struct sockaddr *address)
197 {
198 	return protocol->next->module->get_mtu(protocol->next, address);
199 }
200 
201 
202 status_t
203 icmp_receive_data(net_buffer *buffer)
204 {
205 	dprintf("ICMP received some data, buffer length %lu\n", buffer->size);
206 
207 	NetBufferHeader<icmp_header> bufferHeader(buffer);
208 	if (bufferHeader.Status() < B_OK)
209 		return bufferHeader.Status();
210 
211 	icmp_header &header = bufferHeader.Data();
212 	bufferHeader.Detach();
213 		// the pointer stays valid after this
214 
215 	dprintf("  got type %u, code %u, checksum %u\n", header.type, header.code,
216 		ntohs(header.checksum));
217 	dprintf("  computed checksum: %ld\n", sBufferModule->checksum(buffer, 0, buffer->size, true));
218 	if (sBufferModule->checksum(buffer, 0, buffer->size, true) != 0)
219 		return B_BAD_DATA;
220 
221 	switch (header.type) {
222 		case ICMP_TYPE_ECHO_REPLY:
223 			break;
224 
225 		case ICMP_TYPE_ECHO_REQUEST:
226 		{
227 			net_domain *domain;
228 			if (buffer->interface != NULL)
229 				domain = buffer->interface->domain;
230 			else
231 				domain = sStackModule->get_domain(buffer->source.ss_family);
232 			if (domain == NULL || domain->module == NULL)
233 				break;
234 
235 			net_buffer *reply = sBufferModule->duplicate(buffer);
236 			if (reply == NULL)
237 				return B_NO_MEMORY;
238 
239 			// switch source/destination address
240 			memcpy(&reply->source, &buffer->destination, buffer->destination.ss_len);
241 			memcpy(&reply->destination, &buffer->source, buffer->source.ss_len);
242 
243 			// There already is an ICMP header, and we'll reuse it
244 			icmp_header *header;
245 			status_t status = sBufferModule->direct_access(reply,
246 				0, sizeof(icmp_header), (void **)&header);
247 			if (status == B_OK) {
248 				header->type = ICMP_TYPE_ECHO_REPLY;
249 				header->code = 0;
250 				header->checksum = 0;
251 				header->checksum = sBufferModule->checksum(reply, 0, reply->size, true);
252 			}
253 
254 			if (status == B_OK)
255 				status = domain->module->send_data(NULL, reply);
256 
257 			if (status < B_OK) {
258 				sBufferModule->free(reply);
259 				return status;
260 			}
261 		}
262 	}
263 
264 	sBufferModule->free(buffer);
265 	return B_OK;
266 }
267 
268 
269 status_t
270 icmp_error(uint32 code, net_buffer *data)
271 {
272 	return B_ERROR;
273 }
274 
275 
276 status_t
277 icmp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
278 	void *errorData)
279 {
280 	return B_ERROR;
281 }
282 
283 
284 //	#pragma mark -
285 
286 
287 static status_t
288 icmp_std_ops(int32 op, ...)
289 {
290 	switch (op) {
291 		case B_MODULE_INIT:
292 		{
293 			status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule);
294 			if (status < B_OK)
295 				return status;
296 			status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule);
297 			if (status < B_OK) {
298 				put_module(NET_STACK_MODULE_NAME);
299 				return status;
300 			}
301 
302 			sStackModule->register_domain_protocols(AF_INET, SOCK_DGRAM, IPPROTO_ICMP,
303 				"network/protocols/icmp/v1",
304 				"network/protocols/ipv4/v1",
305 				NULL);
306 
307 			sStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_ICMP,
308 				"network/protocols/icmp/v1");
309 			return B_OK;
310 		}
311 
312 		case B_MODULE_UNINIT:
313 			put_module(NET_BUFFER_MODULE_NAME);
314 			put_module(NET_STACK_MODULE_NAME);
315 			return B_OK;
316 
317 		default:
318 			return B_ERROR;
319 	}
320 }
321 
322 
323 net_protocol_module_info sICMPModule = {
324 	{
325 		"network/protocols/icmp/v1",
326 		0,
327 		icmp_std_ops
328 	},
329 	icmp_init_protocol,
330 	icmp_uninit_protocol,
331 	icmp_open,
332 	icmp_close,
333 	icmp_free,
334 	icmp_connect,
335 	icmp_accept,
336 	icmp_control,
337 	icmp_bind,
338 	icmp_unbind,
339 	icmp_listen,
340 	icmp_shutdown,
341 	icmp_send_data,
342 	icmp_send_routed_data,
343 	icmp_send_avail,
344 	icmp_read_data,
345 	icmp_read_avail,
346 	icmp_get_domain,
347 	icmp_get_mtu,
348 	icmp_receive_data,
349 	icmp_error,
350 	icmp_error_reply,
351 };
352 
353 module_info *modules[] = {
354 	(module_info *)&sICMPModule,
355 	NULL
356 };
357