github.com/osrg/gobgp@v2.0.0+incompatible/cmd/gobgp/mrt.go (about) 1 // Copyright (C) 2015 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 main 17 18 import ( 19 "fmt" 20 "io" 21 "os" 22 "strconv" 23 "time" 24 25 "github.com/spf13/cobra" 26 27 api "github.com/osrg/gobgp/api" 28 "github.com/osrg/gobgp/internal/pkg/apiutil" 29 "github.com/osrg/gobgp/pkg/packet/bgp" 30 "github.com/osrg/gobgp/pkg/packet/mrt" 31 ) 32 33 func injectMrt() error { 34 35 file, err := os.Open(mrtOpts.Filename) 36 if err != nil { 37 return fmt.Errorf("failed to open file: %s", err) 38 } 39 40 if mrtOpts.NextHop != nil && !mrtOpts.SkipV4 && !mrtOpts.SkipV6 { 41 fmt.Println("You should probably specify either --no-ipv4 or --no-ipv6 when overwriting nexthop, unless your dump contains only one type of routes") 42 } 43 44 var idx int64 45 if mrtOpts.QueueSize < 1 { 46 return fmt.Errorf("Specified queue size is smaller than 1, refusing to run with unbounded memory usage") 47 } 48 49 ch := make(chan []*api.Path, mrtOpts.QueueSize) 50 go func() { 51 52 var peers []*mrt.Peer 53 for { 54 buf := make([]byte, mrt.MRT_COMMON_HEADER_LEN) 55 _, err := file.Read(buf) 56 if err == io.EOF { 57 break 58 } else if err != nil { 59 exitWithError(fmt.Errorf("failed to read: %s", err)) 60 } 61 62 h := &mrt.MRTHeader{} 63 err = h.DecodeFromBytes(buf) 64 if err != nil { 65 exitWithError(fmt.Errorf("failed to parse")) 66 } 67 68 buf = make([]byte, h.Len) 69 _, err = file.Read(buf) 70 if err != nil { 71 exitWithError(fmt.Errorf("failed to read")) 72 } 73 74 msg, err := mrt.ParseMRTBody(h, buf) 75 if err != nil { 76 printError(fmt.Errorf("failed to parse: %s", err)) 77 continue 78 } 79 80 if globalOpts.Debug { 81 fmt.Println(msg) 82 } 83 84 if msg.Header.Type == mrt.TABLE_DUMPv2 { 85 subType := mrt.MRTSubTypeTableDumpv2(msg.Header.SubType) 86 switch subType { 87 case mrt.PEER_INDEX_TABLE: 88 peers = msg.Body.(*mrt.PeerIndexTable).Peers 89 continue 90 case mrt.RIB_IPV4_UNICAST, mrt.RIB_IPV4_UNICAST_ADDPATH: 91 if mrtOpts.SkipV4 { 92 continue 93 } 94 case mrt.RIB_IPV6_UNICAST, mrt.RIB_IPV6_UNICAST_ADDPATH: 95 if mrtOpts.SkipV6 { 96 continue 97 } 98 case mrt.GEO_PEER_TABLE: 99 fmt.Printf("WARNING: Skipping GEO_PEER_TABLE: %s", msg.Body.(*mrt.GeoPeerTable)) 100 default: 101 exitWithError(fmt.Errorf("unsupported subType: %v", subType)) 102 } 103 104 if peers == nil { 105 exitWithError(fmt.Errorf("not found PEER_INDEX_TABLE")) 106 } 107 108 rib := msg.Body.(*mrt.Rib) 109 nlri := rib.Prefix 110 111 paths := make([]*api.Path, 0, len(rib.Entries)) 112 113 for _, e := range rib.Entries { 114 if len(peers) < int(e.PeerIndex) { 115 exitWithError(fmt.Errorf("invalid peer index: %d (PEER_INDEX_TABLE has only %d peers)\n", e.PeerIndex, len(peers))) 116 } 117 //t := time.Unix(int64(e.OriginatedTime), 0) 118 119 var attrs []bgp.PathAttributeInterface 120 switch subType { 121 case mrt.RIB_IPV4_UNICAST, mrt.RIB_IPV4_UNICAST_ADDPATH: 122 if mrtOpts.NextHop != nil { 123 for i, attr := range e.PathAttributes { 124 if attr.GetType() != bgp.BGP_ATTR_TYPE_NEXT_HOP { 125 e.PathAttributes[i] = bgp.NewPathAttributeNextHop(mrtOpts.NextHop.String()) 126 break 127 } 128 } 129 } 130 attrs = e.PathAttributes 131 default: 132 attrs = make([]bgp.PathAttributeInterface, 0, len(e.PathAttributes)) 133 for _, attr := range e.PathAttributes { 134 if attr.GetType() != bgp.BGP_ATTR_TYPE_MP_REACH_NLRI { 135 attrs = append(attrs, attr) 136 } else { 137 a := attr.(*bgp.PathAttributeMpReachNLRI) 138 nexthop := a.Nexthop.String() 139 if mrtOpts.NextHop != nil { 140 nexthop = mrtOpts.NextHop.String() 141 } 142 attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri})) 143 } 144 } 145 } 146 147 path := apiutil.NewPath(nlri, false, attrs, time.Unix(int64(e.OriginatedTime), 0)) 148 path.SourceAsn = peers[e.PeerIndex].AS 149 path.SourceId = peers[e.PeerIndex].BgpId.String() 150 151 // TODO: compare here if mrtOpts.Best is enabled 152 paths = append(paths, path) 153 } 154 155 // TODO: calculate properly if necessary. 156 if mrtOpts.Best { 157 paths = []*api.Path{paths[0]} 158 } 159 160 if idx >= mrtOpts.RecordSkip { 161 ch <- paths 162 } 163 164 idx += 1 165 if idx == mrtOpts.RecordCount+mrtOpts.RecordSkip { 166 break 167 } 168 } 169 } 170 171 close(ch) 172 }() 173 174 stream, err := client.AddPathStream(ctx) 175 if err != nil { 176 return fmt.Errorf("failed to add path: %s", err) 177 } 178 179 for paths := range ch { 180 err = stream.Send(&api.AddPathStreamRequest{ 181 TableType: api.TableType_GLOBAL, 182 Paths: paths, 183 }) 184 if err != nil { 185 return fmt.Errorf("failed to send: %s", err) 186 } 187 } 188 return nil 189 } 190 191 func newMrtCmd() *cobra.Command { 192 globalInjectCmd := &cobra.Command{ 193 Use: cmdGlobal, 194 Run: func(cmd *cobra.Command, args []string) { 195 if len(args) < 1 { 196 exitWithError(fmt.Errorf("usage: gobgp mrt inject global <filename> [<count> [<skip>]]")) 197 } 198 mrtOpts.Filename = args[0] 199 if len(args) > 1 { 200 var err error 201 mrtOpts.RecordCount, err = strconv.ParseInt(args[1], 10, 64) 202 if err != nil { 203 exitWithError(fmt.Errorf("invalid count value: %s", args[1])) 204 } 205 if len(args) > 2 { 206 mrtOpts.RecordSkip, err = strconv.ParseInt(args[2], 10, 64) 207 if err != nil { 208 exitWithError(fmt.Errorf("invalid skip value: %s", args[2])) 209 } 210 } 211 } else { 212 mrtOpts.RecordCount = -1 213 mrtOpts.RecordSkip = 0 214 } 215 err := injectMrt() 216 if err != nil { 217 exitWithError(err) 218 } 219 }, 220 } 221 222 injectCmd := &cobra.Command{ 223 Use: cmdInject, 224 } 225 injectCmd.AddCommand(globalInjectCmd) 226 227 mrtCmd := &cobra.Command{ 228 Use: cmdMRT, 229 } 230 mrtCmd.AddCommand(injectCmd) 231 232 mrtCmd.PersistentFlags().BoolVarP(&mrtOpts.Best, "only-best", "", false, "inject only best paths") 233 mrtCmd.PersistentFlags().BoolVarP(&mrtOpts.SkipV4, "no-ipv4", "", false, "Do not import IPv4 routes") 234 mrtCmd.PersistentFlags().BoolVarP(&mrtOpts.SkipV6, "no-ipv6", "", false, "Do not import IPv6 routes") 235 mrtCmd.PersistentFlags().IntVarP(&mrtOpts.QueueSize, "queue-size", "", 1<<10, "Maximum number of updates to keep queued") 236 mrtCmd.PersistentFlags().IPVarP(&mrtOpts.NextHop, "nexthop", "", nil, "Overwrite nexthop") 237 return mrtCmd 238 }