-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathespstack.go
More file actions
194 lines (177 loc) · 5.63 KB
/
Copy pathespstack.go
File metadata and controls
194 lines (177 loc) · 5.63 KB
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
package espradio
import (
"errors"
"net/netip"
"time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/dhcp/dhcpv4"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/x/xnet"
)
var (
errDHCPInvalidSubnet = errors.New("dhcp server: invalid subnet")
errDHCPNoStaticAddr = errors.New("dhcp server: stack has no static IPv4 address")
)
// Stack wraps an lneto async network stack on top of a NetDev (EthernetDevice).
type Stack struct {
s xnet.StackAsync
dev *NetDev
rxtxBuf []byte
dhcpSrv dhcpv4.Server
}
// StackConfig configures the lneto-based network stack.
type StackConfig struct {
StaticAddress netip.Addr
StaticSubnet netip.Prefix
DNSServer netip.Addr
NTPServer netip.Addr
RandSeed int64
Hostname string
MaxTCPPorts int
MaxUDPPorts int
PassivePeers int
// AcceptBroadcast4 enables reception of IPv4 broadcast packets.
// Must be true when running a DHCP server (AP mode).
AcceptBroadcast4 bool
}
// DHCPConfig configures DHCP address acquisition.
type DHCPConfig struct {
RequestedAddr netip.Addr
}
// NewStack creates a new lneto-based TCP/IP stack on top of the given NetDev.
// The NetDev must already be started (WiFi joined, StartNetDev called).
func NewStack(dev *NetDev, cfg StackConfig) (*Stack, error) {
if cfg.Hostname == "" {
return nil, errors.New("empty hostname")
}
mac, err := dev.HardwareAddr6()
if err != nil {
return nil, err
}
stack := &Stack{dev: dev}
const MTU = MaxFrameSize - ethernet.MaxOverheadSize + 4 // CRC not included:+4
xcfg := xnet.StackConfig{
DNSServer: cfg.DNSServer,
NTPServer: cfg.NTPServer,
Hostname: cfg.Hostname,
MaxActiveTCPPorts: uint16(cfg.MaxTCPPorts),
MaxActiveUDPPorts: uint16(cfg.MaxUDPPorts),
RandSeed: time.Now().UnixNano() ^ cfg.RandSeed,
HardwareAddress: mac,
MTU: MTU,
PassivePeers: cfg.PassivePeers,
AcceptIPv4Broadcast: cfg.AcceptBroadcast4,
}
if cfg.StaticAddress.IsValid() && cfg.StaticAddress.Is4() {
xcfg.StaticAddress4 = cfg.StaticAddress.As4()
}
err = stack.s.Reset(xcfg)
if err != nil {
return nil, err
}
switch {
case cfg.StaticSubnet.IsValid():
addr := cfg.StaticSubnet.Addr()
if addr.Is4() {
stack.s.SetSubnet4(addr.As4(), uint8(cfg.StaticSubnet.Bits()))
}
case cfg.StaticAddress.IsValid() && cfg.StaticAddress.Is4():
// Default: derive a /24 subnet from the static address so passive
// ARP learning works without an explicit subnet.
stack.s.SetSubnet4(cfg.StaticAddress.As4(), 24)
}
dev.SetEthRecvHandler(func(pkt []byte) error {
return stack.s.IngressEthernet(pkt)
})
stack.rxtxBuf = make([]byte, MTU+ethernet.MaxOverheadSize)
return stack, nil
}
// LnetoStack returns the underlying lneto async stack for advanced use.
func (stack *Stack) LnetoStack() *xnet.StackAsync {
return &stack.s
}
// Hostname returns the hostname configured on the stack.
func (stack *Stack) Hostname() string {
return stack.s.Hostname()
}
// RecvAndSend polls the device for received frames and sends any pending
// outgoing frames. Returns the number of bytes sent and received.
func (stack *Stack) RecvAndSend() (send, recv int, err error) {
recv, errrecv := stack.dev.EthPoll(stack.rxtxBuf)
if pcapdebug && recv > 0 {
printPacket("IN", stack.rxtxBuf[:recv])
}
send, err = stack.s.EgressEthernet(stack.rxtxBuf)
if err != nil {
return send, recv, err
} else if errrecv != nil {
err = errrecv
}
if send == 0 {
return send, recv, err
}
if pcapdebug {
printPacket("OUT", stack.rxtxBuf[:send])
}
err = stack.dev.SendEthFrame(stack.rxtxBuf[:send])
return send, recv, err
}
// SetupWithDHCP performs DHCPv4 to obtain an IP address and configures the
// stack with the results. Blocks until complete or timeout.
func (stack *Stack) SetupWithDHCP(cfg DHCPConfig) (*xnet.DHCPResults, error) {
var reqaddr [4]byte
if cfg.RequestedAddr.IsValid() {
if !cfg.RequestedAddr.Is4() {
return nil, errors.New("IPv6 DHCP unsupported")
}
reqaddr = cfg.RequestedAddr.As4()
}
lstack := stack.LnetoStack()
rstack := lstack.StackRetrying(lneto.BackoffStrategy(func(_ uint) time.Duration {
return 50 * time.Millisecond
}))
dhcpResults, err := rstack.DoDHCPv4(reqaddr, 3*time.Second, 3)
if err != nil {
return dhcpResults, err
}
err = lstack.AssimilateDHCPResults(dhcpResults)
if err != nil {
return dhcpResults, err
}
gatewayHW, err := rstack.DoResolveHardwareAddress6(dhcpResults.Router, 500*time.Millisecond, 4)
if err != nil {
return dhcpResults, err
}
lstack.SetGatewayHardwareAddr(gatewayHW)
return dhcpResults, nil
}
// SetupWithDHCPServer starts a DHCPv4 server on the stack, assigning addresses
// from the given subnet. The stack's own address must already be set (via
// StackConfig.StaticAddress). subnet should cover the AP's address, e.g.
// netip.MustParsePrefix("192.168.4.0/24").
func (stack *Stack) SetupWithDHCPServer(subnet netip.Prefix) error {
if !subnet.IsValid() || !subnet.Addr().Is4() {
return errDHCPInvalidSubnet
}
lstack := stack.LnetoStack()
serverAddr := lstack.Addr4()
if serverAddr == [4]byte{} {
return errDHCPNoStaticAddr
}
if !subnet.Contains(netip.AddrFrom4(serverAddr)) {
return errDHCPInvalidSubnet
}
err := stack.dhcpSrv.Configure(dhcpv4.ServerConfig{
ServerAddr: serverAddr,
Gateway: serverAddr,
Subnet: ipv4.PrefixFromNetip(subnet),
})
if err != nil {
return err
}
// Pass zero raddr so lneto's UDP source-IP filter accepts packets from any
// source (0.0.0.0 disables the filter; required for DHCP clients that have
// no IP yet when sending Discovers/Requests).
return lstack.RegisterUDP4(&stack.dhcpSrv, [4]byte{}, dhcpv4.DefaultClientPort)
}