diff --git a/address.go b/address.go index 2a104c0..c5df1dd 100644 --- a/address.go +++ b/address.go @@ -12,7 +12,7 @@ import ( ) var BroadcastAddr = HardwareAddr{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} -var UnsetupedAddr = HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +var EmptyAddr = HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // A media access control address (MAC address) is a unique identifier assigned // to a network interface controller (NIC) for use as a network address in communications @@ -51,7 +51,7 @@ func (h HardwareAddr) Oui() [3]byte { return [3]byte{h[0], h[1], h[2]} } // Network Interface Controller func (h HardwareAddr) Nic() [3]byte { return [3]byte{h[3], h[4], h[5]} } -// String stringify hexadecimal MAC address to output string. +// String stringifies hexadecimal MAC address to output string. // You have to manually check if the mac address is correct func (h HardwareAddr) String() string { return fmt.Sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", @@ -59,12 +59,12 @@ func (h HardwareAddr) String() string { ) } -// Compare comparing two MAC address for equality +// Compare compares two MAC addresses for equality func (h HardwareAddr) Compare(raddr HardwareAddr) bool { return bytes.Compare(h[:], raddr[:]) == 0 } // IsEmpty returns true if MAC address have only zeroes func (h HardwareAddr) IsEmpty() bool { - return h == HardwareAddr{0, 0, 0, 0, 0, 0} + return h == EmptyAddr } diff --git a/ethertype.go b/ethertype.go index f609ae4..f660927 100644 --- a/ethertype.go +++ b/ethertype.go @@ -13,7 +13,7 @@ package ethernet type EtherType uint16 const ( - EtypeTypeIpv4 EtherType = 0x8000 + EtherTypeIPv4 EtherType = 0x8000 EtherTypeIPv6 EtherType = 0x86DD EtherTypeVlan EtherType = 0x8100 ) diff --git a/frame.go b/frame.go index 630dfe8..913c52d 100644 --- a/frame.go +++ b/frame.go @@ -5,7 +5,6 @@ package ethernet import ( "encoding/binary" - "fmt" "hash/crc32" "io" "sync" @@ -20,27 +19,35 @@ import ( // other protocols (for example, Internet Protocol) carried in the frame. // The frame ends with a frame check sequence (FCS), which is a 32-bit cyclic redundancy check // used to detect any in-transit corruption of data. +// +// Preamble and SFD don't count into size of Ethernet frame size, because the physical layer +// determines where the frame starts. type Frame struct { dst HardwareAddr // destination MAC address src HardwareAddr // source MAC address - tag8021q *Tag8021q // 802.1Q (can be nil) + tag8021q *Tag8021Q // 802.1Q (can be nil) etherType EtherType payload []byte fcs [4]byte } -const minSize = 64 +// minHeaderSize is 6 bytes DST + 6 bytes SRC + 4 bytes FCS const minHeaderSize = 18 const minPayloadSize = 46 -// The maximum frame size is 1518 bytes, 18 bytes of which are overhead (header and frame check sequence), -// resulting in an MTU of 1500 bytes. -const MaxFrameSize = 1518 +const ( + // The minimal frame size is 64 bytes, comprising an 18-byte header and a payload of 46 bytes. + MinFrameSize = 64 + MinFrameSizeWithoutFCS = 60 + // The maximum frame size is 1518 bytes, 18 bytes of which are overhead (header and frame check sequence), + // resulting in an MTU of 1500 bytes. + MaxFrameSize = 1518 +) // NewFrame return constructed ethernet frame with basic source, destination MAC address // and payload which this frame contains. If payload have lengh which less than minPayloadSize // we fills remaining bytes with zeroes -func NewFrame(src HardwareAddr, dst HardwareAddr, payload []byte) *Frame { +func NewFrame(src HardwareAddr, dst HardwareAddr, etherType EtherType, payload []byte) *Frame { var b []byte pSz := len(payload) if pSz < minPayloadSize { @@ -54,7 +61,7 @@ func NewFrame(src HardwareAddr, dst HardwareAddr, payload []byte) *Frame { dst: dst, src: src, tag8021q: nil, - etherType: 0x0800, + etherType: etherType, payload: b, } return f @@ -78,14 +85,14 @@ func (f *Frame) EtherType() EtherType { return f.etherType } // Non-standard jumbo frames allow for larger maximum payload size. func (f *Frame) Payload() []byte { return f.payload } -// Tag8021q IEEE 802.1Q, often referred to as Dot1q, is the networking standard that +// Tag8021Q IEEE 802.1Q, often referred to as Dot1q, is the networking standard that // supports virtual LANs (VLANs) on an IEEE 802.3 Ethernet network. // The standard defines a system of VLAN tagging for Ethernet frames and the accompanying // procedures to be used by bridges and switches in handling such frames. // The standard also contains provisions for a quality-of-service (QOS) prioritization scheme commonly // known as IEEE 802.1p and defines the Generic Attribute Registration Protocol. -func (f *Frame) Tag8021q() *Tag8021q { return f.tag8021q } -func (f *Frame) SetTag8021q(tag *Tag8021q) { f.tag8021q = tag } +func (f *Frame) Tag8021Q() *Tag8021Q { return f.tag8021q } +func (f *Frame) SetTag8021Q(tag *Tag8021Q) { f.tag8021q = tag } // Frame Check Sequence (FCS) refers to the extra bits and characters added to // data packets for error detection and control. @@ -105,13 +112,11 @@ func (f *Frame) Size() int { var framePool = &sync.Pool{ New: func() interface{} { - // The maximum frame size is 1518 bytes, 18 bytes of which are overhead (header and frame check sequence), - // resulting in an MTU of 1500 bytes. return make([]byte, MaxFrameSize) }, } -func (f *Frame) marshal(fcs bool) []byte { +func (f *Frame) marshal() []byte { b := framePool.Get().([]byte) defer framePool.Put(b) @@ -120,12 +125,12 @@ func (f *Frame) marshal(fcs bool) []byte { b = append(b, f.src[:]...) if f.tag8021q != nil { b = append(b, - byte(f.tag8021q.Tpid>>8), - byte(f.tag8021q.Tpid), + byte(f.tag8021q.TPID>>8), + byte(f.tag8021q.TPID), ) b = append(b, - byte(f.tag8021q.Tci>>8), - byte(f.tag8021q.Tci), + byte(f.tag8021q.TCI>>8), + byte(f.tag8021q.TCI), ) } b = append(b, @@ -133,31 +138,29 @@ func (f *Frame) marshal(fcs bool) []byte { byte(f.etherType), ) b = append(b, f.payload...) - fmt.Println(len(b)) - if fcs { - sum := crc32.ChecksumIEEE(b[:]) - f.fcs = [4]byte{ - byte(sum >> 24), - byte(sum >> 16), - byte(sum >> 8), byte(sum), - } - b = append(b, f.fcs[:]...) + + sum := crc32.ChecksumIEEE(b[:]) + f.fcs = [4]byte{ + byte(sum >> 24), + byte(sum >> 16), + byte(sum >> 8), byte(sum), } + b = append(b, f.fcs[:]...) return b } -// Marshal implements serialization to the byte representation -// of the Frame structure. If the structure contains tag8021q, performs -// additional serialization of the 802.1Q header within Frame +// Marshal serializes frame into the byte representation. +// If the structure contains 802.1Q tag, performs an additional +// encoding of the 802.1Q header within the frame. func (f *Frame) Marshal() []byte { - return f.marshal(true) + return f.marshal() } // Unmarshal unmarshaling a sequence of bytes into a Frame structure representation. // If array size is less than minSize (64) returns error io.ErrUnexpectedEOF func Unmarshal(b []byte, f *Frame) error { sz := len(b) - if sz < minSize { + if sz < MinFrameSizeWithoutFCS { return io.ErrUnexpectedEOF } @@ -169,9 +172,9 @@ func Unmarshal(b []byte, f *Frame) error { etype := EtherType(binary.BigEndian.Uint16(b[n : n+2])) if etype == EtherTypeVlan { // have a 802.1Q tag - f.tag8021q = new(Tag8021q) - f.tag8021q.Tpid = uint16(etype) - f.tag8021q.Tci = binary.BigEndian.Uint16(b[n+2 : n+4]) + f.tag8021q = new(Tag8021Q) + f.tag8021q.TPID = uint16(etype) + f.tag8021q.TCI = binary.BigEndian.Uint16(b[n+2 : n+4]) f.etherType = EtherType(binary.BigEndian.Uint16(b[n+4 : n+6])) n += 6 } else { diff --git a/frame80211_test.go b/frame80211_test.go index 6edc963..e192c54 100644 --- a/frame80211_test.go +++ b/frame80211_test.go @@ -18,7 +18,7 @@ func TestFrame80211Marshal(t *testing.T) { qos uint16 ht uint32 sc uint16 - tag8021q *Tag8021q + tag8021q *Tag8021Q payload []byte wantLen int } diff --git a/frame_test.go b/frame_test.go index a00db4e..7324ac4 100644 --- a/frame_test.go +++ b/frame_test.go @@ -1,7 +1,6 @@ package ethernet import ( - "fmt" "math/rand" "testing" "time" @@ -14,14 +13,14 @@ func TestFrameMarshal(t *testing.T) { name string src HardwareAddr dst HardwareAddr - tag8021q *Tag8021q + tag8021q *Tag8021Q payload []byte wantLen int } testCases := []suite{ { - name: "positive_minimum", + name: "positive_min_padding", src: HardwareAddr{127, 127, 127, 50, 50, 50}, dst: HardwareAddr{255, 255, 255, 50, 50, 50}, payload: []byte("HELLO"), @@ -31,9 +30,9 @@ func TestFrameMarshal(t *testing.T) { name: "positive_tag8021q", src: HardwareAddr{127, 127, 127, 50, 50, 50}, dst: HardwareAddr{255, 255, 255, 50, 50, 50}, - tag8021q: &Tag8021q{ - Tpid: 0x15, - Tci: 0x55, + tag8021q: &Tag8021Q{ + TPID: 0x15, + TCI: 0x55, }, payload: []byte("HELLO"), wantLen: 68, @@ -42,12 +41,11 @@ func TestFrameMarshal(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - f := NewFrame(tc.src, tc.dst, tc.payload) + f := NewFrame(tc.src, tc.dst, EtherTypeIPv4, tc.payload) if tc.tag8021q != nil { - f.SetTag8021q(tc.tag8021q) + f.SetTag8021Q(tc.tag8021q) } b := f.Marshal() - fmt.Println(b) assert.NotEmpty(t, b) assert.Len(t, b, tc.wantLen) }) @@ -64,7 +62,7 @@ func generatePayload() []byte { func BenchmarkFrameMarshal(b *testing.B) { payload := generatePayload() b.ResetTimer() - f := NewFrame(HardwareAddr{127, 127, 127, 50, 50, 50}, HardwareAddr{255, 255, 255, 50, 50, 50}, payload) + f := NewFrame(HardwareAddr{127, 127, 127, 50, 50, 50}, HardwareAddr{255, 255, 255, 50, 50, 50}, EtherTypeIPv4, payload) for i := 0; i < b.N; i++ { _ = f.Marshal() } @@ -80,7 +78,7 @@ func TestFrameUnmarshal(t *testing.T) { testCases := []suite{ { - name: "positive_minimum_fcs", + name: "positive_min_fcs", data: []byte{127, 127, 127, 50, 50, 50, 255, 255, 255, 50, 50, 50, 8, 0, 72, 69, 76, 76, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 123, 123, 123}, wantSource: HardwareAddr{255, 255, 255, 50, 50, 50}, wantDestination: HardwareAddr{127, 127, 127, 50, 50, 50}, @@ -107,7 +105,7 @@ func TestFrameUnmarshal(t *testing.T) { func BenchmarkFrameUnmarshal(b *testing.B) { payload := generatePayload() - data := NewFrame(HardwareAddr{127, 127, 127, 50, 50, 50}, HardwareAddr{255, 255, 255, 50, 50, 50}, payload).Marshal() + data := NewFrame(HardwareAddr{127, 127, 127, 50, 50, 50}, HardwareAddr{255, 255, 255, 50, 50, 50}, EtherTypeIPv4, payload).Marshal() for i := 0; i < b.N; i++ { var f Frame err := Unmarshal(data, &f) diff --git a/ieee8021p.go b/ieee8021p.go index d0c75ff..a5611bc 100644 --- a/ieee8021p.go +++ b/ieee8021p.go @@ -4,12 +4,12 @@ package ethernet const ( - PcpBE = iota // Best Effort - PcpBK // Background - PcpEE // Excellent Effort - PcpCA // Critical Applications - PcpVI // Video, < 100 ms latency and jitter - PcpVO // Voice, < 10 ms latency and jitter - PcpIC // Internetwork Control - PCpNC // Network Control (highest) + PcpBE = iota + 1 // Best Effort + PcpBK // Background + PcpEE // Excellent Effort + PcpCA // Critical Applications + PcpVI // Video, < 100 ms latency and jitter + PcpVO // Voice, < 10 ms latency and jitter + PcpIC // Internetwork Control + PCpNC // Network Control (highest) ) diff --git a/ieee8021q.go b/ieee8021q.go index d5273c0..984df18 100644 --- a/ieee8021q.go +++ b/ieee8021q.go @@ -3,21 +3,21 @@ // that can be found in the LICENSE file. package ethernet -type Tag8021q struct { - Tpid uint16 - Tci uint16 +type Tag8021Q struct { + TPID uint16 + TCI uint16 } const maxPcp = 7 // from 0-7 const maxDei = 1 // from 0-1 const maxVlan = 4095 // from 0-4095 -// EncodeTag8021q encodes the 3 values PCP, DEI, VLAN using bitwise operations into 1 resulting value -func Encode8021qTci(pcp uint16, dei uint16, vlan uint16) uint16 { +// Encode8021qTCI encodes PCP, DEI, VLAN using bitwise operations. +func Encode8021qTCI(pcp uint16, dei uint16, vlan uint16) uint16 { return (vlan << 4) | (dei << 3) | pcp } -// DecodeTag8021q decodes the resulting encoded value to 3 universal values PCP, DEI, VLAN -func Decode8021qTci(encoded uint16) (pcp uint16, dei uint16, vlan uint16) { +// Decode8021qTCI decodes encoded TCI to 3 universal values PCP, DEI, VLAN +func Decode8021qTCI(encoded uint16) (pcp uint16, dei uint16, vlan uint16) { return encoded & maxPcp, (encoded >> 3) & maxDei, (encoded >> 4) & maxVlan }