mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-01 22:12:31 +00:00

wgengine/router: add missing router file Change-Id: I1b6573ea5e2d3ab23f1d297209f822adee7b10e0
156 lines
3.7 KiB
Go
156 lines
3.7 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package router
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"net/netip"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/tailscale/wireguard-go/tun"
|
|
"tailscale.com/health"
|
|
"tailscale.com/net/netmon"
|
|
"tailscale.com/types/logger"
|
|
)
|
|
|
|
func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
|
|
r := &plan9Router{
|
|
logf: logf,
|
|
tundev: tundev,
|
|
netMon: netMon,
|
|
}
|
|
cleanAllTailscaleRoutes(logf)
|
|
return r, nil
|
|
}
|
|
|
|
type plan9Router struct {
|
|
logf logger.Logf
|
|
tundev tun.Device
|
|
netMon *netmon.Monitor
|
|
health *health.Tracker
|
|
}
|
|
|
|
func (r *plan9Router) Up() error {
|
|
return nil
|
|
}
|
|
|
|
func (r *plan9Router) Set(cfg *Config) error {
|
|
if cfg == nil {
|
|
cleanAllTailscaleRoutes(r.logf)
|
|
return nil
|
|
}
|
|
|
|
var self4, self6 netip.Addr
|
|
for _, addr := range cfg.LocalAddrs {
|
|
ctl := r.tundev.File()
|
|
maskBits := addr.Bits()
|
|
if addr.Addr().Is4() {
|
|
// The mask sizes in Plan9 are in IPv6 bits, even for IPv4.
|
|
maskBits += (128 - 32)
|
|
self4 = addr.Addr()
|
|
}
|
|
if addr.Addr().Is6() {
|
|
self6 = addr.Addr()
|
|
}
|
|
_, err := fmt.Fprintf(ctl, "add %s /%d\n", addr.Addr().String(), maskBits)
|
|
r.logf("XXX add %s /%d = %v", addr.Addr().String(), maskBits, err)
|
|
}
|
|
|
|
r.logf("XXX TODO: Set Routes %v", cfg.Routes)
|
|
|
|
ipr, err := os.OpenFile("/net/iproute", os.O_RDWR, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("open /net/iproute: %w", err)
|
|
}
|
|
defer ipr.Close()
|
|
|
|
// TODO(bradfitz): read existing routes, delete ones tagged "tail"
|
|
// that aren't in cfg.LocalRoutes.
|
|
|
|
if _, err := fmt.Fprintf(ipr, "tag tail\n"); err != nil {
|
|
return fmt.Errorf("tag tail: %w", err)
|
|
}
|
|
|
|
for _, route := range cfg.Routes {
|
|
maskBits := route.Bits()
|
|
if route.Addr().Is4() {
|
|
// The mask sizes in Plan9 are in IPv6 bits, even for IPv4.
|
|
maskBits += (128 - 32)
|
|
}
|
|
var nextHop netip.Addr
|
|
if route.Addr().Is4() {
|
|
nextHop = self4
|
|
} else if route.Addr().Is6() {
|
|
nextHop = self6
|
|
}
|
|
if !nextHop.IsValid() {
|
|
r.logf("XXX skipping route %s: no next hop (no self addr)", route.String())
|
|
continue
|
|
}
|
|
r.logf("XXX plan9.router: add %s /%d %s", route.Addr(), maskBits, nextHop)
|
|
if _, err := fmt.Fprintf(ipr, "add %s /%d %s\n", route.Addr(), maskBits, nextHop); err != nil {
|
|
return fmt.Errorf("add %s: %w", route.String(), err)
|
|
}
|
|
}
|
|
|
|
r.logf("XXX TODO: Set LocalRoutes %v", cfg.LocalRoutes)
|
|
// TODO(bradfitz): implement this.
|
|
return nil
|
|
}
|
|
|
|
// UpdateMagicsockPort implements the Router interface. This implementation
|
|
// does nothing and returns nil because this router does not currently need
|
|
// to know what the magicsock UDP port is.
|
|
func (r *plan9Router) UpdateMagicsockPort(_ uint16, _ string) error {
|
|
return nil
|
|
}
|
|
|
|
func (r *plan9Router) Close() error {
|
|
// TODO(bradfitz): unbind
|
|
return nil
|
|
}
|
|
|
|
func cleanUp(logf logger.Logf, _ string) {
|
|
cleanAllTailscaleRoutes(logf)
|
|
}
|
|
|
|
func cleanAllTailscaleRoutes(logf logger.Logf) {
|
|
routes, err := os.OpenFile("/net/iproute", os.O_RDWR, 0)
|
|
if err != nil {
|
|
logf("cleaning routes: %v", err)
|
|
return
|
|
}
|
|
defer routes.Close()
|
|
|
|
// Using io.ReadAll or os.ReadFile on /net/iproute fails; it results in a
|
|
// 511 byte result when the actual /net/iproute contents are over 1k.
|
|
// So do it in one big read instead. Who knows.
|
|
routeBuf := make([]byte, 1<<20)
|
|
n, err := routes.Read(routeBuf)
|
|
if err != nil {
|
|
logf("cleaning routes: %v", err)
|
|
return
|
|
}
|
|
routeBuf = routeBuf[:n]
|
|
|
|
//logf("cleaning routes: %d bytes: %q", len(routeBuf), routeBuf)
|
|
|
|
bs := bufio.NewScanner(bytes.NewReader(routeBuf))
|
|
for bs.Scan() {
|
|
f := strings.Fields(bs.Text())
|
|
if len(f) < 6 {
|
|
continue
|
|
}
|
|
tag := f[4]
|
|
if tag != "tail" {
|
|
continue
|
|
}
|
|
_, err := fmt.Fprintf(routes, "remove %s %s\n", f[0], f[1])
|
|
logf("router: cleaning route %s %s: %v", f[0], f[1], err)
|
|
}
|
|
}
|