Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

slam.c 19KB

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