router7/cmd/captured/captured.go

161 lines
3.6 KiB
Go
Raw Normal View History

2018-06-28 13:39:48 +02:00
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2018-06-25 20:12:51 +02:00
// Binary captured streams network packets to wireshark via SSH, replaying
// buffered packets upon connection for retroactive debugging.
package main
import (
"container/ring"
"context"
"flag"
"fmt"
"log"
2018-06-27 19:44:39 +02:00
"os"
"os/signal"
"strings"
"sync"
2018-06-27 19:44:39 +02:00
"syscall"
2018-07-09 08:54:04 +02:00
"github.com/rtr7/router7/internal/multilisten"
2018-06-27 19:44:39 +02:00
"github.com/gokrazy/gokrazy"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcapgo"
_ "net/http/pprof"
)
var (
perm = flag.String("perm", "/perm", "path to replace /perm")
hostKeyPath = flag.String("host_key",
"/perm/breakglass.host_key",
"path to a PEM-encoded RSA, DSA or ECDSA private key (create using e.g. ssh-keygen -f /perm/breakglass.host_key -N '' -t rsa)")
)
func capturePackets(ctx context.Context) (chan gopacket.Packet, error) {
packets := make(chan gopacket.Packet)
for _, ifname := range []string{"uplink0", "lan0"} {
handle, err := pcapgo.NewEthernetHandle(ifname)
if err != nil {
return nil, fmt.Errorf("pcapgo.NewEthernetHandle(%v): %v", ifname, err)
}
if err := handle.SetBPF(instructions); err != nil {
return nil, fmt.Errorf("SetBPF: %v", err)
}
pkgsrc := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
go func() {
defer handle.Close()
for {
packet, err := pkgsrc.NextPacket()
if err != nil {
log.Printf("NextPacket: %v", err)
return
}
select {
case packets <- packet:
case <-ctx.Done():
return
}
}
}()
}
return packets, nil
}
type packetRingBuffer struct {
sync.Mutex
r *ring.Ring
}
func newPacketRingBuffer(size int) *packetRingBuffer {
return &packetRingBuffer{
r: ring.New(size),
}
}
func (prb *packetRingBuffer) writePacket(p gopacket.Packet) {
prb.Lock()
defer prb.Unlock()
prb.r.Value = p
prb.r = prb.r.Next()
}
func (prb *packetRingBuffer) packetsLocked() []gopacket.Packet {
packets := make([]gopacket.Packet, 0, prb.r.Len())
prb.r.Do(func(x interface{}) {
if x != nil {
packets = append(packets, x.(gopacket.Packet))
}
})
return packets
}
2018-06-27 19:44:39 +02:00
var sshListeners = multilisten.NewPool()
func updateListeners(srv *server) error {
hosts, err := gokrazy.PrivateInterfaceAddrs()
if err != nil {
return err
}
sshListeners.ListenAndServe(hosts, func(host string) multilisten.Listener {
return srv.listenerFor(host)
})
return nil
}
func logic() error {
prb := newPacketRingBuffer(50000)
2018-06-27 19:44:39 +02:00
srv, err := newServer(prb)
if err != nil {
return err
}
if err := updateListeners(srv); err != nil {
return err
}
2018-06-27 19:44:39 +02:00
go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGUSR1)
for range ch {
if err := updateListeners(srv); err != nil {
log.Printf("updateListeners: %v", err)
}
}
2018-06-27 19:44:39 +02:00
}()
2018-06-27 19:44:39 +02:00
packets, err := capturePackets(context.Background())
if err != nil {
return err
}
for packet := range packets {
prb.writePacket(packet)
}
return nil
}
func main() {
flag.Parse()
if *hostKeyPath == "/perm/breakglass.host_key" && *perm != "/perm" {
*hostKeyPath = strings.Replace(*hostKeyPath, "/perm", *perm, 1)
}
if err := logic(); err != nil {
log.Fatal(err)
}
}