tun_freebsd.go raw
1 /* SPDX-License-Identifier: MIT
2 *
3 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
4 */
5
6 package tun
7
8 import (
9 "errors"
10 "fmt"
11 "io"
12 "net"
13 "os"
14 "sync"
15 "syscall"
16 "unsafe"
17
18 "golang.org/x/sys/unix"
19 )
20
21 const (
22 _TUNSIFHEAD = 0x80047460
23 _TUNSIFMODE = 0x8004745e
24 _TUNGIFNAME = 0x4020745d
25 _TUNSIFPID = 0x2000745f
26
27 _SIOCGIFINFO_IN6 = 0xc048696c
28 _SIOCSIFINFO_IN6 = 0xc048696d
29 _ND6_IFF_AUTO_LINKLOCAL = 0x20
30 _ND6_IFF_NO_DAD = 0x100
31 )
32
33 // Iface requests with just the name
34 type ifreqName struct {
35 Name [unix.IFNAMSIZ]byte
36 _ [16]byte
37 }
38
39 // Iface requests with a pointer
40 type ifreqPtr struct {
41 Name [unix.IFNAMSIZ]byte
42 Data uintptr
43 _ [16 - unsafe.Sizeof(uintptr(0))]byte
44 }
45
46 // Iface requests with MTU
47 type ifreqMtu struct {
48 Name [unix.IFNAMSIZ]byte
49 MTU uint32
50 _ [12]byte
51 }
52
53 // ND6 flag manipulation
54 type nd6Req struct {
55 Name [unix.IFNAMSIZ]byte
56 Linkmtu uint32
57 Maxmtu uint32
58 Basereachable uint32
59 Reachable uint32
60 Retrans uint32
61 Flags uint32
62 Recalctm int
63 Chlim uint8
64 Initialized uint8
65 Randomseed0 [8]byte
66 Randomseed1 [8]byte
67 Randomid [8]byte
68 }
69
70 type NativeTun struct {
71 name string
72 tunFile *os.File
73 events chan Event
74 errors chan error
75 routeSocket int
76 closeOnce sync.Once
77 }
78
79 func (tun *NativeTun) routineRouteListener(tunIfindex int) {
80 var (
81 statusUp bool
82 statusMTU int
83 )
84
85 defer close(tun.events)
86
87 data := make([]byte, os.Getpagesize())
88 for {
89 retry:
90 n, err := unix.Read(tun.routeSocket, data)
91 if err != nil {
92 if errors.Is(err, syscall.EINTR) {
93 goto retry
94 }
95 tun.errors <- err
96 return
97 }
98
99 if n < 14 {
100 continue
101 }
102
103 if data[3 /* type */] != unix.RTM_IFINFO {
104 continue
105 }
106 ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
107 if ifindex != tunIfindex {
108 continue
109 }
110
111 iface, err := net.InterfaceByIndex(ifindex)
112 if err != nil {
113 tun.errors <- err
114 return
115 }
116
117 // Up / Down event
118 up := (iface.Flags & net.FlagUp) != 0
119 if up != statusUp && up {
120 tun.events <- EventUp
121 }
122 if up != statusUp && !up {
123 tun.events <- EventDown
124 }
125 statusUp = up
126
127 // MTU changes
128 if iface.MTU != statusMTU {
129 tun.events <- EventMTUUpdate
130 }
131 statusMTU = iface.MTU
132 }
133 }
134
135 func tunName(fd uintptr) (string, error) {
136 var ifreq ifreqName
137 _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, _TUNGIFNAME, uintptr(unsafe.Pointer(&ifreq)))
138 if err != 0 {
139 return "", err
140 }
141 return unix.ByteSliceToString(ifreq.Name[:]), nil
142 }
143
144 // Destroy a named system interface
145 func tunDestroy(name string) error {
146 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
147 if err != nil {
148 return err
149 }
150 defer unix.Close(fd)
151
152 var ifr [32]byte
153 copy(ifr[:], name)
154 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCIFDESTROY), uintptr(unsafe.Pointer(&ifr[0])))
155 if errno != 0 {
156 return fmt.Errorf("failed to destroy interface %s: %w", name, errno)
157 }
158
159 return nil
160 }
161
162 func CreateTUN(name string, mtu int) (Device, error) {
163 if len(name) > unix.IFNAMSIZ-1 {
164 return nil, errors.New("interface name too long")
165 }
166
167 // See if interface already exists
168 iface, _ := net.InterfaceByName(name)
169 if iface != nil {
170 return nil, fmt.Errorf("interface %s already exists", name)
171 }
172
173 tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR|unix.O_CLOEXEC, 0)
174 if err != nil {
175 return nil, err
176 }
177
178 tun := NativeTun{tunFile: tunFile}
179 var assignedName string
180 tun.operateOnFd(func(fd uintptr) {
181 assignedName, err = tunName(fd)
182 })
183 if err != nil {
184 tunFile.Close()
185 return nil, err
186 }
187
188 // Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
189 ifheadmode := 1
190 var errno syscall.Errno
191 tun.operateOnFd(func(fd uintptr) {
192 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFHEAD, uintptr(unsafe.Pointer(&ifheadmode)))
193 })
194
195 if errno != 0 {
196 tunFile.Close()
197 tunDestroy(assignedName)
198 return nil, fmt.Errorf("unable to put into IFHEAD mode: %w", errno)
199 }
200
201 // Get out of PTP mode.
202 ifflags := syscall.IFF_BROADCAST | syscall.IFF_MULTICAST
203 tun.operateOnFd(func(fd uintptr) {
204 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(_TUNSIFMODE), uintptr(unsafe.Pointer(&ifflags)))
205 })
206
207 if errno != 0 {
208 tunFile.Close()
209 tunDestroy(assignedName)
210 return nil, fmt.Errorf("unable to put into IFF_BROADCAST mode: %w", errno)
211 }
212
213 // Disable link-local v6, not just because WireGuard doesn't do that anyway, but
214 // also because there are serious races with attaching and detaching LLv6 addresses
215 // in relation to interface lifetime within the FreeBSD kernel.
216 confd6, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
217 if err != nil {
218 tunFile.Close()
219 tunDestroy(assignedName)
220 return nil, err
221 }
222 defer unix.Close(confd6)
223 var ndireq nd6Req
224 copy(ndireq.Name[:], assignedName)
225 _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCGIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
226 if errno != 0 {
227 tunFile.Close()
228 tunDestroy(assignedName)
229 return nil, fmt.Errorf("unable to get nd6 flags for %s: %w", assignedName, errno)
230 }
231 ndireq.Flags = ndireq.Flags &^ _ND6_IFF_AUTO_LINKLOCAL
232 ndireq.Flags = ndireq.Flags | _ND6_IFF_NO_DAD
233 _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCSIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
234 if errno != 0 {
235 tunFile.Close()
236 tunDestroy(assignedName)
237 return nil, fmt.Errorf("unable to set nd6 flags for %s: %w", assignedName, errno)
238 }
239
240 if name != "" {
241 confd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
242 if err != nil {
243 tunFile.Close()
244 tunDestroy(assignedName)
245 return nil, err
246 }
247 defer unix.Close(confd)
248 var newnp [unix.IFNAMSIZ]byte
249 copy(newnp[:], name)
250 var ifr ifreqPtr
251 copy(ifr.Name[:], assignedName)
252 ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
253 _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd), uintptr(unix.SIOCSIFNAME), uintptr(unsafe.Pointer(&ifr)))
254 if errno != 0 {
255 tunFile.Close()
256 tunDestroy(assignedName)
257 return nil, fmt.Errorf("Failed to rename %s to %s: %w", assignedName, name, errno)
258 }
259 }
260
261 return CreateTUNFromFile(tunFile, mtu)
262 }
263
264 func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
265 tun := &NativeTun{
266 tunFile: file,
267 events: make(chan Event, 10),
268 errors: make(chan error, 1),
269 }
270
271 var errno syscall.Errno
272 tun.operateOnFd(func(fd uintptr) {
273 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFPID, uintptr(0))
274 })
275 if errno != 0 {
276 tun.tunFile.Close()
277 return nil, fmt.Errorf("unable to become controlling TUN process: %w", errno)
278 }
279
280 name, err := tun.Name()
281 if err != nil {
282 tun.tunFile.Close()
283 return nil, err
284 }
285
286 tunIfindex, err := func() (int, error) {
287 iface, err := net.InterfaceByName(name)
288 if err != nil {
289 return -1, err
290 }
291 return iface.Index, nil
292 }()
293 if err != nil {
294 tun.tunFile.Close()
295 return nil, err
296 }
297
298 tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.AF_UNSPEC)
299 if err != nil {
300 tun.tunFile.Close()
301 return nil, err
302 }
303
304 go tun.routineRouteListener(tunIfindex)
305
306 err = tun.setMTU(mtu)
307 if err != nil {
308 tun.Close()
309 return nil, err
310 }
311
312 return tun, nil
313 }
314
315 func (tun *NativeTun) Name() (string, error) {
316 var name string
317 var err error
318 tun.operateOnFd(func(fd uintptr) {
319 name, err = tunName(fd)
320 })
321 if err != nil {
322 return "", err
323 }
324 tun.name = name
325 return name, nil
326 }
327
328 func (tun *NativeTun) File() *os.File {
329 return tun.tunFile
330 }
331
332 func (tun *NativeTun) Events() <-chan Event {
333 return tun.events
334 }
335
336 func (tun *NativeTun) Read(bufs [][]byte, sizes []int, offset int) (int, error) {
337 select {
338 case err := <-tun.errors:
339 return 0, err
340 default:
341 buf := bufs[0][offset-4:]
342 n, err := tun.tunFile.Read(buf[:])
343 if n < 4 {
344 return 0, err
345 }
346 sizes[0] = n - 4
347 return 1, err
348 }
349 }
350
351 func (tun *NativeTun) Write(bufs [][]byte, offset int) (int, error) {
352 if offset < 4 {
353 return 0, io.ErrShortBuffer
354 }
355 for i, buf := range bufs {
356 buf = buf[offset-4:]
357 if len(buf) < 5 {
358 return i, io.ErrShortBuffer
359 }
360 buf[0] = 0x00
361 buf[1] = 0x00
362 buf[2] = 0x00
363 switch buf[4] >> 4 {
364 case 4:
365 buf[3] = unix.AF_INET
366 case 6:
367 buf[3] = unix.AF_INET6
368 default:
369 return i, unix.EAFNOSUPPORT
370 }
371 if _, err := tun.tunFile.Write(buf); err != nil {
372 return i, err
373 }
374 }
375 return len(bufs), nil
376 }
377
378 func (tun *NativeTun) Close() error {
379 var err1, err2, err3 error
380 tun.closeOnce.Do(func() {
381 err1 = tun.tunFile.Close()
382 err2 = tunDestroy(tun.name)
383 if tun.routeSocket != -1 {
384 unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
385 err3 = unix.Close(tun.routeSocket)
386 tun.routeSocket = -1
387 } else if tun.events != nil {
388 close(tun.events)
389 }
390 })
391 if err1 != nil {
392 return err1
393 }
394 if err2 != nil {
395 return err2
396 }
397 return err3
398 }
399
400 func (tun *NativeTun) setMTU(n int) error {
401 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
402 if err != nil {
403 return err
404 }
405 defer unix.Close(fd)
406
407 var ifr ifreqMtu
408 copy(ifr.Name[:], tun.name)
409 ifr.MTU = uint32(n)
410 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ifr)))
411 if errno != 0 {
412 return fmt.Errorf("failed to set MTU on %s: %w", tun.name, errno)
413 }
414 return nil
415 }
416
417 func (tun *NativeTun) MTU() (int, error) {
418 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
419 if err != nil {
420 return 0, err
421 }
422 defer unix.Close(fd)
423
424 var ifr ifreqMtu
425 copy(ifr.Name[:], tun.name)
426 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFMTU), uintptr(unsafe.Pointer(&ifr)))
427 if errno != 0 {
428 return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, errno)
429 }
430 return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
431 }
432
433 func (tun *NativeTun) BatchSize() int {
434 return 1
435 }
436