...

Source file src/github.com/mdlayher/ethernet/ethernet.go

Documentation: github.com/mdlayher/ethernet

     1  // Package ethernet implements marshaling and unmarshaling of IEEE 802.3
     2  // Ethernet II frames and IEEE 802.1Q VLAN tags.
     3  package ethernet
     4  
     5  import (
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"hash/crc32"
    10  	"io"
    11  	"net"
    12  )
    13  
    14  //go:generate stringer -output=string.go -type=EtherType
    15  
    16  const (
    17  	// minPayload is the minimum payload size for an Ethernet frame, assuming
    18  	// that no 802.1Q VLAN tags are present.
    19  	minPayload = 46
    20  )
    21  
    22  // Broadcast is a special hardware address which indicates a Frame should
    23  // be sent to every device on a given LAN segment.
    24  var Broadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
    25  
    26  // ErrInvalidFCS is returned when Frame.UnmarshalFCS detects an incorrect
    27  // Ethernet frame check sequence in a byte slice for a Frame.
    28  var ErrInvalidFCS = errors.New("invalid frame check sequence")
    29  
    30  // An EtherType is a value used to identify an upper layer protocol
    31  // encapsulated in a Frame.
    32  //
    33  // A list of IANA-assigned EtherType values may be found here:
    34  // http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml.
    35  type EtherType uint16
    36  
    37  // Common EtherType values frequently used in a Frame.
    38  const (
    39  	EtherTypeIPv4 EtherType = 0x0800
    40  	EtherTypeARP  EtherType = 0x0806
    41  	EtherTypeIPv6 EtherType = 0x86DD
    42  
    43  	// EtherTypeVLAN and EtherTypeServiceVLAN are used as 802.1Q Tag Protocol
    44  	// Identifiers (TPIDs).
    45  	EtherTypeVLAN        EtherType = 0x8100
    46  	EtherTypeServiceVLAN EtherType = 0x88a8
    47  )
    48  
    49  // A Frame is an IEEE 802.3 Ethernet II frame.  A Frame contains information
    50  // such as source and destination hardware addresses, zero or more optional
    51  // 802.1Q VLAN tags, an EtherType, and payload data.
    52  type Frame struct {
    53  	// Destination specifies the destination hardware address for this Frame.
    54  	//
    55  	// If this address is set to Broadcast, the Frame will be sent to every
    56  	// device on a given LAN segment.
    57  	Destination net.HardwareAddr
    58  
    59  	// Source specifies the source hardware address for this Frame.
    60  	//
    61  	// Typically, this is the hardware address of the network interface used to
    62  	// send this Frame.
    63  	Source net.HardwareAddr
    64  
    65  	// ServiceVLAN specifies an optional 802.1Q service VLAN tag, for use with
    66  	// 802.1ad double tagging, or "Q-in-Q". If ServiceVLAN is not nil, VLAN must
    67  	// not be nil as well.
    68  	//
    69  	// Most users should leave this field set to nil and use VLAN instead.
    70  	ServiceVLAN *VLAN
    71  
    72  	// VLAN specifies an optional 802.1Q customer VLAN tag, which may or may
    73  	// not be present in a Frame.  It is important to note that the operating
    74  	// system may automatically strip VLAN tags before they can be parsed.
    75  	VLAN *VLAN
    76  
    77  	// EtherType is a value used to identify an upper layer protocol
    78  	// encapsulated in this Frame.
    79  	EtherType EtherType
    80  
    81  	// Payload is a variable length data payload encapsulated by this Frame.
    82  	Payload []byte
    83  }
    84  
    85  // MarshalBinary allocates a byte slice and marshals a Frame into binary form.
    86  func (f *Frame) MarshalBinary() ([]byte, error) {
    87  	b := make([]byte, f.length())
    88  	_, err := f.read(b)
    89  	return b, err
    90  }
    91  
    92  // MarshalFCS allocates a byte slice, marshals a Frame into binary form, and
    93  // finally calculates and places a 4-byte IEEE CRC32 frame check sequence at
    94  // the end of the slice.
    95  //
    96  // Most users should use MarshalBinary instead.  MarshalFCS is provided as a
    97  // convenience for rare occasions when the operating system cannot
    98  // automatically generate a frame check sequence for an Ethernet frame.
    99  func (f *Frame) MarshalFCS() ([]byte, error) {
   100  	// Frame length with 4 extra bytes for frame check sequence
   101  	b := make([]byte, f.length()+4)
   102  	if _, err := f.read(b); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	// Compute IEEE CRC32 checksum of frame bytes and place it directly
   107  	// in the last four bytes of the slice
   108  	binary.BigEndian.PutUint32(b[len(b)-4:], crc32.ChecksumIEEE(b[0:len(b)-4]))
   109  	return b, nil
   110  }
   111  
   112  // read reads data from a Frame into b.  read is used to marshal a Frame
   113  // into binary form, but does not allocate on its own.
   114  func (f *Frame) read(b []byte) (int, error) {
   115  	// S-VLAN must also have accompanying C-VLAN.
   116  	if f.ServiceVLAN != nil && f.VLAN == nil {
   117  		return 0, ErrInvalidVLAN
   118  	}
   119  
   120  	copy(b[0:6], f.Destination)
   121  	copy(b[6:12], f.Source)
   122  
   123  	// Marshal each non-nil VLAN tag into bytes, inserting the appropriate
   124  	// EtherType/TPID before each, so devices know that one or more VLANs
   125  	// are present.
   126  	vlans := []struct {
   127  		vlan *VLAN
   128  		tpid EtherType
   129  	}{
   130  		{vlan: f.ServiceVLAN, tpid: EtherTypeServiceVLAN},
   131  		{vlan: f.VLAN, tpid: EtherTypeVLAN},
   132  	}
   133  
   134  	n := 12
   135  	for _, vt := range vlans {
   136  		if vt.vlan == nil {
   137  			continue
   138  		}
   139  
   140  		// Add VLAN EtherType and VLAN bytes.
   141  		binary.BigEndian.PutUint16(b[n:n+2], uint16(vt.tpid))
   142  		if _, err := vt.vlan.read(b[n+2 : n+4]); err != nil {
   143  			return 0, err
   144  		}
   145  		n += 4
   146  	}
   147  
   148  	// Marshal actual EtherType after any VLANs, copy payload into
   149  	// output bytes.
   150  	binary.BigEndian.PutUint16(b[n:n+2], uint16(f.EtherType))
   151  	copy(b[n+2:], f.Payload)
   152  
   153  	return len(b), nil
   154  }
   155  
   156  // UnmarshalBinary unmarshals a byte slice into a Frame.
   157  func (f *Frame) UnmarshalBinary(b []byte) error {
   158  	// Verify that both hardware addresses and a single EtherType are present
   159  	if len(b) < 14 {
   160  		return io.ErrUnexpectedEOF
   161  	}
   162  
   163  	// Track offset in packet for reading data
   164  	n := 14
   165  
   166  	// Continue looping and parsing VLAN tags until no more VLAN EtherType
   167  	// values are detected
   168  	et := EtherType(binary.BigEndian.Uint16(b[n-2 : n]))
   169  	switch et {
   170  	case EtherTypeServiceVLAN, EtherTypeVLAN:
   171  		// VLAN type is hinted for further parsing.  An index is returned which
   172  		// indicates how many bytes were consumed by VLAN tags.
   173  		nn, err := f.unmarshalVLANs(et, b[n:])
   174  		if err != nil {
   175  			return err
   176  		}
   177  
   178  		n += nn
   179  	default:
   180  		// No VLANs detected.
   181  		f.EtherType = et
   182  	}
   183  
   184  	// Allocate single byte slice to store destination and source hardware
   185  	// addresses, and payload
   186  	bb := make([]byte, 6+6+len(b[n:]))
   187  	copy(bb[0:6], b[0:6])
   188  	f.Destination = bb[0:6]
   189  	copy(bb[6:12], b[6:12])
   190  	f.Source = bb[6:12]
   191  
   192  	// There used to be a minimum payload length restriction here, but as
   193  	// long as two hardware addresses and an EtherType are present, it
   194  	// doesn't really matter what is contained in the payload.  We will
   195  	// follow the "robustness principle".
   196  	copy(bb[12:], b[n:])
   197  	f.Payload = bb[12:]
   198  
   199  	return nil
   200  }
   201  
   202  // UnmarshalFCS computes the IEEE CRC32 frame check sequence of a Frame,
   203  // verifies it against the checksum present in the byte slice, and finally,
   204  // unmarshals a byte slice into a Frame.
   205  //
   206  // Most users should use UnmarshalBinary instead.  UnmarshalFCS is provided as
   207  // a convenience for rare occasions when the operating system cannot
   208  // automatically verify a frame check sequence for an Ethernet frame.
   209  func (f *Frame) UnmarshalFCS(b []byte) error {
   210  	// Must contain enough data for FCS, to avoid panics
   211  	if len(b) < 4 {
   212  		return io.ErrUnexpectedEOF
   213  	}
   214  
   215  	// Verify checksum in slice versus newly computed checksum
   216  	want := binary.BigEndian.Uint32(b[len(b)-4:])
   217  	got := crc32.ChecksumIEEE(b[0 : len(b)-4])
   218  	if want != got {
   219  		return ErrInvalidFCS
   220  	}
   221  
   222  	return f.UnmarshalBinary(b[0 : len(b)-4])
   223  }
   224  
   225  // length calculates the number of bytes required to store a Frame.
   226  func (f *Frame) length() int {
   227  	// If payload is less than the required minimum length, we zero-pad up to
   228  	// the required minimum length
   229  	pl := len(f.Payload)
   230  	if pl < minPayload {
   231  		pl = minPayload
   232  	}
   233  
   234  	// Add additional length if VLAN tags are needed.
   235  	var vlanLen int
   236  	switch {
   237  	case f.ServiceVLAN != nil && f.VLAN != nil:
   238  		vlanLen = 8
   239  	case f.VLAN != nil:
   240  		vlanLen = 4
   241  	}
   242  
   243  	// 6 bytes: destination hardware address
   244  	// 6 bytes: source hardware address
   245  	// N bytes: VLAN tags (if present)
   246  	// 2 bytes: EtherType
   247  	// N bytes: payload length (may be padded)
   248  	return 6 + 6 + vlanLen + 2 + pl
   249  }
   250  
   251  // unmarshalVLANs unmarshals S/C-VLAN tags.  It is assumed that tpid
   252  // is a valid S/C-VLAN TPID.
   253  func (f *Frame) unmarshalVLANs(tpid EtherType, b []byte) (int, error) {
   254  	// 4 or more bytes must remain for valid S/C-VLAN tag and EtherType.
   255  	if len(b) < 4 {
   256  		return 0, io.ErrUnexpectedEOF
   257  	}
   258  
   259  	// Track how many bytes are consumed by VLAN tags.
   260  	var n int
   261  
   262  	switch tpid {
   263  	case EtherTypeServiceVLAN:
   264  		vlan := new(VLAN)
   265  		if err := vlan.UnmarshalBinary(b[n : n+2]); err != nil {
   266  			return 0, err
   267  		}
   268  		f.ServiceVLAN = vlan
   269  
   270  		// Assume that a C-VLAN immediately trails an S-VLAN.
   271  		if EtherType(binary.BigEndian.Uint16(b[n+2:n+4])) != EtherTypeVLAN {
   272  			return 0, ErrInvalidVLAN
   273  		}
   274  
   275  		// 4 or more bytes must remain for valid C-VLAN tag and EtherType.
   276  		n += 4
   277  		if len(b[n:]) < 4 {
   278  			return 0, io.ErrUnexpectedEOF
   279  		}
   280  
   281  		// Continue to parse the C-VLAN.
   282  		fallthrough
   283  	case EtherTypeVLAN:
   284  		vlan := new(VLAN)
   285  		if err := vlan.UnmarshalBinary(b[n : n+2]); err != nil {
   286  			return 0, err
   287  		}
   288  
   289  		f.VLAN = vlan
   290  		f.EtherType = EtherType(binary.BigEndian.Uint16(b[n+2 : n+4]))
   291  		n += 4
   292  	default:
   293  		panic(fmt.Sprintf("unknown VLAN TPID: %04x", tpid))
   294  	}
   295  
   296  	return n, nil
   297  }
   298  

View as plain text