github.com/osrg/gobgp@v2.0.0+incompatible/pkg/server/mrt.go (about) 1 // Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package server 17 18 import ( 19 "bytes" 20 "fmt" 21 "os" 22 "time" 23 24 "github.com/osrg/gobgp/internal/pkg/config" 25 "github.com/osrg/gobgp/internal/pkg/table" 26 "github.com/osrg/gobgp/pkg/packet/bgp" 27 "github.com/osrg/gobgp/pkg/packet/mrt" 28 29 log "github.com/sirupsen/logrus" 30 ) 31 32 const ( 33 minRotationInterval = 60 34 minDumpInterval = 60 35 ) 36 37 type mrtWriter struct { 38 dead chan struct{} 39 s *BgpServer 40 c *config.MrtConfig 41 file *os.File 42 rotationInterval uint64 43 dumpInterval uint64 44 } 45 46 func (m *mrtWriter) Stop() { 47 close(m.dead) 48 } 49 50 func (m *mrtWriter) loop() error { 51 ops := []watchOption{} 52 switch m.c.DumpType { 53 case config.MRT_TYPE_UPDATES: 54 ops = append(ops, watchUpdate(false)) 55 case config.MRT_TYPE_TABLE: 56 if len(m.c.TableName) > 0 { 57 ops = append(ops, watchTableName(m.c.TableName)) 58 } 59 } 60 w := m.s.watch(ops...) 61 rotator := func() *time.Ticker { 62 if m.rotationInterval == 0 { 63 return &time.Ticker{} 64 } 65 return time.NewTicker(time.Second * time.Duration(m.rotationInterval)) 66 }() 67 dump := func() *time.Ticker { 68 if m.dumpInterval == 0 { 69 return &time.Ticker{} 70 } 71 return time.NewTicker(time.Second * time.Duration(m.dumpInterval)) 72 }() 73 74 defer func() { 75 if m.file != nil { 76 m.file.Close() 77 } 78 if m.rotationInterval != 0 { 79 rotator.Stop() 80 } 81 if m.dumpInterval != 0 { 82 dump.Stop() 83 } 84 w.Stop() 85 }() 86 87 for { 88 serialize := func(ev watchEvent) []*mrt.MRTMessage { 89 msg := make([]*mrt.MRTMessage, 0, 1) 90 switch e := ev.(type) { 91 case *watchEventUpdate: 92 if e.Init { 93 return nil 94 } 95 mp := mrt.NewBGP4MPMessage(e.PeerAS, e.LocalAS, 0, e.PeerAddress.String(), e.LocalAddress.String(), e.FourBytesAs, nil) 96 mp.BGPMessagePayload = e.Payload 97 isAddPath := e.Neighbor.IsAddPathReceiveEnabled(e.PathList[0].GetRouteFamily()) 98 subtype := mrt.MESSAGE 99 switch { 100 case isAddPath && e.FourBytesAs: 101 subtype = mrt.MESSAGE_AS4_ADDPATH 102 case isAddPath: 103 subtype = mrt.MESSAGE_ADDPATH 104 case e.FourBytesAs: 105 subtype = mrt.MESSAGE_AS4 106 } 107 if bm, err := mrt.NewMRTMessage(uint32(e.Timestamp.Unix()), mrt.BGP4MP, subtype, mp); err != nil { 108 log.WithFields(log.Fields{ 109 "Topic": "mrt", 110 "Data": e, 111 "Error": err, 112 }).Warnf("Failed to create MRT BGP4MP message (subtype %d)", subtype) 113 } else { 114 msg = append(msg, bm) 115 } 116 case *watchEventTable: 117 t := uint32(time.Now().Unix()) 118 119 peers := make([]*mrt.Peer, 1, len(e.Neighbor)+1) 120 // Adding dummy Peer record for locally generated routes 121 peers[0] = mrt.NewPeer("0.0.0.0", "0.0.0.0", 0, true) 122 neighborMap := make(map[string]*config.Neighbor) 123 for _, pconf := range e.Neighbor { 124 peers = append(peers, mrt.NewPeer(pconf.State.RemoteRouterId, pconf.State.NeighborAddress, pconf.Config.PeerAs, true)) 125 neighborMap[pconf.State.NeighborAddress] = pconf 126 } 127 128 if bm, err := mrt.NewMRTMessage(t, mrt.TABLE_DUMPv2, mrt.PEER_INDEX_TABLE, mrt.NewPeerIndexTable(e.RouterID, "", peers)); err != nil { 129 log.WithFields(log.Fields{ 130 "Topic": "mrt", 131 "Data": e, 132 "Error": err, 133 }).Warnf("Failed to create MRT TABLE_DUMPv2 message (subtype %d)", mrt.PEER_INDEX_TABLE) 134 break 135 } else { 136 msg = append(msg, bm) 137 } 138 139 idx := func(p *table.Path) uint16 { 140 for i, peer := range peers { 141 if peer.IpAddress.String() == p.GetSource().Address.String() { 142 return uint16(i) 143 } 144 } 145 return uint16(len(peers)) 146 } 147 148 subtype := func(p *table.Path, isAddPath bool) mrt.MRTSubTypeTableDumpv2 { 149 t := mrt.RIB_GENERIC 150 switch p.GetRouteFamily() { 151 case bgp.RF_IPv4_UC: 152 t = mrt.RIB_IPV4_UNICAST 153 case bgp.RF_IPv4_MC: 154 t = mrt.RIB_IPV4_MULTICAST 155 case bgp.RF_IPv6_UC: 156 t = mrt.RIB_IPV6_UNICAST 157 case bgp.RF_IPv6_MC: 158 t = mrt.RIB_IPV6_MULTICAST 159 } 160 if isAddPath { 161 // Shift non-additional-path version to *_ADDPATH 162 t += 6 163 } 164 return t 165 } 166 167 seq := uint32(0) 168 appendTableDumpMsg := func(path *table.Path, entries []*mrt.RibEntry, isAddPath bool) { 169 st := subtype(path, isAddPath) 170 if bm, err := mrt.NewMRTMessage(t, mrt.TABLE_DUMPv2, st, mrt.NewRib(seq, path.GetNlri(), entries)); err != nil { 171 log.WithFields(log.Fields{ 172 "Topic": "mrt", 173 "Data": e, 174 "Error": err, 175 }).Warnf("Failed to create MRT TABLE_DUMPv2 message (subtype %d)", st) 176 } else { 177 msg = append(msg, bm) 178 seq++ 179 } 180 } 181 for _, pathList := range e.PathList { 182 entries := make([]*mrt.RibEntry, 0, len(pathList)) 183 entriesAddPath := make([]*mrt.RibEntry, 0, len(pathList)) 184 for _, path := range pathList { 185 isAddPath := false 186 if path.IsLocal() { 187 isAddPath = true 188 } else if neighbor, ok := neighborMap[path.GetSource().Address.String()]; ok { 189 isAddPath = neighbor.IsAddPathReceiveEnabled(path.GetRouteFamily()) 190 } 191 if !isAddPath { 192 entries = append(entries, mrt.NewRibEntry(idx(path), uint32(path.GetTimestamp().Unix()), 0, path.GetPathAttrs(), false)) 193 } else { 194 entriesAddPath = append(entriesAddPath, mrt.NewRibEntry(idx(path), uint32(path.GetTimestamp().Unix()), path.GetNlri().PathIdentifier(), path.GetPathAttrs(), true)) 195 } 196 } 197 if len(entries) > 0 { 198 appendTableDumpMsg(pathList[0], entries, false) 199 } 200 if len(entriesAddPath) > 0 { 201 appendTableDumpMsg(pathList[0], entriesAddPath, true) 202 } 203 } 204 } 205 return msg 206 } 207 208 drain := func(ev watchEvent) { 209 events := make([]watchEvent, 0, 1+len(w.Event())) 210 if ev != nil { 211 events = append(events, ev) 212 } 213 214 for len(w.Event()) > 0 { 215 events = append(events, <-w.Event()) 216 } 217 218 w := func(buf []byte) { 219 if _, err := m.file.Write(buf); err == nil { 220 m.file.Sync() 221 } else { 222 log.WithFields(log.Fields{ 223 "Topic": "mrt", 224 "Error": err, 225 }).Warn("Can't write to destination MRT file") 226 } 227 } 228 229 var b bytes.Buffer 230 for _, e := range events { 231 for _, m := range serialize(e) { 232 if buf, err := m.Serialize(); err != nil { 233 log.WithFields(log.Fields{ 234 "Topic": "mrt", 235 "Data": e, 236 "Error": err, 237 }).Warn("Failed to serialize event") 238 } else { 239 b.Write(buf) 240 if b.Len() > 1*1000*1000 { 241 w(b.Bytes()) 242 b.Reset() 243 } 244 } 245 } 246 } 247 if b.Len() > 0 { 248 w(b.Bytes()) 249 } 250 } 251 rotate := func() { 252 m.file.Close() 253 file, err := mrtFileOpen(m.c.FileName, m.rotationInterval) 254 if err == nil { 255 m.file = file 256 } else { 257 log.WithFields(log.Fields{ 258 "Topic": "mrt", 259 "Error": err, 260 }).Warn("can't rotate MRT file") 261 } 262 } 263 264 select { 265 case <-m.dead: 266 drain(nil) 267 return nil 268 case e := <-w.Event(): 269 drain(e) 270 if m.c.DumpType == config.MRT_TYPE_TABLE && m.rotationInterval != 0 { 271 rotate() 272 } 273 case <-rotator.C: 274 if m.c.DumpType == config.MRT_TYPE_UPDATES { 275 rotate() 276 } else { 277 w.Generate(watchEventTypeTable) 278 } 279 case <-dump.C: 280 w.Generate(watchEventTypeTable) 281 } 282 } 283 } 284 285 func mrtFileOpen(filename string, rInterval uint64) (*os.File, error) { 286 realname := filename 287 if rInterval != 0 { 288 realname = time.Now().Format(filename) 289 } 290 log.WithFields(log.Fields{ 291 "Topic": "mrt", 292 "Filename": realname, 293 "RotationInterval": rInterval, 294 }).Debug("Setting new MRT destination file") 295 296 i := len(realname) 297 for i > 0 && os.IsPathSeparator(realname[i-1]) { 298 // skip trailing path separators 299 i-- 300 } 301 j := i 302 303 for j > 0 && !os.IsPathSeparator(realname[j-1]) { 304 j-- 305 } 306 307 if j > 0 { 308 if err := os.MkdirAll(realname[0:j-1], 0755); err != nil { 309 log.WithFields(log.Fields{ 310 "Topic": "mrt", 311 "Error": err, 312 }).Warn("can't create MRT destination directory") 313 return nil, err 314 } 315 } 316 317 file, err := os.OpenFile(realname, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) 318 if err != nil { 319 log.WithFields(log.Fields{ 320 "Topic": "mrt", 321 "Error": err, 322 }).Warn("can't create MRT destination file") 323 } 324 return file, err 325 } 326 327 func newMrtWriter(s *BgpServer, c *config.MrtConfig, rInterval, dInterval uint64) (*mrtWriter, error) { 328 file, err := mrtFileOpen(c.FileName, rInterval) 329 if err != nil { 330 return nil, err 331 } 332 m := mrtWriter{ 333 s: s, 334 c: c, 335 file: file, 336 rotationInterval: rInterval, 337 dumpInterval: dInterval, 338 } 339 go m.loop() 340 return &m, nil 341 } 342 343 type mrtManager struct { 344 bgpServer *BgpServer 345 writer map[string]*mrtWriter 346 } 347 348 func (m *mrtManager) enable(c *config.MrtConfig) error { 349 if _, ok := m.writer[c.FileName]; ok { 350 return fmt.Errorf("%s already exists", c.FileName) 351 } 352 353 rInterval := c.RotationInterval 354 dInterval := c.DumpInterval 355 356 setRotationMin := func() { 357 if rInterval < minRotationInterval { 358 log.WithFields(log.Fields{ 359 "Topic": "MRT", 360 }).Infof("minimum mrt rotation interval is %d seconds", minRotationInterval) 361 rInterval = minRotationInterval 362 } 363 } 364 365 if c.DumpType == config.MRT_TYPE_TABLE { 366 if rInterval == 0 { 367 if dInterval < minDumpInterval { 368 log.WithFields(log.Fields{ 369 "Topic": "MRT", 370 }).Infof("minimum mrt dump interval is %d seconds", minDumpInterval) 371 dInterval = minDumpInterval 372 } 373 } else if dInterval == 0 { 374 setRotationMin() 375 } else { 376 return fmt.Errorf("can't specify both intervals in the table dump type") 377 } 378 } else if c.DumpType == config.MRT_TYPE_UPDATES { 379 // ignore the dump interval 380 dInterval = 0 381 if len(c.TableName) > 0 { 382 return fmt.Errorf("can't specify the table name with the update dump type") 383 } 384 setRotationMin() 385 } 386 387 w, err := newMrtWriter(m.bgpServer, c, rInterval, dInterval) 388 if err == nil { 389 m.writer[c.FileName] = w 390 } 391 return err 392 } 393 394 func (m *mrtManager) disable(c *config.MrtConfig) error { 395 w, ok := m.writer[c.FileName] 396 if !ok { 397 return fmt.Errorf("%s doesn't exists", c.FileName) 398 } 399 w.Stop() 400 delete(m.writer, c.FileName) 401 return nil 402 } 403 404 func newMrtManager(s *BgpServer) *mrtManager { 405 return &mrtManager{ 406 bgpServer: s, 407 writer: make(map[string]*mrtWriter), 408 } 409 }