Files
lneto/examples/bridge/main.go
T
2025-07-01 00:29:11 -03:00

307 lines
6.7 KiB
Go

package main
import (
"crypto/rand"
"encoding/binary"
"errors"
"flag"
"fmt"
"net"
"net/netip"
"os"
"strconv"
"strings"
"time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/arp"
"github.com/soypat/lneto/dhcpv4"
"github.com/soypat/lneto/dns"
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/internal"
"github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/internet"
"github.com/soypat/lneto/internet/pcap"
)
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 = ""
)
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to use. Either tap* or the name of an existing interface to bridge to.")
flag.BoolVar(&flagUseHTTP, "http", 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.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
brHW[5] += byte(softRand)%128 + 1 // We'll be using a similar HW address but with NIC specific identifier modified.
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 Stack
err = stack.Reset(brHW, nicAddr.Addr().Next(), uint16(mtu))
if err != nil {
return err
}
err = stack.BeginDHCPRequest([4]byte{192, 168, 1, 199})
if err != nil {
return err
}
var shark pcap.PacketBreakdown
buf := make([]byte, mtu)
var iframes []pcap.Frame
lastAction := time.Now()
dnsOngoing := false
for {
dhcpIsDone := stack.dhcp.State() == dhcpv4.StateBound
if dhcpIsDone {
if !dnsOngoing {
err = stack.StartLookupIP(flagHostToResolve)
if err != nil {
return err
}
dnsOngoing = true
} else {
addrs, err := stack.ResultLookupIP()
if err == nil {
// END PROGRAM.
fmt.Println(flagHostToResolve, "resolved to", addrs)
return nil
}
}
}
_ = dhcpIsDone
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 := iface.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 := iface.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
dns dns.Client
lookup dns.Message
}
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) StartLookupIP(host string) error {
name, err := dns.NewName(host)
if err != nil {
return err
}
err = s.dns.StartResolve(dns.ResolveConfig{
Questions: []dns.Question{
{
Name: name,
Type: dns.TypeA,
Class: dns.ClassINET,
},
},
EnableRecursion: true,
})
if err != nil {
return err
}
var u internet.StackUDPPort
u.SetStackNode(&s.dns, nil, dns.ServerPort)
err = s.udps.Register(&u)
if err != nil {
return err
}
return err
return nil
}
func (s *Stack) ResultLookupIP() ([]netip.Addr, error) {
done, err := s.dns.MessageCopyTo(&s.lookup)
if err != nil {
return nil, err
} else if !done {
return nil, errors.New("DNS not done")
}
var addrs []netip.Addr
ans := s.lookup.Answers
for i := range ans {
data := ans[i].RawData()
if len(data) == 4 {
addrs = append(addrs, netip.AddrFrom4([4]byte(data)))
} else if len(data) == 16 {
addrs = append(addrs, netip.AddrFrom16([16]byte(data)))
}
}
return addrs, nil
}
func (s *Stack) BeginDHCPRequest(request [4]byte) error {
var buf [4]byte
rand.Read(buf[:])
xid := binary.LittleEndian.Uint32(buf[:])
err := s.dhcp.BeginRequest(xid, dhcpv4.RequestConfig{
RequestedAddr: request,
ClientHardwareAddr: s.link.HardwareAddr6(),
Hostname: "lneto" + strconv.FormatInt(softRand%100, 16),
})
if err != nil {
return err
}
var u internet.StackUDPPort
u.SetStackNode(&s.dhcp, nil, 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
}
}