mirror of
https://github.com/soypat/lneto.git
synced 2026-08-09 09:23:40 +00:00
239 lines
5.9 KiB
Go
239 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/netip"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/soypat/lneto/dns"
|
|
"github.com/soypat/lneto/http/httpraw"
|
|
"github.com/soypat/lneto/internal"
|
|
"github.com/soypat/lneto/internal/ltesto"
|
|
"github.com/soypat/lneto/internet/pcap"
|
|
"github.com/soypat/lneto/x/xnet"
|
|
)
|
|
|
|
var softRand = time.Now().Unix()
|
|
|
|
func main() {
|
|
err := run()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("success")
|
|
}
|
|
|
|
func run() (err error) {
|
|
var (
|
|
flagInterface = "tap0"
|
|
flagUseHTTP = false
|
|
flagHostToResolve = ""
|
|
flagRequestedIP = ""
|
|
flagDoNTP = false
|
|
flagHTTPGet = false
|
|
)
|
|
flag.BoolVar(&flagHTTPGet, "httpget", flagHTTPGet, "Do an HTTP GET request ")
|
|
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to use. Either tap* or the name of an existing interface to bridge to.")
|
|
flag.BoolVar(&flagUseHTTP, "ihttp", flagUseHTTP, "Use HTTP tap interface.")
|
|
flag.StringVar(&flagHostToResolve, "host", flagHostToResolve, "Hostname to resolve via DNS.")
|
|
flag.StringVar(&flagRequestedIP, "addr", flagRequestedIP, "IP address to request via DHCP.")
|
|
flag.BoolVar(&flagDoNTP, "ntp", flagDoNTP, "Do NTP round and print result time")
|
|
flag.Parse()
|
|
fmt.Println("softrand", softRand)
|
|
_, err = dns.NewName(flagHostToResolve)
|
|
if err != nil {
|
|
flag.Usage()
|
|
return err
|
|
}
|
|
var iface ltesto.Interface
|
|
if flagUseHTTP {
|
|
iface = ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
|
|
} else {
|
|
if strings.HasPrefix(flagInterface, "tap") {
|
|
tap, err := internal.NewTap(flagInterface, netip.MustParsePrefix("192.168.1.1/24"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
iface = tap
|
|
} else {
|
|
bridge, err := internal.NewBridge(flagInterface)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
iface = bridge
|
|
}
|
|
}
|
|
defer iface.Close()
|
|
|
|
nicHW, err := iface.HardwareAddress6()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
brHW := nicHW
|
|
mtu, err := iface.MTU()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nicAddr, err := iface.IPMask()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
|
var stack xnet.StackAsync
|
|
err = stack.Reset(xnet.StackConfig{
|
|
Hostname: "xnet-test",
|
|
RandSeed: softRand,
|
|
HardwareAddress: brHW,
|
|
MTU: uint16(mtu),
|
|
MaxTCPConns: 1,
|
|
TCPBufferSizeTx: 2048,
|
|
TCPBufferSizeRx: 2048,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Loop goroutine.
|
|
go func() {
|
|
lastAction := time.Now()
|
|
buf := make([]byte, mtu)
|
|
var cap pcap.PacketBreakdown
|
|
for {
|
|
clear(buf)
|
|
nwrite, err := stack.Encapsulate(buf[:], 0)
|
|
if err != nil {
|
|
fmt.Println("ERR:ENCAPSULATE", err)
|
|
} else if nwrite > 0 {
|
|
frames, err := cap.CaptureEthernet(nil, buf[:nwrite], 0)
|
|
if len(frames) > 0 {
|
|
fmt.Println("OUT", frames)
|
|
}
|
|
n, err := iface.Write(buf[:nwrite])
|
|
if err != nil {
|
|
log.Fatal("groutine encapsulate:", err)
|
|
} else if n != nwrite {
|
|
log.Fatalf("mismatch written bytes %d!=%d", nwrite, n)
|
|
}
|
|
}
|
|
|
|
clear(buf)
|
|
nread, err := iface.Read(buf)
|
|
if err != nil {
|
|
log.Fatal("groutine read:", err)
|
|
} else if nread > 0 {
|
|
err = stack.Demux(buf[:nread], 0)
|
|
if err != nil {
|
|
log.Println("groutine demux:", err)
|
|
}
|
|
}
|
|
|
|
if nread == 0 && nwrite == 0 && time.Since(lastAction) > 4*time.Second {
|
|
time.Sleep(5 * time.Millisecond)
|
|
} else {
|
|
lastAction = time.Now()
|
|
runtime.Gosched()
|
|
}
|
|
}
|
|
}()
|
|
|
|
rstack := stack.StackRetrying()
|
|
|
|
const (
|
|
dhcpTimeout = 6 * time.Second
|
|
dhcpRetries = 2
|
|
)
|
|
results, err := rstack.DoDHCPv4([4]byte{192, 168, 1, 96}, dhcpTimeout, dhcpRetries)
|
|
if err != nil {
|
|
return fmt.Errorf("DHCP failed: %w", err)
|
|
}
|
|
err = stack.AssimilateDHCPResults(results)
|
|
if err != nil {
|
|
return fmt.Errorf("assimilating DHCP results: %w", err)
|
|
}
|
|
const (
|
|
arpTimeout = 2 * time.Second
|
|
arpRetries = 2
|
|
)
|
|
const (
|
|
internetTimeout = 3 * time.Second
|
|
internetRetries = 2
|
|
)
|
|
routerHw, err := rstack.DoResolveHardwareAddress6(results.Router, arpTimeout, arpRetries)
|
|
if err != nil {
|
|
return fmt.Errorf("ARP resolution of router failed: %w", err)
|
|
}
|
|
stack.SetGateway6(routerHw)
|
|
if flagDoNTP {
|
|
const ntpHost = "pool.ntp.org"
|
|
addrs, err := rstack.DoLookupIP(ntpHost, internetTimeout, internetRetries)
|
|
if err != nil {
|
|
return fmt.Errorf("NTP address lookup of %q failed: %w", ntpHost, err)
|
|
}
|
|
offset, err := rstack.DoNTP(addrs[0], internetTimeout, internetRetries)
|
|
if err != nil {
|
|
return fmt.Errorf("NTP address lookup of %q failed: %w", ntpHost, err)
|
|
}
|
|
relative := "behind"
|
|
if offset < 0 {
|
|
relative = "ahead"
|
|
}
|
|
fmt.Println("NTP completed. You are", offset.Abs().String(), relative, "of the NTP server")
|
|
}
|
|
addrs, err := rstack.DoLookupIP(flagHostToResolve, internetTimeout, internetRetries)
|
|
if err != nil {
|
|
return fmt.Errorf("DNS of host %q failed: %w", flagHostToResolve, err)
|
|
}
|
|
fmt.Printf("DNS resolution of %q complete and resolved to %v\n", flagHostToResolve, addrs)
|
|
if flagHTTPGet {
|
|
var hdr httpraw.Header
|
|
hdr.SetMethod("GET")
|
|
hdr.SetRequestURI("/")
|
|
hdr.SetProtocol("HTTP/1.1")
|
|
hdr.Set("Host", flagHostToResolve)
|
|
hdr.Set("User-Agent", "lneto")
|
|
hdr.Set("Accept-Language", "en-US,en;q=0.5")
|
|
req, err := hdr.AppendRequest(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
target := netip.AddrPortFrom(addrs[0], 80)
|
|
conn, err := rstack.DoDialTCP(uint16(softRand&0xefff)+1024, target, internetTimeout, internetRetries)
|
|
if err != nil {
|
|
return fmt.Errorf("TCP failed: %w", err)
|
|
}
|
|
conn.SetDeadline(time.Now().Add(internetTimeout))
|
|
_, err = conn.Write(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rxbuf := make([]byte, 2048)
|
|
var page []byte
|
|
for {
|
|
var n int
|
|
n, err = conn.Read(rxbuf)
|
|
page = append(page, rxbuf[:n]...)
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
if len(page) == 0 {
|
|
return err
|
|
}
|
|
os.Stdout.Write(page)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func clear(buf []byte) {
|
|
for i := range buf {
|
|
buf[i] = 0
|
|
}
|
|
}
|