package ethernet import ( "encoding/binary" "errors" "io" ) const ( // VLANNone is a special VLAN ID which indicates that no VLAN is being // used in a Frame. In this case, the VLAN's other fields may be used // to indicate a Frame's priority. VLANNone = 0x000 // VLANMax is a reserved VLAN ID which may indicate a wildcard in some // management systems, but may not be configured or transmitted in a // VLAN tag. VLANMax = 0xfff ) // ErrInvalidVLAN is returned when a VLAN tag is invalid due to one of the // following reasons: // - Priority of greater than 7 is detected // - ID of greater than 4094 (0xffe) is detected // - A customer VLAN does not follow a service VLAN (when using Q-in-Q) var ErrInvalidVLAN = errors.New("invalid VLAN") // Priority is an IEEE P802.1p priority level. Priority can be any value from // 0 to 7. // // It is important to note that priority 1 (PriorityBackground) actually has // a lower priority than 0 (PriorityBestEffort). All other Priority constants // indicate higher priority as the integer values increase. type Priority uint8 // IEEE P802.1p recommended priority levels. Note that PriorityBackground has // a lower priority than PriorityBestEffort. const ( PriorityBackground Priority = 1 PriorityBestEffort Priority = 0 PriorityExcellentEffort Priority = 2 PriorityCriticalApplications Priority = 3 PriorityVideo Priority = 4 PriorityVoice Priority = 5 PriorityInternetworkControl Priority = 6 PriorityNetworkControl Priority = 7 ) // A VLAN is an IEEE 802.1Q Virtual LAN (VLAN) tag. A VLAN contains // information regarding traffic priority and a VLAN identifier for // a given Frame. type VLAN struct { // Priority specifies a IEEE P802.1p priority level. Priority can be any // value from 0 to 7. Priority Priority // DropEligible indicates if a Frame is eligible to be dropped in the // presence of network congestion. DropEligible bool // ID specifies the VLAN ID for a Frame. ID can be any value from 0 to // 4094 (0x000 to 0xffe), allowing up to 4094 VLANs. // // If ID is 0 (0x000, VLANNone), no VLAN is specified, and the other fields // simply indicate a Frame's priority. ID uint16 } // MarshalBinary allocates a byte slice and marshals a VLAN into binary form. func (v *VLAN) MarshalBinary() ([]byte, error) { b := make([]byte, 2) _, err := v.read(b) return b, err } // read reads data from a VLAN into b. read is used to marshal a VLAN into // binary form, but does not allocate on its own. func (v *VLAN) read(b []byte) (int, error) { // Check for VLAN priority in valid range if v.Priority > PriorityNetworkControl { return 0, ErrInvalidVLAN } // Check for VLAN ID in valid range if v.ID >= VLANMax { return 0, ErrInvalidVLAN } // 3 bits: priority ub := uint16(v.Priority) << 13 // 1 bit: drop eligible var drop uint16 if v.DropEligible { drop = 1 } ub |= drop << 12 // 12 bits: VLAN ID ub |= v.ID binary.BigEndian.PutUint16(b, ub) return 2, nil } // UnmarshalBinary unmarshals a byte slice into a VLAN. func (v *VLAN) UnmarshalBinary(b []byte) error { // VLAN tag is always 2 bytes if len(b) != 2 { return io.ErrUnexpectedEOF } // 3 bits: priority // 1 bit : drop eligible // 12 bits: VLAN ID ub := binary.BigEndian.Uint16(b[0:2]) v.Priority = Priority(uint8(ub >> 13)) v.DropEligible = ub&0x1000 != 0 v.ID = ub & 0x0fff // Check for VLAN ID in valid range if v.ID >= VLANMax { return ErrInvalidVLAN } return nil }