device.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package aerotun
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "os"
  8. "os/exec"
  9. "runtime"
  10. "sync"
  11. "syscall"
  12. "unsafe"
  13. "golang.org/x/sys/unix"
  14. )
  15. type VirtualDevice interface {
  16. Read(buf []byte) (int, error)
  17. Write(buf []byte) (int, error)
  18. Close() error
  19. Name() string
  20. }
  21. // CreateDevice creates either a native Linux TUN device, or falls back to a high-speed
  22. // veth pair with AF_PACKET raw socket if running in an unprivileged container without /dev/net/tun.
  23. func CreateDevice(name string, mtu int, localCIDR string) (VirtualDevice, error) {
  24. if _, err := net.InterfaceByName(name); err == nil {
  25. return nil, fmt.Errorf("interface %s already exists", name)
  26. }
  27. if mtu <= 0 {
  28. mtu = DefaultMTU
  29. }
  30. if _, err := os.Stat("/dev/net/tun"); err == nil {
  31. dev, err := createNativeTun(name, mtu, localCIDR)
  32. if err == nil {
  33. return dev, nil
  34. }
  35. }
  36. // Universal container / Proxmox LXC fallback:
  37. return createVethTun(name, mtu, localCIDR)
  38. }
  39. // ---------------------------------------------------------------------------
  40. // 1. Native Linux TUN Implementation (/dev/net/tun)
  41. // ---------------------------------------------------------------------------
  42. type nativeTun struct {
  43. file *os.File
  44. name string
  45. }
  46. func createNativeTun(name string, mtu int, localCIDR string) (*nativeTun, error) {
  47. fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR|syscall.O_NONBLOCK|syscall.O_CLOEXEC, 0)
  48. if err != nil {
  49. return nil, err
  50. }
  51. type ifreq struct {
  52. name [16]byte
  53. flags uint16
  54. _pad [22]byte
  55. }
  56. var ifr ifreq
  57. copy(ifr.name[:], name)
  58. // IFF_TUN = 0x0001, IFF_NO_PI = 0x1000
  59. ifr.flags = 0x0001 | 0x1000 | 0x8000 // IFF_TUN_EXCL: never attach an existing device
  60. _, _, errno := syscall.Syscall(
  61. syscall.SYS_IOCTL,
  62. uintptr(fd),
  63. uintptr(0x400454ca), // TUNSETIFF
  64. uintptr(unsafe.Pointer(&ifr)),
  65. )
  66. if errno != 0 {
  67. _ = syscall.Close(fd)
  68. return nil, errno
  69. }
  70. // Configure MTU and IP
  71. if _, err := runIP("link", "set", name, "mtu", fmt.Sprintf("%d", mtu)); err != nil {
  72. _ = syscall.Close(fd)
  73. return nil, err
  74. }
  75. if _, err := runIP("link", "set", name, "up"); err != nil {
  76. _ = syscall.Close(fd)
  77. return nil, err
  78. }
  79. if localCIDR != "" {
  80. if _, err := runIP("addr", "add", localCIDR, "dev", name); err != nil {
  81. _ = syscall.Close(fd)
  82. return nil, err
  83. }
  84. }
  85. return &nativeTun{
  86. file: os.NewFile(uintptr(fd), name),
  87. name: name,
  88. }, nil
  89. }
  90. func (t *nativeTun) Read(buf []byte) (int, error) {
  91. return t.file.Read(buf)
  92. }
  93. func (t *nativeTun) Write(buf []byte) (int, error) {
  94. return t.file.Write(buf)
  95. }
  96. func (t *nativeTun) Close() error {
  97. // Non-persistent TUN interfaces disappear when the descriptor closes.
  98. return t.file.Close()
  99. }
  100. func (t *nativeTun) Name() string {
  101. return t.name
  102. }
  103. // ---------------------------------------------------------------------------
  104. // 2. High-Performance Container Fallback (VETH + AF_PACKET)
  105. // ---------------------------------------------------------------------------
  106. type vethTun struct {
  107. mu sync.RWMutex
  108. closed bool
  109. name string
  110. epName string
  111. sockFd int
  112. ifIndex int
  113. hwAddr [6]byte
  114. epHwAddr [6]byte
  115. packetBuf []byte
  116. writeBuf []byte
  117. }
  118. func createVethTun(name string, mtu int, localCIDR string) (*vethTun, error) {
  119. epName := fmt.Sprintf("%s_ep", name)
  120. if len(epName) > 15 {
  121. epName = epName[:15]
  122. }
  123. // Existing interfaces may belong to another tunnel: creation must fail safely.
  124. // 1. Create veth pair
  125. out, err := exec.Command("ip", "link", "add", name, "type", "veth", "peer", "name", epName).CombinedOutput()
  126. if err != nil {
  127. return nil, fmt.Errorf("failed to create veth pair %s: %s: %w", name, string(out), err)
  128. }
  129. // 2. Set Point-to-Point / ARP off behavior
  130. if _, err := runIP("link", "set", name, "arp", "off"); err != nil {
  131. _ = exec.Command("ip", "link", "delete", name).Run()
  132. return nil, err
  133. }
  134. if _, err := runIP("link", "set", epName, "arp", "off"); err != nil {
  135. _ = exec.Command("ip", "link", "delete", name).Run()
  136. return nil, err
  137. }
  138. if _, err := runIP("link", "set", name, "mtu", fmt.Sprintf("%d", mtu)); err != nil {
  139. _ = exec.Command("ip", "link", "delete", name).Run()
  140. return nil, err
  141. }
  142. if _, err := runIP("link", "set", epName, "mtu", fmt.Sprintf("%d", mtu)); err != nil {
  143. _ = exec.Command("ip", "link", "delete", name).Run()
  144. return nil, err
  145. }
  146. if _, err := runIP("link", "set", name, "up"); err != nil {
  147. _ = exec.Command("ip", "link", "delete", name).Run()
  148. return nil, err
  149. }
  150. if _, err := runIP("link", "set", epName, "up"); err != nil {
  151. _ = exec.Command("ip", "link", "delete", name).Run()
  152. return nil, err
  153. }
  154. // 3. Assign IP to main device
  155. if localCIDR != "" {
  156. if _, err := runIP("addr", "add", localCIDR, "dev", name); err != nil {
  157. _ = exec.Command("ip", "link", "delete", name).Run()
  158. return nil, err
  159. }
  160. }
  161. // Raw sockets do not complete CHECKSUM_PARTIAL or segment GSO packets.
  162. // Disable these features on both ends before any traffic can be forwarded.
  163. for _, iface := range []string{name, epName} {
  164. if err := disableOffloads(iface); err != nil {
  165. _ = exec.Command("ip", "link", "delete", name).Run()
  166. return nil, err
  167. }
  168. }
  169. // 4. Get endpoint interface index
  170. epIface, err := net.InterfaceByName(epName)
  171. if err != nil {
  172. _ = exec.Command("ip", "link", "delete", name).Run()
  173. return nil, fmt.Errorf("failed to lookup %s: %w", epName, err)
  174. }
  175. // 5. Open AF_PACKET raw socket bound to endpoint
  176. // htons(ETH_P_ALL) = 0x0300
  177. sockFd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, int(htons(syscall.ETH_P_ALL)))
  178. if err != nil {
  179. _ = exec.Command("ip", "link", "delete", name).Run()
  180. return nil, fmt.Errorf("failed to open raw AF_PACKET socket: %w", err)
  181. }
  182. // PACKET_IGNORE_OUTGOING avoids injecting locally sent frames back into VPN.
  183. _ = syscall.SetsockoptInt(sockFd, 263, 23, 1)
  184. // A receive timeout lets Close reliably stop a blocked raw-socket reader.
  185. _ = syscall.SetsockoptTimeval(sockFd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Sec: 1})
  186. addr := syscall.SockaddrLinklayer{
  187. Protocol: htons(syscall.ETH_P_ALL),
  188. Ifindex: epIface.Index,
  189. }
  190. if err := syscall.Bind(sockFd, &addr); err != nil {
  191. _ = syscall.Close(sockFd)
  192. _ = exec.Command("ip", "link", "delete", name).Run()
  193. return nil, fmt.Errorf("failed to bind raw socket to %s: %w", epName, err)
  194. }
  195. dev := &vethTun{
  196. packetBuf: make([]byte, mtu+14), writeBuf: make([]byte, mtu+14),
  197. name: name,
  198. epName: epName,
  199. sockFd: sockFd,
  200. ifIndex: epIface.Index,
  201. }
  202. if len(epIface.HardwareAddr) >= 6 {
  203. copy(dev.epHwAddr[:], epIface.HardwareAddr[:6])
  204. }
  205. if mainIface, err := net.InterfaceByName(name); err == nil && len(mainIface.HardwareAddr) >= 6 {
  206. copy(dev.hwAddr[:], mainIface.HardwareAddr[:6])
  207. }
  208. return dev, nil
  209. }
  210. func (v *vethTun) Read(buf []byte) (int, error) {
  211. for {
  212. v.mu.RLock()
  213. if v.closed {
  214. v.mu.RUnlock()
  215. return 0, net.ErrClosed
  216. }
  217. n, addr, err := syscall.Recvfrom(v.sockFd, v.packetBuf[:], 0)
  218. v.mu.RUnlock()
  219. if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EINTR) {
  220. continue
  221. }
  222. if err != nil {
  223. return 0, err
  224. }
  225. if ll, ok := addr.(*syscall.SockaddrLinklayer); ok && ll.Pkttype == syscall.PACKET_OUTGOING {
  226. continue
  227. }
  228. if n <= 14 {
  229. continue // Too small for ethernet frame
  230. }
  231. // Ethernet frame header: [Dst MAC (6)][Src MAC (6)][EtherType (2)]
  232. etherType := binary.BigEndian.Uint16(v.packetBuf[12:14])
  233. // Check for IPv4 (0x0800) or IPv6 (0x86DD)
  234. if etherType != 0x0800 && etherType != 0x86DD {
  235. continue // Ignore ARP, STP, etc.
  236. }
  237. ipPayload := v.packetBuf[14:n]
  238. copied := copy(buf, ipPayload)
  239. return copied, nil
  240. }
  241. }
  242. func (v *vethTun) Write(buf []byte) (int, error) {
  243. if len(buf) == 0 {
  244. return 0, nil
  245. }
  246. // Build raw Ethernet frame to inject into endpoint
  247. frameLen := 14 + len(buf)
  248. if frameLen > len(v.writeBuf) {
  249. return 0, fmt.Errorf("packet exceeds configured veth MTU")
  250. }
  251. frame := v.writeBuf[:frameLen] // Separate reusable RX/TX storage; one writer per device.
  252. // Dst MAC: main interface, Src MAC: endpoint
  253. copy(frame[0:6], v.hwAddr[:])
  254. copy(frame[6:12], v.epHwAddr[:])
  255. // Detect IP version
  256. var etherType uint16 = 0x0800 // IPv4 default
  257. if (buf[0] >> 4) == 6 {
  258. etherType = 0x86DD // IPv6
  259. }
  260. binary.BigEndian.PutUint16(frame[12:14], etherType)
  261. copy(frame[14:], buf)
  262. addr := syscall.SockaddrLinklayer{
  263. Protocol: htons(etherType),
  264. Ifindex: v.ifIndex,
  265. Halen: 6,
  266. }
  267. copy(addr.Addr[:], v.hwAddr[:])
  268. v.mu.RLock()
  269. defer v.mu.RUnlock()
  270. if v.closed {
  271. return 0, net.ErrClosed
  272. }
  273. err := syscall.Sendto(v.sockFd, frame, 0, &addr)
  274. if err != nil {
  275. return 0, err
  276. }
  277. return len(buf), nil
  278. }
  279. func (v *vethTun) Close() error {
  280. v.mu.Lock()
  281. if v.closed {
  282. v.mu.Unlock()
  283. return nil
  284. }
  285. v.closed = true
  286. _ = syscall.Close(v.sockFd)
  287. v.mu.Unlock()
  288. _ = exec.Command("ip", "link", "delete", v.name).Run()
  289. return nil
  290. }
  291. func (v *vethTun) Name() string {
  292. return v.name
  293. }
  294. func htons(n uint16) uint16 {
  295. return (n<<8)&0xff00 | (n>>8)&0x00ff
  296. }
  297. // Linux ethtool ioctls avoid requiring an external ethtool executable in LXC.
  298. func disableOffloads(name string) error {
  299. fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
  300. if err != nil {
  301. return err
  302. }
  303. defer unix.Close(fd)
  304. for _, cmd := range []uint32{0x1f, 0x24, 0x2c, 0x17} { // TSO, GSO, GRO, TX checksum
  305. value := struct{ Command, Data uint32 }{Command: cmd}
  306. ifr := struct {
  307. Name [16]byte
  308. Data unsafe.Pointer
  309. Pad [16]byte
  310. }{Data: unsafe.Pointer(&value)}
  311. copy(ifr.Name[:], name)
  312. _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), 0x8946, uintptr(unsafe.Pointer(&ifr)))
  313. var err error
  314. if errno != 0 {
  315. err = errno
  316. }
  317. runtime.KeepAlive(&value)
  318. // Unsupported offloads are already unavailable on this device.
  319. if err != nil && !errors.Is(err, unix.EOPNOTSUPP) {
  320. return fmt.Errorf("disable offload %#x on %s: %w", cmd, name, err)
  321. }
  322. }
  323. return nil
  324. }