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