Browse Source

Documented the UDP API calls.

tags/v0.9.3
Michael Brown 19 years ago
parent
commit
d8f187dc6a
1 changed files with 299 additions and 0 deletions
  1. 299
    0
      src/interface/pxe/pxe_udp.c

+ 299
- 0
src/interface/pxe/pxe_udp.c View File

@@ -0,0 +1,299 @@
1
+/** @file
2
+ *
3
+ * PXE UDP API
4
+ *
5
+ */
6
+
7
+#include "pxe.h"
8
+
9
+/*
10
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
11
+ *
12
+ * This program is free software; you can redistribute it and/or
13
+ * modify it under the terms of the GNU General Public License as
14
+ * published by the Free Software Foundation; either version 2 of the
15
+ * License, or any later version.
16
+ *
17
+ * This program is distributed in the hope that it will be useful, but
18
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
+ * General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program; if not, write to the Free Software
24
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
+ */
26
+
27
+/**
28
+ * UDP OPEN (#PXENV_UDP_OPEN)
29
+ *
30
+ * @v udp_open				Pointer to a struct s_PXENV_UDP_OPEN
31
+ * @v s_PXENV_UDP_OPEN::src_ip		IP address of this station, or 0.0.0.0
32
+ * @ret PXENV_EXIT_SUCCESS		Always
33
+ * @ret s_PXENV_UDP_OPEN::Status	PXE status code
34
+ * @err PXENV_STATUS_UNDI_INVALID_STATE	NIC could not be initialised
35
+ *
36
+ * Prepares the PXE stack for communication using pxenv_udp_write()
37
+ * and pxenv_udp_read().  The IP address supplied in
38
+ * s_PXENV_UDP_OPEN::src_ip will be recorded and used as the local
39
+ * station's IP address for all further communication, including
40
+ * communication by means other than pxenv_udp_write() and
41
+ * pxenv_udp_read().  (If s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the
42
+ * local station's IP address will remain unchanged.)
43
+ *
44
+ * You can have multiple UDP connections open simultaneously (and
45
+ * even open concurrently with TFTP connections), provided that
46
+ *
47
+ *   - they all have the same local IP address, and
48
+ *
49
+ *   - you take the multiple connections into account when calling
50
+ *     pxenv_udp_read().
51
+ *
52
+ * You can call pxenv_udp_open() in real mode, 16-bit protected mode
53
+ * with a 16-bit stack segment, 16-bit protected mode with a 32-bit
54
+ * stack segment, or V86 mode.  The pxe::StatusCallout field may be
55
+ * zero even in protected mode.
56
+ * 
57
+ */
58
+PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
59
+	DBG ( "PXENV_UDP_OPEN" );
60
+	ENSURE_READY ( udp_open );
61
+
62
+	if ( udp_open->src_ip &&
63
+	     udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) {
64
+		/* Overwrite our IP address */
65
+		DBG ( " with new IP %@", udp_open->src_ip );
66
+		arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip;
67
+	}
68
+
69
+	udp_open->Status = PXENV_STATUS_SUCCESS;
70
+	return PXENV_EXIT_SUCCESS;
71
+}
72
+
73
+/**
74
+ * UDP CLOSE (#PXENV_UDP_CLOSE)
75
+ *
76
+ * @v udp_close				Pointer to a struct s_PXENV_UDP_CLOSE
77
+ * @ret PXENV_EXIT_SUCCESS		Always
78
+ * @ret s_PXENV_UDP_CLOSE::Status	PXE status code
79
+ * @err None
80
+ *
81
+ * Closes a UDP "connection" opened with pxenv_udp_open().  Since UDP
82
+ * is a connectionless protocol, this is a no-op.
83
+ *
84
+ * You can call pxenv_udp_close() even if there is another active UDP
85
+ * or TFTP connection, since it has no effect on anything.
86
+ *
87
+ * You can call pxenv_udp_close() in real mode, 16-bit protected mode
88
+ * with a 16-bit stack segment, 16-bit protected mode with a 32-bit
89
+ * stack segment, or V86 mode.  The pxe::StatusCallout field may be
90
+ * zero even in protected mode.
91
+ *
92
+ */
93
+PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
94
+	DBG ( "PXENV_UDP_CLOSE" );
95
+	udp_close->Status = PXENV_STATUS_SUCCESS;
96
+	return PXENV_EXIT_SUCCESS;
97
+}
98
+
99
+/**
100
+ * UDP WRITE (#PXENV_UDP_WRITE)
101
+ *
102
+ * @v udp_write				Pointer to a struct s_PXENV_UDP_WRITE
103
+ * @v s_PXENV_UDP_WRITE::ip		Destination IP address
104
+ * @v s_PXENV_UDP_WRITE::gw		Gateway IP address, or 0.0.0.0
105
+ * @v s_PXENV_UDP_WRITE::src_port	Source UDP port, or 0
106
+ * @v s_PXENV_UDP_WRITE::dst_port	Destination UDP port
107
+ * @v s_PXENV_UDP_WRITE::buffer_size	Length of the UDP payload
108
+ * @v s_PXENV_UDP_WRITE::buffer		Address of the UDP payload
109
+ * @ret PXENV_EXIT_SUCCESS		Packet was transmitted successfully
110
+ * @ret PXENV_EXIT_FAILURE		Packet could not be transmitter
111
+ * @ret s_PXENV_UDP_WRITE::Status	PXE status code
112
+ * @err PXENV_STATUS_UNDI_INVALID_STATE	NIC could not be initialised
113
+ * @err PXENV_STATUS_OUT_OF_RESOURCES	Packet was too large to transmit
114
+ * @err other				Any error from pxenv_undi_transmit()
115
+ *
116
+ * Transmits a single UDP packet.  A valid IP and UDP header will be
117
+ * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
118
+ * should not contain precomputed IP and UDP headers, nor should it
119
+ * contain space allocated for these headers.  The first byte of the
120
+ * buffer will be transmitted as the first byte following the UDP
121
+ * header.
122
+ *
123
+ * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take
124
+ * place (using, for example, the default gateway IP address returned
125
+ * by the DHCP server).
126
+ *
127
+ * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
128
+ *
129
+ * It is not necessary to call pxenv_udp_open() before using
130
+ * pxenv_udp_write(), unless you want to change the local station's IP
131
+ * address.  pxenv_udp_write() can be called even if there is another
132
+ * active UDP or TFTP connection,.
133
+ *
134
+ * You can call pxenv_udp_write() in real mode, 16-bit protected mode
135
+ * with a 16-bit stack segment, 16-bit protected mode with a 32-bit
136
+ * stack segment, or V86 mode.  The pxe::StatusCallout field may be
137
+ * zero even in protected mode.
138
+ *
139
+ * @bug s_PXENV_UDP_WRITE::gw is ignored; the default routing table is
140
+ * always used.
141
+ *
142
+ */
143
+PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write ) {
144
+	uint16_t src_port;
145
+	uint16_t dst_port;
146
+	struct udppacket *packet = (struct udppacket *)nic.packet;
147
+	int packet_size;
148
+
149
+	DBG ( "PXENV_UDP_WRITE" );
150
+	ENSURE_READY ( udp_write );
151
+
152
+	/* PXE spec says source port is 2069 if not specified */
153
+	src_port = ntohs(udp_write->src_port);
154
+	if ( src_port == 0 ) src_port = 2069;
155
+	dst_port = ntohs(udp_write->dst_port);
156
+	DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port,
157
+	      udp_write->buffer_size );
158
+	
159
+	/* FIXME: we ignore the gateway specified, since we're
160
+	 * confident of being able to do our own routing.  We should
161
+	 * probably allow for multiple gateways.
162
+	 */
163
+	
164
+	/* Copy payload to packet buffer */
165
+	packet_size = ( (void*)&packet->payload - (void*)packet )
166
+		+ udp_write->buffer_size;
167
+	if ( packet_size > ETH_FRAME_LEN ) {
168
+		udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
169
+		return PXENV_EXIT_FAILURE;
170
+	}
171
+	memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer),
172
+		 udp_write->buffer_size );
173
+
174
+	/* Transmit packet */
175
+	if ( ! udp_transmit ( udp_write->ip, src_port, dst_port,
176
+			      packet_size, packet ) ) {
177
+		udp_write->Status = errno;
178
+		return PXENV_EXIT_FAILURE;
179
+	}
180
+
181
+	udp_write->Status = PXENV_STATUS_SUCCESS;
182
+	return PXENV_EXIT_SUCCESS;
183
+}
184
+
185
+/* Utility function for pxenv_udp_read() */
186
+static int await_pxe_udp ( int ival __unused, void *ptr,
187
+			   unsigned short ptype __unused,
188
+			   struct iphdr *ip, struct udphdr *udp,
189
+			   struct tcphdr *tcp __unused ) {
190
+	t_PXENV_UDP_READ *udp_read = (t_PXENV_UDP_READ*)ptr;
191
+	uint16_t d_port;
192
+	size_t size;
193
+
194
+	/* Ignore non-UDP packets */
195
+	if ( !udp ) {
196
+		DBG ( " non-UDP" );
197
+		return 0;
198
+	}
199
+	
200
+	/* Check dest_ip */
201
+	if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) {
202
+		DBG ( " wrong dest IP (got %@, wanted %@)",
203
+		      ip->dest.s_addr, udp_read->dest_ip );
204
+		return 0;
205
+	}
206
+
207
+	/* Check dest_port */
208
+	d_port = ntohs ( udp_read->d_port );
209
+	if ( d_port && ( d_port != ntohs(udp->dest) ) ) {
210
+		DBG ( " wrong dest port (got %d, wanted %d)",
211
+		      ntohs(udp->dest), d_port );
212
+		return 0;
213
+	}
214
+
215
+	/* Copy packet to buffer and fill in information */
216
+	udp_read->src_ip = ip->src.s_addr;
217
+	udp_read->s_port = udp->src; /* Both in network order */
218
+	size = ntohs(udp->len) - sizeof(*udp);
219
+	/* Workaround: NTLDR expects us to fill these in, even though
220
+	 * PXESPEC clearly defines them as input parameters.
221
+	 */
222
+	udp_read->dest_ip = ip->dest.s_addr;
223
+	udp_read->d_port = udp->dest;
224
+	DBG ( " %@:%d->%@:%d (%d)",
225
+	      udp_read->src_ip, ntohs(udp_read->s_port),
226
+	      udp_read->dest_ip, ntohs(udp_read->d_port), size );
227
+	if ( udp_read->buffer_size < size ) {
228
+		/* PXESPEC: what error code should we actually return? */
229
+		DBG ( " buffer too small (%d)", udp_read->buffer_size );
230
+		udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES;
231
+		return 0;
232
+	}
233
+	memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size );
234
+	udp_read->buffer_size = size;
235
+
236
+	return 1;
237
+}
238
+
239
+/**
240
+ * UDP READ (#PXENV_UDP_READ)
241
+ *
242
+ * @v udp_read				Pointer to a struct s_PXENV_UDP_READ
243
+ * @v s_PXENV_UDP_READ::dest_ip		Destination IP address, or 0.0.0.0
244
+ * @v s_PXENV_UDP_READ::d_port		Destination UDP port, or 0
245
+ * @v s_PXENV_UDP_READ::buffer_size	Size of the UDP payload buffer
246
+ * @v s_PXENV_UDP_READ::buffer		Address of the UDP payload buffer
247
+ * @ret PXENV_EXIT_SUCCESS		A packet has been received
248
+ * @ret PXENV_EXIT_FAILURE		No packet has been received
249
+ * @ret s_PXENV_UDP_READ::Status	PXE status code
250
+ * @ret s_PXENV_UDP_READ::src_ip	Source IP address
251
+ * @ret s_PXEND_UDP_READ::dest_ip	Destination IP address
252
+ * @ret s_PXENV_UDP_READ::s_port	Source UDP port
253
+ * @ret s_PXENV_UDP_READ::d_port	Destination UDP port
254
+ * @ret s_PXENV_UDP_READ::buffer_size	Length of UDP payload
255
+ * @err PXENV_STATUS_UNDI_INVALID_STATE	NIC could not be initialised
256
+ * @err PXENV_STATUS_OUT_OF_RESOURCES	Buffer was too small for payload
257
+ * @err PXENV_STATUS_FAILURE		No packet was ready to read
258
+ *
259
+ * Receive a single UDP packet.  This is a non-blocking call; if no
260
+ * packet is ready to read, the call will return instantly with
261
+ * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.
262
+ *
263
+ * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to
264
+ * any IP address will be accepted and may be returned.
265
+ *
266
+ * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP
267
+ * port will be accepted and may be returned.
268
+ *
269
+ * It is not necessary to call pxenv_udp_open() before using
270
+ * pxenv_udp_read().  pxenv_udp_read() can be called even if there is
271
+ * another active UDP or TFTP connection, but be aware that you might
272
+ * then receive (or cause to be lost) a packet belonging to another
273
+ * connection.
274
+ *
275
+ * You can call pxenv_udp_read() in real mode, 16-bit protected mode
276
+ * with a 16-bit stack segment, 16-bit protected mode with a 32-bit
277
+ * stack segment, or V86 mode.  The pxe::StatusCallout field may be
278
+ * zero even in protected mode.
279
+ *
280
+ * @note The PXE specification (version 2.1) does not state that we
281
+ * should fill in s_PXENV_UDP_READ::dest_ip and
282
+ * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program
283
+ * expects us to do so, and will fail if we don't.
284
+ *
285
+ */
286
+PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read ) {
287
+	DBG ( "PXENV_UDP_READ" );
288
+	ENSURE_READY ( udp_read );
289
+
290
+	/* Use await_reply with a timeout of zero */
291
+	/* Allow await_reply to change Status if necessary */
292
+	udp_read->Status = PXENV_STATUS_FAILURE;
293
+	if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) {
294
+		return PXENV_EXIT_FAILURE;
295
+	}
296
+
297
+	udp_read->Status = PXENV_STATUS_SUCCESS;
298
+	return PXENV_EXIT_SUCCESS;
299
+}

Loading…
Cancel
Save