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