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