]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ethernet] Add minimal support for receiving LLC frames
authorMichael Brown <mcb30@ipxe.org>
Thu, 25 Jun 2015 13:37:18 +0000 (14:37 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 25 Jun 2015 14:28:42 +0000 (15:28 +0100)
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>
src/net/ethernet.c

index 33e057250d86f66b708d54ed00a934e2622e033e..6ddf053445a6ca16895ff5162b7db0668a0a2330 100644 (file)
@@ -46,6 +46,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 /** Ethernet broadcast MAC address */
 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
  *
@@ -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;
 }