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