mirror of
https://github.com/soypat/lneto.git
synced 2026-08-13 19:33:42 +00:00
fix ethernet gateway addr not being set; add IPID field; hardware addr support of tap; better tap interface
This commit is contained in:
+25
-24
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user