-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdevice_ip_device.go
130 lines (112 loc) · 3.39 KB
/
device_ip_device.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
package exu
import (
log "github.com/sirupsen/logrus"
"net"
"sync"
"time"
)
type IpDevice struct {
*EthernetDevice
portIPs map[*VPort]net.IPNet
onReceiveIp func(srcPort *VPort, data *EthernetFrame)
onConnectIp func(port *VPort)
onDisconnectIp func(port *VPort)
arpTableMu sync.RWMutex
arpTable map[string]net.HardwareAddr
}
func NewIpDevice(name string, numberOfPorts int, onReceive func(srcPort *VPort, data *EthernetFrame), onConnect func(port *VPort), onDisconnect func(port *VPort)) *IpDevice {
ipDevice := &IpDevice{
portIPs: make(map[*VPort]net.IPNet),
arpTable: make(map[string]net.HardwareAddr),
arpTableMu: sync.RWMutex{},
onReceiveIp: onReceive,
onConnectIp: onConnect,
onDisconnectIp: onDisconnect,
}
ipDevice.EthernetDevice = NewEthernetDevice(name, numberOfPorts, ipDevice.onReceive, func(*VPort) {}, ipDevice.onDisconnect)
ipDevice.EthernetDevice.capabilities = append(ipDevice.EthernetDevice.capabilities, CapabilityIcmp{
IpDevice: ipDevice,
})
ipDevice.EthernetDevice.capabilities = append(ipDevice.EthernetDevice.capabilities, CapabilityArp{
IpDevice: ipDevice,
})
return ipDevice
}
func (d *IpDevice) SetPortIPNet(port *VPort, ipNet net.IPNet) {
d.portIPs[port] = ipNet
log.WithFields(log.Fields{
"device": d.name,
"port": port.portCname,
"ip": ipNet.IP,
}).Debug("set port IP")
}
func (d *IpDevice) onReceive(srcPort *VPort, data *EthernetFrame) {
d.onReceiveIp(srcPort, data)
}
func (d *IpDevice) onDisconnect(port *VPort) {
d.onDisconnectIp(port)
}
func (d *IpDevice) ArpResolve(requested net.IP) (net.HardwareAddr, error) {
log.WithFields(log.Fields{
"device": d.name,
"ip": requested,
}).Debug("resolving ARP")
// check if we already have the MAC address in our ARP table
if mac, ok := d.arpTable[requested.String()]; ok {
return mac, nil
}
// check which port is in the same network as the requested IP
ports := make([]*VPort, 0)
for p, ipNet := range d.portIPs {
networkAddress := net.IPNet{
IP: ipNet.IP.Mask(ipNet.Mask),
Mask: ipNet.Mask,
}
if networkAddress.Contains(requested) {
ports = append(ports, p)
}
}
// if we don't have a port in the same network, we just ask every port
if len(ports) == 0 {
ports = d.ports
}
for _, port := range ports {
arpRequestPayload := &ArpPacket{
HardwareType: ArpHardwareTypeEthernet,
ProtocolType: ArpProtocolTypeIPv4,
Opcode: ArpOpcodeRequest,
SenderIP: d.portIPs[d.ports[0]].IP,
TargetIP: requested,
SenderMac: d.ports[0].mac,
TargetMac: net.HardwareAddr{0, 0, 0, 0, 0, 0},
}
// create the ethernet frame
ethernetFrame, err := NewEthernetFrame(net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, port.mac, WithTagging(TaggingUntagged), arpRequestPayload)
if err != nil {
return nil, err
}
// write the frame to the port
_ = port.Write(ethernetFrame)
}
// wait for the ARP table to be updated with a timeout of 5 seconds
resultChan := make(chan net.HardwareAddr)
go func() {
for i := 0; i < 50; i++ {
d.arpTableMu.RLock()
mac, ok := d.arpTable[requested.String()]
d.arpTableMu.RUnlock()
if ok {
resultChan <- mac
return
}
time.Sleep(100 * time.Millisecond)
}
resultChan <- nil
}()
select {
case mac := <-resultChan:
return mac, nil
//case <-time.After(5 * time.Second):
// return nil, errors.New("timed out waiting for ARP response")
}
}