manikkaraj k | 9eb6cac | 2019-05-09 12:32:03 -0400 | [diff] [blame] | 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 | var ( |
| 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 | |
| 28 | var ( |
| 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. |
| 39 | type EtherType uint16 |
| 40 | |
| 41 | // Common EtherType values frequently used in a Frame. |
| 42 | const ( |
| 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. |
| 56 | type 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. |
| 90 | func (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. |
| 103 | func (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. |
| 118 | func (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. |
| 161 | func (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. |
| 213 | func (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. |
| 230 | func (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. |
| 257 | func (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 | } |