fix ethernet gateway addr not being set; add IPID field; hardware addr support of tap; better tap interface

This commit is contained in:
soypat
2025-05-31 12:47:09 -03:00
parent 6c65764070
commit 0008791bd8
5 changed files with 187 additions and 43 deletions
+25 -24
View File
@@ -23,8 +23,6 @@ import (
)
const (
mtu = 2048
iface = "192.168.10.1/24"
stackIP = "192.168.10.2"
stackPort = 80
iss = 100
@@ -34,8 +32,10 @@ var stackHWAddr = [6]byte{0xc0, 0xff, 0xee, 0x00, 0xde, 0xad}
func main() {
ip := netip.MustParseAddr(stackIP)
iface := netip.MustParsePrefix(iface)
if !iface.Contains(ip) {
tap := ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
ippfx := tap.IPPrefix()
if !ippfx.Contains(ip) {
log.Fatal("interface does not contain stack address")
}
addrPort := netip.AddrPortFrom(ip, stackPort)
@@ -44,26 +44,22 @@ func main() {
}))
slogger := logger{lg}
lStack, handler, err := NewEthernetTCPStack(stackHWAddr, addrPort, slogger)
gatewayMAC := tap.HardwareAddr6()
mtu := tap.MTU()
lStack, handler, err := NewEthernetTCPStack(stackHWAddr, gatewayMAC, addrPort, uint16(mtu), slogger)
if err != nil {
log.Fatal(err)
}
// h := &handler.H
// _ = h.AwaitingSynAck()
// _ = h.AwaitingSynResponse()
// _ = h.AwaitingSynSend()
// _ = h
err = handler.OpenListen(addrPort.Port(), iss)
if err != nil {
log.Fatal(err)
}
tap := ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
defer tap.Close()
tap.ReadDiscard() // Discard all unread content.
fmt.Println("hosting server at ", addrPort.String())
var buf [mtu]byte
fmt.Println("hosting server at ", addrPort.String(), "over tap interface of mtu:", mtu, "prefix:", ippfx, "gateway:", net.HardwareAddr(gatewayMAC[:]).String())
buf := make([]byte, mtu)
var hdr httpraw.Header
for {
nread, err := tap.Read(buf[:])
@@ -114,12 +110,13 @@ func main() {
}
}
func NewEthernetTCPStack(mac [6]byte, ip netip.AddrPort, slogger logger) (*LinkStack, *internet.TCPConn, error) {
func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, slogger logger) (*LinkStack, *internet.TCPConn, error) {
var err error
lStack := LinkStack{
logger: slogger,
mac: mac,
mac: ourMAC,
mtu: mtu,
gwmac: gwMAC,
}
var ipStack internet.StackBasic
@@ -161,7 +158,7 @@ func NewEthernetTCPStack(mac [6]byte, ip netip.AddrPort, slogger logger) (*LinkS
proto = ethernet.TypeIPv6
}
arphandler, err := arp.NewHandler(arp.HandlerConfig{
HardwareAddr: mac[:],
HardwareAddr: ourMAC[:],
ProtocolAddr: ip.Addr().AsSlice(),
MaxQueries: 4,
MaxPending: 4,
@@ -196,8 +193,9 @@ type handler struct {
type LinkStack struct {
handlers []handler
logger
mac [6]byte
mtu uint16
mac [6]byte
gwmac [6]byte
mtu uint16
}
func (ls *LinkStack) Register(h handler) error {
@@ -239,23 +237,26 @@ DROP:
}
func (ls *LinkStack) HandleEth(dst []byte) (n int, err error) {
if len(dst) < int(ls.mtu) {
mtu := ls.mtu
if len(dst) < int(mtu) {
return 0, io.ErrShortBuffer
}
efrm, err := ethernet.NewFrame(dst)
if err != nil {
return 0, err
}
copy(efrm.DestinationHardwareAddr()[:], ls.gwmac[:]) // default set the gateway.
for i := range ls.handlers {
h := &ls.handlers[i]
n, err = h.handle(dst[:ls.mtu], 14)
n, err = h.handle(dst[:mtu], 14)
if err != nil {
ls.error("handling", slog.String("proto", ethernet.Type(h.proto).String()), slog.String("err", err.Error()))
continue
}
if n > 0 {
// Found packet
efrm, _ := ethernet.NewFrame(dst[:14])
copy(efrm.DestinationHardwareAddr()[:], h.raddr)
*efrm.SourceHardwareAddr() = ls.mac
efrm.SetEtherType(ethernet.Type(h.proto))
return n + 14, nil
}
}
+6 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"log/slog"
"net"
"net/http"
"net/netip"
"time"
@@ -37,7 +38,11 @@ func run() error {
return err
}
defer sv.Close()
fmt.Println("listening on http://127.0.0.1:7070/recv and http://127.0.0.1:7070/send")
hwaddr, err := sv.HardwareAddress6()
if err != nil {
return err
}
fmt.Println("listening on http://127.0.0.1:7070/recv and http://127.0.0.1:7070/send on hwaddr:", net.HardwareAddr(hwaddr[:]).String())
go http.ListenAndServe(":7070", sv)
misses := 0
for {
+86
View File
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"log/slog"
"net"
"net/http"
"net/netip"
"net/url"
@@ -13,11 +14,14 @@ import (
"github.com/soypat/lneto/internal"
)
const minMTU = 256
// NewHTTPTapClient returns a HTTPTapClient ready for use.
func NewHTTPTapClient(baseURL string) *HTTPTapClient {
var h HTTPTapClient
h.sendurl = baseURL + "/send"
h.recvurl = baseURL + "/recv"
h.infoURL = baseURL + "/info"
_, err := url.Parse(h.sendurl)
if err != nil {
panic(err)
@@ -25,10 +29,59 @@ func NewHTTPTapClient(baseURL string) *HTTPTapClient {
return &h
}
func (h *HTTPTapClient) IPPrefix() netip.Prefix {
h.ensureMTU()
return h.ip
}
func (h *HTTPTapClient) MTU() int {
h.ensureMTU()
return len(h.buf)
}
func (h *HTTPTapClient) HardwareAddr6() [6]byte {
return h.hwaddr
}
func (h *HTTPTapClient) ensureMTU() (err error) {
if len(h.buf) != 0 {
return nil // MTU processed correctly.
}
defer func() {
if err != nil {
err = fmt.Errorf("unable to get MTU from server: %w", err)
}
}()
resp, err := h.c.Get(h.infoURL)
if err != nil {
return err
}
var info TapInfo
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return err
} else if info.MTU <= minMTU {
return errors.New("small MTU")
}
h.ip, err = netip.ParsePrefix(info.IPPrefix)
if err != nil {
return err
}
h.buf = make([]byte, info.MTU)
hw, err := net.ParseMAC(info.HardwareAddr)
if err == nil {
copy(h.hwaddr[:], hw)
}
return nil
}
type HTTPTapClient struct {
c http.Client
infoURL string
recvurl string
sendurl string
ip netip.Prefix
hwaddr [6]byte
buf []byte
}
@@ -41,6 +94,10 @@ func (h *HTTPTapClient) ReadDiscard() {
}
func (h *HTTPTapClient) Read(b []byte) (int, error) {
err := h.ensureMTU()
if err != nil {
return 0, err
}
resp, err := h.c.Get(h.recvurl)
if err != nil {
return 0, err
@@ -59,6 +116,10 @@ func (h *HTTPTapClient) Read(b []byte) (int, error) {
}
func (h *HTTPTapClient) Write(b []byte) (int, error) {
err := h.ensureMTU()
if err != nil {
return 0, err
}
data, _ := json.Marshal(b)
resp, err := h.c.Post(h.sendurl, "application/json", bytes.NewReader(data))
if err != nil {
@@ -79,7 +140,16 @@ type HTTPTapServer struct {
tapfailed bool
}
type TapInfo struct {
MTU int
IPPrefix string
HardwareAddr string
}
func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int) (*HTTPTapServer, error) {
if mtu < minMTU {
return nil, errors.New("too small MTU")
}
tap, err := internal.NewTap(iface, ip)
if err != nil {
return nil, err
@@ -113,6 +183,18 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
json.NewEncoder(w).Encode("") // send empty string.
}
})
ipstr := ip.String()
sv.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
info := TapInfo{
MTU: mtu,
IPPrefix: ipstr,
}
hw, err := tap.HardwareAddress6()
if err == nil {
info.HardwareAddr = net.HardwareAddr(hw[:]).String()
}
json.NewEncoder(w).Encode(info)
})
taps := HTTPTapServer{
router: sv,
stack: s,
@@ -122,6 +204,10 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
return &taps, nil
}
func (sv *HTTPTapServer) HardwareAddress6() (hwaddr [6]byte, err error) {
return sv.tap.HardwareAddress6()
}
func (sv *HTTPTapServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
sv.router.ServeHTTP(w, r)
}
+57 -10
View File
@@ -13,7 +13,7 @@ import (
)
type Tap struct {
fd int
fd int // points to /dev/net/tun device.
name string
}
@@ -25,19 +25,19 @@ func NewTap(name string, ip netip.Prefix) (*Tap, error) {
if err != nil {
return nil, fmt.Errorf("failed to open tun device: %w", err)
}
var ifr [syscall.IFNAMSIZ + 64]byte // extra space for compatibility
// Set the name; it will be zero-padded automatically.
copy(ifr[:syscall.IFNAMSIZ-1], name)
tap := Tap{
name: name,
fd: fd,
}
ifr := tap.ifreq()
// Set the flags (starting at offset IFNAMSIZ).
flags := uint16(syscall.IFF_TAP | syscall.IFF_NO_PI)
*(*uint16)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ])) = flags
ifr.setflags(flags)
// Issue the ioctl to create the interface.
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&ifr[0])))
if errno != 0 {
return nil, fmt.Errorf("creating tap interface: %w", errno)
err = ioctl(fd, syscall.TUNSETIFF, ifr.ptr())
if err != nil {
return nil, fmt.Errorf("creating tap interface: %w", err)
}
if ip.IsValid() {
// Optionally, bring the interface up and assign an IP address.
@@ -66,3 +66,50 @@ func (tap *Tap) Write(b []byte) (int, error) {
func (tap *Tap) Close() error {
return syscall.Close(tap.fd)
}
func ioctl(fd int, request uintptr, argp unsafe.Pointer) error {
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), request, uintptr(argp))
if errno != 0 {
return os.NewSyscallError("ioctl", errno)
}
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)
if err != nil {
return hw, fmt.Errorf("socket open: %w", err)
}
defer syscall.Close(sock)
ifr := tap.ifreq()
err = ioctl(sock, syscall.SIOCGIFHWADDR, ifr.ptr())
if err != nil {
return hw, err
}
sa_family := *(*uint16)(unsafe.Pointer(&ifr.Data[0]))
if sa_family != 1 {
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
}
type ifreq struct {
Name [syscall.IFNAMSIZ]byte
Data [64]byte // union data (covers ifr_hwaddr, etc.)
}
func (ifr *ifreq) setflags(flags uint16) {
*(*uint16)(unsafe.Pointer(&ifr.Data[0])) = flags
}
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
}
+13 -8
View File
@@ -30,6 +30,7 @@ type TCPConn struct {
lastTx time.Time
lastRx time.Time
ipID uint16
abortErr error
logger
}
@@ -102,7 +103,7 @@ func (conn *TCPConn) RecvIP(buf []byte, off int) (err error) {
if off >= len(buf) {
return errors.New("bad offset in TCPConn.Recv")
}
raddr, err := getIPAddr(buf[:off])
raddr, id, err := getIPAddr(buf[:off])
if err != nil {
return err
}
@@ -115,6 +116,7 @@ func (conn *TCPConn) RecvIP(buf []byte, off int) (err error) {
}
if !conn.isRaddrSet() && conn.h.RemotePort() != 0 {
conn.remoteAddr = append(conn.remoteAddr[:0], raddr...)
conn.ipID = ^(id - 1)
}
return nil
}
@@ -200,7 +202,7 @@ func (conn *TCPConn) HandleIP(buf []byte, off int) (n int, err error) {
if len(conn.remoteAddr) == 0 {
return 0, errors.New("unset IP address")
}
raddr, err := getIPAddr(buf[:off])
raddr, _, err := getIPAddr(buf[:off])
if err != nil {
return 0, err
} else if len(raddr) != len(conn.remoteAddr) {
@@ -211,10 +213,11 @@ func (conn *TCPConn) HandleIP(buf []byte, off int) (n int, err error) {
return 0, err
}
err = setDstAddr(buf[:off], conn.remoteAddr)
err = setDstAddr(buf[:off], conn.ipID, conn.remoteAddr)
if err != nil {
return 0, err
}
conn.ipID++
return n, nil
}
@@ -223,27 +226,28 @@ func (conn *TCPConn) Send(response []byte) (n int, err error) {
return conn.h.Send(response)
}
func getIPAddr(buf []byte) (addr []byte, err error) {
func getIPAddr(buf []byte) (addr []byte, id uint16, err error) {
switch buf[0] >> 4 {
case 4:
ifrm4, err := ipv4.NewFrame(buf)
if err != nil {
return addr, err
return addr, 0, err
}
addr = ifrm4.SourceAddr()[:]
id = ifrm4.ID()
case 6:
ifrm6, err := ipv6.NewFrame(buf)
if err != nil {
return addr, err
return addr, 0, err
}
addr = ifrm6.SourceAddr()[:]
default:
err = errors.New("unsupported IP version")
}
return addr, err
return addr, id, err
}
func setDstAddr(buf []byte, addr []byte) (err error) {
func setDstAddr(buf []byte, id uint16, addr []byte) (err error) {
var dstaddr []byte
switch buf[0] >> 4 {
case 4:
@@ -252,6 +256,7 @@ func setDstAddr(buf []byte, addr []byte) (err error) {
return err
}
dstaddr = ifrm4.DestinationAddr()[:]
ifrm4.SetID(id)
case 6:
ifrm6, err := ipv6.NewFrame(buf)
if err != nil {