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