mirror of
https://github.com/soypat/lneto.git
synced 2026-08-12 19:03:42 +00:00
huge tap/bridge overhaul; udp node; dhcp node; DHCP example
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ vendor/
|
||||
**__debug_bin*
|
||||
# `__debug_bin` Debug binary generated in VSCode when using the built-in debugger.
|
||||
*bin
|
||||
|
||||
/bridge
|
||||
# IDE
|
||||
.vscode/
|
||||
|
||||
|
||||
@@ -21,6 +21,26 @@ Userspace networking primitives.
|
||||
- [`lneto/ntp`](./ntp): NTP implementation and low level logic. Includes NTP time primitives manipulation and conversion to Go native types.
|
||||
- [`lneto/internal`](./internal): Lightweight and flexible ring buffer implementation and debugging primitives.
|
||||
|
||||
### Abstractions
|
||||
The following interface is implemented by networking stack nodes and the stack themselves.
|
||||
|
||||
```go
|
||||
type StackNode interface {
|
||||
// Encapsulate receives a buffer the receiver must fill with data.
|
||||
// The receiver's start byte is at carrierData[frameOffset].
|
||||
Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
||||
// Demux receives a buffer the receiver must decode and pass on to corresponding child StackNode(s).
|
||||
// The receiver's start byte is at carrierData[frameOffset].
|
||||
Demux(carrierData []byte, frameOffset int) error
|
||||
// LocalPort returns the port of the node if applicable or zero. Used for UDP/TCP nodes.
|
||||
LocalPort() uint16
|
||||
// Protocol returns the protocol of this node if applicable or zero. Usually either a ethernet.Type (EtherType) or lneto.IPProto (IP Protocol number).
|
||||
Protocol() uint64
|
||||
// ConnectionID returns a pointer to the connection ID of the StackNode.
|
||||
// A change in the ID means the node is no longer valid and should be discarded.
|
||||
// A change in the ID could mean the connection was closed by the user or that the node will not send nor receive any more data over said connection ID.
|
||||
ConnectionID() *uint64
|
||||
```
|
||||
|
||||
## Install
|
||||
How to install package with newer versions of Go (+1.16):
|
||||
|
||||
+20
-4
@@ -7,9 +7,13 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"math/bits"
|
||||
"net"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
connID uint64
|
||||
reqHostname string
|
||||
hostname []byte
|
||||
dns [][4]byte
|
||||
@@ -41,8 +45,13 @@ type RequestConfig struct {
|
||||
func (c *Client) BeginRequest(xid uint32, cfg RequestConfig) error {
|
||||
if len(cfg.Hostname) > 36 {
|
||||
return errors.New("requested hostname too long")
|
||||
} else if c.state != StateInit && c.state != 0 {
|
||||
return errors.New("dhcp client must be closed/done before new request")
|
||||
} else if xid == 0 {
|
||||
return errors.New("zero xid")
|
||||
}
|
||||
c.reset(xid)
|
||||
c.state = StateInit
|
||||
c.currentXID = xid
|
||||
c.reqHostname = cfg.Hostname
|
||||
c.reqIP = cfg.RequestedAddr
|
||||
@@ -50,14 +59,19 @@ func (c *Client) BeginRequest(xid uint32, cfg RequestConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Send(dst []byte) (int, error) {
|
||||
func (c *Client) Protocol() uint64 { return uint64(lneto.IPProtoUDP) }
|
||||
func (c *Client) LocalPort() uint16 { return DefaultClientPort }
|
||||
func (c *Client) ConnectionID() *uint64 { return &c.connID }
|
||||
|
||||
func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error) {
|
||||
if c.isClosed() {
|
||||
return 0, io.EOF
|
||||
return 0, net.ErrClosed
|
||||
} else if c.state == StateSelecting && c.offer == [4]byte{} {
|
||||
return 0, nil // No offer received yet.
|
||||
} else if c.state == StateBound {
|
||||
return 0, nil // Done!
|
||||
}
|
||||
dst := carrierFrame[frameOffset:]
|
||||
frm, err := NewFrame(dst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -108,10 +122,11 @@ func (c *Client) Send(dst []byte) (int, error) {
|
||||
return optionsOffset + n, nil
|
||||
}
|
||||
|
||||
func (c *Client) Recv(pkt []byte) error {
|
||||
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
if c.isClosed() {
|
||||
return io.EOF
|
||||
return net.ErrClosed
|
||||
}
|
||||
pkt := carrierData[frameOffset:]
|
||||
frm, err := NewFrame(pkt)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -217,6 +232,7 @@ func (c *Client) setHeader(frm Frame) {
|
||||
|
||||
func (c *Client) reset(xid uint32) {
|
||||
*c = Client{
|
||||
connID: c.connID + 1,
|
||||
reqHostname: c.reqHostname,
|
||||
currentXID: xid,
|
||||
reqIP: c.reqIP,
|
||||
|
||||
@@ -4,6 +4,10 @@ const (
|
||||
sizeHeaderNoVLAN = 14
|
||||
)
|
||||
|
||||
func BroadcastAddr() [6]byte {
|
||||
return [6]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
}
|
||||
|
||||
//go:generate stringer -type=Type -linecomment -output stringers.go .
|
||||
|
||||
type Type uint16
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/arp"
|
||||
"github.com/soypat/lneto/dhcpv4"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/internal/ltesto"
|
||||
"github.com/soypat/lneto/internet"
|
||||
"github.com/soypat/lneto/internet/pcap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("success")
|
||||
}
|
||||
|
||||
func run() (err error) {
|
||||
br := ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
|
||||
defer br.Close()
|
||||
|
||||
nicHW := br.HardwareAddr6()
|
||||
|
||||
brHW := nicHW
|
||||
brHW[5]++ // We'll be using a similar HW address but with NIC specific identifier modified.
|
||||
mtu := br.MTU()
|
||||
nicAddr := br.IPPrefix()
|
||||
|
||||
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
||||
var stack Stack
|
||||
err = stack.Reset(brHW, nicAddr.Addr().Next(), uint16(mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = stack.BeginDHCPRequest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var shark pcap.PacketBreakdown
|
||||
buf := make([]byte, mtu)
|
||||
var iframes []pcap.Frame
|
||||
lastAction := time.Now()
|
||||
for {
|
||||
clear(buf)
|
||||
nwrite, err := stack.Encapsulate(buf[:], 0)
|
||||
if err != nil {
|
||||
fmt.Println("ERR:ENCAPSULATE", err)
|
||||
} else if nwrite > 0 {
|
||||
iframes, err = shark.CaptureEthernet(iframes[:0], buf[:nwrite], 0)
|
||||
if err != nil {
|
||||
fmt.Println("OU", iframes, err.Error())
|
||||
} else {
|
||||
fmt.Println("OU", iframes)
|
||||
}
|
||||
n, err := br.Write(buf[:nwrite])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n != nwrite {
|
||||
return fmt.Errorf("mismatch written bytes %d!=%d", nwrite, n)
|
||||
}
|
||||
}
|
||||
|
||||
clear(buf)
|
||||
nread, err := br.Read(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if nread > 0 {
|
||||
iframes, err = shark.CaptureEthernet(iframes[:0], buf[:nread], 0)
|
||||
if err != nil {
|
||||
fmt.Println("IN", iframes, err.Error())
|
||||
} else {
|
||||
fmt.Println("IN", iframes)
|
||||
}
|
||||
err = stack.Demux(buf[:nread], 0)
|
||||
if err != nil {
|
||||
fmt.Println("ERR:DEMUX", err)
|
||||
}
|
||||
}
|
||||
|
||||
if nread == 0 && nwrite == 0 && time.Since(lastAction) > 4*time.Second {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
} else {
|
||||
lastAction = time.Now()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Stack struct {
|
||||
link internet.StackEthernet
|
||||
ip internet.StackIP
|
||||
arp internet.NodeARP
|
||||
udps internet.StackPorts
|
||||
dhcp dhcpv4.Client
|
||||
}
|
||||
|
||||
func (s *Stack) Demux(b []byte, _ int) error {
|
||||
return s.link.Demux(b, 0)
|
||||
}
|
||||
|
||||
func (s *Stack) Encapsulate(b []byte, _ int) (int, error) {
|
||||
return s.link.Encapsulate(b, 0)
|
||||
}
|
||||
|
||||
func (s *Stack) Reset(mac [6]byte, addr netip.Addr, mtu uint16) error {
|
||||
err := s.link.Reset6(mac, ethernet.BroadcastAddr(), int(mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ip.Reset(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipaddr := addr.AsSlice()
|
||||
proto := ethernet.TypeIPv4
|
||||
if addr.Is6() {
|
||||
proto = ethernet.TypeIPv6
|
||||
}
|
||||
err = s.arp.Reset(arp.HandlerConfig{
|
||||
HardwareAddr: mac[:],
|
||||
ProtocolAddr: ipaddr,
|
||||
MaxQueries: 3,
|
||||
MaxPending: 3,
|
||||
HardwareType: 1,
|
||||
ProtocolType: proto,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.udps.Reset(uint64(lneto.IPProtoUDP), 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now setup stacks.
|
||||
err = s.link.Register(&s.arp) // ARP.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.link.Register(&s.ip) // IPv4 | IPv6
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ip.Register(&s.udps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) BeginDHCPRequest() error {
|
||||
addr4 := s.ip.Addr().As4()
|
||||
var buf [4]byte
|
||||
rand.Read(buf[:])
|
||||
xid := binary.LittleEndian.Uint32(buf[:])
|
||||
err := s.dhcp.BeginRequest(xid, dhcpv4.RequestConfig{
|
||||
RequestedAddr: addr4,
|
||||
ClientHardwareAddr: s.link.HardwareAddr6(),
|
||||
Hostname: "lneto",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var u internet.StackUDPPort
|
||||
u.SetStackNode(&s.dhcp, dhcpv4.DefaultServerPort)
|
||||
err = s.udps.Register(&u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func clear(buf []byte) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
}
|
||||
+41
-12
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
@@ -9,9 +10,11 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/internal/ltesto"
|
||||
"github.com/soypat/lneto/internet/pcap"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
@@ -27,17 +30,35 @@ func main() {
|
||||
}
|
||||
|
||||
func run() error {
|
||||
var (
|
||||
flagInterface = "tap0"
|
||||
)
|
||||
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to select. tap* creates a tap interface. Any other name will create a bridge to the name of the interface i.e: 'enp7s0', 'wlp8s0', 'lo'")
|
||||
var (
|
||||
flagNet = "192.168.10.1/24"
|
||||
flagiface = "tap0"
|
||||
flagMTU = 1500
|
||||
flagPacketQueueSize = 2048
|
||||
)
|
||||
ip, err := netip.ParsePrefix(flagNet)
|
||||
if err != nil {
|
||||
return err
|
||||
var iface ltesto.Interface
|
||||
if strings.HasPrefix(flagInterface, "tap") {
|
||||
pfx, err := netip.ParsePrefix(flagNet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tap, err := internal.NewTap(flagiface, pfx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iface = tap
|
||||
} else {
|
||||
br, err := internal.NewBridge(flagInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iface = br
|
||||
}
|
||||
sv, err := ltesto.NewHTTPTapServer(flagiface, ip, flagMTU, flagPacketQueueSize, flagPacketQueueSize)
|
||||
|
||||
sv, err := ltesto.NewHTTPTapServer(iface, flagPacketQueueSize, flagPacketQueueSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,8 +69,12 @@ func run() error {
|
||||
frames, err := cap.CaptureEthernet(nil, pkt, 0)
|
||||
if err == nil {
|
||||
flags, src, dst := getTCPData(frames, pkt)
|
||||
if flags != 0 {
|
||||
fmt.Println(channel, captime.Format("15:04:05.000"), frames, flags.String(), src, "->", dst)
|
||||
if src != 0 {
|
||||
if flags != 0 {
|
||||
fmt.Println(channel, captime.Format("15:04:05.000"), frames, flags.String(), src, "->", dst)
|
||||
} else {
|
||||
fmt.Println(channel, captime.Format("15:04:05.000"), frames, src, "->", dst)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(channel, captime.Format("15:04:05.000"), frames)
|
||||
}
|
||||
@@ -86,12 +111,16 @@ func run() error {
|
||||
|
||||
func getTCPData(frames []pcap.Frame, pkt []byte) (flags tcp.Flags, src, dst uint16) {
|
||||
for i := range frames {
|
||||
if frames[i].Protocol != lneto.IPProtoTCP {
|
||||
continue
|
||||
proto := frames[i].Protocol
|
||||
if proto == lneto.IPProtoTCP {
|
||||
return tcp.Flags(getFrameClassUint(frames[i], pkt, pcap.FieldClassFlags)),
|
||||
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassSrc)),
|
||||
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassDst))
|
||||
} else if proto == lneto.IPProtoUDP {
|
||||
return 0,
|
||||
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassSrc)),
|
||||
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassDst))
|
||||
}
|
||||
return tcp.Flags(getFrameClassUint(frames[i], pkt, pcap.FieldClassFlags)),
|
||||
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassSrc)),
|
||||
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassDst))
|
||||
}
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
@@ -9,12 +9,19 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
|
||||
const minMTU = 256
|
||||
|
||||
type Interface interface {
|
||||
Read(b []byte) (int, error)
|
||||
Write(b []byte) (int, error)
|
||||
Close() error
|
||||
HardwareAddress6() ([6]byte, error)
|
||||
MTU() (int, error)
|
||||
IPMask() (netip.Prefix, error)
|
||||
}
|
||||
|
||||
// NewHTTPTapClient returns a HTTPTapClient ready for use.
|
||||
func NewHTTPTapClient(baseURL string) *HTTPTapClient {
|
||||
var h HTTPTapClient
|
||||
@@ -150,7 +157,7 @@ func (h *HTTPTapClient) Close() error { return nil }
|
||||
type HTTPTapServer struct {
|
||||
router *http.ServeMux
|
||||
stack stack
|
||||
tap *internal.Tap
|
||||
tap Interface
|
||||
buf []byte
|
||||
onTx func(channel int, pkt []byte)
|
||||
tapfailed bool
|
||||
@@ -166,11 +173,17 @@ func (sv *HTTPTapServer) OnTransfer(cb func(channel int, pkt []byte)) {
|
||||
sv.onTx = cb
|
||||
}
|
||||
|
||||
func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int) (*HTTPTapServer, error) {
|
||||
if mtu < minMTU {
|
||||
func NewHTTPTapServer(iface Interface, queueOut, queueIn int) (*HTTPTapServer, error) {
|
||||
if iface == nil {
|
||||
return nil, errors.New("nil interface argument to HTTP interface server")
|
||||
}
|
||||
mtu, err := iface.MTU()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if mtu < minMTU {
|
||||
return nil, errors.New("too small MTU")
|
||||
}
|
||||
tap, err := internal.NewTap(iface, ip)
|
||||
netmask, err := iface.IPMask()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -183,7 +196,7 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
|
||||
taps := &HTTPTapServer{
|
||||
router: sv,
|
||||
stack: s,
|
||||
tap: tap,
|
||||
tap: iface,
|
||||
buf: make([]byte, mtu),
|
||||
}
|
||||
sv.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -210,13 +223,14 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
|
||||
json.NewEncoder(w).Encode("") // send empty string.
|
||||
}
|
||||
})
|
||||
ipstr := ip.String()
|
||||
|
||||
ipstr := netmask.String()
|
||||
sv.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
info := tapInfo{
|
||||
MTU: mtu,
|
||||
IPPrefix: ipstr,
|
||||
}
|
||||
hw, err := tap.HardwareAddress6()
|
||||
hw, err := iface.HardwareAddress6()
|
||||
if err == nil {
|
||||
info.HardwareAddr = net.HardwareAddr(hw[:]).String()
|
||||
}
|
||||
|
||||
+168
-19
@@ -3,8 +3,11 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -12,6 +15,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const safamily_hw6 = 1
|
||||
|
||||
type Tap struct {
|
||||
fd int // points to /dev/net/tun device.
|
||||
name string
|
||||
@@ -25,12 +30,7 @@ func NewTap(name string, ip netip.Prefix) (*Tap, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open tun device: %w", err)
|
||||
}
|
||||
tap := Tap{
|
||||
name: name,
|
||||
fd: fd,
|
||||
}
|
||||
ifr := tap.ifreq()
|
||||
|
||||
ifr := makeifreq(name)
|
||||
// Set the flags (starting at offset IFNAMSIZ).
|
||||
flags := uint16(syscall.IFF_TAP | syscall.IFF_NO_PI)
|
||||
ifr.setflags(flags)
|
||||
@@ -55,6 +55,14 @@ func NewTap(name string, ip netip.Prefix) (*Tap, error) {
|
||||
return &Tap{fd: fd, name: name}, nil
|
||||
}
|
||||
|
||||
func (tap *Tap) IPMask() (netip.Prefix, error) {
|
||||
sockfd, err := tap.getSock()
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
return getSocketMask(sockfd, tap.name)
|
||||
}
|
||||
|
||||
func (tap *Tap) Read(b []byte) (int, error) {
|
||||
return syscall.Read(tap.fd, b)
|
||||
}
|
||||
@@ -75,27 +83,108 @@ func ioctl(fd int, request uintptr, argp unsafe.Pointer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tap *Tap) HardwareAddress6() (hw [6]byte, err error) {
|
||||
// We cannot use tap.sock to query the hardware address, this is something known by the network stack, so get a sock to network stack.
|
||||
sock, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
|
||||
func (tap *Tap) MTU() (int, error) {
|
||||
sock, err := tap.getSock()
|
||||
if err != nil {
|
||||
return hw, fmt.Errorf("socket open: %w", err)
|
||||
return 0, err
|
||||
}
|
||||
defer syscall.Close(sock)
|
||||
ifr := tap.ifreq()
|
||||
return getSocketMTU(sock, tap.name)
|
||||
}
|
||||
|
||||
err = ioctl(sock, syscall.SIOCGIFHWADDR, ifr.ptr())
|
||||
func (tap *Tap) HardwareAddress6() (hw [6]byte, err error) {
|
||||
// We cannot use tap.sock to query the hardware address, this is something known by the network stack, so get a sock to network stack.
|
||||
sock, err := tap.getSock()
|
||||
if err != nil {
|
||||
return hw, err
|
||||
}
|
||||
sa_family := *(*uint16)(unsafe.Pointer(&ifr.Data[0]))
|
||||
if sa_family != 1 {
|
||||
defer syscall.Close(sock)
|
||||
return getSocketHW(sock, tap.name)
|
||||
}
|
||||
|
||||
func (tap *Tap) getSock() (int, error) {
|
||||
sock, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("tap socket open: %w", err)
|
||||
}
|
||||
return sock, err
|
||||
}
|
||||
|
||||
func getSocketMTU(sockfd int, ifaceName string) (int, error) {
|
||||
ifr := makeifreq(ifaceName)
|
||||
err := ioctl(sockfd, syscall.SIOCGIFMTU, ifr.ptr())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mtu := *(*int32)(unsafe.Pointer(&ifr.Data[0]))
|
||||
return int(mtu), nil
|
||||
}
|
||||
|
||||
func getSocketHW(sockfd int, ifaceName string) (hw [6]byte, err error) {
|
||||
ifr := makeifreq(ifaceName)
|
||||
err = ioctl(sockfd, syscall.SIOCGIFHWADDR, ifr.ptr())
|
||||
if err != nil {
|
||||
return hw, err
|
||||
}
|
||||
sa_family := *(*uint16)(unsafe.Pointer(&ifr.Data[0])) // Host order.
|
||||
if sa_family != safamily_hw6 {
|
||||
return hw, fmt.Errorf("expecting sa_family=1 got %d", sa_family)
|
||||
}
|
||||
copy(hw[:], ifr.Data[2:]) // first two bytes are sa_family
|
||||
return hw, nil
|
||||
}
|
||||
|
||||
func getSocketMask(sockfd int, ifaceName string) (netip.Prefix, error) {
|
||||
addrp, err := getSocketIP(sockfd, ifaceName)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
ifr := makeifreq(ifaceName)
|
||||
err = ioctl(sockfd, syscall.SIOCGIFNETMASK, ifr.ptr())
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
addr32 := binary.BigEndian.Uint32(ifr.Data[4:8])
|
||||
cidr := bits.OnesCount32(addr32)
|
||||
return netip.PrefixFrom(addrp.Addr(), cidr), nil
|
||||
}
|
||||
|
||||
func setSocketHW(sockfd int, ifaceName string, hw [6]byte) error {
|
||||
ifr := makeifreq(ifaceName)
|
||||
*(*uint16)(unsafe.Pointer(&ifr.Data[0])) = safamily_hw6
|
||||
copy(ifr.Data[2:], hw[:])
|
||||
err := ioctl(sockfd, syscall.SIOCSIFHWADDR, ifr.ptr())
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting hw addr: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSocketIP(sockfd int, ifaceName string) (addrp netip.AddrPort, err error) {
|
||||
ifr := makeifreq(ifaceName)
|
||||
err = ioctl(sockfd, syscall.SIOCGIFADDR, ifr.ptr())
|
||||
if err != nil {
|
||||
return netip.AddrPort{}, err
|
||||
}
|
||||
safamily := *(*uint16)(unsafe.Pointer(&ifr.Data[0]))
|
||||
port := *(*uint16)(unsafe.Pointer(&ifr.Data[2]))
|
||||
switch safamily {
|
||||
case 2:
|
||||
addr, _ := netip.AddrFromSlice(ifr.Data[4:8])
|
||||
addrp = netip.AddrPortFrom(addr, port)
|
||||
default:
|
||||
return addrp, fmt.Errorf("unsupported IP addr sa_family=%d", safamily)
|
||||
}
|
||||
return addrp, nil
|
||||
}
|
||||
|
||||
func makeifreq(name string) ifreq {
|
||||
// Set the name; it will be zero-padded automatically.
|
||||
var ifr ifreq
|
||||
copy(ifr.Name[:], name)
|
||||
return ifr
|
||||
}
|
||||
|
||||
type ifreq struct {
|
||||
Name [syscall.IFNAMSIZ]byte
|
||||
Data [64]byte // union data (covers ifr_hwaddr, etc.)
|
||||
@@ -107,9 +196,69 @@ func (ifr *ifreq) setflags(flags uint16) {
|
||||
|
||||
func (ifr *ifreq) ptr() unsafe.Pointer { return unsafe.Pointer(ifr) }
|
||||
|
||||
func (tap *Tap) ifreq() ifreq {
|
||||
// Set the name; it will be zero-padded automatically.
|
||||
var ifr ifreq
|
||||
copy(ifr.Name[:], tap.name)
|
||||
return ifr
|
||||
// Bridge serves as a virtual socket that "bridges" to an existing interface (could be TAP or an actual NIC that connects to internet).
|
||||
// Bridge is used in this project to
|
||||
type Bridge struct {
|
||||
fd int
|
||||
name string
|
||||
index int
|
||||
}
|
||||
|
||||
func NewBridge(name string) (*Bridge, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proto := htons(syscall.ETH_P_IP)
|
||||
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(proto))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ll := syscall.SockaddrLinklayer{
|
||||
Protocol: proto,
|
||||
Ifindex: iface.Index,
|
||||
}
|
||||
if err := syscall.Bind(fd, &ll); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Bridge{fd: fd, name: iface.Name, index: iface.Index}, nil
|
||||
}
|
||||
|
||||
func (br *Bridge) Write(frame []byte) (int, error) {
|
||||
return syscall.Write(br.fd, frame)
|
||||
}
|
||||
|
||||
func (br *Bridge) Read(frame []byte) (int, error) {
|
||||
return syscall.Read(br.fd, frame)
|
||||
}
|
||||
|
||||
func (br *Bridge) Close() error {
|
||||
return syscall.Close(br.fd)
|
||||
}
|
||||
|
||||
func (br *Bridge) HardwareAddress6() (hw [6]byte, err error) {
|
||||
return getSocketHW(br.fd, br.name)
|
||||
}
|
||||
|
||||
func (br *Bridge) SetHardwareAddress6(hw [6]byte) error {
|
||||
return setSocketHW(br.fd, br.name, hw)
|
||||
}
|
||||
|
||||
func (br *Bridge) IPMask() (netip.Prefix, error) {
|
||||
return getSocketMask(br.fd, br.name)
|
||||
}
|
||||
|
||||
func (br *Bridge) Addr() (netip.Addr, error) {
|
||||
addrp, err := getSocketIP(br.fd, br.name)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
return addrp.Addr(), nil
|
||||
}
|
||||
|
||||
func (br *Bridge) MTU() (int, error) {
|
||||
return getSocketMTU(br.fd, br.name)
|
||||
}
|
||||
|
||||
// htons converts a uint16 from host to network byte order.
|
||||
func htons(i uint16) uint16 { return (i<<8)&0xff00 | i>>8 }
|
||||
|
||||
+43
-13
@@ -24,7 +24,6 @@ type StackNode interface {
|
||||
Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
||||
// Demux reads from the argument buffer where frameOffset is the offset of this StackNode's frame first byte.
|
||||
// The stack node then dispatches(demuxes) the encapsulated frames to its corresponding sub-stack-node(s).
|
||||
//
|
||||
Demux(carrierData []byte, frameOffset int) error
|
||||
LocalPort() uint16
|
||||
Protocol() uint64
|
||||
@@ -39,7 +38,6 @@ type node struct {
|
||||
connID *uint64
|
||||
demux func([]byte, int) error
|
||||
encapsulate func([]byte, int) (int, error)
|
||||
lastErrs [2]error
|
||||
proto uint16
|
||||
port uint16
|
||||
}
|
||||
@@ -57,34 +55,66 @@ func handleNodeError(nodesPtr *[]node, nodeIdx int, err error) (discarded bool)
|
||||
panic("unreachable")
|
||||
}
|
||||
nodes := *nodesPtr
|
||||
badConnID := nodes[nodeIdx].connID != nil && *nodes[nodeIdx].connID != nodes[nodeIdx].currConnID
|
||||
if err == net.ErrClosed || badConnID {
|
||||
if checkNodeErr(&nodes[nodeIdx], err) {
|
||||
*nodesPtr = slices.Delete(nodes, nodeIdx, nodeIdx+1)
|
||||
discarded = true
|
||||
} else {
|
||||
// Advance Queue of errors
|
||||
nodes[nodeIdx].lastErrs[1] = nodes[nodeIdx].lastErrs[0]
|
||||
nodes[nodeIdx].lastErrs[0] = err
|
||||
}
|
||||
}
|
||||
return discarded
|
||||
}
|
||||
|
||||
func checkNode(node *node) (discard bool) {
|
||||
return node.demux == nil || node.connID != nil && node.currConnID != *node.connID
|
||||
}
|
||||
|
||||
func checkNodeErr(node *node, err error) (discard bool) {
|
||||
return checkNode(node) || (err != nil && err == net.ErrClosed)
|
||||
}
|
||||
|
||||
func addNode(nodes *[]node, h StackNode, port uint16, protocol uint64) {
|
||||
*nodes = append(*nodes, nodeFromStackNode(h, port, protocol))
|
||||
}
|
||||
|
||||
func nodeFromStackNode(s StackNode, port uint16, protocol uint64) node {
|
||||
if protocol > math.MaxUint16 {
|
||||
panic(">16bit protocol number unsupported")
|
||||
}
|
||||
var currConnID uint64
|
||||
connIDPtr := h.ConnectionID()
|
||||
connIDPtr := s.ConnectionID()
|
||||
if connIDPtr != nil {
|
||||
currConnID = *connIDPtr
|
||||
}
|
||||
*nodes = append(*nodes, node{
|
||||
return node{
|
||||
currConnID: currConnID,
|
||||
connID: connIDPtr,
|
||||
demux: h.Demux,
|
||||
encapsulate: h.Encapsulate,
|
||||
demux: s.Demux,
|
||||
encapsulate: s.Encapsulate,
|
||||
proto: uint16(protocol),
|
||||
port: port,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getNode(nodes []node, port uint16, protocol uint16) (node *node) {
|
||||
for i := range nodes {
|
||||
node := &nodes[i]
|
||||
if node.port == port && node.proto == protocol {
|
||||
return node
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// destroy removes all references to underlying StackNode. Allows garbage collection of node if possible.
|
||||
func (n *node) destroy() {
|
||||
*n = node{}
|
||||
}
|
||||
|
||||
func getNodeByProto(nodes []node, protocol uint16) int {
|
||||
for i := range nodes {
|
||||
node := &nodes[i]
|
||||
if node.proto == protocol {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package internet
|
||||
|
||||
import (
|
||||
"github.com/soypat/lneto/ntp"
|
||||
)
|
||||
|
||||
var _ StackNode = (*NodeNTPClient)(nil)
|
||||
|
||||
type NodeNTPClient struct {
|
||||
c ntp.Client
|
||||
}
|
||||
|
||||
func (n *NodeNTPClient) Protocol() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (n *NodeNTPClient) LocalPort() uint16 {
|
||||
return ntp.ClientPort
|
||||
}
|
||||
|
||||
func (n *NodeNTPClient) ConnectionID() *uint64 {
|
||||
return n.c.ConnectionID()
|
||||
}
|
||||
|
||||
func (n *NodeNTPClient) Demux(carrierData []byte, ntpOffset int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NodeNTPClient) Encapsulate(carrierData []byte, ntpOffset int) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -208,6 +208,11 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
||||
ifrm4.CRCWriteTCPPseudo(&crc)
|
||||
tfrm, err := tcp.NewFrame(ifrm4.Payload())
|
||||
if err == nil {
|
||||
tfrm.ValidateSize(pc.validator())
|
||||
if pc.vld.HasError() {
|
||||
println("BAD TCP")
|
||||
return dst, pc.vld.ErrPop()
|
||||
}
|
||||
tfrm.CRCWrite(&crc)
|
||||
wantSum := crc.Sum16()
|
||||
gotSum := tfrm.CRC()
|
||||
@@ -215,10 +220,15 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
||||
protoErrs = append(protoErrs, &crcError16{protocol: "ipv4+tcp", want: wantSum, got: gotSum})
|
||||
}
|
||||
}
|
||||
} else if proto == lneto.IPProtoUDP || proto == lneto.IPProtoUDPLite {
|
||||
} else if proto == lneto.IPProtoUDP {
|
||||
ifrm4.CRCWriteUDPPseudo(&crc)
|
||||
ufrm, err := udp.NewFrame(ifrm4.Payload())
|
||||
if err == nil {
|
||||
ufrm.ValidateSize(pc.validator())
|
||||
if pc.vld.HasError() {
|
||||
println("BAD UDP")
|
||||
return dst, pc.vld.ErrPop()
|
||||
}
|
||||
ufrm.CRCWriteIPv4(&crc)
|
||||
wantSum := crc.Sum16()
|
||||
gotSum := ufrm.CRC()
|
||||
@@ -289,6 +299,9 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
// }
|
||||
|
||||
func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("UDP must be parsed at byte boundary")
|
||||
|
||||
@@ -20,6 +20,18 @@ type StackEthernet struct {
|
||||
mtu uint16
|
||||
}
|
||||
|
||||
func (ls *StackEthernet) SetGateway6(gw [6]byte) {
|
||||
ls.gwmac = gw
|
||||
}
|
||||
|
||||
func (ls *StackEthernet) SetHardwareAddr6(mac [6]byte) {
|
||||
ls.mac = mac
|
||||
}
|
||||
|
||||
func (ls *StackEthernet) HardwareAddr6() [6]byte {
|
||||
return ls.mac
|
||||
}
|
||||
|
||||
func (ls *StackEthernet) Reset6(mac, gateway [6]byte, mtu int) error {
|
||||
if mtu > math.MaxUint16 || mtu < 256 {
|
||||
return errors.New("invalid MTU")
|
||||
|
||||
+77
-52
@@ -4,15 +4,14 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/ipv4"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
"github.com/soypat/lneto/udp"
|
||||
)
|
||||
|
||||
var _ StackNode = (*StackIP)(nil)
|
||||
@@ -73,40 +72,60 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
|
||||
}
|
||||
dst := ifrm.DestinationAddr()
|
||||
if *dst != sb.ip {
|
||||
goto DROP
|
||||
}
|
||||
{
|
||||
sb.validator.ResetErr()
|
||||
ifrm.ValidateExceptCRC(&sb.validator)
|
||||
if err = sb.validator.ErrPop(); err != nil {
|
||||
return err
|
||||
}
|
||||
gotCRC := ifrm.CRC()
|
||||
wantCRC := ifrm.CalculateHeaderCRC()
|
||||
if gotCRC != wantCRC {
|
||||
sb.error("StackIP:Demux:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC)))
|
||||
return errors.New("IPv4 CRC mismatch")
|
||||
}
|
||||
off := ifrm.HeaderLength()
|
||||
totalLen := ifrm.TotalLength()
|
||||
for i := range sb.handlers {
|
||||
h := &sb.handlers[i]
|
||||
proto := ifrm.Protocol()
|
||||
if h.proto == uint16(proto) {
|
||||
sb.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
|
||||
err = h.demux(frame[:totalLen], off)
|
||||
if err == net.ErrClosed {
|
||||
sb.info("ipclose", slog.String("proto", proto.String()))
|
||||
sb.handlers = slices.Delete(sb.handlers, i, i+1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil // Not meant for us.
|
||||
}
|
||||
|
||||
DROP:
|
||||
sb.info("iprecv:drop", slog.String("dstaddr", netip.AddrFrom4(*ifrm.DestinationAddr()).String()), slog.String("proto", ifrm.Protocol().String()))
|
||||
return nil
|
||||
sb.validator.ResetErr()
|
||||
ifrm.ValidateExceptCRC(&sb.validator)
|
||||
if err = sb.validator.ErrPop(); err != nil {
|
||||
return err
|
||||
}
|
||||
gotCRC := ifrm.CRC()
|
||||
wantCRC := ifrm.CalculateHeaderCRC()
|
||||
if gotCRC != wantCRC {
|
||||
sb.error("StackIP:Demux:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC)))
|
||||
return errors.New("IPv4 CRC mismatch")
|
||||
}
|
||||
off := ifrm.HeaderLength()
|
||||
totalLen := ifrm.TotalLength()
|
||||
proto := ifrm.Protocol()
|
||||
nodeIdx := getNodeByProto(sb.handlers, uint16(proto))
|
||||
if nodeIdx < 0 {
|
||||
// Drop packet.
|
||||
sb.info("iprecv:drop", slog.String("dstaddr", netip.AddrFrom4(*ifrm.DestinationAddr()).String()), slog.String("proto", ifrm.Protocol().String()))
|
||||
return nil
|
||||
}
|
||||
// Incoming CRC Validation of common IP Protocols.
|
||||
var crc lneto.CRC791
|
||||
switch proto {
|
||||
case lneto.IPProtoTCP:
|
||||
ifrm.CRCWriteTCPPseudo(&crc)
|
||||
tfrm, err := tcp.NewFrame(ifrm.Payload())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tfrm.CRCWrite(&crc)
|
||||
if crc.Sum16() != tfrm.CRC() {
|
||||
return errors.New("TCP CRC mismatch")
|
||||
}
|
||||
case lneto.IPProtoUDP:
|
||||
ifrm.CRCWriteUDPPseudo(&crc)
|
||||
ufrm, err := udp.NewFrame(ifrm.Payload())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ufrm.CRCWriteIPv4(&crc)
|
||||
if crc.Sum16() != ufrm.CRC() {
|
||||
return errors.New("UDP CRC mismatch")
|
||||
}
|
||||
}
|
||||
sb.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
|
||||
err = sb.handlers[nodeIdx].demux(frame[:totalLen], off)
|
||||
if handleNodeError(&sb.handlers, nodeIdx, err) {
|
||||
sb.info("ipclose", slog.String("proto", proto.String()))
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (sb *StackIP) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
||||
@@ -117,7 +136,7 @@ func (sb *StackIP) Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
||||
ifrm, _ := ipv4.NewFrame(frame)
|
||||
const ihl = 5
|
||||
const headerlen = ihl * 4
|
||||
ifrm.SetVersionAndIHL(4, 5)
|
||||
ifrm.SetVersionAndIHL(4, ihl)
|
||||
ifrm.SetToS(0)
|
||||
ifrm.SetID(0)
|
||||
*ifrm.SourceAddr() = sb.ip
|
||||
@@ -128,25 +147,31 @@ func (sb *StackIP) Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
||||
if err != nil {
|
||||
sb.error("StackIP:handle", slog.String("proto", proto.String()), slog.String("err", err.Error()))
|
||||
continue
|
||||
} else if n == 0 {
|
||||
continue
|
||||
}
|
||||
if n > 0 {
|
||||
const dontFrag = 0x4000
|
||||
totalLen := n + headerlen
|
||||
ifrm.SetTotalLength(uint16(totalLen))
|
||||
ifrm.SetFlags(dontFrag)
|
||||
ifrm.SetTTL(64)
|
||||
ifrm.SetProtocol(proto)
|
||||
ifrm.SetCRC(ifrm.CalculateHeaderCRC())
|
||||
if ifrm.Protocol() == lneto.IPProtoTCP {
|
||||
var crc lneto.CRC791
|
||||
ifrm.CRCWriteTCPPseudo(&crc)
|
||||
tfrm, _ := tcp.NewFrame(ifrm.Payload())
|
||||
tfrm.CRCWrite(&crc)
|
||||
tfrm.SetCRC(crc.Sum16())
|
||||
sb.info("StackIP:send", slog.String("ip", ifrm.String()), slog.String("tcp", tfrm.String()))
|
||||
}
|
||||
return totalLen, nil
|
||||
const dontFrag = 0x4000
|
||||
totalLen := n + headerlen
|
||||
ifrm.SetTotalLength(uint16(totalLen))
|
||||
ifrm.SetFlags(dontFrag)
|
||||
ifrm.SetTTL(64)
|
||||
ifrm.SetProtocol(proto)
|
||||
ifrm.SetCRC(ifrm.CalculateHeaderCRC())
|
||||
// Calculate CRC for our newly generated packet.
|
||||
var crc lneto.CRC791
|
||||
switch proto {
|
||||
case lneto.IPProtoTCP:
|
||||
ifrm.CRCWriteTCPPseudo(&crc)
|
||||
tfrm, _ := tcp.NewFrame(ifrm.Payload())
|
||||
tfrm.CRCWrite(&crc)
|
||||
tfrm.SetCRC(crc.Sum16())
|
||||
case lneto.IPProtoUDP:
|
||||
ifrm.CRCWriteUDPPseudo(&crc)
|
||||
ufrm, _ := udp.NewFrame(ifrm.Payload())
|
||||
ufrm.CRCWriteIPv4(&crc)
|
||||
ufrm.SetCRC(crc.Sum16())
|
||||
}
|
||||
return totalLen, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
+12
-6
@@ -3,26 +3,32 @@ package internet
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
type StackPorts struct {
|
||||
connID uint64
|
||||
protocol uint64
|
||||
handlers []node
|
||||
dstPortOff int
|
||||
protocol uint16
|
||||
}
|
||||
|
||||
func (ps *StackPorts) Reset(protocol uint64, dstPortOffset int) {
|
||||
func (ps *StackPorts) Reset(protocol uint64, dstPortOffset int) error {
|
||||
if protocol > math.MaxUint16 {
|
||||
return errInvalidProto
|
||||
}
|
||||
*ps = StackPorts{
|
||||
connID: ps.connID + 1,
|
||||
handlers: ps.handlers[:0],
|
||||
dstPortOff: dstPortOffset,
|
||||
protocol: protocol,
|
||||
protocol: uint16(protocol),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *StackPorts) LocalPort() uint16 { return 0 }
|
||||
|
||||
func (ps *StackPorts) Protocol() uint64 { return ps.protocol }
|
||||
func (ps *StackPorts) Protocol() uint64 { return uint64(ps.protocol) }
|
||||
|
||||
func (ps *StackPorts) ConnectionID() *uint64 { return &ps.connID }
|
||||
|
||||
@@ -65,13 +71,13 @@ func (ps *StackPorts) Register(h StackNode) error {
|
||||
proto := h.Protocol()
|
||||
if port <= 0 {
|
||||
return errZeroPort
|
||||
} else if proto != ps.protocol {
|
||||
} else if proto != uint64(ps.protocol) {
|
||||
return errInvalidProto
|
||||
}
|
||||
ps.handlers = append(ps.handlers, node{
|
||||
demux: h.Demux,
|
||||
encapsulate: h.Encapsulate,
|
||||
port: uint16(port),
|
||||
port: port,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package internet
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/udp"
|
||||
)
|
||||
|
||||
type StackUDPPort struct {
|
||||
h node
|
||||
vld lneto.Validator
|
||||
rmport uint16
|
||||
}
|
||||
|
||||
func (sudp *StackUDPPort) SetStackNode(node StackNode, rmport uint16) {
|
||||
sudp.h = nodeFromStackNode(node, node.LocalPort(), node.Protocol())
|
||||
sudp.rmport = rmport
|
||||
}
|
||||
|
||||
func (sudp *StackUDPPort) Protocol() uint64 { return uint64(lneto.IPProtoUDP) }
|
||||
|
||||
func (sudp *StackUDPPort) LocalPort() uint16 { return sudp.h.port }
|
||||
|
||||
func (sudp *StackUDPPort) ConnectionID() *uint64 { return sudp.h.connID }
|
||||
|
||||
func (sudp *StackUDPPort) Demux(carrierData []byte, frameOffset int) error {
|
||||
if checkNode(&sudp.h) {
|
||||
sudp.h.destroy()
|
||||
return net.ErrClosed
|
||||
}
|
||||
ufrm, err := udp.NewFrame(carrierData[frameOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ufrm.ValidateSize(&sudp.vld)
|
||||
if sudp.vld.HasError() {
|
||||
return sudp.vld.ErrPop()
|
||||
}
|
||||
dst := ufrm.DestinationPort()
|
||||
if dst != sudp.h.port {
|
||||
return nil // Not meant for us.
|
||||
}
|
||||
|
||||
src := ufrm.SourcePort()
|
||||
if sudp.rmport != 0 && src != sudp.rmport {
|
||||
return nil // Not from our target remote port.
|
||||
}
|
||||
err = sudp.h.demux(ufrm.Payload(), 8)
|
||||
if err != nil {
|
||||
if checkNodeErr(&sudp.h, err) {
|
||||
sudp.h.destroy()
|
||||
}
|
||||
slog.Error("stackudp:demux", slog.String("err", err.Error()))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (sudp *StackUDPPort) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
||||
if checkNode(&sudp.h) {
|
||||
sudp.h.destroy()
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
ufrm, err := udp.NewFrame(carrierData[frameOffset:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ufrm.SetSourcePort(sudp.h.port)
|
||||
ufrm.SetDestinationPort(sudp.rmport)
|
||||
n, err := sudp.h.encapsulate(carrierData[frameOffset:], 8)
|
||||
if err != nil {
|
||||
slog.Error("stackudp:demux", slog.String("err", err.Error()))
|
||||
}
|
||||
ufrm.SetLength(8 + uint16(n))
|
||||
// UDP CRC left to IP layer.
|
||||
return n, err
|
||||
}
|
||||
+4
-9
@@ -19,13 +19,6 @@ const (
|
||||
|
||||
const sysprecRecalcNeeded int8 = 127
|
||||
|
||||
func NewClient(now func() time.Time) *Client {
|
||||
return &Client{
|
||||
_now: now,
|
||||
_sysprec: sysprecRecalcNeeded,
|
||||
}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
connID uint64
|
||||
start time.Time
|
||||
@@ -54,10 +47,11 @@ func (c *Client) ConnectionID() *uint64 {
|
||||
return &c.connID
|
||||
}
|
||||
|
||||
func (c *Client) Send(payload []byte) (int, error) {
|
||||
func (c *Client) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
||||
if c.isDone() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
payload := carrierData[frameOffset:]
|
||||
frm, err := NewFrame(payload)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -88,10 +82,11 @@ func (c *Client) Send(payload []byte) (int, error) {
|
||||
return SizeHeader, nil
|
||||
}
|
||||
|
||||
func (c *Client) Read(payload []byte) error {
|
||||
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
if c.isDone() {
|
||||
return io.EOF
|
||||
}
|
||||
payload := carrierData[frameOffset:]
|
||||
frm, err := NewFrame(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user