|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2008 Michael Brown <[email protected]>. ! 3: * ! 4: * This program is free software; you can redistribute it and/or ! 5: * modify it under the terms of the GNU General Public License as ! 6: * published by the Free Software Foundation; either version 2 of the ! 7: * License, or any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, but ! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 12: * General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program; if not, write to the Free Software ! 16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: #include <stdint.h> ! 22: #include <stdlib.h> ! 23: #include <string.h> ! 24: #include <strings.h> ! 25: #include <errno.h> ! 26: #include <assert.h> ! 27: #include <byteswap.h> ! 28: #include <ipxe/features.h> ! 29: #include <ipxe/iobuf.h> ! 30: #include <ipxe/bitmap.h> ! 31: #include <ipxe/xfer.h> ! 32: #include <ipxe/open.h> ! 33: #include <ipxe/uri.h> ! 34: #include <ipxe/tcpip.h> ! 35: #include <ipxe/timer.h> ! 36: #include <ipxe/retry.h> ! 37: ! 38: /** @file ! 39: * ! 40: * Scalable Local Area Multicast protocol ! 41: * ! 42: * The SLAM protocol is supported only by Etherboot; it was designed ! 43: * and implemented by Eric Biederman. A server implementation is ! 44: * available in contrib/mini-slamd. There does not appear to be any ! 45: * documentation beyond a few sparse comments in Etherboot's ! 46: * proto_slam.c. ! 47: * ! 48: * SLAM packets use three types of data field: ! 49: * ! 50: * Nul : A single NUL (0) byte, used as a list terminator ! 51: * ! 52: * Raw : A block of raw data ! 53: * ! 54: * Int : A variable-length integer, in big-endian order. The length ! 55: * of the integer is encoded in the most significant three bits. ! 56: * ! 57: * Packets received by the client have the following layout: ! 58: * ! 59: * Int : Transaction identifier. This is an opaque value. ! 60: * ! 61: * Int : Total number of bytes in the transfer. ! 62: * ! 63: * Int : Block size, in bytes. ! 64: * ! 65: * Int : Packet sequence number within the transfer (if this packet ! 66: * contains data). ! 67: * ! 68: * Raw : Packet data (if this packet contains data). ! 69: * ! 70: * Packets transmitted by the client consist of a run-length-encoded ! 71: * representation of the received-blocks bitmap, looking something ! 72: * like: ! 73: * ! 74: * Int : Number of consecutive successfully-received packets ! 75: * Int : Number of consecutive missing packets ! 76: * Int : Number of consecutive successfully-received packets ! 77: * Int : Number of consecutive missing packets ! 78: * .... ! 79: * Nul ! 80: * ! 81: */ ! 82: ! 83: FEATURE ( FEATURE_PROTOCOL, "SLAM", DHCP_EB_FEATURE_SLAM, 1 ); ! 84: ! 85: /** Default SLAM server port */ ! 86: #define SLAM_DEFAULT_PORT 10000 ! 87: ! 88: /** Default SLAM multicast IP address */ ! 89: #define SLAM_DEFAULT_MULTICAST_IP \ ! 90: ( ( 239 << 24 ) | ( 255 << 16 ) | ( 1 << 8 ) | ( 1 << 0 ) ) ! 91: ! 92: /** Default SLAM multicast port */ ! 93: #define SLAM_DEFAULT_MULTICAST_PORT 10000 ! 94: ! 95: /** Maximum SLAM header length */ ! 96: #define SLAM_MAX_HEADER_LEN ( 7 /* transaction id */ + 7 /* total_bytes */ + \ ! 97: 7 /* block_size */ ) ! 98: ! 99: /** Maximum number of blocks to request per NACK ! 100: * ! 101: * This is a policy decision equivalent to selecting a TCP window ! 102: * size. ! 103: */ ! 104: #define SLAM_MAX_BLOCKS_PER_NACK 4 ! 105: ! 106: /** Maximum SLAM NACK length ! 107: * ! 108: * We only ever send a NACK for a single range of up to @c ! 109: * SLAM_MAX_BLOCKS_PER_NACK blocks. ! 110: */ ! 111: #define SLAM_MAX_NACK_LEN ( 7 /* block */ + 7 /* #blocks */ + 1 /* NUL */ ) ! 112: ! 113: /** SLAM slave timeout */ ! 114: #define SLAM_SLAVE_TIMEOUT ( 1 * TICKS_PER_SEC ) ! 115: ! 116: /** A SLAM request */ ! 117: struct slam_request { ! 118: /** Reference counter */ ! 119: struct refcnt refcnt; ! 120: ! 121: /** Data transfer interface */ ! 122: struct interface xfer; ! 123: /** Unicast socket */ ! 124: struct interface socket; ! 125: /** Multicast socket */ ! 126: struct interface mc_socket; ! 127: ! 128: /** Master client retry timer */ ! 129: struct retry_timer master_timer; ! 130: /** Slave client retry timer */ ! 131: struct retry_timer slave_timer; ! 132: ! 133: /** Cached header */ ! 134: uint8_t header[SLAM_MAX_HEADER_LEN]; ! 135: /** Size of cached header */ ! 136: size_t header_len; ! 137: /** Total number of bytes in transfer */ ! 138: unsigned long total_bytes; ! 139: /** Transfer block size */ ! 140: unsigned long block_size; ! 141: /** Number of blocks in transfer */ ! 142: unsigned long num_blocks; ! 143: /** Block bitmap */ ! 144: struct bitmap bitmap; ! 145: /** NACK sent flag */ ! 146: int nack_sent; ! 147: }; ! 148: ! 149: /** ! 150: * Free a SLAM request ! 151: * ! 152: * @v refcnt Reference counter ! 153: */ ! 154: static void slam_free ( struct refcnt *refcnt ) { ! 155: struct slam_request *slam = ! 156: container_of ( refcnt, struct slam_request, refcnt ); ! 157: ! 158: bitmap_free ( &slam->bitmap ); ! 159: free ( slam ); ! 160: } ! 161: ! 162: /** ! 163: * Mark SLAM request as complete ! 164: * ! 165: * @v slam SLAM request ! 166: * @v rc Return status code ! 167: */ ! 168: static void slam_finished ( struct slam_request *slam, int rc ) { ! 169: static const uint8_t slam_disconnect[] = { 0 }; ! 170: ! 171: DBGC ( slam, "SLAM %p finished with status code %d (%s)\n", ! 172: slam, rc, strerror ( rc ) ); ! 173: ! 174: /* Send a disconnect message if we ever sent anything to the ! 175: * server. ! 176: */ ! 177: if ( slam->nack_sent ) { ! 178: xfer_deliver_raw ( &slam->socket, slam_disconnect, ! 179: sizeof ( slam_disconnect ) ); ! 180: } ! 181: ! 182: /* Stop the retry timers */ ! 183: stop_timer ( &slam->master_timer ); ! 184: stop_timer ( &slam->slave_timer ); ! 185: ! 186: /* Close all data transfer interfaces */ ! 187: intf_shutdown ( &slam->socket, rc ); ! 188: intf_shutdown ( &slam->mc_socket, rc ); ! 189: intf_shutdown ( &slam->xfer, rc ); ! 190: } ! 191: ! 192: /**************************************************************************** ! 193: * ! 194: * TX datapath ! 195: * ! 196: */ ! 197: ! 198: /** ! 199: * Add a variable-length value to a SLAM packet ! 200: * ! 201: * @v slam SLAM request ! 202: * @v iobuf I/O buffer ! 203: * @v value Value to add ! 204: * @ret rc Return status code ! 205: * ! 206: * Adds a variable-length value to the end of an I/O buffer. Will ! 207: * always leave at least one byte of tailroom in the I/O buffer (to ! 208: * allow space for the terminating NUL). ! 209: */ ! 210: static int slam_put_value ( struct slam_request *slam, ! 211: struct io_buffer *iobuf, unsigned long value ) { ! 212: uint8_t *data; ! 213: size_t len; ! 214: unsigned int i; ! 215: ! 216: /* Calculate variable length required to store value. Always ! 217: * leave at least one byte in the I/O buffer. ! 218: */ ! 219: len = ( ( flsl ( value ) + 10 ) / 8 ); ! 220: if ( len >= iob_tailroom ( iobuf ) ) { ! 221: DBGC2 ( slam, "SLAM %p cannot add %zd-byte value\n", ! 222: slam, len ); ! 223: return -ENOBUFS; ! 224: } ! 225: /* There is no valid way within the protocol that we can end ! 226: * up trying to push a full-sized long (i.e. without space for ! 227: * the length encoding). ! 228: */ ! 229: assert ( len <= sizeof ( value ) ); ! 230: ! 231: /* Add value */ ! 232: data = iob_put ( iobuf, len ); ! 233: for ( i = len ; i-- ; ) { ! 234: data[i] = value; ! 235: value >>= 8; ! 236: } ! 237: *data |= ( len << 5 ); ! 238: assert ( value == 0 ); ! 239: ! 240: return 0; ! 241: } ! 242: ! 243: /** ! 244: * Send SLAM NACK packet ! 245: * ! 246: * @v slam SLAM request ! 247: * @ret rc Return status code ! 248: */ ! 249: static int slam_tx_nack ( struct slam_request *slam ) { ! 250: struct io_buffer *iobuf; ! 251: unsigned long first_block; ! 252: unsigned long num_blocks; ! 253: uint8_t *nul; ! 254: int rc; ! 255: ! 256: /* Mark NACK as sent, so that we know we have to disconnect later */ ! 257: slam->nack_sent = 1; ! 258: ! 259: /* Allocate I/O buffer */ ! 260: iobuf = xfer_alloc_iob ( &slam->socket, SLAM_MAX_NACK_LEN ); ! 261: if ( ! iobuf ) { ! 262: DBGC ( slam, "SLAM %p could not allocate I/O buffer\n", ! 263: slam ); ! 264: return -ENOMEM; ! 265: } ! 266: ! 267: /* Construct NACK. We always request only a single packet; ! 268: * this allows us to force multicast-TFTP-style flow control ! 269: * on the SLAM server, which will otherwise just blast the ! 270: * data out as fast as it can. On a gigabit network, without ! 271: * RX checksumming, this would inevitably cause packet drops. ! 272: */ ! 273: first_block = bitmap_first_gap ( &slam->bitmap ); ! 274: for ( num_blocks = 1 ; ; num_blocks++ ) { ! 275: if ( num_blocks >= SLAM_MAX_BLOCKS_PER_NACK ) ! 276: break; ! 277: if ( ( first_block + num_blocks ) >= slam->num_blocks ) ! 278: break; ! 279: if ( bitmap_test ( &slam->bitmap, ! 280: ( first_block + num_blocks ) ) ) ! 281: break; ! 282: } ! 283: if ( first_block ) { ! 284: DBGCP ( slam, "SLAM %p transmitting NACK for blocks " ! 285: "%ld-%ld\n", slam, first_block, ! 286: ( first_block + num_blocks - 1 ) ); ! 287: } else { ! 288: DBGC ( slam, "SLAM %p transmitting initial NACK for blocks " ! 289: "0-%ld\n", slam, ( num_blocks - 1 ) ); ! 290: } ! 291: if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 ) ! 292: return rc; ! 293: if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 ) ! 294: return rc; ! 295: nul = iob_put ( iobuf, 1 ); ! 296: *nul = 0; ! 297: ! 298: /* Transmit packet */ ! 299: return xfer_deliver_iob ( &slam->socket, iobuf ); ! 300: } ! 301: ! 302: /** ! 303: * Handle SLAM master client retry timer expiry ! 304: * ! 305: * @v timer Master retry timer ! 306: * @v fail Failure indicator ! 307: */ ! 308: static void slam_master_timer_expired ( struct retry_timer *timer, ! 309: int fail ) { ! 310: struct slam_request *slam = ! 311: container_of ( timer, struct slam_request, master_timer ); ! 312: ! 313: if ( fail ) { ! 314: /* Allow timer to stop running. We will terminate the ! 315: * connection only if the slave timer times out. ! 316: */ ! 317: DBGC ( slam, "SLAM %p giving up acting as master client\n", ! 318: slam ); ! 319: } else { ! 320: /* Retransmit NACK */ ! 321: start_timer ( timer ); ! 322: slam_tx_nack ( slam ); ! 323: } ! 324: } ! 325: ! 326: /** ! 327: * Handle SLAM slave client retry timer expiry ! 328: * ! 329: * @v timer Master retry timer ! 330: * @v fail Failure indicator ! 331: */ ! 332: static void slam_slave_timer_expired ( struct retry_timer *timer, ! 333: int fail ) { ! 334: struct slam_request *slam = ! 335: container_of ( timer, struct slam_request, slave_timer ); ! 336: ! 337: if ( fail ) { ! 338: /* Terminate connection */ ! 339: slam_finished ( slam, -ETIMEDOUT ); ! 340: } else { ! 341: /* Try sending a NACK */ ! 342: DBGC ( slam, "SLAM %p trying to become master client\n", ! 343: slam ); ! 344: start_timer ( timer ); ! 345: slam_tx_nack ( slam ); ! 346: } ! 347: } ! 348: ! 349: /**************************************************************************** ! 350: * ! 351: * RX datapath ! 352: * ! 353: */ ! 354: ! 355: /** ! 356: * Read and strip a variable-length value from a SLAM packet ! 357: * ! 358: * @v slam SLAM request ! 359: * @v iobuf I/O buffer ! 360: * @v value Value to fill in, or NULL to ignore value ! 361: * @ret rc Return status code ! 362: * ! 363: * Reads a variable-length value from the start of the I/O buffer. ! 364: */ ! 365: static int slam_pull_value ( struct slam_request *slam, ! 366: struct io_buffer *iobuf, ! 367: unsigned long *value ) { ! 368: uint8_t *data; ! 369: size_t len; ! 370: ! 371: /* Sanity check */ ! 372: if ( iob_len ( iobuf ) == 0 ) { ! 373: DBGC ( slam, "SLAM %p empty value\n", slam ); ! 374: return -EINVAL; ! 375: } ! 376: ! 377: /* Read and verify length of value */ ! 378: data = iobuf->data; ! 379: len = ( *data >> 5 ); ! 380: if ( ( len == 0 ) || ! 381: ( value && ( len > sizeof ( *value ) ) ) ) { ! 382: DBGC ( slam, "SLAM %p invalid value length %zd bytes\n", ! 383: slam, len ); ! 384: return -EINVAL; ! 385: } ! 386: if ( len > iob_len ( iobuf ) ) { ! 387: DBGC ( slam, "SLAM %p value extends beyond I/O buffer\n", ! 388: slam ); ! 389: return -EINVAL; ! 390: } ! 391: ! 392: /* Read value */ ! 393: iob_pull ( iobuf, len ); ! 394: *value = ( *data & 0x1f ); ! 395: while ( --len ) { ! 396: *value <<= 8; ! 397: *value |= *(++data); ! 398: } ! 399: ! 400: return 0; ! 401: } ! 402: ! 403: /** ! 404: * Read and strip SLAM header ! 405: * ! 406: * @v slam SLAM request ! 407: * @v iobuf I/O buffer ! 408: * @ret rc Return status code ! 409: */ ! 410: static int slam_pull_header ( struct slam_request *slam, ! 411: struct io_buffer *iobuf ) { ! 412: void *header = iobuf->data; ! 413: int rc; ! 414: ! 415: /* If header matches cached header, just pull it and return */ ! 416: if ( ( slam->header_len <= iob_len ( iobuf ) ) && ! 417: ( memcmp ( slam->header, iobuf->data, slam->header_len ) == 0 )){ ! 418: iob_pull ( iobuf, slam->header_len ); ! 419: return 0; ! 420: } ! 421: ! 422: DBGC ( slam, "SLAM %p detected changed header; resetting\n", slam ); ! 423: ! 424: /* Read and strip transaction ID, total number of bytes, and ! 425: * block size. ! 426: */ ! 427: if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 ) ! 428: return rc; ! 429: if ( ( rc = slam_pull_value ( slam, iobuf, ! 430: &slam->total_bytes ) ) != 0 ) ! 431: return rc; ! 432: if ( ( rc = slam_pull_value ( slam, iobuf, ! 433: &slam->block_size ) ) != 0 ) ! 434: return rc; ! 435: ! 436: /* Update the cached header */ ! 437: slam->header_len = ( iobuf->data - header ); ! 438: assert ( slam->header_len <= sizeof ( slam->header ) ); ! 439: memcpy ( slam->header, header, slam->header_len ); ! 440: ! 441: /* Calculate number of blocks */ ! 442: slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) / ! 443: slam->block_size ); ! 444: ! 445: DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num " ! 446: "blocks %ld\n", slam, slam->total_bytes, slam->block_size, ! 447: slam->num_blocks ); ! 448: ! 449: /* Discard and reset the bitmap */ ! 450: bitmap_free ( &slam->bitmap ); ! 451: memset ( &slam->bitmap, 0, sizeof ( slam->bitmap ) ); ! 452: ! 453: /* Allocate a new bitmap */ ! 454: if ( ( rc = bitmap_resize ( &slam->bitmap, ! 455: slam->num_blocks ) ) != 0 ) { ! 456: /* Failure to allocate a bitmap is fatal */ ! 457: DBGC ( slam, "SLAM %p could not allocate bitmap for %ld " ! 458: "blocks: %s\n", slam, slam->num_blocks, ! 459: strerror ( rc ) ); ! 460: slam_finished ( slam, rc ); ! 461: return rc; ! 462: } ! 463: ! 464: /* Notify recipient of file size */ ! 465: xfer_seek ( &slam->xfer, slam->total_bytes ); ! 466: ! 467: return 0; ! 468: } ! 469: ! 470: /** ! 471: * Receive SLAM data packet ! 472: * ! 473: * @v slam SLAM request ! 474: * @v iobuf I/O buffer ! 475: * @ret rc Return status code ! 476: */ ! 477: static int slam_mc_socket_deliver ( struct slam_request *slam, ! 478: struct io_buffer *iobuf, ! 479: struct xfer_metadata *rx_meta __unused ) { ! 480: struct xfer_metadata meta; ! 481: unsigned long packet; ! 482: size_t len; ! 483: int rc; ! 484: ! 485: /* Stop the master client timer. Restart the slave client timer. */ ! 486: stop_timer ( &slam->master_timer ); ! 487: stop_timer ( &slam->slave_timer ); ! 488: start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); ! 489: ! 490: /* Read and strip packet header */ ! 491: if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) ! 492: goto err_discard; ! 493: ! 494: /* Read and strip packet number */ ! 495: if ( ( rc = slam_pull_value ( slam, iobuf, &packet ) ) != 0 ) ! 496: goto err_discard; ! 497: ! 498: /* Sanity check packet number */ ! 499: if ( packet >= slam->num_blocks ) { ! 500: DBGC ( slam, "SLAM %p received out-of-range packet %ld " ! 501: "(num_blocks=%ld)\n", slam, packet, slam->num_blocks ); ! 502: rc = -EINVAL; ! 503: goto err_discard; ! 504: } ! 505: ! 506: /* Sanity check length */ ! 507: len = iob_len ( iobuf ); ! 508: if ( len > slam->block_size ) { ! 509: DBGC ( slam, "SLAM %p received oversize packet of %zd bytes " ! 510: "(block_size=%ld)\n", slam, len, slam->block_size ); ! 511: rc = -EINVAL; ! 512: goto err_discard; ! 513: } ! 514: if ( ( packet != ( slam->num_blocks - 1 ) ) && ! 515: ( len < slam->block_size ) ) { ! 516: DBGC ( slam, "SLAM %p received short packet of %zd bytes " ! 517: "(block_size=%ld)\n", slam, len, slam->block_size ); ! 518: rc = -EINVAL; ! 519: goto err_discard; ! 520: } ! 521: ! 522: /* If we have already seen this packet, discard it */ ! 523: if ( bitmap_test ( &slam->bitmap, packet ) ) { ! 524: goto discard; ! 525: } ! 526: ! 527: /* Pass to recipient */ ! 528: memset ( &meta, 0, sizeof ( meta ) ); ! 529: meta.flags = XFER_FL_ABS_OFFSET; ! 530: meta.offset = ( packet * slam->block_size ); ! 531: if ( ( rc = xfer_deliver ( &slam->xfer, iobuf, &meta ) ) != 0 ) ! 532: goto err; ! 533: ! 534: /* Mark block as received */ ! 535: bitmap_set ( &slam->bitmap, packet ); ! 536: ! 537: /* If we have received all blocks, terminate */ ! 538: if ( bitmap_full ( &slam->bitmap ) ) ! 539: slam_finished ( slam, 0 ); ! 540: ! 541: return 0; ! 542: ! 543: err_discard: ! 544: discard: ! 545: free_iob ( iobuf ); ! 546: err: ! 547: return rc; ! 548: } ! 549: ! 550: /** ! 551: * Receive SLAM non-data packet ! 552: * ! 553: * @v slam SLAM request ! 554: * @v iobuf I/O buffer ! 555: * @ret rc Return status code ! 556: */ ! 557: static int slam_socket_deliver ( struct slam_request *slam, ! 558: struct io_buffer *iobuf, ! 559: struct xfer_metadata *rx_meta __unused ) { ! 560: int rc; ! 561: ! 562: /* Restart the master client timer */ ! 563: stop_timer ( &slam->master_timer ); ! 564: start_timer ( &slam->master_timer ); ! 565: ! 566: /* Read and strip packet header */ ! 567: if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) ! 568: goto discard; ! 569: ! 570: /* Sanity check */ ! 571: if ( iob_len ( iobuf ) != 0 ) { ! 572: DBGC ( slam, "SLAM %p received trailing garbage:\n", slam ); ! 573: DBGC_HD ( slam, iobuf->data, iob_len ( iobuf ) ); ! 574: rc = -EINVAL; ! 575: goto discard; ! 576: } ! 577: ! 578: /* Discard packet */ ! 579: free_iob ( iobuf ); ! 580: ! 581: /* Send NACK in reply */ ! 582: slam_tx_nack ( slam ); ! 583: ! 584: return 0; ! 585: ! 586: discard: ! 587: free_iob ( iobuf ); ! 588: return rc; ! 589: ! 590: } ! 591: ! 592: /** SLAM unicast socket interface operations */ ! 593: static struct interface_operation slam_socket_operations[] = { ! 594: INTF_OP ( xfer_deliver, struct slam_request *, slam_socket_deliver ), ! 595: INTF_OP ( intf_close, struct slam_request *, slam_finished ), ! 596: }; ! 597: ! 598: /** SLAM unicast socket interface descriptor */ ! 599: static struct interface_descriptor slam_socket_desc = ! 600: INTF_DESC ( struct slam_request, socket, slam_socket_operations ); ! 601: ! 602: /** SLAM multicast socket interface operations */ ! 603: static struct interface_operation slam_mc_socket_operations[] = { ! 604: INTF_OP ( xfer_deliver, struct slam_request *, slam_mc_socket_deliver ), ! 605: INTF_OP ( intf_close, struct slam_request *, slam_finished ), ! 606: }; ! 607: ! 608: /** SLAM multicast socket interface descriptor */ ! 609: static struct interface_descriptor slam_mc_socket_desc = ! 610: INTF_DESC ( struct slam_request, mc_socket, slam_mc_socket_operations ); ! 611: ! 612: /**************************************************************************** ! 613: * ! 614: * Data transfer interface ! 615: * ! 616: */ ! 617: ! 618: /** SLAM data transfer interface operations */ ! 619: static struct interface_operation slam_xfer_operations[] = { ! 620: INTF_OP ( intf_close, struct slam_request *, slam_finished ), ! 621: }; ! 622: ! 623: /** SLAM data transfer interface descriptor */ ! 624: static struct interface_descriptor slam_xfer_desc = ! 625: INTF_DESC ( struct slam_request, xfer, slam_xfer_operations ); ! 626: ! 627: /** ! 628: * Parse SLAM URI multicast address ! 629: * ! 630: * @v slam SLAM request ! 631: * @v path Path portion of x-slam:// URI ! 632: * @v address Socket address to fill in ! 633: * @ret rc Return status code ! 634: */ ! 635: static int slam_parse_multicast_address ( struct slam_request *slam, ! 636: const char *path, ! 637: struct sockaddr_in *address ) { ! 638: char path_dup[ strlen ( path ) /* no +1 */ ]; ! 639: char *sep; ! 640: char *end; ! 641: ! 642: /* Create temporary copy of path, minus the leading '/' */ ! 643: assert ( *path == '/' ); ! 644: memcpy ( path_dup, ( path + 1 ) , sizeof ( path_dup ) ); ! 645: ! 646: /* Parse port, if present */ ! 647: sep = strchr ( path_dup, ':' ); ! 648: if ( sep ) { ! 649: *(sep++) = '\0'; ! 650: address->sin_port = htons ( strtoul ( sep, &end, 0 ) ); ! 651: if ( *end != '\0' ) { ! 652: DBGC ( slam, "SLAM %p invalid multicast port " ! 653: "\"%s\"\n", slam, sep ); ! 654: return -EINVAL; ! 655: } ! 656: } ! 657: ! 658: /* Parse address */ ! 659: if ( inet_aton ( path_dup, &address->sin_addr ) == 0 ) { ! 660: DBGC ( slam, "SLAM %p invalid multicast address \"%s\"\n", ! 661: slam, path_dup ); ! 662: return -EINVAL; ! 663: } ! 664: ! 665: return 0; ! 666: } ! 667: ! 668: /** ! 669: * Initiate a SLAM request ! 670: * ! 671: * @v xfer Data transfer interface ! 672: * @v uri Uniform Resource Identifier ! 673: * @ret rc Return status code ! 674: */ ! 675: static int slam_open ( struct interface *xfer, struct uri *uri ) { ! 676: static const struct sockaddr_in default_multicast = { ! 677: .sin_family = AF_INET, ! 678: .sin_port = htons ( SLAM_DEFAULT_MULTICAST_PORT ), ! 679: .sin_addr = { htonl ( SLAM_DEFAULT_MULTICAST_IP ) }, ! 680: }; ! 681: struct slam_request *slam; ! 682: struct sockaddr_tcpip server; ! 683: struct sockaddr_in multicast; ! 684: int rc; ! 685: ! 686: /* Sanity checks */ ! 687: if ( ! uri->host ) ! 688: return -EINVAL; ! 689: ! 690: /* Allocate and populate structure */ ! 691: slam = zalloc ( sizeof ( *slam ) ); ! 692: if ( ! slam ) ! 693: return -ENOMEM; ! 694: ref_init ( &slam->refcnt, slam_free ); ! 695: intf_init ( &slam->xfer, &slam_xfer_desc, &slam->refcnt ); ! 696: intf_init ( &slam->socket, &slam_socket_desc, &slam->refcnt ); ! 697: intf_init ( &slam->mc_socket, &slam_mc_socket_desc, &slam->refcnt ); ! 698: timer_init ( &slam->master_timer, slam_master_timer_expired, ! 699: &slam->refcnt ); ! 700: timer_init ( &slam->slave_timer, slam_slave_timer_expired, ! 701: &slam->refcnt ); ! 702: /* Fake an invalid cached header of { 0x00, ... } */ ! 703: slam->header_len = 1; ! 704: /* Fake parameters for initial NACK */ ! 705: slam->num_blocks = 1; ! 706: if ( ( rc = bitmap_resize ( &slam->bitmap, 1 ) ) != 0 ) { ! 707: DBGC ( slam, "SLAM %p could not allocate initial bitmap: " ! 708: "%s\n", slam, strerror ( rc ) ); ! 709: goto err; ! 710: } ! 711: ! 712: /* Open unicast socket */ ! 713: memset ( &server, 0, sizeof ( server ) ); ! 714: server.st_port = htons ( uri_port ( uri, SLAM_DEFAULT_PORT ) ); ! 715: if ( ( rc = xfer_open_named_socket ( &slam->socket, SOCK_DGRAM, ! 716: ( struct sockaddr * ) &server, ! 717: uri->host, NULL ) ) != 0 ) { ! 718: DBGC ( slam, "SLAM %p could not open unicast socket: %s\n", ! 719: slam, strerror ( rc ) ); ! 720: goto err; ! 721: } ! 722: ! 723: /* Open multicast socket */ ! 724: memcpy ( &multicast, &default_multicast, sizeof ( multicast ) ); ! 725: if ( uri->path && ! 726: ( ( rc = slam_parse_multicast_address ( slam, uri->path, ! 727: &multicast ) ) != 0 ) ) { ! 728: goto err; ! 729: } ! 730: if ( ( rc = xfer_open_socket ( &slam->mc_socket, SOCK_DGRAM, ! 731: ( struct sockaddr * ) &multicast, ! 732: ( struct sockaddr * ) &multicast ) ) != 0 ) { ! 733: DBGC ( slam, "SLAM %p could not open multicast socket: %s\n", ! 734: slam, strerror ( rc ) ); ! 735: goto err; ! 736: } ! 737: ! 738: /* Start slave retry timer */ ! 739: start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); ! 740: ! 741: /* Attach to parent interface, mortalise self, and return */ ! 742: intf_plug_plug ( &slam->xfer, xfer ); ! 743: ref_put ( &slam->refcnt ); ! 744: return 0; ! 745: ! 746: err: ! 747: slam_finished ( slam, rc ); ! 748: ref_put ( &slam->refcnt ); ! 749: return rc; ! 750: } ! 751: ! 752: /** SLAM URI opener */ ! 753: struct uri_opener slam_uri_opener __uri_opener = { ! 754: .scheme = "x-slam", ! 755: .open = slam_open, ! 756: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.