xref: /haiku/src/tests/add-ons/kernel/network/userland_ipc.c (revision b6f76ebe7153b94820cf35f8db4facc158841abb)
1 /* userland_ipc - Communication between the network driver
2 **		and the userland stack.
3 **
4 ** Initial version by Axel Dörfler, axeld@pinc-software.de
5 ** This file may be used under the terms of the MIT License.
6 */
7 
8 
9 #include "userland_ipc.h"
10 
11 #include "sys/socket.h"
12 #include "net_misc.h"
13 #include "core_module.h"
14 #include "net_module.h"
15 #include "sys/sockio.h"
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include <OS.h>
22 #include <Drivers.h>	// for B_SET_(NON)BLOCKING_IO
23 
24 
25 typedef struct commands_info {
26 	int op;
27 	const char *name;
28 } commands_info;
29 
30 #define C2N(op) { op, #op }
31 
32 commands_info g_commands_info[] = {
33 	C2N(NET_STACK_SOCKET),
34 	C2N(NET_STACK_BIND),
35 	C2N(NET_STACK_RECVFROM),
36 	C2N(NET_STACK_RECV),
37 	C2N(NET_STACK_SENDTO),
38 	C2N(NET_STACK_SEND),
39 	C2N(NET_STACK_LISTEN),
40 	C2N(NET_STACK_ACCEPT),
41 	C2N(NET_STACK_CONNECT),
42 	C2N(NET_STACK_SHUTDOWN),
43 	C2N(NET_STACK_GETSOCKOPT),
44 	C2N(NET_STACK_SETSOCKOPT),
45 	C2N(NET_STACK_GETSOCKNAME),
46 	C2N(NET_STACK_GETPEERNAME),
47 	C2N(NET_STACK_SYSCTL),
48 	C2N(NET_STACK_SELECT),
49 	C2N(NET_STACK_DESELECT),
50 	C2N(NET_STACK_GET_COOKIE),
51 //	C2N(NET_STACK_STOP),
52 	C2N(NET_STACK_NOTIFY_SOCKET_EVENT),
53 	C2N(NET_STACK_CONTROL_NET_MODULE),
54 
55 	// Userland IPC-specific opcodes
56 	C2N(NET_STACK_OPEN),
57 	C2N(NET_STACK_CLOSE),
58 	C2N(NET_STACK_NEW_CONNECTION),
59 
60 	{ 0, "Unknown!" }
61 };
62 
63 #undef C2N
64 
65 
66 
67 extern struct core_module_info *core;
68 
69 // installs a main()
70 //#define COMMUNICATION_TEST
71 
72 #define NUM_COMMANDS 32
73 #define CONNECTION_BUFFER_SIZE (65536 + 4096 - CONNECTION_COMMAND_SIZE)
74 
75 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
76 
77 struct socket; /* forward declaration */
78 
79 typedef struct {
80 	port_id			localPort,port;
81 	area_id			area;
82 	struct socket * socket;
83 
84 	uint8		*buffer;
85 	net_command *commands;
86 	sem_id		commandSemaphore;
87 
88 	int32		openFlags;
89 
90 	thread_id	runner;
91 
92 	// for socket select events support
93 	port_id 	socket_event_port;
94 	void *		notify_cookie;
95 } connection_cookie;
96 
97 
98 port_id gStackPort = -1;
99 thread_id gConnectionOpener = -1;
100 
101 // prototypes
102 static int32 connection_runner(void *_cookie);
103 static status_t init_connection(net_connection *connection, connection_cookie **_cookie);
104 static void shutdown_connection(connection_cookie *cookie);
105 
106 
107 static void
delete_cloned_areas(net_area_info * area)108 delete_cloned_areas(net_area_info *area)
109 {
110 	int32 i;
111 	for (i = 0;i < MAX_NET_AREAS;i++) {
112 		if (area[i].id == 0)
113 			continue;
114 
115 		delete_area(area[i].id);
116 	}
117 }
118 
119 
120 static status_t
clone_command_areas(net_area_info * localArea,net_command * command)121 clone_command_areas(net_area_info *localArea,net_command *command)
122 {
123 	int32 i;
124 
125 	memset(localArea,0,sizeof(net_area_info) * MAX_NET_AREAS);
126 
127 	for (i = 0;i < MAX_NET_AREAS;i++) {
128 		if (command->area[i].id <= 0)
129 			continue;
130 
131 		localArea[i].id = clone_area("net connection",(void **)&localArea[i].offset,B_ANY_ADDRESS,
132 				B_READ_AREA | B_WRITE_AREA,command->area[i].id);
133 		if (localArea[i].id < B_OK)
134 			return localArea[i].id;
135 	}
136 	return B_OK;
137 }
138 
139 
140 static uint8 *
convert_address(net_area_info * fromArea,net_area_info * toArea,uint8 * data)141 convert_address(net_area_info *fromArea,net_area_info *toArea,uint8 *data)
142 {
143 	if (data == NULL)
144 		return NULL;
145 
146 	if (data < fromArea->offset) {
147 		printf("could not translate address: %p\n",data);
148 		return data;
149 	}
150 
151 	return data - fromArea->offset + toArea->offset;
152 }
153 
154 
155 static inline void *
convert_to_local(net_area_info * foreignArea,net_area_info * localArea,void * data)156 convert_to_local(net_area_info *foreignArea,net_area_info *localArea,void *data)
157 {
158 	return convert_address(foreignArea,localArea,data);
159 }
160 
161 
162 static void *
convert_to_foreign(net_area_info * foreignArea,net_area_info * localArea,void * data)163 convert_to_foreign(net_area_info *foreignArea,net_area_info *localArea,void *data)
164 {
165 	return convert_address(localArea,foreignArea,data);
166 }
167 
168 
169 static void
on_socket_event(void * socket,uint32 event,void * cookie)170 on_socket_event(void * socket, uint32 event, void * cookie)
171 {
172 	connection_cookie * cc = (connection_cookie *) cookie;
173 	struct socket_event_data sed;
174 	status_t status;
175 
176 	if (!cc)
177 		return;
178 
179 	if (cc->socket != socket) {
180 		printf("on_socket_event(%p, %ld, %p): socket is higly suspect! Aborting.\n", socket, event, cookie);
181 		return;
182 	}
183 
184 	printf("on_socket_event(%p, %ld, %p)\n", socket, event, cookie);
185 
186 	sed.event = event;
187 	sed.cookie = cc->notify_cookie;
188 
189 	// TODO: don't block here => write_port_etc() ?
190 	status = write_port(cc->socket_event_port, NET_STACK_SOCKET_EVENT_NOTIFICATION,
191 				&sed, sizeof(sed));
192 	if (status != B_OK)
193 		printf("write_port(NET_STACK_SOCKET_EVENT_NOTIFICATION) failure: %s\n",
194 			strerror(status));
195 	return;
196 }
197 
198 
199 
200 static int32
connection_runner(void * _cookie)201 connection_runner(void *_cookie)
202 {
203 	connection_cookie *cookie = (connection_cookie *)_cookie;
204 	bool run = true;
205 	commands_info *ci;
206 
207 	while (run) {
208 		net_area_info area[MAX_NET_AREAS];
209 		net_command *command;
210 		status_t status = B_OK;
211 		struct stack_driver_args *args;
212 		int32 index;
213 		ssize_t bytes = read_port(cookie->localPort,&index,NULL,0);
214 		if (bytes < B_OK)
215 			break;
216 
217 		if (index >= NUM_COMMANDS || index < 0) {
218 			printf("got bad command index: %lx\n",index);
219 			continue;
220 		}
221 		command = cookie->commands + index;
222 		if (clone_command_areas(area, command) < B_OK) {
223 			printf("could not clone command areas!\n");
224 			continue;
225 		}
226 
227 
228 		ci = g_commands_info;
229 		while(ci && ci->op) {
230 			if (ci->op == command->op)
231 				break;
232 			ci++;
233 		}
234 
235 		args = convert_to_local(&command->area[0],&area[0], command->data);
236 		printf("command %s (0x%lx) (index = %ld), buffer = %p, length = %ld, result = %ld\n",
237 			ci->name, command->op, index, args, command->length, command->result);
238 
239 		switch (command->op) {
240 			case NET_STACK_OPEN:
241 				cookie->openFlags = args->u.integer;
242 				printf("Opening socket connection, mode = %lx!\n", cookie->openFlags);
243 				break;
244 
245 			case NET_STACK_CLOSE:
246 				printf("Closing socket connection...\n");
247 				run = false;
248 				break;
249 
250 			case NET_STACK_SOCKET:
251 				printf("Creating stack socket... family = %d, type = %d, proto = %d\n",
252 					args->u.socket.family, args->u.socket.type, args->u.socket.proto);
253 				status = core->socket_init(&cookie->socket);
254 				if (status == 0)
255 					status = core->socket_create(cookie->socket, args->u.socket.family, args->u.socket.type, args->u.socket.proto);
256 				break;
257 
258 			case NET_STACK_GETSOCKOPT:
259 			case NET_STACK_SETSOCKOPT:
260 				if (command->op == (int32) NET_STACK_GETSOCKOPT) {
261 					status = core->socket_getsockopt(cookie->socket, args->u.sockopt.level, args->u.sockopt.option,
262 						convert_to_local(&command->area[1], &area[1], args->u.sockopt.optval),
263 						(size_t *) &args->u.sockopt.optlen);
264 				} else {
265 					status = core->socket_setsockopt(cookie->socket, args->u.sockopt.level, args->u.sockopt.option,
266 						(const void *) convert_to_local(&command->area[1], &area[1], args->u.sockopt.optval),
267 						args->u.sockopt.optlen);
268 				}
269 				break;
270 
271 			case NET_STACK_CONNECT:
272 			case NET_STACK_BIND:
273 			case NET_STACK_GETSOCKNAME:
274 			case NET_STACK_GETPEERNAME: {
275 				caddr_t addr = (caddr_t) convert_to_local(&command->area[1], &area[1], args->u.sockaddr.addr);
276 
277 				switch (command->op) {
278 					case NET_STACK_CONNECT:
279 						status = core->socket_connect(cookie->socket, addr, args->u.sockaddr.addrlen);
280 						break;
281 					case NET_STACK_BIND:
282 						status = core->socket_bind(cookie->socket, addr, args->u.sockaddr.addrlen);
283 						break;
284 					case NET_STACK_GETSOCKNAME:
285 						status = core->socket_getsockname(cookie->socket, (struct sockaddr *) addr, &args->u.sockaddr.addrlen);
286 						break;
287 					case NET_STACK_GETPEERNAME:
288 						status = core->socket_getpeername(cookie->socket,(struct sockaddr *) addr, &args->u.sockaddr.addrlen);
289 						break;
290 				}
291 				break;
292 			}
293 			case NET_STACK_LISTEN:
294 				status = core->socket_listen(cookie->socket, args->u.integer);
295 				break;
296 
297 			case NET_STACK_GET_COOKIE:
298 				/* this is needed by accept() call, to be able to pass back
299 				 * in NET_STACK_ACCEPT opcode the cookie of the filedescriptor to
300 				 * use for the new accepted socket
301 				 */
302 				*((void **) args) = cookie;
303 				break;
304 
305 			case NET_STACK_ACCEPT:
306 			{
307 				connection_cookie *otherCookie = (connection_cookie *) args->u.accept.cookie;
308 				status = core->socket_accept(cookie->socket, &otherCookie->socket,
309 					convert_to_local(&command->area[1], &area[1], args->u.accept.addr),
310 					&args->u.accept.addrlen);
311 			}
312 			case NET_STACK_SEND:
313 			{
314 				struct iovec iov;
315 				int flags = 0;
316 
317 				iov.iov_base = convert_to_local(&command->area[1], &area[1], args->u.transfer.data);
318 				iov.iov_len = args->u.transfer.datalen;
319 
320 				status = core->socket_writev(cookie->socket, &iov, flags);
321 				break;
322 			}
323 			case NET_STACK_RECV:
324 			{
325 				struct iovec iov;
326 				int flags = 0;
327 
328 				iov.iov_base = convert_to_local(&command->area[1], &area[1], args->u.transfer.data);
329 				iov.iov_len = args->u.transfer.datalen;
330 
331 				/* flags gets ignored here... */
332 				status = core->socket_readv(cookie->socket, &iov, &flags);
333 				break;
334 			}
335 			case NET_STACK_RECVFROM:
336 			{
337 				struct msghdr *msg = (struct msghdr *) args;
338 				int received;
339 
340 				msg->msg_name = convert_to_local(&command->area[1],&area[1],msg->msg_name);
341 				msg->msg_iov = convert_to_local(&command->area[2],&area[2],msg->msg_iov);
342 				msg->msg_control = convert_to_local(&command->area[3],&area[3],msg->msg_control);
343 
344 				status = core->socket_recv(cookie->socket, msg, (caddr_t)&msg->msg_namelen,&received);
345 				if (status == 0)
346 					status = received;
347 
348 				msg->msg_name = convert_to_foreign(&command->area[1],&area[1],msg->msg_name);
349 				msg->msg_iov = convert_to_foreign(&command->area[2],&area[2],msg->msg_iov);
350 				msg->msg_control = convert_to_foreign(&command->area[3],&area[3],msg->msg_control);
351 				break;
352 			}
353 			case NET_STACK_SENDTO:
354 			{
355 				struct msghdr *msg = (struct msghdr *) args;
356 				int sent;
357 
358 				msg->msg_name = convert_to_local(&command->area[1],&area[1],msg->msg_name);
359 				msg->msg_iov = convert_to_local(&command->area[2],&area[2],msg->msg_iov);
360 				msg->msg_control = convert_to_local(&command->area[3],&area[3],msg->msg_control);
361 
362 				status = core->socket_send(cookie->socket,msg,msg->msg_flags,&sent);
363 				if (status == 0)
364 					status = sent;
365 
366 				msg->msg_name = convert_to_foreign(&command->area[1],&area[1],msg->msg_name);
367 				msg->msg_iov = convert_to_foreign(&command->area[2],&area[2],msg->msg_iov);
368 				msg->msg_control = convert_to_foreign(&command->area[3],&area[3],msg->msg_control);
369 				break;
370 			}
371 
372 			case NET_STACK_NOTIFY_SOCKET_EVENT:
373 			{
374 				struct notify_socket_event_args *nsea = (struct notify_socket_event_args *) args;
375 
376 				cookie->socket_event_port = nsea->notify_port;
377 				cookie->notify_cookie = nsea->cookie;
378 
379 				if (cookie->socket_event_port != -1)
380 					// start notify socket event
381 					status = core->socket_set_event_callback(cookie->socket, on_socket_event, cookie, 0);
382 				else
383 					// stop notify socket event
384 					status = core->socket_set_event_callback(cookie->socket, NULL, NULL, 0);
385 				break;
386 			}
387 
388 			case NET_STACK_SYSCTL:
389 			{
390 				status = core->net_sysctl(convert_to_local(&command->area[1],&area[1], args->u.sysctl.name),
391 					args->u.sysctl.namelen, convert_to_local(&command->area[2],&area[2],args->u.sysctl.oldp),
392 					convert_to_local(&command->area[3],&area[3],args->u.sysctl.oldlenp),
393 					convert_to_local(&command->area[4],&area[4],args->u.sysctl.newp),
394 					args->u.sysctl.newlen);
395 				break;
396 			}
397 /*
398 			case NET_STACK_STOP:
399 				core->stop();
400 				break;
401 */
402 			case NET_STACK_CONTROL_NET_MODULE:
403 				// TODO!
404 				break;
405 
406 			case B_SET_BLOCKING_IO:
407 				cookie->openFlags &= ~O_NONBLOCK;
408 				break;
409 
410 			case B_SET_NONBLOCKING_IO:
411 				cookie->openFlags |= O_NONBLOCK;
412 				break;
413 
414 			case OSIOCGIFCONF:
415 			case SIOCGIFCONF:
416 			{
417 				struct ifconf *ifc = (struct ifconf *) args;
418 				ifc->ifc_buf = convert_to_local(&command->area[1], &area[1], ifc->ifc_buf);
419 
420 				status = core->socket_ioctl(cookie->socket, command->op, (char *) args);
421 
422 				ifc->ifc_buf = convert_to_foreign(&command->area[1], &area[1], ifc->ifc_buf);
423 				break;
424 			}
425 
426 			default:
427 				status = core->socket_ioctl(cookie->socket,command->op, (char *) args);
428 				break;
429 		}
430 		// mark the command as done
431 		command->result = status;
432 		command->op = 0;
433 		delete_cloned_areas(area);
434 
435 		// notify the command pipeline that we're done with the command
436 		release_sem(cookie->commandSemaphore);
437 	}
438 
439 	cookie->runner = -1;
440 	shutdown_connection(cookie);
441 
442 	return 0;
443 }
444 
445 
446 static status_t
init_connection(net_connection * connection,connection_cookie ** _cookie)447 init_connection(net_connection *connection, connection_cookie **_cookie)
448 {
449 	connection_cookie *cookie;
450 	net_command *commands;
451 
452 	cookie = (connection_cookie *) malloc(sizeof(connection_cookie));
453 	if (cookie == NULL) {
454 		fprintf(stderr, "couldn't allocate memory for cookie.\n");
455 		return B_NO_MEMORY;
456 	}
457 
458 	connection->area = create_area("net connection", (void *) &commands, B_ANY_ADDRESS,
459 			CONNECTION_BUFFER_SIZE + CONNECTION_COMMAND_SIZE,
460 			B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
461 	if (connection->area < B_OK) {
462 		fprintf(stderr, "couldn't create area: %s.\n", strerror(connection->area));
463 		free(cookie);
464 		return connection->area;
465 	}
466 	memset(commands,0,NUM_COMMANDS * sizeof(net_command));
467 
468 	connection->port = create_port(CONNECTION_QUEUE_LENGTH, "net stack connection");
469 	if (connection->port < B_OK) {
470 		fprintf(stderr, "couldn't create port: %s.\n", strerror(connection->port));
471 		delete_area(connection->area);
472 		free(cookie);
473 		return connection->port;
474 	}
475 
476 	connection->commandSemaphore = create_sem(0, "net command queue");
477 	if (connection->commandSemaphore < B_OK) {
478 		fprintf(stderr, "couldn't create semaphore: %s.\n", strerror(connection->commandSemaphore));
479 		delete_area(connection->area);
480 		delete_port(connection->port);
481 		free(cookie);
482 		return connection->commandSemaphore;
483 	}
484 
485 	cookie->runner = spawn_thread(connection_runner, "connection runner", B_NORMAL_PRIORITY, cookie);
486 	if (cookie->runner < B_OK) {
487 		fprintf(stderr, "couldn't create thread: %s.\n", strerror(cookie->runner));
488 		delete_sem(connection->commandSemaphore);
489 		delete_area(connection->area);
490 		delete_port(connection->port);
491 		free(cookie);
492 		return B_ERROR;
493 	}
494 
495 	connection->socket_thread = cookie->runner;
496 
497 	connection->numCommands = NUM_COMMANDS;
498 	connection->bufferSize = CONNECTION_BUFFER_SIZE;
499 
500 	// setup connection cookie
501 	cookie->area = connection->area;
502 	cookie->commands = commands;
503 	cookie->buffer = (uint8 *)commands + CONNECTION_COMMAND_SIZE;
504 	cookie->commandSemaphore = connection->commandSemaphore;
505 	cookie->localPort = connection->port;
506 	cookie->openFlags = 0;
507 
508 	cookie->socket_event_port = -1;
509 	cookie->notify_cookie = NULL;
510 
511 	resume_thread(cookie->runner);
512 
513 	*_cookie = cookie;
514 	return B_OK;
515 }
516 
517 
518 static void
shutdown_connection(connection_cookie * cookie)519 shutdown_connection(connection_cookie *cookie)
520 {
521 	printf("free cookie: %p\n",cookie);
522 	kill_thread(cookie->runner);
523 
524 	delete_port(cookie->localPort);
525 	delete_sem(cookie->commandSemaphore);
526 	delete_area(cookie->area);
527 
528 	free(cookie);
529 }
530 
531 
532 static int32
connection_opener(void * _unused)533 connection_opener(void *_unused)
534 {
535 	while(true) {
536 		port_id port;
537 		int32 msg;
538 		ssize_t bytes = read_port(gStackPort, &msg, &port, sizeof(port_id));
539 		if (bytes < B_OK)
540 			return bytes;
541 
542 		if (msg == (int32) NET_STACK_NEW_CONNECTION) {
543 			net_connection connection;
544 			connection_cookie *cookie;
545 
546 			printf("incoming connection...\n");
547 			if (init_connection(&connection, &cookie) == B_OK)
548 				write_port(port, NET_STACK_NEW_CONNECTION, &connection, sizeof(net_connection));
549 		} else
550 			fprintf(stderr, "connection_opener: received unknown command: %lx (expected = %lx)\n",
551 				msg, (int32) NET_STACK_NEW_CONNECTION);
552 	}
553 	return 0;
554 }
555 
556 
557 status_t
init_userland_ipc(void)558 init_userland_ipc(void)
559 {
560 	gStackPort = create_port(CONNECTION_QUEUE_LENGTH, NET_STACK_PORTNAME);
561 	if (gStackPort < B_OK)
562 		return gStackPort;
563 
564 	gConnectionOpener = spawn_thread(connection_opener, "connection opener", B_NORMAL_PRIORITY, NULL);
565 	if (resume_thread(gConnectionOpener) < B_OK) {
566 		delete_port(gStackPort);
567 		if (gConnectionOpener >= B_OK) {
568 			kill_thread(gConnectionOpener);
569 			return B_BAD_THREAD_STATE;
570 		}
571 		return gConnectionOpener;
572 	}
573 
574 	return B_OK;
575 }
576 
577 
578 void
shutdown_userland_ipc(void)579 shutdown_userland_ipc(void)
580 {
581 	delete_port(gStackPort);
582 	kill_thread(gConnectionOpener);
583 }
584 
585 
586 #ifdef COMMUNICATION_TEST
587 int
main(void)588 main(void)
589 {
590 	char buffer[8];
591 
592 	if (init_userland_ipc() < B_OK)
593 		return -1;
594 
595 	puts("Userland_ipc - test is running. Press <Return> to quit.");
596 	fgets(buffer, sizeof(buffer), stdin);
597 
598 	shutdown_userland_ipc();
599 
600 	return 0;
601 }
602 #endif	/* COMMUNICATION_TEST */
603