-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathethernet_frame.go
136 lines (115 loc) · 3.88 KB
/
ethernet_frame.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package exu
import (
"errors"
"net"
)
var BroadcastMAC = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
// EthernetFrame represents an ethernet frame. The length of the underlying slice of a
// EthernetFrame should always reflect the ethernet frame length.
type EthernetFrame []byte
// VlanTagging is a type used to indicate whether/how a frame is tagged. The value
// is number of bytes taken by tagging.
type VlanTagging byte
// Const values for different VlanTagging
const (
TaggingUntagged VlanTagging = 0
TaggingTagged VlanTagging = 4
TaggingDoubleTagged VlanTagging = 8
)
// Destination returns the destination address field of the frame. The address
// references a slice on the frame.
//
// It is not safe to use this method if f is nil or an invalid ethernet frame.
func (f *EthernetFrame) Destination() net.HardwareAddr {
return net.HardwareAddr((*f)[:6:6])
}
// Source returns the source address field of the frame. The address references
// a slice on the frame.
//
// It is not safe to use this method if f is nil or an invalid ethernet frame.
func (f *EthernetFrame) Source() net.HardwareAddr {
return net.HardwareAddr((*f)[6:12:12])
}
// Tagging returns whether/how the frame has 802.1Q tag(s).
func (f *EthernetFrame) Tagging() VlanTagging {
if (*f)[12] == 0x81 && (*f)[13] == 0x00 {
return TaggingTagged
} else if (*f)[12] == 0x88 && (*f)[13] == 0xa8 {
return TaggingDoubleTagged
}
return TaggingUntagged
}
// Tags returns a slice holding the tag part of the frame, if any. Note that
// this includes the Tag Protocol Identifier (TPID), e.g. 0x8100 or 0x88a8.
// Upper layer should use the returned slice for both reading and writing.
func (f *EthernetFrame) Tags() []byte {
tagging := f.Tagging()
return (*f)[12 : 12+tagging : 12+tagging]
}
// EtherType returns the ethertype field of the frame.
//
// It is not safe to use this method if f is nil or an invalid ethernet frame.
func (f *EthernetFrame) EtherType() EtherType {
ethertypePos := 12 + f.Tagging()
return EtherType{(*f)[ethertypePos], (*f)[ethertypePos+1]}
}
// Payload returns a slice holding the payload part of the frame. Upper layer
// should use the returned slice for both reading and writing purposes.
func (f *EthernetFrame) Payload() []byte {
return (*f)[12+f.Tagging()+2:]
}
// Resize re-slices (*f) so that len(*f) holds exactly payloadSize bytes of
// payload. If cap(*f) is not large enough, a new slice is made and content
// from old slice is copied to the new one.
//
// If len(*f) is less than 14 bytes, it is assumed to be not tagged.
func (f *EthernetFrame) Resize(payloadSize int) {
tagging := TaggingUntagged
if len(*f) > 6+6+2 {
tagging = f.Tagging()
}
f.resize(6 + 6 + int(tagging) + 2 + payloadSize)
}
func (f *EthernetFrame) resize(length int) {
if cap(*f) < length {
old := *f
*f = make(EthernetFrame, length)
copy(*f, old)
} else {
*f = (*f)[:length]
}
}
func (f *EthernetFrame) FromBytes(data []byte) error {
if len(data) < 14 {
return errors.New("ethernet frame must be at least 14 bytes")
}
*f = data
return nil
}
// NewEthernetFrame prepares *f to be used, by filling in dst/src address, setting up
// proper tagging and ethertype, and resizing it to proper length.
func NewEthernetFrame(dst net.HardwareAddr, src net.HardwareAddr, tagData TagData, payload EthernetPayload) (f *EthernetFrame, err error) {
tagging := tagData.GetTagging()
ethertype := payload.EtherType()
payloadData, err := payload.MarshalBinary()
if err != nil {
return
}
payloadSize := len(payloadData)
f = &EthernetFrame{}
f.resize(6 + 6 + int(tagging) + 2 + payloadSize)
copy((*f)[0:6:6], dst)
copy((*f)[6:12:12], src)
if tagging == TaggingTagged {
(*f)[12] = 0x81
(*f)[13] = 0x00
} else if tagging == TaggingDoubleTagged {
(*f)[12] = 0x88
(*f)[13] = 0xa8
}
(*f)[12+tagging] = ethertype[0]
(*f)[12+tagging+1] = ethertype[1]
copy((*f)[12+tagging+2:], payloadData)
err = nil
return
}