mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
tstest/natlab: network address allocation
Pairing with @danderson
This commit is contained in:
parent
bb1a9e4700
commit
a477e70632
2
go.mod
2
go.mod
@ -29,6 +29,6 @@ require (
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||
inet.af/netaddr v0.0.0-20200701194149-10bc159763c4
|
||||
inet.af/netaddr v0.0.0-20200702150737-4591d218f82c
|
||||
rsc.io/goversion v1.2.0
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -166,5 +166,7 @@ inet.af/netaddr v0.0.0-20200701171350-6509743f79d9 h1:F41nQsn8UGDPDXsOPwZQiaK8Bm
|
||||
inet.af/netaddr v0.0.0-20200701171350-6509743f79d9/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww=
|
||||
inet.af/netaddr v0.0.0-20200701194149-10bc159763c4 h1:dSXrLpRy86h4wfZWvzjyA2tuhMzX/lx0st4hzAh1VMA=
|
||||
inet.af/netaddr v0.0.0-20200701194149-10bc159763c4/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww=
|
||||
inet.af/netaddr v0.0.0-20200702150737-4591d218f82c h1:j3Z4HL4KcLBDU1kmRpXTD5fikKBqIkE+7vFKS5mCz3Y=
|
||||
inet.af/netaddr v0.0.0-20200702150737-4591d218f82c/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww=
|
||||
rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
|
||||
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
|
||||
|
@ -29,25 +29,100 @@ type PacketConner interface {
|
||||
PacketConn() net.PacketConn
|
||||
}
|
||||
|
||||
func mustPrefix(s string) netaddr.IPPrefix {
|
||||
ipp, err := netaddr.ParseIPPrefix(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ipp
|
||||
}
|
||||
|
||||
// NewInternet returns a network that simulates the internet.
|
||||
func NewInternet() *Network {
|
||||
return &Network{
|
||||
v4Pool: mustPrefix("203.0.113.0/24"), // documentation netblock that looks Internet-y
|
||||
v6Pool: mustPrefix("fc00:52::/64"),
|
||||
}
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
name string
|
||||
dhcpPool netaddr.IPPrefix
|
||||
alloced map[netaddr.IP]bool
|
||||
name string
|
||||
v4Pool netaddr.IPPrefix
|
||||
v6Pool netaddr.IPPrefix
|
||||
|
||||
pushRoute netaddr.IPPrefix
|
||||
|
||||
mu sync.Mutex
|
||||
machine map[netaddr.IP]*Machine
|
||||
lastV4 netaddr.IP
|
||||
lastV6 netaddr.IP
|
||||
}
|
||||
|
||||
func (n *Network) AllocIPv4() netaddr.IP {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
if n.lastV4.IsZero() {
|
||||
n.lastV4 = n.v4Pool.IP
|
||||
}
|
||||
a := n.lastV4.As16()
|
||||
addOne(&a, 15)
|
||||
n.lastV4 = netaddr.IPFrom16(a)
|
||||
if !n.v4Pool.Contains(n.lastV4) {
|
||||
panic("pool exhausted")
|
||||
}
|
||||
return n.lastV4
|
||||
}
|
||||
|
||||
func (n *Network) AllocIPv6() netaddr.IP {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
if n.lastV6.IsZero() {
|
||||
n.lastV6 = n.v6Pool.IP
|
||||
}
|
||||
a := n.lastV6.As16()
|
||||
addOne(&a, 15)
|
||||
n.lastV6 = netaddr.IPFrom16(a)
|
||||
if !n.v6Pool.Contains(n.lastV6) {
|
||||
panic("pool exhausted")
|
||||
}
|
||||
return n.lastV6
|
||||
}
|
||||
|
||||
func addOne(a *[16]byte, index int) {
|
||||
if v := a[index]; v < 255 {
|
||||
a[index]++
|
||||
} else {
|
||||
a[index] = 0
|
||||
addOne(a, index-1)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Network) write(p []byte, dst, src netaddr.IPPort) (num int, err error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
type iface struct {
|
||||
type Interface struct {
|
||||
net *Network
|
||||
name string // optional
|
||||
ips []netaddr.IP // static; not mutated once created
|
||||
}
|
||||
|
||||
func (f *iface) String() string {
|
||||
// V4 returns the machine's first IPv4 address, or the zero value if none.
|
||||
func (f *Interface) V4() netaddr.IP { return f.pickIP(netaddr.IP.Is4) }
|
||||
|
||||
// V6 returns the machine's first IPv6 address, or the zero value if none.
|
||||
func (f *Interface) V6() netaddr.IP { return f.pickIP(netaddr.IP.Is6) }
|
||||
|
||||
func (f *Interface) pickIP(pred func(netaddr.IP) bool) netaddr.IP {
|
||||
for _, ip := range f.ips {
|
||||
if pred(ip) {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
return netaddr.IP{}
|
||||
}
|
||||
|
||||
func (f *Interface) String() string {
|
||||
// TODO: make this all better
|
||||
if f.name != "" {
|
||||
return f.name
|
||||
@ -56,7 +131,7 @@ func (f *iface) String() string {
|
||||
}
|
||||
|
||||
// Contains reports whether f contains ip as an IP.
|
||||
func (f *iface) Contains(ip netaddr.IP) bool {
|
||||
func (f *Interface) Contains(ip netaddr.IP) bool {
|
||||
for _, v := range f.ips {
|
||||
if ip == v {
|
||||
return true
|
||||
@ -67,19 +142,43 @@ func (f *iface) Contains(ip netaddr.IP) bool {
|
||||
|
||||
type routeEntry struct {
|
||||
prefix netaddr.IPPrefix
|
||||
iface *iface
|
||||
iface *Interface
|
||||
}
|
||||
|
||||
// NewMachine returns a new Machine without any network connection.
|
||||
// The name is just for debugging and need not be globally unique.
|
||||
// Use Attach to add networks.
|
||||
func NewMachine(name string) *Machine {
|
||||
return &Machine{name: name}
|
||||
}
|
||||
|
||||
// A Machine is a representation of an operating system's network stack.
|
||||
// It has a network routing table and can have multiple attached networks.
|
||||
type Machine struct {
|
||||
name string
|
||||
|
||||
mu sync.Mutex
|
||||
interfaces []*iface
|
||||
interfaces []*Interface
|
||||
routes []routeEntry // sorted by longest prefix to shortest
|
||||
|
||||
conns map[netaddr.IPPort]*conn
|
||||
}
|
||||
|
||||
// Attach
|
||||
func (m *Machine) Attach(interfaceName string, n *Network) *Interface {
|
||||
f := &Interface{
|
||||
net: n,
|
||||
name: interfaceName,
|
||||
}
|
||||
// TODO: get f.ips, routes
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.interfaces = append(m.interfaces, f)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
var (
|
||||
v4unspec = netaddr.IPv4(0, 0, 0, 0)
|
||||
v6unspec = netaddr.IPv6Unspecified()
|
||||
@ -90,19 +189,25 @@ func (m *Machine) writePacket(p []byte, dst, src netaddr.IPPort) (n int, err err
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
unspec := src.IP == v4unspec || src.IP == v6unspec
|
||||
if unspec {
|
||||
// TODO: pick a src IP
|
||||
} else {
|
||||
origSrcIP := src.IP
|
||||
switch {
|
||||
case src.IP == v4unspec:
|
||||
src.IP = iface.V4()
|
||||
case src.IP == v6unspec:
|
||||
src.IP = iface.V6()
|
||||
default:
|
||||
if !iface.Contains(src.IP) {
|
||||
return 0, fmt.Errorf("can't send to %v with src %v on interface %v", dst.IP, src.IP, iface)
|
||||
}
|
||||
}
|
||||
if src.IP.IsZero() {
|
||||
return 0, fmt.Errorf("no matching address for address family for %v", origSrcIP)
|
||||
}
|
||||
|
||||
return iface.net.write(p, dst, src)
|
||||
}
|
||||
|
||||
func (m *Machine) interfaceForIP(ip netaddr.IP) (*iface, error) {
|
||||
func (m *Machine) interfaceForIP(ip netaddr.IP) (*Interface, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for _, re := range m.routes {
|
||||
|
Loading…
Reference in New Issue
Block a user