Annotation of qemu/roms/ipxe/src/net/udp/slam.c, revision 1.1.1.1

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.