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