github.com/osrg/gobgp@v2.0.0+incompatible/cmd/gobgp/common.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  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net"
    24  	"os"
    25  	"strconv"
    26  	"time"
    27  
    28  	"google.golang.org/grpc"
    29  	"google.golang.org/grpc/credentials"
    30  
    31  	api "github.com/osrg/gobgp/api"
    32  	"github.com/osrg/gobgp/pkg/packet/bgp"
    33  )
    34  
    35  const globalRIBName = "global"
    36  
    37  const (
    38  	cmdGlobal         = "global"
    39  	cmdNeighbor       = "neighbor"
    40  	cmdPolicy         = "policy"
    41  	cmdRib            = "rib"
    42  	cmdAdd            = "add"
    43  	cmdDel            = "del"
    44  	cmdAll            = "all"
    45  	cmdSet            = "set"
    46  	cmdLocal          = "local"
    47  	cmdAdjIn          = "adj-in"
    48  	cmdAdjOut         = "adj-out"
    49  	cmdReset          = "reset"
    50  	cmdSoftReset      = "softreset"
    51  	cmdSoftResetIn    = "softresetin"
    52  	cmdSoftResetOut   = "softresetout"
    53  	cmdShutdown       = "shutdown"
    54  	cmdEnable         = "enable"
    55  	cmdDisable        = "disable"
    56  	cmdPrefix         = "prefix"
    57  	cmdAspath         = "as-path"
    58  	cmdCommunity      = "community"
    59  	cmdExtcommunity   = "ext-community"
    60  	cmdImport         = "import"
    61  	cmdExport         = "export"
    62  	cmdIn             = "in"
    63  	cmdMonitor        = "monitor"
    64  	cmdMRT            = "mrt"
    65  	cmdInject         = "inject"
    66  	cmdRPKI           = "rpki"
    67  	cmdRPKITable      = "table"
    68  	cmdRPKIServer     = "server"
    69  	cmdVRF            = "vrf"
    70  	cmdAccepted       = "accepted"
    71  	cmdRejected       = "rejected"
    72  	cmdStatement      = "statement"
    73  	cmdCondition      = "condition"
    74  	cmdAction         = "action"
    75  	cmdUpdate         = "update"
    76  	cmdBMP            = "bmp"
    77  	cmdLargecommunity = "large-community"
    78  	cmdSummary        = "summary"
    79  )
    80  
    81  const (
    82  	paramFlag = iota
    83  	paramSingle
    84  	paramList
    85  )
    86  
    87  var subOpts struct {
    88  	AddressFamily string `short:"a" long:"address-family" description:"specifying an address family"`
    89  }
    90  
    91  var neighborsOpts struct {
    92  	Reason    string `short:"r" long:"reason" description:"specifying communication field on Cease NOTIFICATION message with Administrative Shutdown subcode"`
    93  	Transport string `short:"t" long:"transport" description:"specifying a transport protocol"`
    94  }
    95  
    96  var mrtOpts struct {
    97  	Filename    string `long:"filename" description:"MRT file name"`
    98  	RecordCount int64  `long:"count" description:"Number of records to inject"`
    99  	RecordSkip  int64  `long:"skip" description:"Number of records to skip before injecting"`
   100  	QueueSize   int    `long:"batch-size" description:"Maximum number of updates to keep queued"`
   101  	Best        bool   `long:"only-best" description:"only keep best path routes"`
   102  	SkipV4      bool   `long:"no-ipv4" description:"Skip importing IPv4 routes"`
   103  	SkipV6      bool   `long:"no-ipv4" description:"Skip importing IPv6 routes"`
   104  	NextHop     net.IP `long:"nexthop" description:"Rewrite nexthop"`
   105  }
   106  
   107  var bmpOpts struct {
   108  	StatisticsTimeout int `short:"s" long:"statistics-timeout" description:"Interval for Statistics Report"`
   109  }
   110  
   111  func formatTimedelta(t time.Time) string {
   112  	d := time.Now().Unix() - t.Unix()
   113  	u := uint64(d)
   114  	neg := d < 0
   115  	if neg {
   116  		u = -u
   117  	}
   118  	secs := u % 60
   119  	u /= 60
   120  	mins := u % 60
   121  	u /= 60
   122  	hours := u % 24
   123  	days := u / 24
   124  
   125  	if days == 0 {
   126  		return fmt.Sprintf("%02d:%02d:%02d", hours, mins, secs)
   127  	}
   128  	return fmt.Sprintf("%dd ", days) + fmt.Sprintf("%02d:%02d:%02d", hours, mins, secs)
   129  }
   130  
   131  func cidr2prefix(cidr string) string {
   132  	_, n, err := net.ParseCIDR(cidr)
   133  	if err != nil {
   134  		return cidr
   135  	}
   136  	var buffer bytes.Buffer
   137  	for i := 0; i < len(n.IP); i++ {
   138  		buffer.WriteString(fmt.Sprintf("%08b", n.IP[i]))
   139  	}
   140  	ones, _ := n.Mask.Size()
   141  	return buffer.String()[:ones]
   142  }
   143  
   144  func extractReserved(args []string, keys map[string]int) (map[string][]string, error) {
   145  	m := make(map[string][]string, len(keys))
   146  	var k string
   147  	isReserved := func(s string) bool {
   148  		for r := range keys {
   149  			if s == r {
   150  				return true
   151  			}
   152  		}
   153  		return false
   154  	}
   155  	for _, arg := range args {
   156  		if isReserved(arg) {
   157  			k = arg
   158  			m[k] = make([]string, 0, 1)
   159  		} else {
   160  			m[k] = append(m[k], arg)
   161  		}
   162  	}
   163  	for k, v := range m {
   164  		if k == "" {
   165  			continue
   166  		}
   167  		switch keys[k] {
   168  		case paramFlag:
   169  			if len(v) != 0 {
   170  				return nil, fmt.Errorf("%s should not have arguments", k)
   171  			}
   172  		case paramSingle:
   173  			if len(v) != 1 {
   174  				return nil, fmt.Errorf("%s should have one argument", k)
   175  			}
   176  		case paramList:
   177  			if len(v) == 0 {
   178  				return nil, fmt.Errorf("%s should have one or more arguments", k)
   179  			}
   180  		}
   181  	}
   182  	return m, nil
   183  }
   184  
   185  func newClient(ctx context.Context) (api.GobgpApiClient, context.CancelFunc, error) {
   186  	grpcOpts := []grpc.DialOption{grpc.WithBlock()}
   187  	if globalOpts.TLS {
   188  		var creds credentials.TransportCredentials
   189  		if globalOpts.CaFile == "" {
   190  			creds = credentials.NewClientTLSFromCert(nil, "")
   191  		} else {
   192  			var err error
   193  			creds, err = credentials.NewClientTLSFromFile(globalOpts.CaFile, "")
   194  			if err != nil {
   195  				exitWithError(err)
   196  			}
   197  		}
   198  		grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(creds))
   199  	} else {
   200  		grpcOpts = append(grpcOpts, grpc.WithInsecure())
   201  	}
   202  
   203  	target := net.JoinHostPort(globalOpts.Host, strconv.Itoa(globalOpts.Port))
   204  	if target == "" {
   205  		target = ":50051"
   206  	}
   207  	cc, cancel := context.WithTimeout(ctx, time.Second)
   208  	conn, err := grpc.DialContext(cc, target, grpcOpts...)
   209  	if err != nil {
   210  		return nil, cancel, err
   211  	}
   212  	return api.NewGobgpApiClient(conn), cancel, nil
   213  }
   214  
   215  func addr2AddressFamily(a net.IP) *api.Family {
   216  	if a.To4() != nil {
   217  		return &api.Family{
   218  			Afi:  api.Family_AFI_IP,
   219  			Safi: api.Family_SAFI_UNICAST,
   220  		}
   221  	} else if a.To16() != nil {
   222  		return &api.Family{
   223  			Afi:  api.Family_AFI_IP6,
   224  			Safi: api.Family_SAFI_UNICAST,
   225  		}
   226  	}
   227  	return nil
   228  }
   229  
   230  var (
   231  	ipv4UC = &api.Family{
   232  		Afi:  api.Family_AFI_IP,
   233  		Safi: api.Family_SAFI_UNICAST,
   234  	}
   235  	ipv6UC = &api.Family{
   236  		Afi:  api.Family_AFI_IP6,
   237  		Safi: api.Family_SAFI_UNICAST,
   238  	}
   239  	ipv4VPN = &api.Family{
   240  		Afi:  api.Family_AFI_IP,
   241  		Safi: api.Family_SAFI_MPLS_VPN,
   242  	}
   243  	ipv6VPN = &api.Family{
   244  		Afi:  api.Family_AFI_IP6,
   245  		Safi: api.Family_SAFI_MPLS_VPN,
   246  	}
   247  	ipv4MPLS = &api.Family{
   248  		Afi:  api.Family_AFI_IP,
   249  		Safi: api.Family_SAFI_MPLS_LABEL,
   250  	}
   251  	ipv6MPLS = &api.Family{
   252  		Afi:  api.Family_AFI_IP6,
   253  		Safi: api.Family_SAFI_MPLS_LABEL,
   254  	}
   255  	evpn = &api.Family{
   256  		Afi:  api.Family_AFI_L2VPN,
   257  		Safi: api.Family_SAFI_EVPN,
   258  	}
   259  	ipv4Encap = &api.Family{
   260  		Afi:  api.Family_AFI_IP,
   261  		Safi: api.Family_SAFI_ENCAPSULATION,
   262  	}
   263  	ipv6Encap = &api.Family{
   264  		Afi:  api.Family_AFI_IP6,
   265  		Safi: api.Family_SAFI_ENCAPSULATION,
   266  	}
   267  	rtc = &api.Family{
   268  		Afi:  api.Family_AFI_IP,
   269  		Safi: api.Family_SAFI_ROUTE_TARGET_CONSTRAINTS,
   270  	}
   271  	ipv4Flowspec = &api.Family{
   272  		Afi:  api.Family_AFI_IP,
   273  		Safi: api.Family_SAFI_FLOW_SPEC_UNICAST,
   274  	}
   275  	ipv6Flowspec = &api.Family{
   276  		Afi:  api.Family_AFI_IP6,
   277  		Safi: api.Family_SAFI_FLOW_SPEC_UNICAST,
   278  	}
   279  	ipv4VPNflowspec = &api.Family{
   280  		Afi:  api.Family_AFI_IP,
   281  		Safi: api.Family_SAFI_FLOW_SPEC_VPN,
   282  	}
   283  	ipv6VPNflowspec = &api.Family{
   284  		Afi:  api.Family_AFI_IP6,
   285  		Safi: api.Family_SAFI_FLOW_SPEC_VPN,
   286  	}
   287  	l2VPNflowspec = &api.Family{
   288  		Afi:  api.Family_AFI_L2VPN,
   289  		Safi: api.Family_SAFI_FLOW_SPEC_VPN,
   290  	}
   291  	opaque = &api.Family{
   292  		Afi:  api.Family_AFI_OPAQUE,
   293  		Safi: api.Family_SAFI_KEY_VALUE,
   294  	}
   295  )
   296  
   297  func checkAddressFamily(def *api.Family) (*api.Family, error) {
   298  	var f *api.Family
   299  	var e error
   300  	switch subOpts.AddressFamily {
   301  	case "ipv4", "v4", "4":
   302  		f = ipv4UC
   303  	case "ipv6", "v6", "6":
   304  		f = ipv6UC
   305  	case "ipv4-l3vpn", "vpnv4", "vpn-ipv4":
   306  		f = ipv4VPN
   307  	case "ipv6-l3vpn", "vpnv6", "vpn-ipv6":
   308  		f = ipv6VPN
   309  	case "ipv4-labeled", "ipv4-labelled", "ipv4-mpls":
   310  		f = ipv4MPLS
   311  	case "ipv6-labeled", "ipv6-labelled", "ipv6-mpls":
   312  		f = ipv6MPLS
   313  	case "evpn":
   314  		f = evpn
   315  	case "encap", "ipv4-encap":
   316  		f = ipv4Encap
   317  	case "ipv6-encap":
   318  		f = ipv6Encap
   319  	case "rtc":
   320  		f = rtc
   321  	case "ipv4-flowspec", "ipv4-flow", "flow4":
   322  		f = ipv4Flowspec
   323  	case "ipv6-flowspec", "ipv6-flow", "flow6":
   324  		f = ipv6Flowspec
   325  	case "ipv4-l3vpn-flowspec", "ipv4vpn-flowspec", "flowvpn4":
   326  		f = ipv4VPNflowspec
   327  	case "ipv6-l3vpn-flowspec", "ipv6vpn-flowspec", "flowvpn6":
   328  		f = ipv6VPNflowspec
   329  	case "l2vpn-flowspec":
   330  		f = l2VPNflowspec
   331  	case "opaque":
   332  		f = opaque
   333  	case "":
   334  		f = def
   335  	default:
   336  		e = fmt.Errorf("unsupported address family: %s", subOpts.AddressFamily)
   337  	}
   338  	return f, e
   339  }
   340  
   341  func printError(err error) {
   342  	if globalOpts.Json {
   343  		j, _ := json.Marshal(struct {
   344  			Error string `json:"error"`
   345  		}{Error: err.Error()})
   346  		fmt.Println(string(j))
   347  	} else {
   348  		fmt.Println(err)
   349  	}
   350  }
   351  
   352  func exitWithError(err error) {
   353  	printError(err)
   354  	os.Exit(1)
   355  }
   356  
   357  func getNextHopFromPathAttributes(attrs []bgp.PathAttributeInterface) net.IP {
   358  	for _, attr := range attrs {
   359  		switch a := attr.(type) {
   360  		case *bgp.PathAttributeNextHop:
   361  			return a.Value
   362  		case *bgp.PathAttributeMpReachNLRI:
   363  			return a.Nexthop
   364  		}
   365  	}
   366  	return nil
   367  }