00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021 #include <stdint.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <strings.h>
00025 #include <errno.h>
00026 #include <assert.h>
00027 #include <byteswap.h>
00028 #include <gpxe/features.h>
00029 #include <gpxe/iobuf.h>
00030 #include <gpxe/bitmap.h>
00031 #include <gpxe/xfer.h>
00032 #include <gpxe/open.h>
00033 #include <gpxe/uri.h>
00034 #include <gpxe/tcpip.h>
00035 #include <gpxe/timer.h>
00036 #include <gpxe/retry.h>
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 FEATURE ( FEATURE_PROTOCOL, "SLAM", DHCP_EB_FEATURE_SLAM, 1 );
00084
00085
00086 #define SLAM_DEFAULT_PORT 10000
00087
00088
00089 #define SLAM_DEFAULT_MULTICAST_IP \
00090 ( ( 239 << 24 ) | ( 255 << 16 ) | ( 1 << 8 ) | ( 1 << 0 ) )
00091
00092
00093 #define SLAM_DEFAULT_MULTICAST_PORT 10000
00094
00095
00096 #define SLAM_MAX_HEADER_LEN ( 7 + 7 + \
00097 7 )
00098
00099
00100
00101
00102
00103
00104 #define SLAM_MAX_BLOCKS_PER_NACK 4
00105
00106
00107
00108
00109
00110
00111 #define SLAM_MAX_NACK_LEN ( 7 + 7 + 1 )
00112
00113
00114 #define SLAM_SLAVE_TIMEOUT ( 1 * TICKS_PER_SEC )
00115
00116
00117 struct slam_request {
00118
00119 struct refcnt refcnt;
00120
00121
00122 struct xfer_interface xfer;
00123
00124 struct xfer_interface socket;
00125
00126 struct xfer_interface mc_socket;
00127
00128
00129 struct retry_timer master_timer;
00130
00131 struct retry_timer slave_timer;
00132
00133
00134 uint8_t header[SLAM_MAX_HEADER_LEN];
00135
00136 size_t header_len;
00137
00138 unsigned long total_bytes;
00139
00140 unsigned long block_size;
00141
00142 unsigned long num_blocks;
00143
00144 struct bitmap bitmap;
00145
00146 int nack_sent;
00147 };
00148
00149
00150
00151
00152
00153
00154 static void slam_free ( struct refcnt *refcnt ) {
00155 struct slam_request *slam =
00156 container_of ( refcnt, struct slam_request, refcnt );
00157
00158 bitmap_free ( &slam->bitmap );
00159 free ( slam );
00160 }
00161
00162
00163
00164
00165
00166
00167
00168 static void slam_finished ( struct slam_request *slam, int rc ) {
00169 static const uint8_t slam_disconnect[] = { 0 };
00170
00171 DBGC ( slam, "SLAM %p finished with status code %d (%s)\n",
00172 slam, rc, strerror ( rc ) );
00173
00174
00175
00176
00177 if ( slam->nack_sent ) {
00178 xfer_deliver_raw ( &slam->socket, slam_disconnect,
00179 sizeof ( slam_disconnect ) );
00180 }
00181
00182
00183 stop_timer ( &slam->master_timer );
00184 stop_timer ( &slam->slave_timer );
00185
00186
00187 xfer_nullify ( &slam->socket );
00188 xfer_close ( &slam->socket, rc );
00189 xfer_nullify ( &slam->mc_socket );
00190 xfer_close ( &slam->mc_socket, rc );
00191 xfer_nullify ( &slam->xfer );
00192 xfer_close ( &slam->xfer, rc );
00193 }
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213 static int slam_put_value ( struct slam_request *slam,
00214 struct io_buffer *iobuf, unsigned long value ) {
00215 uint8_t *data;
00216 size_t len;
00217 unsigned int i;
00218
00219
00220
00221
00222 len = ( ( flsl ( value ) + 10 ) / 8 );
00223 if ( len >= iob_tailroom ( iobuf ) ) {
00224 DBGC2 ( slam, "SLAM %p cannot add %zd-byte value\n",
00225 slam, len );
00226 return -ENOBUFS;
00227 }
00228
00229
00230
00231
00232 assert ( len <= sizeof ( value ) );
00233
00234
00235 data = iob_put ( iobuf, len );
00236 for ( i = len ; i-- ; ) {
00237 data[i] = value;
00238 value >>= 8;
00239 }
00240 *data |= ( len << 5 );
00241 assert ( value == 0 );
00242
00243 return 0;
00244 }
00245
00246
00247
00248
00249
00250
00251
00252 static int slam_tx_nack ( struct slam_request *slam ) {
00253 struct io_buffer *iobuf;
00254 unsigned long first_block;
00255 unsigned long num_blocks;
00256 uint8_t *nul;
00257 int rc;
00258
00259
00260 slam->nack_sent = 1;
00261
00262
00263 iobuf = xfer_alloc_iob ( &slam->socket, SLAM_MAX_NACK_LEN );
00264 if ( ! iobuf ) {
00265 DBGC ( slam, "SLAM %p could not allocate I/O buffer\n",
00266 slam );
00267 return -ENOMEM;
00268 }
00269
00270
00271
00272
00273
00274
00275
00276 first_block = bitmap_first_gap ( &slam->bitmap );
00277 for ( num_blocks = 1 ; ; num_blocks++ ) {
00278 if ( num_blocks >= SLAM_MAX_BLOCKS_PER_NACK )
00279 break;
00280 if ( ( first_block + num_blocks ) >= slam->num_blocks )
00281 break;
00282 if ( bitmap_test ( &slam->bitmap,
00283 ( first_block + num_blocks ) ) )
00284 break;
00285 }
00286 if ( first_block ) {
00287 DBGCP ( slam, "SLAM %p transmitting NACK for blocks "
00288 "%ld-%ld\n", slam, first_block,
00289 ( first_block + num_blocks - 1 ) );
00290 } else {
00291 DBGC ( slam, "SLAM %p transmitting initial NACK for blocks "
00292 "0-%ld\n", slam, ( num_blocks - 1 ) );
00293 }
00294 if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 )
00295 return rc;
00296 if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 )
00297 return rc;
00298 nul = iob_put ( iobuf, 1 );
00299 *nul = 0;
00300
00301
00302 return xfer_deliver_iob ( &slam->socket, iobuf );
00303 }
00304
00305
00306
00307
00308
00309
00310
00311 static void slam_master_timer_expired ( struct retry_timer *timer,
00312 int fail ) {
00313 struct slam_request *slam =
00314 container_of ( timer, struct slam_request, master_timer );
00315
00316 if ( fail ) {
00317
00318
00319
00320 DBGC ( slam, "SLAM %p giving up acting as master client\n",
00321 slam );
00322 } else {
00323
00324 start_timer ( timer );
00325 slam_tx_nack ( slam );
00326 }
00327 }
00328
00329
00330
00331
00332
00333
00334
00335 static void slam_slave_timer_expired ( struct retry_timer *timer,
00336 int fail ) {
00337 struct slam_request *slam =
00338 container_of ( timer, struct slam_request, slave_timer );
00339
00340 if ( fail ) {
00341
00342 slam_finished ( slam, -ETIMEDOUT );
00343 } else {
00344
00345 DBGC ( slam, "SLAM %p trying to become master client\n",
00346 slam );
00347 start_timer ( timer );
00348 slam_tx_nack ( slam );
00349 }
00350 }
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368 static int slam_pull_value ( struct slam_request *slam,
00369 struct io_buffer *iobuf,
00370 unsigned long *value ) {
00371 uint8_t *data;
00372 size_t len;
00373
00374
00375 if ( iob_len ( iobuf ) == 0 ) {
00376 DBGC ( slam, "SLAM %p empty value\n", slam );
00377 return -EINVAL;
00378 }
00379
00380
00381 data = iobuf->data;
00382 len = ( *data >> 5 );
00383 if ( ( len == 0 ) ||
00384 ( value && ( len > sizeof ( *value ) ) ) ) {
00385 DBGC ( slam, "SLAM %p invalid value length %zd bytes\n",
00386 slam, len );
00387 return -EINVAL;
00388 }
00389 if ( len > iob_len ( iobuf ) ) {
00390 DBGC ( slam, "SLAM %p value extends beyond I/O buffer\n",
00391 slam );
00392 return -EINVAL;
00393 }
00394
00395
00396 iob_pull ( iobuf, len );
00397 *value = ( *data & 0x1f );
00398 while ( --len ) {
00399 *value <<= 8;
00400 *value |= *(++data);
00401 }
00402
00403 return 0;
00404 }
00405
00406
00407
00408
00409
00410
00411
00412
00413 static int slam_pull_header ( struct slam_request *slam,
00414 struct io_buffer *iobuf ) {
00415 void *header = iobuf->data;
00416 int rc;
00417
00418
00419 if ( ( slam->header_len <= iob_len ( iobuf ) ) &&
00420 ( memcmp ( slam->header, iobuf->data, slam->header_len ) == 0 )){
00421 iob_pull ( iobuf, slam->header_len );
00422 return 0;
00423 }
00424
00425 DBGC ( slam, "SLAM %p detected changed header; resetting\n", slam );
00426
00427
00428
00429
00430 if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 )
00431 return rc;
00432 if ( ( rc = slam_pull_value ( slam, iobuf,
00433 &slam->total_bytes ) ) != 0 )
00434 return rc;
00435 if ( ( rc = slam_pull_value ( slam, iobuf,
00436 &slam->block_size ) ) != 0 )
00437 return rc;
00438
00439
00440 slam->header_len = ( iobuf->data - header );
00441 assert ( slam->header_len <= sizeof ( slam->header ) );
00442 memcpy ( slam->header, header, slam->header_len );
00443
00444
00445 slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) /
00446 slam->block_size );
00447
00448 DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num "
00449 "blocks %ld\n", slam, slam->total_bytes, slam->block_size,
00450 slam->num_blocks );
00451
00452
00453 bitmap_free ( &slam->bitmap );
00454 memset ( &slam->bitmap, 0, sizeof ( slam->bitmap ) );
00455
00456
00457 if ( ( rc = bitmap_resize ( &slam->bitmap,
00458 slam->num_blocks ) ) != 0 ) {
00459
00460 DBGC ( slam, "SLAM %p could not allocate bitmap for %ld "
00461 "blocks: %s\n", slam, slam->num_blocks,
00462 strerror ( rc ) );
00463 slam_finished ( slam, rc );
00464 return rc;
00465 }
00466
00467
00468 xfer_seek ( &slam->xfer, slam->total_bytes, SEEK_SET );
00469
00470 return 0;
00471 }
00472
00473
00474
00475
00476
00477
00478
00479
00480 static int slam_mc_socket_deliver ( struct xfer_interface *mc_socket,
00481 struct io_buffer *iobuf,
00482 struct xfer_metadata *rx_meta __unused ) {
00483 struct slam_request *slam =
00484 container_of ( mc_socket, struct slam_request, mc_socket );
00485 struct xfer_metadata meta;
00486 unsigned long packet;
00487 size_t len;
00488 int rc;
00489
00490
00491 stop_timer ( &slam->master_timer );
00492 stop_timer ( &slam->slave_timer );
00493 start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT );
00494
00495
00496 if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 )
00497 goto err_discard;
00498
00499
00500 if ( ( rc = slam_pull_value ( slam, iobuf, &packet ) ) != 0 )
00501 goto err_discard;
00502
00503
00504 if ( packet >= slam->num_blocks ) {
00505 DBGC ( slam, "SLAM %p received out-of-range packet %ld "
00506 "(num_blocks=%ld)\n", slam, packet, slam->num_blocks );
00507 rc = -EINVAL;
00508 goto err_discard;
00509 }
00510
00511
00512 len = iob_len ( iobuf );
00513 if ( len > slam->block_size ) {
00514 DBGC ( slam, "SLAM %p received oversize packet of %zd bytes "
00515 "(block_size=%ld)\n", slam, len, slam->block_size );
00516 rc = -EINVAL;
00517 goto err_discard;
00518 }
00519 if ( ( packet != ( slam->num_blocks - 1 ) ) &&
00520 ( len < slam->block_size ) ) {
00521 DBGC ( slam, "SLAM %p received short packet of %zd bytes "
00522 "(block_size=%ld)\n", slam, len, slam->block_size );
00523 rc = -EINVAL;
00524 goto err_discard;
00525 }
00526
00527
00528 if ( bitmap_test ( &slam->bitmap, packet ) ) {
00529 goto discard;
00530 }
00531
00532
00533 memset ( &meta, 0, sizeof ( meta ) );
00534 meta.whence = SEEK_SET;
00535 meta.offset = ( packet * slam->block_size );
00536 if ( ( rc = xfer_deliver_iob_meta ( &slam->xfer, iobuf,
00537 &meta ) ) != 0 )
00538 goto err;
00539
00540
00541 bitmap_set ( &slam->bitmap, packet );
00542
00543
00544 if ( bitmap_full ( &slam->bitmap ) )
00545 slam_finished ( slam, 0 );
00546
00547 return 0;
00548
00549 err_discard:
00550 discard:
00551 free_iob ( iobuf );
00552 err:
00553 return rc;
00554 }
00555
00556
00557
00558
00559
00560
00561
00562
00563 static int slam_socket_deliver ( struct xfer_interface *socket,
00564 struct io_buffer *iobuf,
00565 struct xfer_metadata *rx_meta __unused ) {
00566 struct slam_request *slam =
00567 container_of ( socket, struct slam_request, socket );
00568 int rc;
00569
00570
00571 stop_timer ( &slam->master_timer );
00572 start_timer ( &slam->master_timer );
00573
00574
00575 if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 )
00576 goto discard;
00577
00578
00579 if ( iob_len ( iobuf ) != 0 ) {
00580 DBGC ( slam, "SLAM %p received trailing garbage:\n", slam );
00581 DBGC_HD ( slam, iobuf->data, iob_len ( iobuf ) );
00582 rc = -EINVAL;
00583 goto discard;
00584 }
00585
00586
00587 free_iob ( iobuf );
00588
00589
00590 slam_tx_nack ( slam );
00591
00592 return 0;
00593
00594 discard:
00595 free_iob ( iobuf );
00596 return rc;
00597
00598 }
00599
00600
00601
00602
00603
00604
00605
00606 static void slam_socket_close ( struct xfer_interface *socket, int rc ) {
00607 struct slam_request *slam =
00608 container_of ( socket, struct slam_request, socket );
00609
00610 DBGC ( slam, "SLAM %p unicast socket closed: %s\n",
00611 slam, strerror ( rc ) );
00612
00613 slam_finished ( slam, rc );
00614 }
00615
00616
00617 static struct xfer_interface_operations slam_socket_operations = {
00618 .close = slam_socket_close,
00619 .vredirect = xfer_vreopen,
00620 .window = unlimited_xfer_window,
00621 .alloc_iob = default_xfer_alloc_iob,
00622 .deliver_iob = slam_socket_deliver,
00623 .deliver_raw = xfer_deliver_as_iob,
00624 };
00625
00626
00627
00628
00629
00630
00631
00632 static void slam_mc_socket_close ( struct xfer_interface *mc_socket, int rc ){
00633 struct slam_request *slam =
00634 container_of ( mc_socket, struct slam_request, mc_socket );
00635
00636 DBGC ( slam, "SLAM %p multicast socket closed: %s\n",
00637 slam, strerror ( rc ) );
00638
00639 slam_finished ( slam, rc );
00640 }
00641
00642
00643 static struct xfer_interface_operations slam_mc_socket_operations = {
00644 .close = slam_mc_socket_close,
00645 .vredirect = xfer_vreopen,
00646 .window = unlimited_xfer_window,
00647 .alloc_iob = default_xfer_alloc_iob,
00648 .deliver_iob = slam_mc_socket_deliver,
00649 .deliver_raw = xfer_deliver_as_iob,
00650 };
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664 static void slam_xfer_close ( struct xfer_interface *xfer, int rc ) {
00665 struct slam_request *slam =
00666 container_of ( xfer, struct slam_request, xfer );
00667
00668 DBGC ( slam, "SLAM %p data transfer interface closed: %s\n",
00669 slam, strerror ( rc ) );
00670
00671 slam_finished ( slam, rc );
00672 }
00673
00674
00675 static struct xfer_interface_operations slam_xfer_operations = {
00676 .close = slam_xfer_close,
00677 .vredirect = ignore_xfer_vredirect,
00678 .window = unlimited_xfer_window,
00679 .alloc_iob = default_xfer_alloc_iob,
00680 .deliver_iob = xfer_deliver_as_raw,
00681 .deliver_raw = ignore_xfer_deliver_raw,
00682 };
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692 static int slam_parse_multicast_address ( struct slam_request *slam,
00693 const char *path,
00694 struct sockaddr_in *address ) {
00695 char path_dup[ strlen ( path ) ];
00696 char *sep;
00697 char *end;
00698
00699
00700 assert ( *path == '/' );
00701 memcpy ( path_dup, ( path + 1 ) , sizeof ( path_dup ) );
00702
00703
00704 sep = strchr ( path_dup, ':' );
00705 if ( sep ) {
00706 *(sep++) = '\0';
00707 address->sin_port = htons ( strtoul ( sep, &end, 0 ) );
00708 if ( *end != '\0' ) {
00709 DBGC ( slam, "SLAM %p invalid multicast port "
00710 "\"%s\"\n", slam, sep );
00711 return -EINVAL;
00712 }
00713 }
00714
00715
00716 if ( inet_aton ( path_dup, &address->sin_addr ) == 0 ) {
00717 DBGC ( slam, "SLAM %p invalid multicast address \"%s\"\n",
00718 slam, path_dup );
00719 return -EINVAL;
00720 }
00721
00722 return 0;
00723 }
00724
00725
00726
00727
00728
00729
00730
00731
00732 static int slam_open ( struct xfer_interface *xfer, struct uri *uri ) {
00733 static const struct sockaddr_in default_multicast = {
00734 .sin_family = AF_INET,
00735 .sin_port = htons ( SLAM_DEFAULT_MULTICAST_PORT ),
00736 .sin_addr = { htonl ( SLAM_DEFAULT_MULTICAST_IP ) },
00737 };
00738 struct slam_request *slam;
00739 struct sockaddr_tcpip server;
00740 struct sockaddr_in multicast;
00741 int rc;
00742
00743
00744 if ( ! uri->host )
00745 return -EINVAL;
00746
00747
00748 slam = zalloc ( sizeof ( *slam ) );
00749 if ( ! slam )
00750 return -ENOMEM;
00751 slam->refcnt.free = slam_free;
00752 xfer_init ( &slam->xfer, &slam_xfer_operations, &slam->refcnt );
00753 xfer_init ( &slam->socket, &slam_socket_operations, &slam->refcnt );
00754 xfer_init ( &slam->mc_socket, &slam_mc_socket_operations,
00755 &slam->refcnt );
00756 slam->master_timer.expired = slam_master_timer_expired;
00757 slam->slave_timer.expired = slam_slave_timer_expired;
00758
00759 slam->header_len = 1;
00760
00761 slam->num_blocks = 1;
00762 if ( ( rc = bitmap_resize ( &slam->bitmap, 1 ) ) != 0 ) {
00763 DBGC ( slam, "SLAM %p could not allocate initial bitmap: "
00764 "%s\n", slam, strerror ( rc ) );
00765 goto err;
00766 }
00767
00768
00769 memset ( &server, 0, sizeof ( server ) );
00770 server.st_port = htons ( uri_port ( uri, SLAM_DEFAULT_PORT ) );
00771 if ( ( rc = xfer_open_named_socket ( &slam->socket, SOCK_DGRAM,
00772 ( struct sockaddr * ) &server,
00773 uri->host, NULL ) ) != 0 ) {
00774 DBGC ( slam, "SLAM %p could not open unicast socket: %s\n",
00775 slam, strerror ( rc ) );
00776 goto err;
00777 }
00778
00779
00780 memcpy ( &multicast, &default_multicast, sizeof ( multicast ) );
00781 if ( uri->path &&
00782 ( ( rc = slam_parse_multicast_address ( slam, uri->path,
00783 &multicast ) ) != 0 ) ) {
00784 goto err;
00785 }
00786 if ( ( rc = xfer_open_socket ( &slam->mc_socket, SOCK_DGRAM,
00787 ( struct sockaddr * ) &multicast,
00788 ( struct sockaddr * ) &multicast ) ) != 0 ) {
00789 DBGC ( slam, "SLAM %p could not open multicast socket: %s\n",
00790 slam, strerror ( rc ) );
00791 goto err;
00792 }
00793
00794
00795 start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT );
00796
00797
00798 xfer_plug_plug ( &slam->xfer, xfer );
00799 ref_put ( &slam->refcnt );
00800 return 0;
00801
00802 err:
00803 slam_finished ( slam, rc );
00804 ref_put ( &slam->refcnt );
00805 return rc;
00806 }
00807
00808
00809 struct uri_opener slam_uri_opener __uri_opener = {
00810 .scheme = "x-slam",
00811 .open = slam_open,
00812 };