Browse Source

First version

tags/v0.9.3
Michael Brown 19 years ago
parent
commit
8ae966720d
1 changed files with 299 additions and 0 deletions
  1. 299
    0
      src/proto/tftpcore.c

+ 299
- 0
src/proto/tftpcore.c View File

@@ -0,0 +1,299 @@
1
+#include "tftp.h"
2
+#include "tcp.h" /* for struct tcphdr */
3
+#include "errno.h"
4
+#include "etherboot.h"
5
+
6
+/** @file
7
+ *
8
+ * TFTP core functions
9
+ *
10
+ * This file provides functions that are common to the TFTP (rfc1350),
11
+ * TFTM (rfc2090) and MTFTP (PXE) protocols.
12
+ *
13
+ */
14
+
15
+/**
16
+ * Wait for a TFTP packet
17
+ *
18
+ * @v ptr			Pointer to a struct tftp_state
19
+ * @v ip			IP header
20
+ * @v udp			UDP header
21
+ * @ret True			This is our TFTP packet
22
+ * @ret False			This is not one of our TFTP packets
23
+ *
24
+ * Wait for a TFTP packet that is part of the current connection
25
+ * (i.e. comes from the TFTP server, has the correct destination port,
26
+ * and is addressed either to our IP address or to our multicast
27
+ * listening address).
28
+ */
29
+static int await_tftp ( int ival __unused, void *ptr,
30
+			unsigned short ptype __unused, struct iphdr *ip,
31
+			struct udphdr *udp, struct tcphdr *tcp __unused ) {
32
+	struct tftp_state *state = ptr;
33
+
34
+	/* Must have valid UDP (and, therefore, also IP) headers */
35
+	if ( ! udp ) {
36
+		return 0;
37
+	}
38
+	/* Packet must come from the TFTP server */
39
+	if ( ip->src.s_addr != state->server.sin_addr.s_addr )
40
+		return 0;
41
+	/* Packet must be addressed to the correct UDP port */
42
+	if ( ntohs ( udp->dest ) != state->client.sin_port )
43
+		return 0;
44
+	/* Packet must be addressed to us, or to our multicast
45
+	 * listening address (if we have one).
46
+	 */
47
+	if ( ! ( ( ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr ) ||
48
+		 ( ( state->client.sin_addr.s_addr ) && 
49
+		   ( ip->dest.s_addr == state->client.sin_addr.s_addr ) ) ) )
50
+		return 0;
51
+	return 1;
52
+}
53
+
54
+
55
+/**
56
+ * Issue a TFTP open request (RRQ)
57
+ *
58
+ * @v filename				File name
59
+ * @v state				TFTP transfer state
60
+ * @v tftp_state::server::sin_addr	TFTP server IP address
61
+ * @v tftp_state::server::sin_port	TFTP server UDP port, or 0
62
+ * @v tftp_state::client::sin_addr	Client multicast IP address, or 0.0.0.0
63
+ * @v tftp_state::client::sin_port	Client UDP port, or 0
64
+ * @v tftp_state::blksize		Requested blksize, or 0
65
+ * @ret True				Received a non-error response
66
+ * @ret False				Received error response / no response
67
+ * @ret tftp_state::client::sin_port	Client UDP port
68
+ * @ret tftp_state::client::blksize	Always #TFTP_DEFAULT_BLKSIZE
69
+ * @ret *tftp				The server's response, if any
70
+ *
71
+ * Send a TFTP/TFTM/MTFTP RRQ (read request) to a TFTP server, and
72
+ * return the server's reply (which may be an OACK, DATA or ERROR
73
+ * packet).  The server's reply will not be acknowledged, or processed
74
+ * in any way.
75
+ *
76
+ * If tftp_state::server::sin_port is 0, the standard tftp server port
77
+ * (#TFTP_PORT) will be used.
78
+ *
79
+ * If tftp_state::client::sin_addr is not 0.0.0.0, it will be used as
80
+ * a multicast listening address for replies from the TFTP server.
81
+ *
82
+ * If tftp_state::client::sin_port is 0, the standard mechanism of
83
+ * using a new, unique port number for each TFTP request will be used.
84
+ * 
85
+ * For the various different types of TFTP server, you should treat
86
+ * tftp_state::client as follows:
87
+ *
88
+ *   - Standard TFTP server: set tftp_state::client::sin_addr to
89
+ *     0.0.0.0 and tftp_state::client::sin_port to 0.  tftp_open()
90
+ *     will set tftp_state::client::sin_port to the assigned local UDP
91
+ *     port.
92
+ *
93
+ *   - TFTM server: set tftp_state::client::sin_addr to 0.0.0.0 and
94
+ *     tftp_state::client::sin_port to 0.  tftp_open() will set
95
+ *     tftp_state::client::sin_port to the assigned local UDP port.
96
+ *     (Your call to tftp_process_opts() will then overwrite both
97
+ *     tftp_state::client::sin_addr and tftp_state::client::sin_port
98
+ *     with the values return in the OACK packet.)
99
+ *
100
+ *   - MTFTP server: set tftp_state::client::sin_addr to the client
101
+ *     multicast address and tftp_state::client::sin_port to the
102
+ *     client multicast port (both of which must be previously known,
103
+ *     e.g. provided by a DHCP server).  tftp_open() will not alter
104
+ *     these values.
105
+ *
106
+ * If tftp_state::blksize is 0, the maximum blocksize
107
+ * (#TFTP_MAX_BLKSIZE) will be requested.
108
+ *
109
+ * On exit, tftp_state::blksize will always contain
110
+ * #TFTP_DEFAULT_BLKSIZE, since this is the blocksize value that must
111
+ * be assumed until the OACK packet is processed (by a subsequent call
112
+ * to tftp_process_opts()).
113
+ *
114
+ * The options "blksize", "tsize" and "multicast" will always be
115
+ * appended to a TFTP open request.  Servers that do not understand
116
+ * any of these options should simply ignore them.
117
+ *
118
+ * tftp_open() will not automatically join or leave multicast groups;
119
+ * the caller is responsible for calling join_group() and
120
+ * leave_group() at appropriate times.
121
+ *
122
+ */
123
+int tftp_open ( const char *filename, struct tftp_state *state,
124
+		union tftp_any **tftp ) {
125
+	static unsigned short lport = 2000; /* local port */
126
+	int fixed_lport;
127
+	struct tftp_rrq rrq;
128
+	unsigned int rrqlen;
129
+	int retry;
130
+
131
+	/* Flush receive queue */
132
+	rx_qdrain();
133
+
134
+	/* Default to blksize of TFTP_MAX_BLKSIZE if none specified */
135
+	if ( ! state->blksize )
136
+		state->blksize = TFTP_MAX_BLKSIZE;
137
+
138
+	/* Use default TFTP server port if none specified */
139
+	if ( ! state->server.sin_port )
140
+		state->server.sin_port = TFTP_PORT;
141
+
142
+	/* Determine whether or not to use lport */
143
+	fixed_lport = state->server.sin_port;
144
+
145
+	/* Set up RRQ */
146
+	rrq.opcode = htons ( TFTP_RRQ );
147
+	rrqlen = ( offsetof ( typeof ( rrq ), data ) +
148
+		    sprintf ( rrq.data,
149
+			      "%s%coctet%cblksize%c%d%ctsize%c0%cmulticast%c",
150
+			      filename, 0, 0, 0, state->blksize, 0, 0, 0, 0 )
151
+		    + 1 );
152
+
153
+	/* Set negotiated blksize to default value */
154
+	state->blksize = TFTP_DEFAULT_BLKSIZE;
155
+	
156
+	/* Nullify received packet pointer */
157
+	*tftp = NULL;
158
+
159
+	/* Transmit RRQ until we get a response */
160
+	for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
161
+		long timeout = rfc2131_sleep_interval ( TIMEOUT, retry );
162
+
163
+		/* Set client UDP port, if not already fixed */
164
+		if ( ! fixed_lport )
165
+			state->client.sin_port = ++lport;
166
+		
167
+		/* Send the RRQ */
168
+		if ( ! udp_transmit ( state->server.sin_addr.s_addr,
169
+				      state->client.sin_port,
170
+				      state->server.sin_port,
171
+				      rrqlen, &rrq ) )
172
+			return 0;
173
+		
174
+		/* Wait for response */
175
+		if ( await_reply ( await_tftp, 0, state, timeout ) ) {
176
+			*tftp = ( union tftp_any * ) &nic.packet[ETH_HLEN];
177
+			return 1;
178
+		}
179
+	}
180
+
181
+	errno = PXENV_STATUS_TFTP_OPEN_TIMEOUT;
182
+	return 0;
183
+}
184
+
185
+/**
186
+ * Process a TFTP OACK packet
187
+ *
188
+ * @v oack				The TFTP OACK packet
189
+ * @v state				TFTP transfer state
190
+ * @ret True				Options were processed successfully
191
+ * @ret False				Options were not processed successfully
192
+ * @ret tftp_state::blksize		Negotiated blksize
193
+ * @ret tftp_state::tsize		File size (if known), or 0
194
+ * @ret tftp_state::client::sin_addr	Client multicast IP address, or 0.0.0.0
195
+ * @ret tftp_state::client::sin_port	Client UDP port
196
+ * @ret tftp_state::master		Client is master
197
+ * @err EINVAL				An invalid option value was encountered
198
+ *
199
+ * Process the options returned by the TFTP server in an rfc2347 OACK
200
+ * packet.  The options "blksize" (rfc2348), "tsize" (rfc2349) and
201
+ * "multicast" (rfc2090) are recognised and processed; any other
202
+ * options are silently ignored.
203
+ *
204
+ * Where an option is not present in the OACK packet, the
205
+ * corresponding field(s) in #state will be left unaltered.
206
+ *
207
+ * Calling tftp_process_opts() does not send an acknowledgement for
208
+ * the OACK packet; this is the responsibility of the caller.
209
+ *
210
+ * @note If the "blksize" option is not present, tftp_state::blksize
211
+ * will @b not be implicitly set to #TFTP_DEFAULT_BLKSIZE.  However,
212
+ * since tftp_open() always sets tftp_state::blksize to
213
+ * #TFTP_DEFAULT_BLKSIZE before returning, you probably don't need to
214
+ * worry about this.
215
+ */
216
+int tftp_process_opts ( struct tftp_oack *oack, struct tftp_state *state ) {
217
+	const char *p;
218
+	const char *end;
219
+
220
+	/* End of options */
221
+	end = ( ( char * ) &oack->udp ) + ntohs ( oack->udp.len );
222
+
223
+	/* Only possible error */
224
+	errno = EINVAL;
225
+
226
+	for ( p = oack->data ; p < end ; ) {
227
+		if ( strcasecmp ( "blksize", p ) == 0 ) {
228
+			p += 8;
229
+			state->blksize = strtoul ( p, &p, 10 );
230
+			if ( *p ) {
231
+				DBG ( "TFTPCORE: garbage \"%s\" "
232
+				      "after blksize\n", p );
233
+				return 0;
234
+			}
235
+			p++;
236
+		} else if ( strcasecmp ( "tsize", p ) == 0 ) {
237
+			p += 6;
238
+			state->tsize = strtoul ( p, &p, 10 );
239
+			if ( *p ) {
240
+				DBG ( "TFTPCORE: garbage \"%s\" "
241
+				      "after tsize\n", p );
242
+				return 0;
243
+			}
244
+			p++;
245
+		} else if ( strcasecmp ( "multicast", p ) == 0 ) {
246
+			char *e = strchr ( p, ',' );
247
+			if ( ( ! e ) || ( e >= end ) ) {
248
+				DBG ( "TFTPCORE: malformed multicast field "
249
+				      "\"%s\"\n", p );
250
+				return 0;
251
+			}
252
+			/* IP address may be missing, in which case we
253
+			 * should leave state->client.sin_addr
254
+			 * unaltered.
255
+			 */
256
+			if ( e != p ) {
257
+				int rc;
258
+				*e = '\0';
259
+				rc = inet_aton ( p, &state->client.sin_addr );
260
+				*e = ',';
261
+				if ( ! rc ) {
262
+					DBG ( "TFTPCORE: malformed multicast "
263
+					      "IP address \"%s\"\n", p );
264
+					return 0;
265
+				}
266
+			}
267
+			p = e + 1;
268
+			/* UDP port may also be missing */
269
+			if ( *p != ',' ) {
270
+				state->client.sin_port = strtoul ( p, &p, 10 );
271
+				if ( *p != ',' ) {
272
+					DBG ( "TFTPCORE: garbage \"%s\" "
273
+					      "after multicast port\n", p );
274
+					return 0;
275
+				}
276
+			} else {
277
+				p++;
278
+			}
279
+			/* "Master Client" must always be present */
280
+			state->master = strtoul ( p, &p, 10 );
281
+			if ( *p ) {
282
+				DBG ( "TFTPCORE: garbage \"%s\" "
283
+				      "after multicast mc\n", p );
284
+				return 0;
285
+			}
286
+			p++;
287
+		} else {
288
+			p += strlen ( p ) + 1; /* skip option name */
289
+			p += strlen ( p ) + 1; /* skip option value */
290
+		}
291
+	}
292
+
293
+	if ( p > end ) {
294
+		DBG ( "TFTPCORE: overran options in OACK\n" );
295
+		return 0;
296
+	}
297
+
298
+	return 1;
299
+}

Loading…
Cancel
Save