mirror of
https://github.com/soypat/lneto.git
synced 2026-08-20 06:29:03 +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:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user