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
25typedef struct commands_info {
26	int op;
27	const char *name;
28} commands_info;
29
30#define C2N(op) { op, #op }
31
32commands_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
67extern 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
77struct socket; /* forward declaration */
78
79typedef 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
98port_id gStackPort = -1;
99thread_id gConnectionOpener = -1;
100
101// prototypes
102static int32 connection_runner(void *_cookie);
103static status_t init_connection(net_connection *connection, connection_cookie **_cookie);
104static void shutdown_connection(connection_cookie *cookie);
105
106
107static void
108delete_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
120static status_t
121clone_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
140static uint8 *
141convert_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
155static inline void *
156convert_to_local(net_area_info *foreignArea,net_area_info *localArea,void *data)
157{
158	return convert_address(foreignArea,localArea,data);
159}
160
161
162static void *
163convert_to_foreign(net_area_info *foreignArea,net_area_info *localArea,void *data)
164{
165	return convert_address(localArea,foreignArea,data);
166}
167
168
169static void
170on_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
200static int32
201connection_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
446static status_t
447init_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
518static void
519shutdown_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
532static int32
533connection_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
557status_t
558init_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
578void
579shutdown_userland_ipc(void)
580{
581	delete_port(gStackPort);
582	kill_thread(gConnectionOpener);
583}
584
585
586#ifdef COMMUNICATION_TEST
587int
588main(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