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_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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 579 shutdown_userland_ipc(void) 580 { 581 delete_port(gStackPort); 582 kill_thread(gConnectionOpener); 583 } 584 585 586 #ifdef COMMUNICATION_TEST 587 int 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