-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
136 lines (118 loc) · 3.3 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package main
import (
"net"
"os"
"os/signal"
"sync"
"syscall"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var flagRouteMapPath string
var flagListenAddr string
var flagOutIf string
var flagDstMAC string
var flagNumWorkers int
var flagMaxPayload int
var flagDebugEnabled bool
var rootCmd = &cobra.Command{
Use: "sflow-patcher",
Run: rootCmdHandler,
}
func init() {
rootCmd.PersistentFlags().StringVarP(&flagRouteMapPath, "route-map", "r", "", "path to the collector route map file")
rootCmd.MarkPersistentFlagRequired("route-map")
rootCmd.PersistentFlags().StringVarP(&flagOutIf, "out-if", "i", "", "outgoing interface")
rootCmd.MarkPersistentFlagRequired("out-if")
rootCmd.PersistentFlags().StringVarP(&flagDstMAC, "dst-mac", "m", "", "destination MAC address")
rootCmd.MarkPersistentFlagRequired("dst-mac")
rootCmd.PersistentFlags().StringVarP(&flagListenAddr, "bind", "b", "0.0.0.0:5000", "address and port to bind on")
rootCmd.PersistentFlags().IntVarP(&flagNumWorkers, "workers", "w", 10, "number of workers")
rootCmd.PersistentFlags().IntVarP(&flagMaxPayload, "buffer-size", "s", 1500, "input buffer size in bytes")
rootCmd.PersistentFlags().BoolVarP(&flagDebugEnabled, "debug", "d", false, "enable debug logging")
}
func runWorker(conn *net.UDPConn, writer *pcapWriter, wg *sync.WaitGroup) {
c := newCopier(flagMaxPayload)
for {
n, addr, err := conn.ReadFromUDP(c.src)
c.reset(n)
if err != nil {
if opErr, ok := err.(*net.OpError); ok {
// see https://github.com/golang/go/issues/4373
if opErr.Unwrap().Error() == "use of closed network connection" {
break
}
}
log.Error(err)
}
log.Debugf("Received %d bytes from %s", n, addr)
data := c.process()
if err := writer.write(addr, data); err != nil {
log.Error(err)
}
}
wg.Done()
}
// Reload route map on SIGHUP, stop UDP server on SIGINT/SIGTERM
func signalHandler(server net.PacketConn) {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
for {
sig := <-sigCh
switch sig {
case syscall.SIGHUP:
log.Info("SIGHUP received, reloading route map")
if err := routeMapReload(); err != nil {
log.Error(err)
}
case os.Interrupt, syscall.SIGTERM:
log.Info(sig)
server.Close()
return
}
}
}
func rootCmdHandler(_ *cobra.Command, _ []string) {
if flagDebugEnabled {
log.SetLevel(log.DebugLevel)
log.Debug("Debug logging enabled")
}
if err := routeMapReload(); err != nil {
log.Fatal(err)
}
// Create an outgoing interface handle for spoofing
dstMAC, err := net.ParseMAC(flagDstMAC)
if err != nil {
log.Fatal(err)
}
log.Infof("Setting %s as outgoing interface", flagOutIf)
writer, err := newPcapWriter(flagOutIf, dstMAC)
if err != nil {
log.Fatal(err)
}
defer writer.close()
// Set up UDP server
srcAddr, err := net.ResolveUDPAddr("udp4", flagListenAddr)
if err != nil {
log.Fatal(err)
}
log.Infof("Listening UDP on %s", srcAddr)
conn, err := net.ListenUDP("udp4", srcAddr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
go signalHandler(conn)
log.Infof("Starting %d workers", flagNumWorkers)
wg := &sync.WaitGroup{}
wg.Add(flagNumWorkers)
for i := 0; i < flagNumWorkers; i++ {
go runWorker(conn, writer, wg)
}
wg.Wait()
}
func main() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}