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/sys v0.0.0-20200501052902-10377860bb8e
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
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
|
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-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 h1:dSXrLpRy86h4wfZWvzjyA2tuhMzX/lx0st4hzAh1VMA=
|
||||||
inet.af/netaddr v0.0.0-20200701194149-10bc159763c4/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww=
|
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 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
|
||||||
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
|
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
|
||||||
|
|
|
@ -29,25 +29,100 @@ type PacketConner interface {
|
||||||
PacketConn() net.PacketConn
|
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 {
|
type Network struct {
|
||||||
name string
|
name string
|
||||||
dhcpPool netaddr.IPPrefix
|
v4Pool netaddr.IPPrefix
|
||||||
alloced map[netaddr.IP]bool
|
v6Pool netaddr.IPPrefix
|
||||||
|
|
||||||
pushRoute 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) {
|
func (n *Network) write(p []byte, dst, src netaddr.IPPort) (num int, err error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
type iface struct {
|
type Interface struct {
|
||||||
net *Network
|
net *Network
|
||||||
name string // optional
|
name string // optional
|
||||||
ips []netaddr.IP // static; not mutated once created
|
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
|
// TODO: make this all better
|
||||||
if f.name != "" {
|
if f.name != "" {
|
||||||
return f.name
|
return f.name
|
||||||
|
@ -56,7 +131,7 @@ func (f *iface) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains reports whether f contains ip as an IP.
|
// 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 {
|
for _, v := range f.ips {
|
||||||
if ip == v {
|
if ip == v {
|
||||||
return true
|
return true
|
||||||
|
@ -67,19 +142,43 @@ func (f *iface) Contains(ip netaddr.IP) bool {
|
||||||
|
|
||||||
type routeEntry struct {
|
type routeEntry struct {
|
||||||
prefix netaddr.IPPrefix
|
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.
|
// A Machine is a representation of an operating system's network stack.
|
||||||
// It has a network routing table and can have multiple attached networks.
|
// It has a network routing table and can have multiple attached networks.
|
||||||
type Machine struct {
|
type Machine struct {
|
||||||
|
name string
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
interfaces []*iface
|
interfaces []*Interface
|
||||||
routes []routeEntry // sorted by longest prefix to shortest
|
routes []routeEntry // sorted by longest prefix to shortest
|
||||||
|
|
||||||
conns map[netaddr.IPPort]*conn
|
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 (
|
var (
|
||||||
v4unspec = netaddr.IPv4(0, 0, 0, 0)
|
v4unspec = netaddr.IPv4(0, 0, 0, 0)
|
||||||
v6unspec = netaddr.IPv6Unspecified()
|
v6unspec = netaddr.IPv6Unspecified()
|
||||||
|
@ -90,19 +189,25 @@ func (m *Machine) writePacket(p []byte, dst, src netaddr.IPPort) (n int, err err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
unspec := src.IP == v4unspec || src.IP == v6unspec
|
origSrcIP := src.IP
|
||||||
if unspec {
|
switch {
|
||||||
// TODO: pick a src IP
|
case src.IP == v4unspec:
|
||||||
} else {
|
src.IP = iface.V4()
|
||||||
|
case src.IP == v6unspec:
|
||||||
|
src.IP = iface.V6()
|
||||||
|
default:
|
||||||
if !iface.Contains(src.IP) {
|
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)
|
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)
|
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()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
for _, re := range m.routes {
|
for _, re := range m.routes {
|
||||||
|
|
Loading…
Reference in New Issue