Files
lneto/x/xnet/netstack.go
T
Pat Whittingslow 813b7b5e57 Lay out device/stack interfaces - Netdev/Netlink (#92)
* good code today

* add netdev.Runner

* round off APIs more

* better interface method documentation

* improve DHCP netstack API

* add pico w netdev example

* remove temp file

* keep thinking about this. this is hard :/

* fix ci

* small fixer

* working dhcp

* fix rebase API mismatches

* begin adding espradio example

* keep working on espradio, icmp not working

* fix icmp by fixing dhcp
2026-06-17 14:18:08 -03:00

112 lines
3.7 KiB
Go

package xnet
import (
"context"
"net/netip"
"time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/x/netdev"
)
// Netstack is a more modern outward facing API wrapper on StackAsync.
type Netstack struct {
stack StackAsync
//gstack references stack above.
gstack StackGo
awaitDHCP bool
}
var _ netdev.Stack = (*Netstack)(nil)
// Configure configures this Stack with the argument mac, ip and gateway addresses.
// The Stack must resolve the gateway hardware address if set.
func (netstack *Netstack) Reset(cfg StackConfig, stackBackoff lneto.BackoffStrategy, poolcfg TCPPoolConfig) error {
err := netstack.stack.Reset(cfg)
if err != nil {
return err
}
netstack.gstack = netstack.stack.StackGo(stackBackoff, StackGoConfig{
ListenerPoolConfig: poolcfg,
})
return nil
}
func (netstack *Netstack) IPAddr4() [4]byte {
return netstack.stack.Addr4()
}
// EnableICMP enables responding/sending ICMP echo frames.
func (netstack *Netstack) EnableICMP(enabled bool) error {
return netstack.stack.EnableICMP(enabled)
}
// EnableDHCP performs a DHCPv4 request, writes the assigned address into the
// stack, and resolves the gateway hardware address via ARP.
func (netstack *Netstack) EnableDHCP(ctx context.Context, enabled bool, requestAddr netip.Addr) (assigned netip.Addr, routerGW netip.Addr, subnetBits int, err error) {
var addr [4]byte
if !enabled {
netstack.stack.dhcp.Reset()
return netip.Addr{}, netip.Addr{}, 0, nil
} else if requestAddr.Is4() {
addr = requestAddr.As4()
} else if requestAddr.IsValid() {
return netip.Addr{}, netip.Addr{}, 0, lneto.ErrUnsupported
}
timeout := 4 * time.Second
deadline, ok := ctx.Deadline()
if ok {
timeout = time.Until(deadline)
}
results, err := netstack.gstack.blk.DoDHCPv4(addr, timeout)
if err != nil {
return netip.Addr{}, netip.Addr{}, 0, err
}
// Write IP, subnet and DNS into the stack.
if err = netstack.stack.AssimilateDHCPResults(results); err != nil {
return netip.AddrFrom4(results.AssignedAddr4), results.Router, results.Subnet.Bits(), err
}
// Resolve gateway hardware address so egress packets are unicasted to the
// router rather than broadcast. Failure is non-fatal.
if results.Router.IsValid() {
gwHW, gwErr := netstack.gstack.blk.DoResolveHardwareAddress6(results.Router, 2*time.Second)
if gwErr == nil {
netstack.stack.SetGatewayHardwareAddr(gwHW)
}
}
return netip.AddrFrom4(results.AssignedAddr4), results.Router, results.Subnet.Bits(), nil
}
// Socket is a berkeley socket abstraction. Returns an [net.Listener] or [net.Conn] depending on laddr/raddr combination.
func (netstack *Netstack) Socket(ctx context.Context, network string, family, sotype int, laddr, raddr netip.AddrPort) (c any, err error) {
return netstack.gstack.SocketNetip(ctx, network, family, sotype, laddr, raddr)
}
// EgressPackets instructs Stack to write outgoing packets into bufs and writing the sizes into sizes not including initial offset.
// offset can be used to tell the stack to start writing after an offset for each buffer.
func (netstack *Netstack) EgressPackets(bufs [][]byte, sizes []int, offset int) (err error) {
var err0 error
for i := range bufs {
sizes[i], err0 = netstack.stack.EgressEthernet(bufs[i][offset:])
if sizes[i] == 0 {
return err
} else if err0 != nil {
err = err0
}
}
return err
}
// IngressPackets is called on incoming packets so the Stack can direct packets to
// their respective node/connection and update internal state.
// offset instructs the Stack to start reading packets at an offset.
func (netstack *Netstack) IngressPackets(bufs [][]byte, offset int) (err error) {
for _, buf := range bufs {
err0 := netstack.stack.IngressEthernet(buf[offset:])
if err0 != nil {
err = err0
}
}
return err
}