-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdevice_vrouter.go
110 lines (90 loc) · 2.36 KB
/
device_vrouter.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
package exu
import (
"bytes"
"errors"
"net"
)
type Route struct {
Network net.IPNet
Via net.IP
}
type VRouter struct {
*IpDevice
routingTable []Route
defaultRoute *Route
}
func NewVRouter(name string, numberOfPorts int) *VRouter {
vRouter := &VRouter{
routingTable: make([]Route, 0),
}
vRouter.IpDevice = NewIpDevice(name, numberOfPorts, vRouter.onReceive, func(*VPort) {}, vRouter.onDisconnect)
vRouter.EthernetDevice.capabilities = append(vRouter.EthernetDevice.capabilities, CapabilityForwardArp{
IpDevice: vRouter.IpDevice,
})
return vRouter
}
func (r *VRouter) AddRoute(route Route) error {
// if the network is 0.0.0.0/0, set it as the default route
if route.Network.IP.Equal(net.IPv4zero) && bytes.Equal(route.Network.Mask, net.IPv4Mask(0, 0, 0, 0)) {
if r.defaultRoute != nil {
return errors.New("default route already set")
}
r.defaultRoute = &route
return nil
}
r.routingTable = append(r.routingTable, route)
return nil
}
func (r *VRouter) onReceive(_ *VPort, data *EthernetFrame) {
// right now we can only route IPv4 and ARP packets
if !data.EtherType().Equal(EtherTypeIPv4) {
return
}
// get the destination IP
ipv4Packet := &IPv4Packet{}
err := ipv4Packet.UnmarshalBinary(data.Payload())
if err != nil {
return
}
bestRoute := r.defaultRoute
for _, route := range r.routingTable {
// find a more fitting route
if route.Network.Contains(ipv4Packet.Header.DestinationIP) {
bestRoute = &route
break
}
}
// find an interface that is in the network of the next hop of the best route
var nextHopPort *VPort
for port, ipNet := range r.portIPs {
networkIP := net.IPNet{
IP: ipNet.IP.Mask(ipNet.Mask),
Mask: ipNet.Mask,
}
if networkIP.Contains(bestRoute.Via) {
nextHopPort = port
break
}
}
// if we don't have a next hop port, we can't route the packet
if nextHopPort == nil {
return
}
// decrement the TTL
ipv4Packet.Header.TTL--
// if the TTL is 0, drop the packet
if ipv4Packet.Header.TTL == 0 {
return
}
// recalculate the checksum
ipv4Packet.Header.HeaderChecksum = 0
ipv4Packet.Header.HeaderChecksum = ipv4Packet.Header.CalculateChecksum()
// create the ethernet frame
ethernetFrame, _ := NewEthernetFrame(nextHopPort.mac, data.Source(), TagData{}, ipv4Packet)
err = nextHopPort.Write(ethernetFrame)
if err != nil {
return
}
}
func (r *VRouter) onDisconnect(*VPort) {
}