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