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  }