| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- //go:build !android && !netbsd && !e2e_testing
- // +build !android,!netbsd,!e2e_testing
- package overlay
- import (
- "fmt"
- "io"
- "net/netip"
- "sync/atomic"
- "github.com/gaissmai/bart"
- "github.com/sirupsen/logrus"
- "github.com/slackhq/nebula/config"
- "github.com/slackhq/nebula/routing"
- "github.com/slackhq/nebula/util"
- wgtun "golang.zx2c4.com/wireguard/tun"
- )
- // wgTun wraps a WireGuard TUN device and implements the overlay.Device interface
- type wgTun struct {
- tunDevice wgtun.Device
- vpnNetworks []netip.Prefix
- MaxMTU int
- DefaultMTU int
- Routes atomic.Pointer[[]Route]
- routeTree atomic.Pointer[bart.Table[routing.Gateways]]
- routeChan chan struct{}
- // Platform-specific route management
- routeManager *tun
- l *logrus.Logger
- }
- // BatchReader interface for readers that support vectorized I/O
- type BatchReader interface {
- BatchRead(buffers [][]byte, sizes []int) (int, error)
- }
- // BatchWriter interface for writers that support vectorized I/O
- type BatchWriter interface {
- BatchWrite(packets [][]byte) (int, error)
- }
- // wgTunReader wraps a single TUN queue for multi-queue support
- type wgTunReader struct {
- parent *wgTun
- tunDevice wgtun.Device
- offset int
- l *logrus.Logger
- }
- func (t *wgTun) Networks() []netip.Prefix {
- return t.vpnNetworks
- }
- func (t *wgTun) Name() string {
- name, err := t.tunDevice.Name()
- if err != nil {
- t.l.WithError(err).Error("Failed to get TUN device name")
- return "unknown"
- }
- return name
- }
- func (t *wgTun) RoutesFor(ip netip.Addr) routing.Gateways {
- r, _ := t.routeTree.Load().Lookup(ip)
- return r
- }
- func (t *wgTun) Activate() error {
- if t.routeManager == nil {
- return fmt.Errorf("route manager not initialized")
- }
- return t.routeManager.Activate(t)
- }
- // Read implements single-packet read for backward compatibility
- func (t *wgTun) Read(b []byte) (int, error) {
- bufs := [][]byte{b}
- sizes := []int{0}
- n, err := t.tunDevice.Read(bufs, sizes, 0)
- if err != nil {
- return 0, err
- }
- if n == 0 {
- return 0, io.ErrNoProgress
- }
- return sizes[0], nil
- }
- // Write implements single-packet write for backward compatibility
- func (t *wgTun) Write(b []byte) (int, error) {
- bufs := [][]byte{b}
- offset := 0
- // WireGuard TUN expects the packet data to start at offset 0
- n, err := t.tunDevice.Write(bufs, offset)
- if err != nil {
- return 0, err
- }
- if n == 0 {
- return 0, io.ErrShortWrite
- }
- return len(b), nil
- }
- func (t *wgTun) Close() error {
- if t.routeChan != nil {
- close(t.routeChan)
- }
- if t.tunDevice != nil {
- return t.tunDevice.Close()
- }
- return nil
- }
- func (t *wgTun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
- // For WireGuard TUN, we need to create separate TUN device instances for multi-queue
- // The platform-specific implementation will handle this
- if t.routeManager == nil {
- return nil, fmt.Errorf("route manager not initialized for multi-queue reader")
- }
- return t.routeManager.NewMultiQueueReader(t)
- }
- func (t *wgTun) reload(c *config.C, initial bool) error {
- routeChange, routes, err := getAllRoutesFromConfig(c, t.vpnNetworks, initial)
- if err != nil {
- return err
- }
- if !initial && !routeChange && !c.HasChanged("tun.mtu") {
- return nil
- }
- routeTree, err := makeRouteTree(t.l, routes, true)
- if err != nil {
- return err
- }
- oldDefaultMTU := t.DefaultMTU
- oldMaxMTU := t.MaxMTU
- newDefaultMTU := c.GetInt("tun.mtu", DefaultMTU)
- newMaxMTU := newDefaultMTU
- for i, r := range routes {
- if r.MTU == 0 {
- routes[i].MTU = newDefaultMTU
- }
- if r.MTU > t.MaxMTU {
- newMaxMTU = r.MTU
- }
- }
- t.MaxMTU = newMaxMTU
- t.DefaultMTU = newDefaultMTU
- // Teach nebula how to handle the routes before establishing them in the system table
- oldRoutes := t.Routes.Swap(&routes)
- t.routeTree.Store(routeTree)
- if !initial && t.routeManager != nil {
- if oldMaxMTU != newMaxMTU {
- t.routeManager.SetMTU(t, t.MaxMTU)
- t.l.Infof("Set max MTU to %v was %v", t.MaxMTU, oldMaxMTU)
- }
- if oldDefaultMTU != newDefaultMTU {
- for i := range t.vpnNetworks {
- err := t.routeManager.SetDefaultRoute(t, t.vpnNetworks[i])
- if err != nil {
- t.l.Warn(err)
- } else {
- t.l.Infof("Set default MTU to %v was %v", t.DefaultMTU, oldDefaultMTU)
- }
- }
- }
- // Remove first, if the system removes a wanted route hopefully it will be re-added next
- t.routeManager.RemoveRoutes(t, findRemovedRoutes(routes, *oldRoutes))
- // Ensure any routes we actually want are installed
- err = t.routeManager.AddRoutes(t, true)
- if err != nil {
- // This should never be called since AddRoutes should log its own errors in a reload condition
- util.LogWithContextIfNeeded("Failed to refresh routes", err, t.l)
- }
- }
- return nil
- }
- // BatchRead reads multiple packets from the TUN device using vectorized I/O
- // The caller provides buffers and sizes slices, and this function returns the number of packets read.
- func (r *wgTunReader) BatchRead(buffers [][]byte, sizes []int) (int, error) {
- return r.tunDevice.Read(buffers, sizes, r.offset)
- }
- // Read implements io.Reader for wgTunReader (single packet for compatibility)
- func (r *wgTunReader) Read(b []byte) (int, error) {
- bufs := [][]byte{b}
- sizes := []int{0}
- n, err := r.tunDevice.Read(bufs, sizes, r.offset)
- if err != nil {
- return 0, err
- }
- if n == 0 {
- return 0, io.ErrNoProgress
- }
- return sizes[0], nil
- }
- // Write implements io.Writer for wgTunReader
- func (r *wgTunReader) Write(b []byte) (int, error) {
- bufs := [][]byte{b}
- n, err := r.tunDevice.Write(bufs, r.offset)
- if err != nil {
- return 0, err
- }
- if n == 0 {
- return 0, io.ErrShortWrite
- }
- return len(b), nil
- }
- // BatchWrite writes multiple packets to the TUN device using vectorized I/O
- func (r *wgTunReader) BatchWrite(packets [][]byte) (int, error) {
- return r.tunDevice.Write(packets, r.offset)
- }
- func (r *wgTunReader) Close() error {
- if r.tunDevice != nil {
- return r.tunDevice.Close()
- }
- return nil
- }
|