Updates #5794 Change-Id: Ib78a3ea971a2374d405b024ab88658ec34be59a6 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>main
parent
e3282c1632
commit
bbdd3c3bde
@ -0,0 +1,156 @@ |
||||
// 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("route/plan9: add %s /%d = %v", addr.Addr().String(), maskBits, err) |
||||
} |
||||
|
||||
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("route/plan9: skipping route %s: no next hop (no self addr)", route.String()) |
||||
continue |
||||
} |
||||
r.logf("route/plan9: 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) |
||||
} |
||||
} |
||||
|
||||
if len(cfg.LocalRoutes) > 0 { |
||||
r.logf("route/plan9: TODO: Set LocalRoutes %v", cfg.LocalRoutes) |
||||
} |
||||
if len(cfg.SubnetRoutes) > 0 { |
||||
r.logf("route/plan9: TODO: Set SubnetRoutes %v", cfg.SubnetRoutes) |
||||
} |
||||
|
||||
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] |
||||
|
||||
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) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue