-
Notifications
You must be signed in to change notification settings - Fork 1
/
ethernet.go
277 lines (256 loc) · 7.3 KB
/
ethernet.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
// Ethernet layer
// +build linux
package main
import (
"bytes"
"encoding/binary"
"github.com/golang/glog"
"net"
"syscall"
"unsafe"
)
const (
PF_PACKET = 17
ETH_P_ALL = 0x0003
READ_BUF_SIZE = 1000
ISIS_NEIGHBORS_TLV = 2
ISIS_IP_INTERNAL_REACH_TLV = 128
ISIS_IP_INTF_ADDR_TLV = 132
)
type RawSock struct {
fd int
intf *net.Interface
}
var RawSocks map[string][2]*RawSock // Map of interfaces to send and receive sockets
type IsisPDUHeader struct {
// Common 8 byte header to all PDUs
// Note that the fields must be exported for the binary.Read
IntraDomainRouteingProtocolDiscriminator byte // 0x83
LengthPDU byte
ProtocolID byte
SystemIDLength byte
TypePDU byte // first three bits are reserved and set to 0, next 5 bits are pdu type
Version byte
Reserved byte
MaximumAreaAddresses byte
}
type IsisTLV struct {
nextTLV *IsisTLV
typeTLV byte
lengthTLV byte
valueTLV []byte
}
func parseTLVs(rawBytes []byte, startIndex int) *IsisTLV {
// Starting from startIndex, build a linked list of TLVs
var firstTLV *IsisTLV = nil
var previousTLV *IsisTLV = nil
first := true
remainingTLVBytes := len(rawBytes) - startIndex
for remainingTLVBytes > 0 {
var currentTLV IsisTLV
// Fill in tlv
currentTLV.typeTLV = rawBytes[startIndex]
currentTLV.lengthTLV = rawBytes[startIndex+1]
glog.V(2).Infof("TLV code %d received, length %d!\n", rawBytes[startIndex], rawBytes[startIndex+1])
currentTLV.valueTLV = make([]byte, currentTLV.lengthTLV)
copy(currentTLV.valueTLV, rawBytes[startIndex+2:startIndex+2+int(currentTLV.lengthTLV)])
remainingTLVBytes -= (int(currentTLV.lengthTLV) + 2) // + 2 for type and length
startIndex += int(currentTLV.lengthTLV) + 2
if first {
// Set the first tlv to point to current tlv
firstTLV = ¤tTLV
// Set previous tlv to current tlv address so on the next iteration we can update next if need be
previousTLV = ¤tTLV
first = false
} else {
// Set previous.nextTLV to current tlv
previousTLV.nextTLV = ¤tTLV
// Set previous to current tlv
previousTLV = ¤tTLV
}
}
return firstTLV
}
func htons(host uint16) uint16 {
return (host&0xff)<<8 | (host >> 8)
}
func NewRawSock(ifname string) (*RawSock, error) {
intf, err := net.InterfaceByName(ifname)
if err != nil {
return nil, err
}
fd, err := syscall.Socket(PF_PACKET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
if err != nil {
return nil, err
}
return &RawSock{
fd: fd,
intf: intf,
}, nil
}
func NewRawSockRecv(ifname string) (*RawSock, error) {
intf, err := net.InterfaceByName(ifname)
if err != nil {
return nil, err
}
fd, err := syscall.Socket(PF_PACKET, syscall.SOCK_RAW, int(htons(ETH_P_ALL)))
if err != nil {
return nil, err
}
sll := syscall.RawSockaddrLinklayer{
Family: PF_PACKET,
Protocol: htons(ETH_P_ALL),
Ifindex: int32(intf.Index),
}
// Take our socket and bind it
_, _, e := syscall.Syscall(syscall.SYS_BIND,
uintptr(fd),
uintptr(unsafe.Pointer(&sll)),
unsafe.Sizeof(sll))
if e > 0 {
return nil, e
}
return &RawSock{
fd: fd,
intf: intf,
}, nil
}
func (c *RawSock) Read(b []byte) (int, *syscall.RawSockaddrLinklayer, error) {
var sll syscall.RawSockaddrLinklayer
size := unsafe.Sizeof(sll)
r1, _, err := syscall.Syscall6(syscall.SYS_RECVFROM,
uintptr(c.fd),
uintptr(unsafe.Pointer(&b[0])),
uintptr(len(b)),
0,
uintptr(unsafe.Pointer(&sll)),
uintptr(unsafe.Pointer(&size)))
if err > 0 {
return 0, nil, err
}
return int(r1), &sll, nil
}
func (c *RawSock) Write(b []byte) (n int, err error) {
// Write a raw ethernet frame to interface in RawSock
var dst [8]uint8
for i := 0; i < len(dst); i++ {
dst[i] = uint8(b[i])
}
sll := syscall.RawSockaddrLinklayer{
Ifindex: int32(c.intf.Index),
Addr: dst,
Halen: 6,
}
r1, _, e := syscall.Syscall6(syscall.SYS_SENDTO,
uintptr(c.fd),
uintptr(unsafe.Pointer(&b[0])),
uintptr(len(b)),
0,
uintptr(unsafe.Pointer(&sll)),
unsafe.Sizeof(sll))
if e > 0 {
return 0, e
}
return int(r1), e
}
func getMac(ifname string) []byte {
intf, _ := net.InterfaceByName(ifname)
src := make([]byte, len(intf.HardwareAddr))
copy(src, intf.HardwareAddr)
return src
}
func buildEthernetFrame(dst []byte, src []byte, payload []byte) []byte {
// Need a way to put a generic payload in an ethernet frame
// output needs to be a large byte slice which can be directly sent with Write
// Ethernet frame needs dst, src, type, payload
// TODO: figure out how to use encoding/gob here
etherType := []byte{0x08, 0x00}
var buf bytes.Buffer
// Can't write binary with nil pointer how to handle the TLVs?
binary.Write(&buf, binary.BigEndian, dst)
binary.Write(&buf, binary.BigEndian, src)
binary.Write(&buf, binary.BigEndian, etherType)
binary.Write(&buf, binary.BigEndian, payload)
return buf.Bytes()
}
func ethernetInit() {
RawSocks = make(map[string][2]*RawSock)
}
func ethernetIntfInit(ifname string) {
// Create raw send and receive sockets for given interface
send, err := NewRawSock(ifname)
if send == nil || err != nil {
glog.Error("Failed to open raw send socket", err)
}
recv, err := NewRawSockRecv(ifname)
if recv == nil || err != nil {
glog.Error("Failed to open raw recv socket", err)
}
var value [2]*RawSock
value[0] = send
value[1] = recv
RawSocks[ifname] = value
}
func sendFrame(frame []byte, ifname string) {
// Take in a byte slice payload and send it
numBytes, e := RawSocks[ifname][0].Write(frame)
if numBytes <= 0 {
glog.Error(e.Error())
}
}
func recvFrame(ifname string) []byte {
// Only return once a packet has been received which is not one
// we sent ourselves
// TODO: tune the buffer size
src := getMac(ifname)
var buf bytes.Buffer
var rawBuf [READ_BUF_SIZE]byte
buf.Grow(READ_BUF_SIZE)
for {
// Blocks until something is available
buf.Reset() // Zero out the buffer
numBytes, _, e := RawSocks[ifname][1].Read(rawBuf[:])
if e != nil {
glog.Error("Error reading bytes: ", e)
} else {
// Return anything that we did not send ourselves
if !bytes.Equal(buf.Bytes()[6:12], src) {
// Now copy numBytes from raw buf into our buffer
buf.Write(rawBuf[:numBytes])
return buf.Bytes()
}
}
}
}
func recvPdus(ifname string, hello chan []byte, update chan []byte) {
// Continuously read from the raw socks associated with the specified
// interface, putting the packets on the appropriate channels
// for the other goroutines to process
// pdu types:
// 0x0F --> l1 lan hello
// 0x12 --> l2 LSP
for {
buf := recvFrame(ifname)
// TODO: basic checks like length, checksum, auth
// Check the common IS-IS header for the pdu type
// This receive frame will have everything including the ethernet frame
// 14 bytes ethernet header, then its the 5th byte after that in the common header
// Make sure it is an IS-IS protocol packet
if buf[14] != 0x83 {
continue
}
pduType := buf[14+4]
if pduType == 0x0F {
hello <- buf
} else if pduType == 0x12 {
glog.Infof("Received an LSP %s", systemIDToString(buf[14+7+5:14+7+5+6]))
update <- buf
}
}
}
func sendPdus(ifname string, send chan []byte) {
// Continuously sendPdus
for {
sendFrame(<-send, ifname)
}
}