Browse Source

[undi] Allow underlying PXE stack to construct link-layer header

Some PXE stacks (observed with a QLogic 8242) will always try to
prepend a link-layer header, even if the caller uses P_UNKNOWN to
indicate that the link-layer header has already been filled in.  This
results in an invalid packet being transmitted.

Work around these faulty PXE stacks where possible by stripping the
existing link-layer header and allowing the PXE stack to (re)construct
the link-layer header itself.

Originally-fixed-by: Buck Huppmann <buckh@pobox.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
6324bd9389
3 changed files with 61 additions and 9 deletions
  1. 47
    2
      src/arch/i386/drivers/net/undinet.c
  2. 8
    0
      src/include/ipxe/ethernet.h
  3. 6
    7
      src/net/ethernet.c

+ 47
- 2
src/arch/i386/drivers/net/undinet.c View File

20
 
20
 
21
 #include <string.h>
21
 #include <string.h>
22
 #include <unistd.h>
22
 #include <unistd.h>
23
+#include <byteswap.h>
23
 #include <pxe.h>
24
 #include <pxe.h>
24
 #include <realmode.h>
25
 #include <realmode.h>
25
 #include <pic8259.h>
26
 #include <pic8259.h>
166
 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
167
 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
167
 #define undinet_tbd __use_data16 ( undinet_tbd )
168
 #define undinet_tbd __use_data16 ( undinet_tbd )
168
 
169
 
170
+/** UNDI transmit destination address */
171
+static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
172
+#define undinet_destaddr __use_data16 ( undinet_destaddr )
173
+
169
 /**
174
 /**
170
  * Transmit packet
175
  * Transmit packet
171
  *
176
  *
175
  */
180
  */
176
 static int undinet_transmit ( struct net_device *netdev,
181
 static int undinet_transmit ( struct net_device *netdev,
177
 			      struct io_buffer *iobuf ) {
182
 			      struct io_buffer *iobuf ) {
183
+	struct undi_nic *undinic = netdev->priv;
178
 	struct s_PXENV_UNDI_TRANSMIT undi_transmit;
184
 	struct s_PXENV_UNDI_TRANSMIT undi_transmit;
179
-	size_t len = iob_len ( iobuf );
185
+	const void *ll_dest;
186
+	const void *ll_source;
187
+	uint16_t net_proto;
188
+	unsigned int flags;
189
+	uint8_t protocol;
190
+	size_t len;
180
 	int rc;
191
 	int rc;
181
 
192
 
182
 	/* Technically, we ought to make sure that the previous
193
 	/* Technically, we ought to make sure that the previous
189
 	 * transmit the next packet.
200
 	 * transmit the next packet.
190
 	 */
201
 	 */
191
 
202
 
203
+	/* Some PXE stacks are unable to cope with P_UNKNOWN, and will
204
+	 * always try to prepend a link-layer header.  Work around
205
+	 * these stacks by stripping the existing link-layer header
206
+	 * and allowing the PXE stack to (re)construct the link-layer
207
+	 * header itself.
208
+	 */
209
+	if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
210
+			       &net_proto, &flags ) ) != 0 ) {
211
+		DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
212
+		       "%s\n", undinic, strerror ( rc ) );
213
+		return rc;
214
+	}
215
+	memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
216
+	switch ( net_proto ) {
217
+	case htons ( ETH_P_IP ) :
218
+		protocol = P_IP;
219
+		break;
220
+	case htons ( ETH_P_ARP ) :
221
+		protocol = P_ARP;
222
+		break;
223
+	case htons ( ETH_P_RARP ) :
224
+		protocol = P_RARP;
225
+		break;
226
+	default:
227
+		/* Unknown protocol; restore the original link-layer header */
228
+		iob_push ( iobuf, sizeof ( struct ethhdr ) );
229
+		protocol = P_UNKNOWN;
230
+		break;
231
+	}
232
+
192
 	/* Copy packet to UNDI I/O buffer */
233
 	/* Copy packet to UNDI I/O buffer */
234
+	len = iob_len ( iobuf );
193
 	if ( len > sizeof ( basemem_packet ) )
235
 	if ( len > sizeof ( basemem_packet ) )
