From ae3ee42469b7c48848d841386ca9c74b7d6bbcd8 Mon Sep 17 00:00:00 2001 From: Dave Russell Date: Mon, 28 Sep 2020 22:31:16 +1000 Subject: [PATCH] Provide hooks for custom message packet handlers This commit augments the Control API by providing new methods to inject message packets destined peer nodes, and/or to intercept message packets of a custom message subtype that are received from peer nodes. --- control.go | 41 +++++++++++++++++++++++++++++++++++++++++ handler.go | 9 ++++++++- outside.go | 4 ++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/control.go b/control.go index e16d07d..56826a6 100644 --- a/control.go +++ b/control.go @@ -1,6 +1,8 @@ package nebula import ( + "encoding/binary" + "fmt" "net" "os" "os/signal" @@ -8,6 +10,7 @@ import ( "github.com/sirupsen/logrus" "github.com/slackhq/nebula/cert" + "golang.org/x/net/ipv4" ) // Every interaction here needs to take extra care to copy memory and not return or use arguments "as is" when touching @@ -167,3 +170,41 @@ func copyHostInfo(h *HostInfo) ControlHostInfo { return chi } + +// Hook provides the ability to hook into the network path for a particular +// message sub type. Any received message of that subtype that is allowed by +// the firewall will be written to the provided write func instead of the +// inside interface. +// TODO: make this an io.Writer +func (c *Control) Hook(t NebulaMessageSubType, w func([]byte) error) error { + if t == 0 { + return fmt.Errorf("non-default message subtype must be specified") + } + if _, ok := c.f.handlers[Version][message][t]; ok { + return fmt.Errorf("message subtype %d already hooked", t) + } + + c.f.handlers[Version][message][t] = c.f.newHook(w) + return nil +} + +// Send provides the ability to send arbitrary message packets to peer nodes. +// The provided payload will be encapsulated in an IPv4 packet from the +// node IP to the provided destination nebula IP. Any protocol handling +// above layer 3 (IP) must be managed by the caller. +func (c *Control) Send(ip uint32, t NebulaMessageSubType, payload []byte) { + hostinfo := c.f.getOrHandshake(ip) + ci := hostinfo.ConnectionState + + length := ipv4.HeaderLen + len(payload) + packet := make([]byte, length) + packet[0] = 0x45 + binary.BigEndian.PutUint16(packet[2:4], uint16(length)) + binary.BigEndian.PutUint32(packet[12:16], ip2int(c.f.inside.CidrNet().IP.To4())) + binary.BigEndian.PutUint32(packet[16:20], ip) + copy(packet[ipv4.HeaderLen:], payload) + + nb := make([]byte, 12) + out := make([]byte, mtu) + c.f.sendNoMetrics(message, t, ci, hostinfo, hostinfo.remote, packet, nb, out) +} diff --git a/handler.go b/handler.go index 369b4dd..eea4560 100644 --- a/handler.go +++ b/handler.go @@ -1,5 +1,12 @@ package nebula +func (f *Interface) newHook(w func([]byte) error) InsideHandler { + fn := func(hostInfo *HostInfo, ci *ConnectionState, addr *udpAddr, header *Header, out []byte, packet []byte, fwPacket *FirewallPacket, nb []byte) { + f.decryptTo(w, hostInfo, header.MessageCounter, out, packet, fwPacket, nb) + } + return f.encrypted(fn) +} + func (f *Interface) encrypted(h InsideHandler) InsideHandler { return func(hostInfo *HostInfo, ci *ConnectionState, addr *udpAddr, header *Header, out []byte, packet []byte, fwPacket *FirewallPacket, nb []byte) { if !f.handleEncrypted(ci, addr, header) { @@ -21,7 +28,7 @@ func (f *Interface) rxMetrics(h InsideHandler) InsideHandler { } func (f *Interface) handleMessagePacket(hostInfo *HostInfo, ci *ConnectionState, addr *udpAddr, header *Header, out []byte, packet []byte, fwPacket *FirewallPacket, nb []byte) { - f.decryptToTun(hostInfo, header.MessageCounter, out, packet, fwPacket, nb) + f.decryptTo(f.inside.WriteRaw, hostInfo, header.MessageCounter, out, packet, fwPacket, nb) } func (f *Interface) handleLighthousePacket(hostInfo *HostInfo, ci *ConnectionState, addr *udpAddr, header *Header, out []byte, packet []byte, fwPacket *FirewallPacket, nb []byte) { diff --git a/outside.go b/outside.go index 4ea1943..db09b73 100644 --- a/outside.go +++ b/outside.go @@ -175,7 +175,7 @@ func (f *Interface) decrypt(hostinfo *HostInfo, mc uint64, out []byte, packet [] return out, nil } -func (f *Interface) decryptToTun(hostinfo *HostInfo, messageCounter uint64, out []byte, packet []byte, fwPacket *FirewallPacket, nb []byte) { +func (f *Interface) decryptTo(write func([]byte) error, hostinfo *HostInfo, messageCounter uint64, out []byte, packet []byte, fwPacket *FirewallPacket, nb []byte) { var err error out, err = hostinfo.ConnectionState.dKey.DecryptDanger(out, packet[:HeaderLen], packet[HeaderLen:], messageCounter, nb) @@ -210,7 +210,7 @@ func (f *Interface) decryptToTun(hostinfo *HostInfo, messageCounter uint64, out } f.connectionManager.In(hostinfo.hostId) - err = f.inside.WriteRaw(out) + err = write(out) if err != nil { l.WithError(err).Error("Failed to write to tun") }