iPXE
slam.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <strings.h>
00030 #include <errno.h>
00031 #include <assert.h>
00032 #include <byteswap.h>
00033 #include <ipxe/features.h>
00034 #include <ipxe/iobuf.h>
00035 #include <ipxe/bitmap.h>
00036 #include <ipxe/xfer.h>
00037 #include <ipxe/open.h>
00038 #include <ipxe/uri.h>
00039 #include <ipxe/tcpip.h>
00040 #include <ipxe/timer.h>
00041 #include <ipxe/retry.h>
00042 
00043 /** @file
00044  *
00045  * Scalable Local Area Multicast protocol
00046  *
00047  * The SLAM protocol is supported only by Etherboot; it was designed
00048  * and implemented by Eric Biederman.  A server implementation is
00049  * available in contrib/mini-slamd.  There does not appear to be any
00050  * documentation beyond a few sparse comments in Etherboot's
00051  * proto_slam.c.
00052  *
00053  * SLAM packets use three types of data field:
00054  *
00055  *  Nul : A single NUL (0) byte, used as a list terminator
00056  *
00057  *  Raw : A block of raw data
00058  *
00059  *  Int : A variable-length integer, in big-endian order.  The length
00060  *        of the integer is encoded in the most significant three bits.
00061  *
00062  * Packets received by the client have the following layout:
00063  *
00064  *  Int : Transaction identifier.  This is an opaque value.
00065  *
00066  *  Int : Total number of bytes in the transfer.
00067  *
00068  *  Int : Block size, in bytes.
00069  *
00070  *  Int : Packet sequence number within the transfer (if this packet
00071  *        contains data).
00072  *
00073  *  Raw : Packet data (if this packet contains data).
00074  *
00075  * Packets transmitted by the client consist of a run-length-encoded
00076  * representation of the received-blocks bitmap, looking something
00077  * like:
00078  *
00079  *  Int : Number of consecutive successfully-received packets
00080  *  Int : Number of consecutive missing packets
00081  *  Int : Number of consecutive successfully-received packets
00082  *  Int : Number of consecutive missing packets
00083  *  ....
00084  *  Nul
00085  *
00086  */
00087 
00088 FEATURE ( FEATURE_PROTOCOL, "SLAM", DHCP_EB_FEATURE_SLAM, 1 );
00089 
00090 /** Default SLAM server port */
00091 #define SLAM_DEFAULT_PORT 10000
00092 
00093 /** Default SLAM multicast IP address */
00094 #define SLAM_DEFAULT_MULTICAST_IP \
00095         ( ( 239 << 24 ) | ( 255 << 16 ) | ( 1 << 8 ) | ( 1 << 0 ) )
00096 
00097 /** Default SLAM multicast port */
00098 #define SLAM_DEFAULT_MULTICAST_PORT 10000
00099 
00100 /** Maximum SLAM header length */
00101 #define SLAM_MAX_HEADER_LEN ( 7 /* transaction id */ + 7 /* total_bytes */ + \
00102                               7 /* block_size */ )
00103 
00104 /** Maximum number of blocks to request per NACK
00105  *
00106  * This is a policy decision equivalent to selecting a TCP window
00107  * size.
00108  */
00109 #define SLAM_MAX_BLOCKS_PER_NACK 4
00110 
00111 /** Maximum SLAM NACK length
00112  *
00113  * We only ever send a NACK for a single range of up to @c
00114  * SLAM_MAX_BLOCKS_PER_NACK blocks.
00115  */
00116 #define SLAM_MAX_NACK_LEN ( 7 /* block */ + 7 /* #blocks */ + 1 /* NUL */ )
00117 
00118 /** SLAM slave timeout */
00119 #define SLAM_SLAVE_TIMEOUT ( 1 * TICKS_PER_SEC )
00120 
00121 /** A SLAM request */
00122 struct slam_request {
00123         /** Reference counter */
00124         struct refcnt refcnt;
00125 
00126         /** Data transfer interface */
00127         struct interface xfer;
00128         /** Unicast socket */
00129         struct interface socket;
00130         /** Multicast socket */
00131         struct interface mc_socket;
00132 
00133         /** Master client retry timer */
00134         struct retry_timer master_timer;
00135         /** Slave client retry timer */
00136         struct retry_timer slave_timer;
00137 
00138         /** Cached header */
00139         uint8_t header[SLAM_MAX_HEADER_LEN];
00140         /** Size of cached header */
00141         size_t header_len;
00142         /** Total number of bytes in transfer */
00143         unsigned long total_bytes;
00144         /** Transfer block size */
00145         unsigned long block_size;
00146         /** Number of blocks in transfer */
00147         unsigned long num_blocks;
00148         /** Block bitmap */
00149         struct bitmap bitmap;
00150         /** NACK sent flag */
00151         int nack_sent;
00152 };
00153 
00154 /**
00155  * Free a SLAM request
00156  *
00157  * @v refcnt            Reference counter
00158  */
00159 static void slam_free ( struct refcnt *refcnt ) {
00160         struct slam_request *slam =
00161                 container_of ( refcnt, struct slam_request, refcnt );
00162 
00163         bitmap_free ( &slam->bitmap );
00164         free ( slam );
00165 }
00166 
00167 /**
00168  * Mark SLAM request as complete
00169  *
00170  * @v slam              SLAM request
00171  * @v rc                Return status code
00172  */
00173 static void slam_finished ( struct slam_request *slam, int rc ) {
00174         static const uint8_t slam_disconnect[] = { 0 };
00175 
00176         DBGC ( slam, "SLAM %p finished with status code %d (%s)\n",
00177                slam, rc, strerror ( rc ) );
00178 
00179         /* Send a disconnect message if we ever sent anything to the
00180          * server.
00181          */
00182         if ( slam->nack_sent ) {
00183                 xfer_deliver_raw ( &slam->socket, slam_disconnect,
00184                                    sizeof ( slam_disconnect ) );
00185         }
00186 
00187         /* Stop the retry timers */
00188         stop_timer ( &slam->master_timer );
00189         stop_timer ( &slam->slave_timer );
00190 
00191         /* Close all data transfer interfaces */
00192         intf_shutdown ( &slam->socket, rc );
00193         intf_shutdown ( &slam->mc_socket, rc );
00194         intf_shutdown ( &slam->xfer, rc );
00195 }
00196 
00197 /****************************************************************************
00198  *
00199  * TX datapath
00200  *
00201  */
00202 
00203 /**
00204  * Add a variable-length value to a SLAM packet
00205  *
00206  * @v slam              SLAM request
00207  * @v iobuf             I/O buffer
00208  * @v value             Value to add
00209  * @ret rc              Return status code
00210  *
00211  * Adds a variable-length value to the end of an I/O buffer.  Will
00212  * always leave at least one byte of tailroom in the I/O buffer (to
00213  * allow space for the terminating NUL).
00214  */
00215 static int slam_put_value ( struct slam_request *slam,
00216                             struct io_buffer *iobuf, unsigned long value ) {
00217         uint8_t *data;
00218         size_t len;
00219         unsigned int i;
00220 
00221         /* Calculate variable length required to store value.  Always
00222          * leave at least one byte in the I/O buffer.
00223          */
00224         len = ( ( flsl ( value ) + 10 ) / 8 );
00225         if ( len >= iob_tailroom ( iobuf ) ) {
00226                 DBGC2 ( slam, "SLAM %p cannot add %zd-byte value\n",
00227                         slam, len );
00228                 return -ENOBUFS;
00229         }
00230         /* There is no valid way within the protocol that we can end
00231          * up trying to push a full-sized long (i.e. without space for
00232          * the length encoding).
00233          */
00234         assert ( len <= sizeof ( value ) );
00235 
00236         /* Add value */
00237         data = iob_put ( iobuf, len );
00238         for ( i = len ; i-- ; ) {
00239                 data[i] = value;
00240                 value >>= 8;
00241         }
00242         *data |= ( len << 5 );
00243         assert ( value == 0 );
00244 
00245         return 0;
00246 }
00247 
00248 /**
00249  * Send SLAM NACK packet
00250  *
00251  * @v slam              SLAM request
00252  * @ret rc              Return status code
00253  */
00254 static int slam_tx_nack ( struct slam_request *slam ) {
00255         struct io_buffer *iobuf;
00256         unsigned long first_block;
00257         unsigned long num_blocks;
00258         uint8_t *nul;
00259         int rc;
00260 
00261         /* Mark NACK as sent, so that we know we have to disconnect later */
00262         slam->nack_sent = 1;
00263 
00264         /* Allocate I/O buffer */
00265         iobuf = xfer_alloc_iob ( &slam->socket, SLAM_MAX_NACK_LEN );
00266         if ( ! iobuf ) {
00267                 DBGC ( slam, "SLAM %p could not allocate I/O buffer\n",
00268                        slam );
00269                 rc = -ENOMEM;
00270                 goto err_alloc;
00271         }
00272 
00273         /* Construct NACK.  We always request only a single packet;
00274          * this allows us to force multicast-TFTP-style flow control
00275          * on the SLAM server, which will otherwise just blast the
00276          * data out as fast as it can.  On a gigabit network, without
00277          * RX checksumming, this would inevitably cause packet drops.
00278          */
00279         first_block = bitmap_first_gap ( &slam->bitmap );
00280         for ( num_blocks = 1 ; ; num_blocks++ ) {
00281                 if ( num_blocks >= SLAM_MAX_BLOCKS_PER_NACK )
00282                         break;
00283                 if ( ( first_block + num_blocks ) >= slam->num_blocks )
00284                         break;
00285                 if ( bitmap_test ( &slam->bitmap,
00286                                    ( first_block + num_blocks ) ) )
00287                         break;
00288         }
00289         if ( first_block ) {
00290                 DBGCP ( slam, "SLAM %p transmitting NACK for blocks "
00291                         "%ld-%ld\n", slam, first_block,
00292                         ( first_block + num_blocks - 1 ) );
00293         } else {
00294                 DBGC ( slam, "SLAM %p transmitting initial NACK for blocks "
00295                        "0-%ld\n", slam, ( num_blocks - 1 ) );
00296         }
00297         if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 )
00298                 goto err_put_value;
00299         if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 )
00300                 goto err_put_value;
00301         nul = iob_put ( iobuf, 1 );
00302         *nul = 0;
00303 
00304         /* Transmit packet */
00305         return xfer_deliver_iob ( &slam->socket, iob_disown ( iobuf ) );
00306 
00307  err_put_value:
00308         free_iob ( iobuf );
00309  err_alloc:
00310         return rc;
00311 }
00312 
00313 /**
00314  * Handle SLAM master client retry timer expiry
00315  *
00316  * @v timer             Master retry timer
00317  * @v fail              Failure indicator
00318  */
00319 static void slam_master_timer_expired ( struct retry_timer *timer,
00320                                         int fail ) {
00321         struct slam_request *slam =
00322                 container_of ( timer, struct slam_request, master_timer );
00323 
00324         if ( fail ) {
00325                 /* Allow timer to stop running.  We will terminate the
00326                  * connection only if the slave timer times out.
00327                  */
00328                 DBGC ( slam, "SLAM %p giving up acting as master client\n",
00329                        slam );
00330         } else {
00331                 /* Retransmit NACK */
00332                 start_timer ( timer );
00333                 slam_tx_nack ( slam );
00334         }
00335 }
00336 
00337 /**
00338  * Handle SLAM slave client retry timer expiry
00339  *
00340  * @v timer             Master retry timer
00341  * @v fail              Failure indicator
00342  */
00343 static void slam_slave_timer_expired ( struct retry_timer *timer,
00344                                         int fail ) {
00345         struct slam_request *slam =
00346                 container_of ( timer, struct slam_request, slave_timer );
00347 
00348         if ( fail ) {
00349                 /* Terminate connection */
00350                 slam_finished ( slam, -ETIMEDOUT );
00351         } else {
00352                 /* Try sending a NACK */
00353                 DBGC ( slam, "SLAM %p trying to become master client\n",
00354                        slam );
00355                 start_timer ( timer );
00356                 slam_tx_nack ( slam );
00357         }
00358 }
00359 
00360 /****************************************************************************
00361  *
00362  * RX datapath
00363  *
00364  */
00365 
00366 /**
00367  * Read and strip a variable-length value from a SLAM packet
00368  *
00369  * @v slam              SLAM request
00370  * @v iobuf             I/O buffer
00371  * @v value             Value to fill in, or NULL to ignore value
00372  * @ret rc              Return status code
00373  *
00374  * Reads a variable-length value from the start of the I/O buffer.  
00375  */
00376 static int slam_pull_value ( struct slam_request *slam,
00377                              struct io_buffer *iobuf,
00378                              unsigned long *value ) {
00379         uint8_t *data;
00380         size_t len;
00381 
00382         /* Sanity check */
00383         if ( iob_len ( iobuf ) == 0 ) {
00384                 DBGC ( slam, "SLAM %p empty value\n", slam );
00385                 return -EINVAL;
00386         }
00387 
00388         /* Read and verify length of value */
00389         data = iobuf->data;
00390         len = ( *data >> 5 );
00391         if ( ( len == 0 ) ||
00392              ( value && ( len > sizeof ( *value ) ) ) ) {
00393                 DBGC ( slam, "SLAM %p invalid value length %zd bytes\n",
00394                        slam, len );
00395                 return -EINVAL;
00396         }
00397         if ( len > iob_len ( iobuf ) ) {
00398                 DBGC ( slam, "SLAM %p value extends beyond I/O buffer\n",
00399                        slam );
00400                 return -EINVAL;
00401         }
00402 
00403         /* Strip value */
00404         iob_pull ( iobuf, len );
00405 
00406         /* Read value, if applicable */
00407         if ( value ) {
00408                 *value = ( *data & 0x1f );
00409                 while ( --len ) {
00410                         *value <<= 8;
00411                         *value |= *(++data);
00412                 }
00413         }
00414 
00415         return 0;
00416 }
00417 
00418 /**
00419  * Read and strip SLAM header
00420  *
00421  * @v slam              SLAM request
00422  * @v iobuf             I/O buffer
00423  * @ret rc              Return status code
00424  */
00425 static int slam_pull_header ( struct slam_request *slam,
00426                               struct io_buffer *iobuf ) {
00427         void *header = iobuf->data;
00428         unsigned long total_bytes;
00429         unsigned long block_size;
00430         int rc;
00431 
00432         /* If header matches cached header, just pull it and return */
00433         if ( ( slam->header_len <= iob_len ( iobuf ) ) &&
00434              ( memcmp ( slam->header, iobuf->data, slam->header_len ) == 0 )){
00435                 iob_pull ( iobuf, slam->header_len );
00436                 return 0;
00437         }
00438 
00439         DBGC ( slam, "SLAM %p detected changed header; resetting\n", slam );
00440 
00441         /* Read and strip transaction ID, total number of bytes, and
00442          * block size.
00443          */
00444         if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 )
00445                 return rc;
00446         if ( ( rc = slam_pull_value ( slam, iobuf, &total_bytes ) ) != 0 )
00447                 return rc;
00448         if ( ( rc = slam_pull_value ( slam, iobuf, &block_size ) ) != 0 )
00449                 return rc;
00450 
00451         /* Sanity check */
00452         if ( block_size == 0 ) {
00453                 DBGC ( slam, "SLAM %p ignoring zero block size\n", slam );
00454                 return -EINVAL;
00455         }
00456 
00457         /* Update the cached header */
00458         slam->header_len = ( iobuf->data - header );
00459         assert ( slam->header_len <= sizeof ( slam->header ) );
00460         memcpy ( slam->header, header, slam->header_len );
00461 
00462         /* Calculate number of blocks */
00463         slam->total_bytes = total_bytes;
00464         slam->block_size = block_size;
00465         slam->num_blocks = ( ( total_bytes + block_size - 1 ) / block_size );
00466         DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num "
00467                "blocks %ld\n", slam, slam->total_bytes, slam->block_size,
00468                slam->num_blocks );
00469 
00470         /* Discard and reset the bitmap */
00471         bitmap_free ( &slam->bitmap );
00472         memset ( &slam->bitmap, 0, sizeof ( slam->bitmap ) );
00473 
00474         /* Allocate a new bitmap */
00475         if ( ( rc = bitmap_resize ( &slam->bitmap,
00476                                     slam->num_blocks ) ) != 0 ) {
00477                 /* Failure to allocate a bitmap is fatal */
00478                 DBGC ( slam, "SLAM %p could not allocate bitmap for %ld "
00479                        "blocks: %s\n", slam, slam->num_blocks,
00480                        strerror ( rc ) );
00481                 slam_finished ( slam, rc );
00482                 return rc;
00483         }
00484 
00485         /* Notify recipient of file size */
00486         xfer_seek ( &slam->xfer, slam->total_bytes );
00487 
00488         return 0;
00489 }
00490 
00491 /**
00492  * Receive SLAM data packet
00493  *
00494  * @v slam              SLAM request
00495  * @v iobuf             I/O buffer
00496  * @ret rc              Return status code
00497  */
00498 static int slam_mc_socket_deliver ( struct slam_request *slam,
00499                                     struct io_buffer *iobuf,
00500                                     struct xfer_metadata *rx_meta __unused ) {
00501         struct xfer_metadata meta;
00502         unsigned long packet;
00503         size_t len;
00504         int rc;
00505 
00506         /* Stop the master client timer.  Restart the slave client timer. */
00507         stop_timer ( &slam->master_timer );
00508         stop_timer ( &slam->slave_timer );
00509         start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT );
00510 
00511         /* Read and strip packet header */
00512         if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 )
00513                 goto err_discard;
00514 
00515         /* Read and strip packet number */
00516         if ( ( rc = slam_pull_value ( slam, iobuf, &packet ) ) != 0 )
00517                 goto err_discard;
00518 
00519         /* Sanity check packet number */
00520         if ( packet >= slam->num_blocks ) {
00521                 DBGC ( slam, "SLAM %p received out-of-range packet %ld "
00522                        "(num_blocks=%ld)\n", slam, packet, slam->num_blocks );
00523                 rc = -EINVAL;
00524                 goto err_discard;
00525         }
00526 
00527         /* Sanity check length */
00528         len = iob_len ( iobuf );
00529         if ( len > slam->block_size ) {
00530                 DBGC ( slam, "SLAM %p received oversize packet of %zd bytes "
00531                        "(block_size=%ld)\n", slam, len, slam->block_size );
00532                 rc = -EINVAL;
00533                 goto err_discard;
00534         }
00535         if ( ( packet != ( slam->num_blocks - 1 ) ) &&
00536              ( len < slam->block_size ) ) {
00537                 DBGC ( slam, "SLAM %p received short packet of %zd bytes "
00538                        "(block_size=%ld)\n", slam, len, slam->block_size );
00539                 rc = -EINVAL;
00540                 goto err_discard;
00541         }
00542 
00543         /* If we have already seen this packet, discard it */
00544         if ( bitmap_test ( &slam->bitmap, packet ) ) {
00545                 goto discard;
00546         }
00547 
00548         /* Pass to recipient */
00549         memset ( &meta, 0, sizeof ( meta ) );
00550         meta.flags = XFER_FL_ABS_OFFSET;
00551         meta.offset = ( packet * slam->block_size );
00552         if ( ( rc = xfer_deliver ( &slam->xfer, iobuf, &meta ) ) != 0 )
00553                 goto err;
00554 
00555         /* Mark block as received */
00556         bitmap_set ( &slam->bitmap, packet );
00557 
00558         /* If we have received all blocks, terminate */
00559         if ( bitmap_full ( &slam->bitmap ) )
00560                 slam_finished ( slam, 0 );
00561 
00562         return 0;
00563 
00564  err_discard:
00565  discard:
00566         free_iob ( iobuf );
00567  err:
00568         return rc;
00569 }
00570 
00571 /**
00572  * Receive SLAM non-data packet
00573  *
00574  * @v slam              SLAM request
00575  * @v iobuf             I/O buffer
00576  * @ret rc              Return status code
00577  */
00578 static int slam_socket_deliver ( struct slam_request *slam,
00579                                  struct io_buffer *iobuf,
00580                                  struct xfer_metadata *rx_meta __unused ) {
00581         int rc;
00582 
00583         /* Restart the master client timer */
00584         stop_timer ( &slam->master_timer );
00585         start_timer ( &slam->master_timer );
00586 
00587         /* Read and strip packet header */
00588         if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 )
00589                 goto discard;
00590 
00591         /* Sanity check */
00592         if ( iob_len ( iobuf ) != 0 ) {
00593                 DBGC ( slam, "SLAM %p received trailing garbage:\n", slam );
00594                 DBGC_HD ( slam, iobuf->data, iob_len ( iobuf ) );
00595                 rc = -EINVAL;
00596                 goto discard;
00597         }
00598 
00599         /* Discard packet */
00600         free_iob ( iobuf );
00601 
00602         /* Send NACK in reply */
00603         slam_tx_nack ( slam );
00604 
00605         return 0;
00606 
00607  discard:
00608         free_iob ( iobuf );
00609         return rc;
00610 
00611 }
00612 
00613 /** SLAM unicast socket interface operations */
00614 static struct interface_operation slam_socket_operations[] = {
00615         INTF_OP ( xfer_deliver, struct slam_request *, slam_socket_deliver ),
00616         INTF_OP ( intf_close, struct slam_request *, slam_finished ),
00617 };
00618 
00619 /** SLAM unicast socket interface descriptor */
00620 static struct interface_descriptor slam_socket_desc =
00621         INTF_DESC ( struct slam_request, socket, slam_socket_operations );
00622 
00623 /** SLAM multicast socket interface operations */
00624 static struct interface_operation slam_mc_socket_operations[] = {
00625         INTF_OP ( xfer_deliver, struct slam_request *, slam_mc_socket_deliver ),
00626         INTF_OP ( intf_close, struct slam_request *, slam_finished ),
00627 };
00628 
00629 /** SLAM multicast socket interface descriptor */
00630 static struct interface_descriptor slam_mc_socket_desc =
00631         INTF_DESC ( struct slam_request, mc_socket, slam_mc_socket_operations );
00632 
00633 /****************************************************************************
00634  *
00635  * Data transfer interface
00636  *
00637  */
00638 
00639 /** SLAM data transfer interface operations */
00640 static struct interface_operation slam_xfer_operations[] = {
00641         INTF_OP ( intf_close, struct slam_request *, slam_finished ),
00642 };
00643 
00644 /** SLAM data transfer interface descriptor */
00645 static struct interface_descriptor slam_xfer_desc =
00646         INTF_DESC ( struct slam_request, xfer, slam_xfer_operations );
00647 
00648 /**
00649  * Parse SLAM URI multicast address
00650  *
00651  * @v slam              SLAM request
00652  * @v path              Path portion of x-slam:// URI
00653  * @v address           Socket address to fill in
00654  * @ret rc              Return status code
00655  */
00656 static int slam_parse_multicast_address ( struct slam_request *slam,
00657                                           const char *path,
00658                                           struct sockaddr_in *address ) {
00659         char path_dup[ strlen ( path ) /* no +1 */ ];
00660         char *sep;
00661         char *end;
00662 
00663         /* Create temporary copy of path, minus the leading '/' */
00664         assert ( *path == '/' );
00665         memcpy ( path_dup, ( path + 1 ) , sizeof ( path_dup ) );
00666 
00667         /* Parse port, if present */
00668         sep = strchr ( path_dup, ':' );
00669         if ( sep ) {
00670                 *(sep++) = '\0';
00671                 address->sin_port = htons ( strtoul ( sep, &end, 0 ) );
00672                 if ( *end != '\0' ) {
00673                         DBGC ( slam, "SLAM %p invalid multicast port "
00674                                "\"%s\"\n", slam, sep );
00675                         return -EINVAL;
00676                 }
00677         }
00678 
00679         /* Parse address */
00680         if ( inet_aton ( path_dup, &address->sin_addr ) == 0 ) {
00681                 DBGC ( slam, "SLAM %p invalid multicast address \"%s\"\n",
00682                        slam, path_dup );
00683                 return -EINVAL;
00684         }
00685 
00686         return 0;
00687 }
00688 
00689 /**
00690  * Initiate a SLAM request
00691  *
00692  * @v xfer              Data transfer interface
00693  * @v uri               Uniform Resource Identifier
00694  * @ret rc              Return status code
00695  */
00696 static int slam_open ( struct interface *xfer, struct uri *uri ) {
00697         static const struct sockaddr_in default_multicast = {
00698                 .sin_family = AF_INET,
00699                 .sin_port = htons ( SLAM_DEFAULT_MULTICAST_PORT ),
00700                 .sin_addr = { htonl ( SLAM_DEFAULT_MULTICAST_IP ) },
00701         };
00702         struct slam_request *slam;
00703         struct sockaddr_tcpip server;
00704         struct sockaddr_in multicast;
00705         int rc;
00706 
00707         /* Sanity checks */
00708         if ( ! uri->host )
00709                 return -EINVAL;
00710 
00711         /* Allocate and populate structure */
00712         slam = zalloc ( sizeof ( *slam ) );
00713         if ( ! slam )
00714                 return -ENOMEM;
00715         ref_init ( &slam->refcnt, slam_free );
00716         intf_init ( &slam->xfer, &slam_xfer_desc, &slam->refcnt );
00717         intf_init ( &slam->socket, &slam_socket_desc, &slam->refcnt );
00718         intf_init ( &slam->mc_socket, &slam_mc_socket_desc, &slam->refcnt );
00719         timer_init ( &slam->master_timer, slam_master_timer_expired,
00720                      &slam->refcnt );
00721         timer_init ( &slam->slave_timer, slam_slave_timer_expired,
00722                      &slam->refcnt );
00723         /* Fake an invalid cached header of { 0x00, ... } */
00724         slam->header_len = 1;
00725         /* Fake parameters for initial NACK */
00726         slam->num_blocks = 1;
00727         if ( ( rc = bitmap_resize ( &slam->bitmap, 1 ) ) != 0 ) {
00728                 DBGC ( slam, "SLAM %p could not allocate initial bitmap: "
00729                        "%s\n", slam, strerror ( rc ) );
00730                 goto err;
00731         }
00732 
00733         /* Open unicast socket */
00734         memset ( &server, 0, sizeof ( server ) );
00735         server.st_port = htons ( uri_port ( uri, SLAM_DEFAULT_PORT ) );
00736         if ( ( rc = xfer_open_named_socket ( &slam->socket, SOCK_DGRAM,
00737                                              ( struct sockaddr * ) &server,
00738                                              uri->host, NULL ) ) != 0 ) {
00739                 DBGC ( slam, "SLAM %p could not open unicast socket: %s\n",
00740                        slam, strerror ( rc ) );
00741                 goto err;
00742         }
00743 
00744         /* Open multicast socket */
00745         memcpy ( &multicast, &default_multicast, sizeof ( multicast ) );
00746         if ( uri->path &&
00747              ( ( rc = slam_parse_multicast_address ( slam, uri->path,
00748                                                      &multicast ) ) != 0 ) ) {
00749                 goto err;
00750         }
00751         if ( ( rc = xfer_open_socket ( &slam->mc_socket, SOCK_DGRAM,
00752                                  ( struct sockaddr * ) &multicast,
00753                                  ( struct sockaddr * ) &multicast ) ) != 0 ) {
00754                 DBGC ( slam, "SLAM %p could not open multicast socket: %s\n",
00755                        slam, strerror ( rc ) );
00756                 goto err;
00757         }
00758 
00759         /* Start slave retry timer */
00760         start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT );
00761 
00762         /* Attach to parent interface, mortalise self, and return */
00763         intf_plug_plug ( &slam->xfer, xfer );
00764         ref_put ( &slam->refcnt );
00765         return 0;
00766 
00767  err:
00768         slam_finished ( slam, rc );
00769         ref_put ( &slam->refcnt );
00770         return rc;
00771 }
00772 
00773 /** SLAM URI opener */
00774 struct uri_opener slam_uri_opener __uri_opener = {
00775         .scheme = "x-slam",
00776         .open   = slam_open,
00777 };