11 * TFTM is a protocol defined in RFC2090 as a multicast extension to
15 static inline int tftm_process_opts ( struct tftp_state *state,
16 struct tftp_oack *oack ) {
17 struct in_addr old_mcast_addr = state->client.sin_addr;
19 if ( ! tftp_process_opts ( state, oack ) )
22 if ( old_mcast_addr.s_addr != state->client.sin_addr.s_addr ) {
23 if ( old_mcast_addr.s_addr ) {
24 DBG ( "TFTM: Leaving multicast group %@\n",
25 old_mcast_addr.s_addr );
26 leave_group ( IGMP_SERVER );
28 DBG ( "TFTM: Joining multicast group %@\n",
29 state->client.sin_addr.s_addr );
30 join_group ( IGMP_SERVER, state->client.sin_addr.s_addr );
33 DBG ( "TFTM: I am a %s client\n",
34 ( state->master ? "master" : "slave" ) );
40 static inline int tftm_process_data ( struct tftp_state *state,
41 struct tftp_data *data,
42 struct buffer *buffer ) {
46 /* Calculate block size and offset within file */
47 blksize = ( ntohs ( data->udp.len )
48 + offsetof ( typeof ( *data ), udp )
49 - offsetof ( typeof ( *data ), data ) );
50 offset = ( ntohs ( data->block ) - 1 ) * state->blksize;
52 /* Check for oversized block */
53 if ( blksize > state->blksize ) {
54 DBG ( "TFTM: oversized block size %d (max %d)\n",
55 blksize, state->blksize );
56 errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
60 /* Place block in the buffer */
61 if ( ! fill_buffer ( buffer, data->data, offset, blksize ) ) {
62 DBG ( "TFTM: could not place data in buffer: %m\n" );
66 /* If this is the last block, record the filesize (in case the
67 * server didn't supply a tsize option.
69 if ( blksize < state->blksize ) {
70 state->tsize = offset + blksize;
73 /* Record the last received block */
74 state->block = ntohs ( data->block );
80 static inline int tftm_next ( struct tftp_state *state,
81 union tftp_any **reply,
82 struct buffer *buffer ) {
85 listen_timeout = rfc2131_sleep_interval ( TIMEOUT, MAX_TFTP_RETRIES );
87 /* If we are not the master client, just listen for the next
90 if ( ! state->master ) {
91 if ( tftp_get ( state, listen_timeout, reply ) ) {
92 /* Heard a non-error packet */
96 /* Received an error packet */
99 /* Didn't hear anything; try prodding the server */
102 /* We are the master client; trigger the next packet
105 state->block = buffer->fill / state->blksize;
106 return tftp_ack ( state, reply );
110 * Download a file via TFTM
112 * @v server TFTP server
114 * @v buffer Buffer into which to load file
115 * @ret True File was downloaded successfully
116 * @ret False File was not downloaded successfully
117 * @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
118 * @err other As returned by tftp_open()
119 * @err other As returned by tftp_process_opts()
120 * @err other As returned by tftp_ack()
121 * @err other As returned by tftp_process_data()
123 * Download a file from a TFTP server into the specified buffer using
126 static int tftm ( char *url __unused, struct sockaddr_in *server, char *file,
127 struct buffer *buffer ) {
128 struct tftp_state state;
129 union tftp_any *reply;
132 /* Initialise TFTP state */
133 memset ( &state, 0, sizeof ( state ) );
134 state.server = *server;
136 /* Start as the master. This means that if the TFTP server
137 * doesn't actually support multicast, we'll still ACK the
138 * packets and it should all proceed as for a normal TFTP
144 if ( ! tftp_open ( &state, file, &reply, 1 ) ) {
145 DBG ( "TFTM: could not open %@:%d/%s : %m\n",
146 server->sin_addr.s_addr, server->sin_port, file );
150 /* Fetch file, a block at a time */
153 /* Process the current packet */
154 switch ( ntohs ( reply->common.opcode ) ) {
156 /* Options can be received at any time */
157 if ( ! tftm_process_opts ( &state, &reply->oack ) ) {
158 DBG ( "TFTM: failed to process OACK: %m\n" );
159 tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
164 if ( ! tftm_process_data ( &state, &reply->data,
166 DBG ( "TFTM: failed to process DATA: %m\n" );
167 tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
173 DBG ( "TFTM: unexpected packet type %d\n",
174 ntohs ( reply->common.opcode ) );
175 errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
176 tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
179 /* If we know the filesize, and we have all the data, stop */
180 if ( state.tsize && ( buffer->fill == state.tsize ) )
182 /* Fetch the next packet */
183 if ( ! tftm_next ( &state, &reply, buffer ) ) {
184 DBG ( "TFTM: could not get next block: %m\n" );
186 tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
193 /* ACK the final packet, as a courtesy to the server */
194 tftp_ack_nowait ( &state );
198 if ( state.client.sin_addr.s_addr ) {
199 leave_group ( IGMP_SERVER );
204 static struct protocol tftm_protocol __protocol = {
206 .default_port = TFTP_PORT,