summaryrefslogtreecommitdiffstats
path: root/src/net/ethernet.c
diff options
context:
space:
mode:
authorMichael Brown2015-06-25 15:37:18 +0200
committerMichael Brown2015-06-25 16:28:42 +0200
commit7e7870984b4e63eeaf8416289c11bc386a77d5ba (patch)
tree5f62f7b0acc482038c08c176958577e5d8a536da /src/net/ethernet.c
parent[mromprefix] Report a dummy size at offset 0x02 of .mrom payload (diff)
downloadipxe-7e7870984b4e63eeaf8416289c11bc386a77d5ba.tar.gz
ipxe-7e7870984b4e63eeaf8416289c11bc386a77d5ba.tar.xz
ipxe-7e7870984b4e63eeaf8416289c11bc386a77d5ba.zip
[ethernet] Add minimal support for receiving LLC frames
In some Ethernet framing variants the two-byte protocol field is used as a length, with the Ethernet header being followed by an IEEE 802.2 LLC header. The first two bytes of the LLC header are the DSAP and SSAP. If the received Ethernet packet appears to use this framing, then interpret the two-byte DSAP and SSAP as being the network-layer protocol. This allows support for receiving Spanning Tree Protocol frames (which use an LLC header with {DSAP,SSAP}=0x4242) to be added without requiring a full LLC protocol layer. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net/ethernet.c')
-rw-r--r--src/net/ethernet.c38
1 files changed, 36 insertions, 2 deletions
diff --git a/src/net/ethernet.c b/src/net/ethernet.c
index 33e05725..6ddf0534 100644
--- a/src/net/ethernet.c
+++ b/src/net/ethernet.c
@@ -47,6 +47,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/**
+ * Check if Ethernet packet has an 802.3 LLC header
+ *
+ * @v ethhdr Ethernet header
+ * @ret is_llc Packet has 802.3 LLC header
+ */
+static inline int eth_is_llc_packet ( struct ethhdr *ethhdr ) {
+ uint8_t len_msb;
+
+ /* Check if the protocol field contains a value short enough
+ * to be a frame length. The slightly convoluted form of the
+ * comparison is designed to reduce to a single x86
+ * instruction.
+ */
+ len_msb = *( ( uint8_t * ) &ethhdr->h_protocol );
+ return ( len_msb < 0x06 );
+}
+
+/**
* Add Ethernet link-layer header
*
* @v netdev Network device
@@ -84,9 +102,14 @@ int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf,
const void **ll_dest, const void **ll_source,
uint16_t *net_proto, unsigned int *flags ) {
struct ethhdr *ethhdr = iobuf->data;
+ uint16_t *llc_proto;
- /* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) {
+ /* Sanity check. While in theory we could receive a one-byte
+ * packet, this will never happen in practice and performing
+ * the combined length check here avoids the need for an
+ * additional comparison if we detect an LLC frame.
+ */
+ if ( iob_len ( iobuf ) < ( sizeof ( *ethhdr ) + sizeof ( *llc_proto ))){
DBG ( "Ethernet packet too short (%zd bytes)\n",
iob_len ( iobuf ) );
return -EINVAL;
@@ -104,6 +127,17 @@ int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf,
( is_broadcast_ether_addr ( ethhdr->h_dest ) ?
LL_BROADCAST : 0 ) );
+ /* If this is an LLC frame (with a length in place of the
+ * protocol field), then use the next two bytes (which happen
+ * to be the LLC DSAP and SSAP) as the protocol. This allows
+ * for minimal-overhead support for receiving (rare) LLC
+ * frames, without requiring a full LLC protocol layer.
+ */
+ if ( eth_is_llc_packet ( ethhdr ) ) {
+ llc_proto = ( &ethhdr->h_protocol + 1 );
+ *net_proto = *llc_proto;
+ }
+
return 0;
}