| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- package aerotun
- import (
- "encoding/binary"
- "fmt"
- "net"
- "os"
- "os/exec"
- "syscall"
- "unsafe"
- )
- type VirtualDevice interface {
- Read(buf []byte) (int, error)
- Write(buf []byte) (int, error)
- Close() error
- Name() string
- }
- // CreateDevice creates either a native Linux TUN device, or falls back to a high-speed
- // veth pair with AF_PACKET raw socket if running in an unprivileged container without /dev/net/tun.
- func CreateDevice(name string, mtu int, localCIDR string) (VirtualDevice, error) {
- if mtu <= 0 {
- mtu = DefaultMTU
- }
- if _, err := os.Stat("/dev/net/tun"); err == nil {
- dev, err := createNativeTun(name, mtu, localCIDR)
- if err == nil {
- return dev, nil
- }
- }
- // Universal container / Proxmox LXC fallback:
- return createVethTun(name, mtu, localCIDR)
- }
- // ---------------------------------------------------------------------------
- // 1. Native Linux TUN Implementation (/dev/net/tun)
- // ---------------------------------------------------------------------------
- type nativeTun struct {
- file *os.File
- name string
- }
- func createNativeTun(name string, mtu int, localCIDR string) (*nativeTun, error) {
- fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR, 0)
- if err != nil {
- return nil, err
- }
- type ifreq struct {
- name [16]byte
- flags uint16
- _pad [22]byte
- }
- var ifr ifreq
- copy(ifr.name[:], name)
- // IFF_TUN = 0x0001, IFF_NO_PI = 0x1000
- ifr.flags = 0x0001 | 0x1000
- _, _, errno := syscall.Syscall(
- syscall.SYS_IOCTL,
- uintptr(fd),
- uintptr(0x400454ca), // TUNSETIFF
- uintptr(unsafe.Pointer(&ifr)),
- )
- if errno != 0 {
- _ = syscall.Close(fd)
- return nil, errno
- }
- // Configure MTU and IP
- _ = exec.Command("ip", "link", "set", name, "mtu", fmt.Sprintf("%d", mtu)).Run()
- _ = exec.Command("ip", "link", "set", name, "up").Run()
- if localCIDR != "" {
- _ = exec.Command("ip", "addr", "add", localCIDR, "dev", name).Run()
- }
- return &nativeTun{
- file: os.NewFile(uintptr(fd), name),
- name: name,
- }, nil
- }
- func (t *nativeTun) Read(buf []byte) (int, error) {
- return t.file.Read(buf)
- }
- func (t *nativeTun) Write(buf []byte) (int, error) {
- return t.file.Write(buf)
- }
- func (t *nativeTun) Close() error {
- err := t.file.Close()
- _ = exec.Command("ip", "link", "delete", t.name).Run()
- return err
- }
- func (t *nativeTun) Name() string {
- return t.name
- }
- // ---------------------------------------------------------------------------
- // 2. High-Performance Container Fallback (VETH + AF_PACKET)
- // ---------------------------------------------------------------------------
- type vethTun struct {
- name string
- epName string
- sockFd int
- ifIndex int
- hwAddr [6]byte
- epHwAddr [6]byte
- packetBuf [2048]byte
- }
- func createVethTun(name string, mtu int, localCIDR string) (*vethTun, error) {
- epName := fmt.Sprintf("%s_ep", name)
- if len(epName) > 15 {
- epName = epName[:15]
- }
- // Clean up any stale interfaces
- _ = exec.Command("ip", "link", "delete", name).Run()
- _ = exec.Command("ip", "link", "delete", epName).Run()
- // 1. Create veth pair
- out, err := exec.Command("ip", "link", "add", name, "type", "veth", "peer", "name", epName).CombinedOutput()
- if err != nil {
- return nil, fmt.Errorf("failed to create veth pair %s: %s: %w", name, string(out), err)
- }
- // 2. Set Point-to-Point / ARP off behavior
- _ = exec.Command("ip", "link", "set", name, "arp", "off").Run()
- _ = exec.Command("ip", "link", "set", epName, "arp", "off").Run()
- _ = exec.Command("ip", "link", "set", name, "mtu", fmt.Sprintf("%d", mtu)).Run()
- _ = exec.Command("ip", "link", "set", epName, "mtu", fmt.Sprintf("%d", mtu)).Run()
- _ = exec.Command("ip", "link", "set", name, "up").Run()
- _ = exec.Command("ip", "link", "set", epName, "up").Run()
- // 3. Assign IP to main device
- if localCIDR != "" {
- _ = exec.Command("ip", "addr", "add", localCIDR, "dev", name).Run()
- }
- // 4. Get endpoint interface index
- epIface, err := net.InterfaceByName(epName)
- if err != nil {
- _ = exec.Command("ip", "link", "delete", name).Run()
- return nil, fmt.Errorf("failed to lookup %s: %w", epName, err)
- }
- // 5. Open AF_PACKET raw socket bound to endpoint
- // htons(ETH_P_ALL) = 0x0300
- const ethPAll = 0x0300
- sockFd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_ALL)))
- if err != nil {
- _ = exec.Command("ip", "link", "delete", name).Run()
- return nil, fmt.Errorf("failed to open raw AF_PACKET socket: %w", err)
- }
- addr := syscall.SockaddrLinklayer{
- Protocol: htons(syscall.ETH_P_ALL),
- Ifindex: epIface.Index,
- }
- if err := syscall.Bind(sockFd, &addr); err != nil {
- _ = syscall.Close(sockFd)
- _ = exec.Command("ip", "link", "delete", name).Run()
- return nil, fmt.Errorf("failed to bind raw socket to %s: %w", epName, err)
- }
- dev := &vethTun{
- name: name,
- epName: epName,
- sockFd: sockFd,
- ifIndex: epIface.Index,
- }
- if len(epIface.HardwareAddr) >= 6 {
- copy(dev.epHwAddr[:], epIface.HardwareAddr[:6])
- }
- if mainIface, err := net.InterfaceByName(name); err == nil && len(mainIface.HardwareAddr) >= 6 {
- copy(dev.hwAddr[:], mainIface.HardwareAddr[:6])
- }
- return dev, nil
- }
- func (v *vethTun) Read(buf []byte) (int, error) {
- for {
- n, _, err := syscall.Recvfrom(v.sockFd, v.packetBuf[:], 0)
- if err != nil {
- return 0, err
- }
- if n <= 14 {
- continue // Too small for ethernet frame
- }
- // Ethernet frame header: [Dst MAC (6)][Src MAC (6)][EtherType (2)]
- etherType := binary.BigEndian.Uint16(v.packetBuf[12:14])
- // Check for IPv4 (0x0800) or IPv6 (0x86DD)
- if etherType != 0x0800 && etherType != 0x86DD {
- continue // Ignore ARP, STP, etc.
- }
- ipPayload := v.packetBuf[14:n]
- copied := copy(buf, ipPayload)
- return copied, nil
- }
- }
- func (v *vethTun) Write(buf []byte) (int, error) {
- if len(buf) == 0 {
- return 0, nil
- }
- // Build raw Ethernet frame to inject into endpoint
- frameLen := 14 + len(buf)
- var frame []byte
- if frameLen <= len(v.packetBuf) {
- frame = v.packetBuf[:frameLen]
- } else {
- frame = make([]byte, frameLen)
- }
- // Dst MAC: main interface, Src MAC: endpoint
- copy(frame[0:6], v.hwAddr[:])
- copy(frame[6:12], v.epHwAddr[:])
- // Detect IP version
- var etherType uint16 = 0x0800 // IPv4 default
- if (buf[0] >> 4) == 6 {
- etherType = 0x86DD // IPv6
- }
- binary.BigEndian.PutUint16(frame[12:14], etherType)
- copy(frame[14:], buf)
- addr := syscall.SockaddrLinklayer{
- Protocol: htons(etherType),
- Ifindex: v.ifIndex,
- Halen: 6,
- }
- copy(addr.Addr[:], v.hwAddr[:])
- err := syscall.Sendto(v.sockFd, frame, 0, &addr)
- if err != nil {
- return 0, err
- }
- return len(buf), nil
- }
- func (v *vethTun) Close() error {
- _ = syscall.Close(v.sockFd)
- _ = exec.Command("ip", "link", "delete", v.name).Run()
- return nil
- }
- func (v *vethTun) Name() string {
- return v.name
- }
- func htons(n uint16) uint16 {
- return (n<<8)&0xff00 | (n>>8)&0x00ff
- }
|