diff options
-rw-r--r-- | api/gobgp.pb.go | 7 | ||||
-rw-r--r-- | api/gobgp.proto | 1 | ||||
-rw-r--r-- | api/util.go | 20 | ||||
-rw-r--r-- | gobgp/common.go | 3 | ||||
-rw-r--r-- | gobgp/mrt.go | 192 | ||||
-rw-r--r-- | server/grpc_server.go | 5 | ||||
-rw-r--r-- | server/server.go | 49 |
7 files changed, 210 insertions, 67 deletions
diff --git a/api/gobgp.pb.go b/api/gobgp.pb.go index 40a1e596..901abcad 100644 --- a/api/gobgp.pb.go +++ b/api/gobgp.pb.go @@ -633,9 +633,10 @@ func (m *PolicyArguments) GetApplyPolicy() *ApplyPolicy { } type MrtArguments struct { - Resource Resource `protobuf:"varint,1,opt,name=resource,enum=api.Resource" json:"resource,omitempty"` - Af *AddressFamily `protobuf:"bytes,2,opt,name=af" json:"af,omitempty"` - Interval uint64 `protobuf:"varint,3,opt,name=interval" json:"interval,omitempty"` + Resource Resource `protobuf:"varint,1,opt,name=resource,enum=api.Resource" json:"resource,omitempty"` + Af *AddressFamily `protobuf:"bytes,2,opt,name=af" json:"af,omitempty"` + Interval uint64 `protobuf:"varint,3,opt,name=interval" json:"interval,omitempty"` + NeighborAddress string `protobuf:"bytes,4,opt,name=neighbor_address" json:"neighbor_address,omitempty"` } func (m *MrtArguments) Reset() { *m = MrtArguments{} } diff --git a/api/gobgp.proto b/api/gobgp.proto index b80964eb..e50e3c71 100644 --- a/api/gobgp.proto +++ b/api/gobgp.proto @@ -76,6 +76,7 @@ message MrtArguments { Resource resource = 1; AddressFamily af = 2; uint64 interval = 3; + string neighbor_address = 4; } enum Resource { diff --git a/api/util.go b/api/util.go index 677d4f6a..d1bd4649 100644 --- a/api/util.go +++ b/api/util.go @@ -26,3 +26,23 @@ var AF_RTC *AddressFamily = &AddressFamily{AFI_IP, SAFI_ROUTE_TARGET_CONSTRAINTS func (lhs *AddressFamily) Equal(rhs *AddressFamily) bool { return lhs.Afi == rhs.Afi && lhs.Safi == rhs.Safi } + +func (af *AddressFamily) ShortString() string { + switch { + case af.Equal(AF_IPV4_UC): + return "ipv4" + case af.Equal(AF_IPV6_UC): + return "ipv6" + case af.Equal(AF_IPV4_VPN): + return "vpnv4" + case af.Equal(AF_IPV4_VPN): + return "vpnv6" + case af.Equal(AF_EVPN): + return "evpn" + case af.Equal(AF_ENCAP): + return "encap" + case af.Equal(AF_RTC): + return "rtc" + } + return "unknown" +} diff --git a/gobgp/common.go b/gobgp/common.go index 436dae8a..f94b36df 100644 --- a/gobgp/common.go +++ b/gobgp/common.go @@ -86,7 +86,8 @@ var actionOpts struct { } var mrtOpts struct { - OutputDir string + OutputDir string + FileFormat string } func formatTimedelta(d int64) string { diff --git a/gobgp/mrt.go b/gobgp/mrt.go index 3dc2d540..e32a0931 100644 --- a/gobgp/mrt.go +++ b/gobgp/mrt.go @@ -28,6 +28,7 @@ import ( "os" "path/filepath" "strconv" + "text/template" "time" ) @@ -69,72 +70,163 @@ func printMrtMsgs(data []byte) { } -func NewMrtCmd() *cobra.Command { - mrtCmd := &cobra.Command{ - Use: CMD_MRT, +func dumpRib(r string, remoteIP net.IP, args []string) error { + var resource api.Resource + switch r { + case CMD_GLOBAL: + resource = api.Resource_GLOBAL + case CMD_LOCAL: + resource = api.Resource_LOCAL + default: + return fmt.Errorf("unknown resource type: %s", r) } - mrtCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family") - dumpCmd := &cobra.Command{ - Use: CMD_DUMP, + af, err := checkAddressFamily(remoteIP) + if err != nil { + return err + } + + var interval uint64 + if len(args) > 0 { + i, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + interval = uint64(i) + } + + arg := &api.MrtArguments{ + Resource: resource, + Af: af, + Interval: interval, + NeighborAddress: remoteIP.String(), + } + + seed := struct { + Y string + M string + D string + H string + Min string + Sec string + Af string + NeighborAddress string + Resource string + }{ + Af: af.ShortString(), + NeighborAddress: remoteIP.String(), + Resource: r, + } + + stream, err := client.GetMrt(context.Background(), arg) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + var fileformat string + + if mrtOpts.FileFormat != "" { + fileformat = mrtOpts.FileFormat + } else if r == CMD_GLOBAL { + fileformat = "rib_{{.Af}}_{{.Y}}{{.M}}{{.D}}_{{.H}}{{.Min}}{{.Sec}}" + } else { + fileformat = "rib_{{.NeighborAddress}}_{{.Y}}{{.M}}{{.D}}_{{.H}}{{.Min}}{{.Sec}}" + } + + for { + s, err := stream.Recv() + if err == io.EOF { + break + } else if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if globalOpts.Debug { + printMrtMsgs(s.Data) + } + + now := time.Now() + y, m, d := now.Date() + seed.Y = fmt.Sprintf("%04d", y) + seed.M = fmt.Sprintf("%02d", int(m)) + seed.D = fmt.Sprintf("%02d", d) + h, min, sec := now.Clock() + seed.H = fmt.Sprintf("%02d", h) + seed.Min = fmt.Sprintf("%02d", min) + seed.Sec = fmt.Sprintf("%02d", sec) + t, err := template.New("f").Parse(fileformat) + if err != nil { + return err + } + buf := bytes.NewBuffer(make([]byte, 0, 32)) + err = t.Execute(buf, seed) + if err != nil { + return err + } + filename := fmt.Sprintf("%s/%s", mrtOpts.OutputDir, buf.String()) + + err = ioutil.WriteFile(filename, s.Data, 0600) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + fmt.Println("mrt dump:", filepath.Clean(filename)) + } + return nil +} + +func NewMrtCmd() *cobra.Command { + + globalCmd := &cobra.Command{ + Use: CMD_GLOBAL, Run: func(cmd *cobra.Command, args []string) { - var interval uint64 - if len(args) > 0 { - i, err := strconv.Atoi(args[0]) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - interval = uint64(i) - } - af, err := checkAddressFamily(net.IP{}) + err := dumpRib(CMD_GLOBAL, net.IP{}, args) if err != nil { fmt.Println(err) os.Exit(1) } + }, + } - arg := &api.MrtArguments{ - Resource: api.Resource_GLOBAL, - Af: af, - Interval: interval, + neighborCmd := &cobra.Command{ + Use: CMD_NEIGHBOR, + Run: func(cmd *cobra.Command, args []string) { + if len(args) < 1 { + fmt.Println("usage: gobgp mrt dump neighbor <neighbor address> [<interval>]") + os.Exit(1) } - - stream, err := client.GetMrt(context.Background(), arg) + remoteIP := net.ParseIP(args[0]) + if remoteIP == nil { + fmt.Println("invalid ip address:", args[0]) + os.Exit(1) + } + err := dumpRib(CMD_LOCAL, remoteIP, args[1:]) if err != nil { fmt.Println(err) os.Exit(1) } - - for { - s, err := stream.Recv() - if err == io.EOF { - break - } else if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if globalOpts.Debug { - printMrtMsgs(s.Data) - } - - now := time.Now() - y, m, d := now.Date() - h, min, sec := now.Clock() - filename := fmt.Sprintf("%s/rib.%04d%02d%02d.%02d%02d%02d", mrtOpts.OutputDir, y, m, d, h, min, sec) - - err = ioutil.WriteFile(filename, s.Data, 0600) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println("mrt dump:", filepath.Clean(filename)) - } }, } - dumpCmd.Flags().StringVarP(&mrtOpts.OutputDir, "outdir", "o", ".", "output directory") + ribCmd := &cobra.Command{ + Use: CMD_RIB, + } + ribCmd.AddCommand(globalCmd, neighborCmd) + ribCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family") + + dumpCmd := &cobra.Command{ + Use: CMD_DUMP, + } + dumpCmd.AddCommand(ribCmd) + dumpCmd.PersistentFlags().StringVarP(&mrtOpts.OutputDir, "outdir", "o", ".", "output directory") + dumpCmd.PersistentFlags().StringVarP(&mrtOpts.FileFormat, "format", "f", "", "file format") + + mrtCmd := &cobra.Command{ + Use: CMD_MRT, + } mrtCmd.AddCommand(dumpCmd) return mrtCmd diff --git a/server/grpc_server.go b/server/grpc_server.go index 3c1c7b08..55dd4381 100644 --- a/server/grpc_server.go +++ b/server/grpc_server.go @@ -83,6 +83,7 @@ const ( REQ_MONITOR_GLOBAL_BEST_CHANGED REQ_MONITOR_NEIGHBOR_PEER_STATE REQ_MRT_GLOBAL_RIB + REQ_MRT_LOCAL_RIB REQ_RPKI ) @@ -598,6 +599,8 @@ func (s *Server) GetMrt(arg *api.MrtArguments, stream api.Grpc_GetMrtServer) err switch arg.Resource { case api.Resource_GLOBAL: reqType = REQ_MRT_GLOBAL_RIB + case api.Resource_LOCAL: + reqType = REQ_MRT_LOCAL_RIB default: return fmt.Errorf("unsupported resource type: %v", arg.Resource) } @@ -606,7 +609,7 @@ func (s *Server) GetMrt(arg *api.MrtArguments, stream api.Grpc_GetMrtServer) err return err } - req := NewGrpcRequest(reqType, "", rf, arg.Interval) + req := NewGrpcRequest(reqType, arg.NeighborAddress, rf, arg.Interval) s.bgpServerCh <- req for res := range req.ResponseCh { if err = res.Err(); err != nil { diff --git a/server/server.go b/server/server.go index 444ef2ca..b2ad3764 100644 --- a/server/server.go +++ b/server/server.go @@ -1290,7 +1290,7 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { server.handleGrpcDelPolicies(grpcReq) case REQ_MONITOR_GLOBAL_BEST_CHANGED, REQ_MONITOR_NEIGHBOR_PEER_STATE: server.broadcastReqs = append(server.broadcastReqs, grpcReq) - case REQ_MRT_GLOBAL_RIB: + case REQ_MRT_GLOBAL_RIB, REQ_MRT_LOCAL_RIB: server.handleMrt(grpcReq) case REQ_RPKI: server.roaClient.handleGRPC(grpcReq) @@ -1914,8 +1914,30 @@ func (server *BgpServer) handleGrpcDelPolicies(grpcReq *GrpcRequest) { func (server *BgpServer) handleMrt(grpcReq *GrpcRequest) { now := uint32(time.Now().Unix()) - msg, err := server.mkMrtPeerIndexTableMsg(now) + view := "" result := &GrpcResponse{} + var manager *table.TableManager + + switch grpcReq.RequestType { + case REQ_MRT_GLOBAL_RIB: + manager = server.localRibMap[GLOBAL_RIB_NAME].rib + case REQ_MRT_LOCAL_RIB: + _, err := server.checkNeighborRequest(grpcReq) + if err != nil { + return + } + loc, ok := server.localRibMap[grpcReq.RemoteAddr] + if !ok { + result.ResponseErr = fmt.Errorf("no local rib for %s", grpcReq.RemoteAddr) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + return + } + manager = loc.rib + view = grpcReq.RemoteAddr + } + + msg, err := server.mkMrtPeerIndexTableMsg(now, view) if err != nil { result.ResponseErr = fmt.Errorf("failed to make new mrt peer index table message: %s", err) grpcReq.ResponseCh <- result @@ -1930,7 +1952,15 @@ func (server *BgpServer) handleMrt(grpcReq *GrpcRequest) { return } - msgs, err := server.mkMrtRibMsgs(grpcReq.RouteFamily, now) + tbl, ok := manager.Tables[grpcReq.RouteFamily] + if !ok { + result.ResponseErr = fmt.Errorf("unsupported route family: %s", grpcReq.RouteFamily) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + return + } + + msgs, err := server.mkMrtRibMsgs(tbl, now) if err != nil { result.ResponseErr = fmt.Errorf("failed to make new mrt rib message: %s", err) grpcReq.ResponseCh <- result @@ -1978,7 +2008,7 @@ func (server *BgpServer) handleMrt(grpcReq *GrpcRequest) { return } -func (server *BgpServer) mkMrtPeerIndexTableMsg(t uint32) (*bgp.MRTMessage, error) { +func (server *BgpServer) mkMrtPeerIndexTableMsg(t uint32, view string) (*bgp.MRTMessage, error) { peers := make([]*bgp.Peer, 0, len(server.neighborMap)) for _, peer := range server.neighborMap { id := peer.peerInfo.ID.To4().String() @@ -1987,16 +2017,11 @@ func (server *BgpServer) mkMrtPeerIndexTableMsg(t uint32) (*bgp.MRTMessage, erro peers = append(peers, bgp.NewPeer(id, ipaddr, asn, true)) } bgpid := server.bgpConfig.Global.GlobalConfig.RouterId.To4().String() - table := bgp.NewPeerIndexTable(bgpid, "", peers) + table := bgp.NewPeerIndexTable(bgpid, view, peers) return bgp.NewMRTMessage(t, bgp.TABLE_DUMPv2, bgp.PEER_INDEX_TABLE, table) } -func (server *BgpServer) mkMrtRibMsgs(rf bgp.RouteFamily, t uint32) ([]*bgp.MRTMessage, error) { - tbl, ok := server.localRibMap[GLOBAL_RIB_NAME].rib.Tables[rf] - if !ok { - return nil, fmt.Errorf("unsupported route family: %s", rf) - } - +func (server *BgpServer) mkMrtRibMsgs(tbl *table.Table, t uint32) ([]*bgp.MRTMessage, error) { getPeerIndex := func(info *table.PeerInfo) uint16 { var idx uint16 for _, peer := range server.neighborMap { @@ -2010,7 +2035,7 @@ func (server *BgpServer) mkMrtRibMsgs(rf bgp.RouteFamily, t uint32) ([]*bgp.MRTM var subtype bgp.MRTSubTypeTableDumpv2 - switch rf { + switch tbl.GetRoutefamily() { case bgp.RF_IPv4_UC: subtype = bgp.RIB_IPV4_UNICAST case bgp.RF_IPv4_MC: |