194
 		len = sizeof ( basemem_packet );
236
 		len = sizeof ( basemem_packet );
195
 	memcpy ( &basemem_packet, iobuf->data, len );
237
 	memcpy ( &basemem_packet, iobuf->data, len );
196
 
238
 
197
 	/* Create PXENV_UNDI_TRANSMIT data structure */
239
 	/* Create PXENV_UNDI_TRANSMIT data structure */
198
 	memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
240
 	memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
241
+	undi_transmit.Protocol = protocol;
242
+	undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
243
+				   XMT_BROADCAST : XMT_DESTADDR );
199
 	undi_transmit.DestAddr.segment = rm_ds;
244
 	undi_transmit.DestAddr.segment = rm_ds;
200
-	undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd );
245
+	undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
201
 	undi_transmit.TBD.segment = rm_ds;
246
 	undi_transmit.TBD.segment = rm_ds;
202
 	undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
247
 	undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
203
 
248
 

+ 8
- 0
src/include/ipxe/ethernet.h View File

10
 FILE_LICENCE ( GPL2_OR_LATER );
10
 FILE_LICENCE ( GPL2_OR_LATER );
11
 
11
 
12
 #include <stdint.h>
12
 #include <stdint.h>
13
+#include <ipxe/netdevice.h>
14
+#include <ipxe/iobuf.h>
13
 
15
 
14
 /**
16
 /**
15
  * Check if Ethernet address is all zeroes
17
  * Check if Ethernet address is all zeroes
77
 		 ( ! is_zero_ether_addr ( addr ) ) );
79
 		 ( ! is_zero_ether_addr ( addr ) ) );
78
 }
80
 }
79
 
81
 
82
+extern int eth_push ( struct net_device *netdev, struct io_buffer *iobuf,
83
+		      const void *ll_dest, const void *ll_source,
84
+		      uint16_t net_proto );
85
+extern int eth_pull ( struct net_device *netdev, struct io_buffer *iobuf,
86
+		      const void **ll_dest, const void **ll_source,
87
+		      uint16_t *net_proto, unsigned int *flags );
80
 extern void eth_init_addr ( const void *hw_addr, void *ll_addr );
88
 extern void eth_init_addr ( const void *hw_addr, void *ll_addr );
81
 extern const char * eth_ntoa ( const void *ll_addr );
89
 extern const char * eth_ntoa ( const void *ll_addr );
82
 extern int eth_mc_hash ( unsigned int af, const void *net_addr,
90
 extern int eth_mc_hash ( unsigned int af, const void *net_addr,

+ 6
- 7
src/net/ethernet.c View File

50
  * @v net_proto		Network-layer protocol, in network-byte order
50
  * @v net_proto		Network-layer protocol, in network-byte order
51
  * @ret rc		Return status code
51
  * @ret rc		Return status code
52
  */
52
  */
53
-static int eth_push ( struct net_device *netdev __unused,
54
-		      struct io_buffer *iobuf, const void *ll_dest,
55
-		      const void *ll_source, uint16_t net_proto ) {
53
+int eth_push ( struct net_device *netdev __unused, struct io_buffer *iobuf,
54
+	       const void *ll_dest, const void *ll_source,
55
+	       uint16_t net_proto ) {
56
 	struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
56
 	struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
57
 
57
 
58
 	/* Build Ethernet header */
58
 	/* Build Ethernet header */
74
  * @ret flags		Packet flags
74
  * @ret flags		Packet flags
75
  * @ret rc		Return status code
75
  * @ret rc		Return status code
76
  */
76
  */
77
-static int eth_pull ( struct net_device *netdev __unused, 
78
-		      struct io_buffer *iobuf, const void **ll_dest,
79
-		      const void **ll_source, uint16_t *net_proto,
80
-		      unsigned int *flags ) {
77
+int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf,
78
+	       const void **ll_dest, const void **ll_source,
79
+	       uint16_t *net_proto, unsigned int *flags ) {
81
 	struct ethhdr *ethhdr = iobuf->data;
80
 	struct ethhdr *ethhdr = iobuf->data;
82
 
81
 
83
 	/* Sanity check */
82
 	/* Sanity check */

Loading…
Cancel
Save