forked from DataDog/datadog-agent
/
server.go
147 lines (121 loc) · 3.48 KB
/
server.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
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2020-present Datadog, Inc.
package traps
import (
"net"
"time"
"github.com/StackVista/stackstate-agent/pkg/util/log"
"github.com/gosnmp/gosnmp"
)
// SnmpPacket is the type of packets yielded by server listeners.
type SnmpPacket struct {
Content *gosnmp.SnmpPacket
Addr *net.UDPAddr
}
// PacketsChannel is the type of channels of trap packets.
type PacketsChannel = chan *SnmpPacket
// TrapServer manages an SNMPv2 trap listener.
type TrapServer struct {
Addr string
config *Config
listener *gosnmp.TrapListener
packets PacketsChannel
}
var (
serverInstance *TrapServer
startError error
)
// StartServer starts the global trap server.
func StartServer() error {
server, err := NewTrapServer()
serverInstance = server
startError = err
return err
}
// StopServer stops the global trap server, if it is running.
func StopServer() {
if serverInstance != nil {
serverInstance.Stop()
serverInstance = nil
startError = nil
}
}
// IsRunning returns whether the trap server is currently running.
func IsRunning() bool {
return serverInstance != nil
}
// GetPacketsChannel returns a channel containing all received trap packets.
func GetPacketsChannel() PacketsChannel {
return serverInstance.packets
}
// NewTrapServer configures and returns a running SNMP traps server.
func NewTrapServer() (*TrapServer, error) {
config, err := ReadConfig()
if err != nil {
return nil, err
}
packets := make(PacketsChannel, packetsChanSize)
listener, err := startSNMPv2Listener(config, packets)
if err != nil {
return nil, err
}
server := &TrapServer{
listener: listener,
config: config,
packets: packets,
}
return server, nil
}
func startSNMPv2Listener(c *Config, packets PacketsChannel) (*gosnmp.TrapListener, error) {
listener := gosnmp.NewTrapListener()
listener.Params = c.BuildV2Params()
listener.OnNewTrap = func(p *gosnmp.SnmpPacket, u *net.UDPAddr) {
if err := validateCredentials(p, c); err != nil {
log.Warnf("Invalid credentials from %s on listener %s, dropping packet", u.String(), c.Addr())
trapsPacketsAuthErrors.Add(1)
return
}
log.Debugf("Packet received from %s on listener %s", u.String(), c.Addr())
trapsPackets.Add(1)
packets <- &SnmpPacket{Content: p, Addr: u}
}
errors := make(chan error, 1)
// Start actually listening in the background.
go func() {
log.Infof("Start listening for traps on %s", c.Addr())
err := listener.Listen(c.Addr())
if err != nil {
errors <- err
}
}()
select {
// Wait for listener to be started and listening to traps.
// See: https://godoc.org/github.com/gosnmp/gosnmp#TrapListener.Listening
case <-listener.Listening():
break
// If the listener failed to start (eg because it couldn't bind to a socket),
// we'll get an error here.
case err := <-errors:
close(errors)
return nil, err
}
return listener, nil
}
// Stop stops the TrapServer.
func (s *TrapServer) Stop() {
stopped := make(chan interface{})
go func() {
log.Infof("Stop listening on %s", s.config.Addr())
s.listener.Close()
close(stopped)
}()
select {
case <-stopped:
case <-time.After(time.Duration(s.config.StopTimeout) * time.Second):
log.Errorf("Stopping server. Timeout after %d seconds", s.config.StopTimeout)
}
// Let consumers know that we will not be sending any more packets.
close(s.packets)
}