-
Notifications
You must be signed in to change notification settings - Fork 1
/
bgpwatch.go
178 lines (150 loc) · 3.83 KB
/
bgpwatch.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//watch will speak BGP and output adjustments to the BGP table, maybe output via streaming gRPC?
// RFC 4271 - BGP4
// Read 6793 to clarify - 4 byte ASN
// RFC 8092 - Large Communities
// RFC 2858 - MPBGP
package main
import (
"bytes"
"flag"
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
)
type bgpWatchServer struct {
listener net.Listener
peers []*peer
mutex sync.RWMutex
}
type config struct {
rid bgpid
port int
logfile string
eor bool
}
func main() {
srid := flag.String("rid", "0.0.0.1", "router id")
logs := flag.String("log", "", "log location, stdout if not given")
port := flag.Int("port", 179, "listen port")
weor := flag.Bool("endofrib", false, "log updates only when EoR received")
flag.Parse()
conf := getConfig(srid, logs, port, weor)
// Set up log file
if conf.logfile != "" {
f, err := os.OpenFile(conf.logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("failed to open logfile: %v", err)
}
defer f.Close()
log.SetOutput(f)
} else {
log.SetOutput(os.Stdout)
}
// Start server
serv := bgpWatchServer{
mutex: sync.RWMutex{},
}
serv.listen(conf)
go serv.clean()
serv.start(conf)
}
func getConfig(srid, logf *string, port *int, eor *bool) config {
rid, err := getRid(srid)
if err != nil {
log.Fatalf("Unable to convert %s to RID format: %v", *srid, err)
}
return config{
rid: rid,
port: *port,
logfile: *logf,
eor: *eor,
}
}
// Convert the string RID to actual RID.
func getRid(srid *string) (bgpid, error) {
s := strings.Split(*srid, ".")
var rid bgpid
if len(s) != 4 {
return rid, fmt.Errorf("RID too short")
}
for i := 0; i < 4; i++ {
num, err := strconv.ParseInt(s[i], 10, 16)
if err != nil {
return rid, err
}
rid[i] = byte(uint8(num))
}
return rid, nil
}
// Start listening
func (s *bgpWatchServer) listen(c config) {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", c.port))
if err != nil {
log.Fatalf("Unable to start server: %v", err)
}
s.listener = l
log.Printf("Listening on port %d\n", c.port)
}
// start will start the listener as well as start each peer worker.
func (s *bgpWatchServer) start(conf config) {
for {
conn, err := s.listener.Accept()
if err != nil {
log.Printf("%v\n", err)
} else {
peer := s.accept(conn, conf)
go peer.peerWorker()
}
}
}
// TODO: Make this work, and remove old clients
func (s *bgpWatchServer) clean() {
time.Sleep(5 * time.Second)
log.Printf("I have %d clients connected\n", len(s.peers))
}
// accept adds a new client to the current list of clients being served.
func (s *bgpWatchServer) accept(conn net.Conn, c config) *peer {
log.Printf("Connection from %v, total peers: %d\n",
conn.RemoteAddr().String(), len(s.peers)+1)
s.mutex.Lock()
defer s.mutex.Unlock()
ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
// If new client trying to connect with existing connection, remove old peer from pool
for _, peer := range s.peers {
if ip == peer.ip {
s.remove(peer)
break
}
}
// Each client will have a buffer with the maximum BGP message size. This is only
// 4k per client, so it's not a big deal.
peer := &peer{
conn: conn,
rid: c.rid,
weor: c.eor,
ip: ip,
out: bytes.NewBuffer(make([]byte, 4096)),
mutex: sync.RWMutex{},
startTime: time.Now(),
}
s.peers = append(s.peers, peer)
log.Printf("New peer added to list: %+v\n", s.peers)
return peer
}
// remove removes a client from the current list of clients being served.
func (s *bgpWatchServer) remove(p *peer) {
log.Printf("Removing dead peer %s\n", p.conn.RemoteAddr().String())
// remove the connection from client array
for i, check := range s.peers {
if check == p {
s.peers = append(s.peers[:i], s.peers[i+1:]...)
}
}
// Don't worry about errors as it's mostly because it's already closed.
p.conn.Close()
}