diff options
author | Michael Brown <mcb30@ipxe.org> | 2015-06-25 14:37:18 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2015-06-25 15:28:42 +0100 |
commit | 7e7870984b4e63eeaf8416289c11bc386a77d5ba (patch) | |
tree | 5f62f7b0acc482038c08c176958577e5d8a536da | |
parent | 36817ea260331d7da9ea088f7e64d86ea9a1ac1d (diff) | |
download | ipxe-7e7870984b4e63eeaf8416289c11bc386a77d5ba.zip ipxe-7e7870984b4e63eeaf8416289c11bc386a77d5ba.tar.gz ipxe-7e7870984b4e63eeaf8416289c11bc386a77d5ba.tar.bz2 |
[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>
-rw-r--r-- | src/net/ethernet.c | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 33e0572..6ddf053 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 * ) ðhdr->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 = ( ðhdr->h_protocol + 1 ); + *net_proto = *llc_proto; + } + return 0; } |