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