forked from boogie-byte/sflow-patcher
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
150 lines (131 loc) · 3.71 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"net"
"os"
"os/signal"
"sync"
"syscall"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var flagVlanPath string
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(&flagVlanPath, "vlan-map", "v", "", "path to the vlan map file")
rootCmd.MarkPersistentFlagRequired("vlan-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 len(data) != 0 {
if err := writer.write(addr, data); err != nil {
log.Error(err)
}
} else {
log.Debugf("drop packet with wrong sflow data lenght")
}
}
wg.Done()
}
// Reload route & vlan maps 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 & vlan maps")
if err := routeMapReload(); err != nil {
log.Error(err)
}
if err := vlanMapReload(); 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)
}
if err := vlanMapReload(); 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)
}
}