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