summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur de Kerhor <arthurdekerhor@gmail.com>2021-03-03 08:49:15 -0800
committerDave Barach <openvpp@barachs.net>2021-03-24 12:16:43 +0000
commit1f13f8fd8a5f5f68e6e03825115c64383f17a1b5 (patch)
tree966eb8209ed495c3a544aeaa32f223f5e67214ae
parent4d2726ece83f391a7cadb1314415ef23104f9ffe (diff)
misc: fuse fs for the stats segment
This extra allows to mount a FUSE filesystem reflecting the state of the stats segment. Type: feature Signed-off-by: Arthur de Kerhor <arthurdekerhor@gmail.com> Change-Id: I692f9ca5a65c1123b3cf28c761455eec36049791
-rw-r--r--Makefile28
-rwxr-xr-xextras/vpp_stats_fs/README.md61
-rw-r--r--extras/vpp_stats_fs/cmd.go91
-rwxr-xr-xextras/vpp_stats_fs/install.sh274
-rw-r--r--extras/vpp_stats_fs/stats_fs.go207
5 files changed, 661 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 05a912ca42b..a1226a7fc37 100644
--- a/Makefile
+++ b/Makefile
@@ -218,6 +218,7 @@ help:
@echo " docs - Build the Sphinx documentation"
@echo " docs-venv - Build the virtual environment for the Sphinx docs"
@echo " docs-clean - Remove the generated files from the Sphinx docs"
+ @echo " stats-fs-help - Help to build the stats segment file system"
@echo ""
@echo "Make Arguments:"
@echo " V=[0|1] - set build verbosity level"
@@ -657,6 +658,33 @@ featurelist: centos-pyyaml
checkfeaturelist: centos-pyyaml
@build-root/scripts/fts.py --validate --all
+
+# Build vpp_stats_fs
+
+.PHONY: stats-fs-install
+stats-fs-install:
+ @extras/vpp_stats_fs/install.sh install
+
+.PHONY: stats-fs-start
+stats-fs-start:
+ @extras/vpp_stats_fs/install.sh start
+
+.PHONY: stats-fs-cleanup
+stats-fs-cleanup:
+ @extras/vpp_stats_fs/install.sh cleanup
+
+.PHONY: stats-fs-help
+stats-fs-help:
+ @extras/vpp_stats_fs/install.sh help
+
+.PHONY: stats-fs-force-unmount
+stats-fs-force-unmount:
+ @extras/vpp_stats_fs/install.sh unmount
+
+.PHONY: stats-fs-stop
+stats-fs-stop:
+ @extras/vpp_stats_fs/install.sh stop
+
#
# Build the documentation
#
diff --git a/extras/vpp_stats_fs/README.md b/extras/vpp_stats_fs/README.md
new file mode 100755
index 00000000000..3b0b09468a6
--- /dev/null
+++ b/extras/vpp_stats_fs/README.md
@@ -0,0 +1,61 @@
+# VPP stats segment FUSE filesystem
+
+The statfs binary allows to create a FUSE filesystem to expose and to browse the stats segment.
+Is is leaned on the Go-FUSE library and requires Go-VPP stats bindings to work.
+
+The binary mounts a filesystem on the local machine whith the data from the stats segments.
+The counters can be opened and read as files (e.g. in a Unix shell).
+Note that the value of a counter is determined when the corresponding file is opened (as for /proc/interrupts).
+
+Directories regularly update their contents so that new counters get added to the filesystem.
+
+## Prerequisites (for building)
+
+**GoVPP** library (master branch)
+**Go-FUSE** library
+vpp, vppapi
+
+## Building
+
+Here, we add the Go librairies before building the binary
+```bash
+go mod init stats_fs
+go get git.fd.io/govpp.git@master
+go get git.fd.io/govpp.git/adapter/statsclient@master
+go get github.com/hanwen/go-fuse/v2
+go build
+```
+
+## Usage
+
+The basic usage is:
+```bash
+sudo ./statfs <MOUNT_POINT> &
+```
+**Options:**
+ - debug \<true|false\> (default is false)
+ - socket \<statSocket\> (default is /run/vpp/stats.sock)
+
+## Browsing the filesystem
+
+You can browse the filesystem as a regular user.
+Example:
+
+```bash
+cd /path/to/mountpoint
+cd sys/node
+ls -al
+cat names
+```
+
+## Unmounting the file system
+
+You can unmount the filesystem with the fusermount command.
+```bash
+sudo fusermount -u /path/to/mountpoint
+```
+
+To force the unmount even if the resource is busy, add the -z option:
+```bash
+sudo fusermount -uz /path/to/mountpoint
+``` \ No newline at end of file
diff --git a/extras/vpp_stats_fs/cmd.go b/extras/vpp_stats_fs/cmd.go
new file mode 100644
index 00000000000..826b011b00d
--- /dev/null
+++ b/extras/vpp_stats_fs/cmd.go
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2021 Cisco Systems and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2016 the Go-FUSE Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+// This file is the main program driver to mount the stats segment filesystem.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "os/signal"
+ "runtime"
+ "strings"
+ "syscall"
+
+ "git.fd.io/govpp.git/adapter/statsclient"
+ "git.fd.io/govpp.git/core"
+ "github.com/hanwen/go-fuse/v2/fs"
+)
+
+func main() {
+ statsSocket := flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
+ debug := flag.Bool("debug", false, "print debugging messages.")
+ flag.Parse()
+ if flag.NArg() < 1 {
+ fmt.Fprintf(os.Stderr, "usage: %s MOUNTPOINT\n", os.Args[0])
+ os.Exit(2)
+ }
+ //Conection to the stat segment socket.
+ sc := statsclient.NewStatsClient(*statsSocket)
+ fmt.Printf("Waiting for the VPP socket to be available. Be sure a VPP instance is running.\n")
+ c, err := core.ConnectStats(sc)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to connect to the stats socket: %v\n", err)
+ os.Exit(1)
+ }
+ defer c.Disconnect()
+ fmt.Printf("Connected to the socket\n")
+ //Creating the filesystem instance
+ root, err := NewStatsFileSystem(sc)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "NewStatsFileSystem failed: %v\n", err)
+ os.Exit(1)
+ }
+
+ //Mounting the filesystem.
+ opts := &fs.Options{}
+ opts.Debug = *debug
+ opts.AllowOther = true
+ server, err := fs.Mount(flag.Arg(0), root, opts)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Mount fail: %v\n", err)
+ os.Exit(1)
+ }
+
+ sigs := make(chan os.Signal)
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+
+ fmt.Printf("Successfully mounted the file system in directory: %s\n", flag.Arg(0))
+ runtime.GC()
+
+ for {
+ go server.Wait()
+
+ <-sigs
+ fmt.Println("Unmounting...")
+ err := server.Unmount()
+ if err == nil || !strings.Contains(err.Error(), "Device or resource busy") {
+ break
+ }
+ fmt.Fprintf(os.Stderr, "Unmount fail: %v\n", err)
+ }
+}
diff --git a/extras/vpp_stats_fs/install.sh b/extras/vpp_stats_fs/install.sh
new file mode 100755
index 00000000000..6249e63c6eb
--- /dev/null
+++ b/extras/vpp_stats_fs/install.sh
@@ -0,0 +1,274 @@
+# Copyright (c) 2021 Cisco Systems and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#!/bin/bash
+
+# A simple script that installs stats_fs, a Fuse file system
+# for the stats segment
+
+set -eo pipefail
+
+OPT_ARG=${1:-}
+
+STATS_FS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"/
+VPP_DIR=$(pwd)/
+BUILD_ROOT=${VPP_DIR}build-root/
+BINARY_DIR=${BUILD_ROOT}install-vpp-native/vpp/bin/
+DEBUG_DIR=${BUILD_ROOT}install-vpp_debug-native/vpp/bin/
+RUN_DIR=/run/vpp/
+
+GOROOT=${GOROOT:-}
+GOPATH=${GOPATH:-}
+
+[ -z "${GOROOT}" ] && GOROOT="${HOME}/.go" && PATH=$GOROOT/bin:$PATH
+[ -z "${GOPATH}" ] && GOPATH="${HOME}/go" && PATH=$GOPATH/bin:$PATH
+
+function install_tools() {
+ echo "Installing downloading tools"
+ apt-get update
+ apt-get install git wget curl -y
+}
+
+# Install latest GO version
+function install_go() {
+ local TMP="/tmp"
+
+ echo "Installing latest GO"
+ if [[ -x "$(command -v go)" ]]; then
+ local installed_ver installed_ver_fmt
+ installed_ver=$(go version)
+ installed_ver_fmt=${installed_ver#"go version go"}
+ echo "Found installed version ${installed_ver_fmt}"
+ return
+ fi
+
+ mkdir -p "${GOROOT}"
+ mkdir -p "${GOPATH}/"{src,pkg,bin}
+
+ wget "https://dl.google.com/go/$(curl https://golang.org/VERSION?m=text).linux-amd64.tar.gz" -O "${TMP}/go.tar.gz"
+ tar -C "$GOROOT" --strip-components=1 -xzf "${TMP}/go.tar.gz"
+
+ rm -f "${TMP}/go.tar.gz"
+
+ # export path for current session to install vpp_stast_fs
+ export GOROOT=${GOROOT}
+ export PATH=$GOROOT/bin:$PATH
+ export GOPATH=$GOPATH
+ export PATH=$GOPATH/bin:$PATH
+
+ echo "Installed $(go version)"
+}
+
+function install_fuse() {
+ echo "Installing Fuse"
+ apt-get update
+ apt-get install fuse -y
+}
+
+function install_go_dep() {
+ echo "Installing Go dependencies"
+ if [[ ! -x "$(command -v go)" ]]; then
+ echo "GO is not installed"
+ exit 1
+ fi
+
+ if [ ! -e "go.mod" ]; then
+ go mod init stats_fs
+ fi
+ # master required
+ go get git.fd.io/govpp.git@master
+ go get git.fd.io/govpp.git/adapter/statsclient@master
+ go get github.com/hanwen/go-fuse/v2
+}
+
+# Resolve stats_fs dependencies and builds the binary
+function build_statfs() {
+ echo "Installing statfs"
+ go build
+ if [ -d "${BINARY_DIR}" ]; then
+ mv stats_fs "${BINARY_DIR}"/stats_fs
+ elif [ -d "${DEBUG_DIR}" ]; then
+ mv stats_fs "${DEBUG_DIR}"/stats_fs
+ else
+ echo "${BINARY_DIR} and ${DEBUG_DIR} directories does not exist, the binary is installed at ${STATS_FS_DIR}stats_fs instead"
+ fi
+}
+
+function install_statfs() {
+ if [[ ! -x "$(command -v go)" ]]; then
+ install_tools
+ install_go
+ fi
+
+ if [[ ! -x "$(command -v fusermount)" ]]; then
+ install_fuse
+ fi
+
+ if [ ! -d "${STATS_FS_DIR}" ]; then
+ echo "${STATS_FS_DIR} directory does not exist"
+ exit 1
+ fi
+ cd "${STATS_FS_DIR}"
+
+ if [[ ! -x "$(command -v ${STATS_FS_DIR}stats_fs)" ]]; then
+ install_go_dep
+ build_statfs
+ else
+ echo "stats_fs already installed at path ${STATS_FS_DIR}stats_fs"
+ fi
+}
+
+# Starts the statfs binary
+function start_statfs() {
+ EXE_DIR=$STATS_FS_DIR
+ if [ -d "${BINARY_DIR}" ]; then
+ EXE_DIR=$BINARY_DIR
+ elif [ -d "${DEBUG_DIR}" ]; then
+ EXE_DIR=$DEBUG_DIR
+ fi
+
+ mountpoint="${RUN_DIR}stats_fs_dir"
+
+ if [[ -x "$(command -v ${EXE_DIR}stats_fs)" ]] ; then
+ if [ ! -d "$mountpoint" ] ; then
+ mkdir "$mountpoint"
+ fi
+ nohup "${EXE_DIR}"stats_fs $mountpoint 0<&- &>/dev/null &
+ return
+ fi
+
+ echo "stats_fs is not installed, use 'make stats-fs-install' first"
+}
+
+function stop_statfs() {
+ EXE_DIR=$STATS_FS_DIR
+ if [ -d "${BINARY_DIR}" ]; then
+ EXE_DIR=$BINARY_DIR
+ elif [ -d "${DEBUG_DIR}" ]; then
+ EXE_DIR=$DEBUG_DIR
+ fi
+ if [[ ! $(pidof "${EXE_DIR}"stats_fs) ]]; then
+ echo "The service stats_fs is not running"
+ exit 1
+ fi
+
+ PID=$(pidof "${EXE_DIR}"stats_fs)
+ kill "$PID"
+ if [[ $(pidof "${EXE_DIR}"stats_fs) ]]; then
+ echo "Can't unmount the file system: Device or resource busy"
+ exit 1
+ fi
+
+ if [ -d "${RUN_DIR}stats_fs_dir" ] ; then
+ rm -df "${RUN_DIR}stats_fs_dir"
+ fi
+}
+
+function force_unmount() {
+ if (( $(mount | grep "${RUN_DIR}stats_fs_dir" | wc -l) == 1 )) ; then
+ fusermount -uz "${RUN_DIR}stats_fs_dir"
+ else
+ echo "The default directory ${RUN_DIR}stats_fs_dir is not mounted."
+ fi
+
+ if [ -d "${RUN_DIR}stats_fs_dir" ] ; then
+ rm -df "${RUN_DIR}stats_fs_dir"
+ fi
+}
+
+# Remove stats_fs Go module
+function cleanup() {
+ echo "Cleaning up stats_fs"
+ if [ ! -d "${STATS_FS_DIR}" ]; then
+ echo "${STATS_FS_DIR} directory does not exist"
+ exit 1
+ fi
+
+ cd "${STATS_FS_DIR}"
+
+ if [ -e "go.mod" ]; then
+ rm -f go.mod
+ fi
+ if [ -e "go.sum" ]; then
+ rm -f go.sum
+ fi
+ if [ -e "stats_fs" ]; then
+ rm -f stats_fs
+ fi
+
+ if [ -d "${BINARY_DIR}" ]; then
+ if [ -e "${BINARY_DIR}stats_fs" ]; then
+ rm -f ${BINARY_DIR}stats_fs
+ fi
+ elif [ -d "${DEBUG_DIR}" ]; then
+ if [ -e "${DEBUG_DIR}stats_fs" ]; then
+ rm -f ${DEBUG_DIR}stats_fs
+ fi
+ fi
+
+ if [ -d "${RUN_DIR}stats_fs_dir" ] ; then
+ rm -df "${RUN_DIR}stats_fs_dir"
+ fi
+}
+
+# Show available commands
+function help() {
+ cat <<__EOF__
+ Stats_fs installer
+
+ stats-fs-install - Installs requirements (Go, GoVPP, GoFUSE) and builds stats_fs
+ stats-fs-start - Launches the stats_fs binary and creates a mountpoint
+ stats-fs-cleanup - Removes stats_fs binary and deletes go module
+ stats-fs-stop - Stops the executable, unmounts the file system
+ and removes the mountpoint directory
+ stats-fs-force-unmount - Forces the unmount of the filesystem even if it is busy
+
+__EOF__
+}
+
+# Resolve chosen option and call appropriate functions
+function resolve_option() {
+ local option=$1
+ case ${option} in
+ "start")
+ start_statfs
+ ;;
+ "install")
+ install_statfs
+ ;;
+ "cleanup")
+ cleanup
+ ;;
+ "unmount")
+ force_unmount
+ ;;
+ "stop")
+ stop_statfs
+ ;;
+ "help")
+ help
+ ;;
+ *) echo invalid option ;;
+ esac
+}
+
+if [[ -n ${OPT_ARG} ]]; then
+ resolve_option "${OPT_ARG}"
+else
+ PS3="--> "
+ options=("install" "cleanup" "help" "start" "unmount")
+ select option in "${options[@]}"; do
+ resolve_option "${option}"
+ break
+ done
+fi
diff --git a/extras/vpp_stats_fs/stats_fs.go b/extras/vpp_stats_fs/stats_fs.go
new file mode 100644
index 00000000000..a9b8ae77633
--- /dev/null
+++ b/extras/vpp_stats_fs/stats_fs.go
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2021 Cisco Systems and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*Go-FUSE allows us to define the behaviour of our filesystem by recoding any primitive function we need.
+ *The structure of the filesystem is constructed as a tree.
+ *Each type of nodes (root, directory, file) follows its own prmitives.
+ */
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/hanwen/go-fuse/v2/fs"
+ "github.com/hanwen/go-fuse/v2/fuse"
+
+ "git.fd.io/govpp.git/adapter"
+ "git.fd.io/govpp.git/adapter/statsclient"
+)
+
+func updateDir(ctx context.Context, n *fs.Inode, cl *statsclient.StatsClient, dirPath string) syscall.Errno {
+ list, err := cl.ListStats(dirPath)
+ if err != nil {
+ log.Println("list stats failed:", err)
+ return syscall.EAGAIN
+ }
+
+ if list == nil {
+ n.ForgetPersistent()
+ return syscall.ENOENT
+ }
+
+ for _, path := range list {
+ localPath := strings.TrimPrefix(path, dirPath)
+ dir, base := filepath.Split(localPath)
+
+ parent := n
+ for _, component := range strings.Split(dir, "/") {
+ if len(component) == 0 {
+ continue
+ }
+ child := parent.GetChild(component)
+ if child == nil {
+ child = parent.NewPersistentInode(ctx, &dirNode{client: cl, lastUpdate: time.Now()},
+ fs.StableAttr{Mode: fuse.S_IFDIR})
+ parent.AddChild(component, child, true)
+ }
+
+ parent = child
+ }
+ filename := strings.Replace(base, " ", "_", -1)
+ child := parent.GetChild(filename)
+ if child == nil {
+ child := parent.NewPersistentInode(ctx, &statNode{client: cl, path: path}, fs.StableAttr{})
+ parent.AddChild(filename, child, true)
+ }
+ }
+ return 0
+}
+
+func getCounterContent(path string, client *statsclient.StatsClient) (content string, status syscall.Errno) {
+ content = ""
+ //We add '$' because we deal with regexp here
+ res, err := client.DumpStats(path + "$")
+ if err != nil {
+ return content, syscall.EAGAIN
+ }
+ if res == nil {
+ return content, syscall.ENOENT
+ }
+
+ result := res[0]
+ if result.Data == nil {
+ return content, 0
+ }
+
+ switch result.Type {
+ case adapter.ScalarIndex:
+ stats := result.Data.(adapter.ScalarStat)
+ content = fmt.Sprintf("%.2f\n", stats)
+ case adapter.ErrorIndex:
+ stats := result.Data.(adapter.ErrorStat)
+ content = fmt.Sprintf("%-16s%s\n", "Index", "Count")
+ for i, value := range stats {
+ content += fmt.Sprintf("%-16d%d\n", i, value)
+ }
+ case adapter.SimpleCounterVector:
+ stats := result.Data.(adapter.SimpleCounterStat)
+ content = fmt.Sprintf("%-16s%-16s%s\n", "Thread", "Index", "Packets")
+ for i, vector := range stats {
+ for j, value := range vector {
+ content += fmt.Sprintf("%-16d%-16d%d\n", i, j, value)
+ }
+ }
+ case adapter.CombinedCounterVector:
+ stats := result.Data.(adapter.CombinedCounterStat)
+ content = fmt.Sprintf("%-16s%-16s%-16s%s\n", "Thread", "Index", "Packets", "Bytes")
+ for i, vector := range stats {
+ for j, value := range vector {
+ content += fmt.Sprintf("%-16d%-16d%-16d%d\n", i, j, value[0], value[1])
+ }
+ }
+ case adapter.NameVector:
+ stats := result.Data.(adapter.NameStat)
+ content = fmt.Sprintf("%-16s%s\n", "Index", "Name")
+ for i, value := range stats {
+ content += fmt.Sprintf("%-16d%s\n", i, string(value))
+ }
+ default:
+ content = fmt.Sprintf("Unknown stat type: %d\n", result.Type)
+ //For now, the empty type (file deleted) is not implemented in GoVPP
+ return content, syscall.ENOENT
+ }
+ return content, fs.OK
+}
+
+type rootNode struct {
+ fs.Inode
+ client *statsclient.StatsClient
+ lastUpdate time.Time
+}
+
+var _ = (fs.NodeOnAdder)((*rootNode)(nil))
+
+func (root *rootNode) OnAdd(ctx context.Context) {
+ updateDir(ctx, &root.Inode, root.client, "/")
+ root.lastUpdate = time.Now()
+}
+
+//The dirNode structure represents directories
+type dirNode struct {
+ fs.Inode
+ client *statsclient.StatsClient
+ lastUpdate time.Time
+}
+
+var _ = (fs.NodeOpendirer)((*dirNode)(nil))
+
+func (dn *dirNode) Opendir(ctx context.Context) syscall.Errno {
+ //We do not update a directory more than once a second, as counters are rarely added/deleted.
+ if time.Now().Sub(dn.lastUpdate) < time.Second {
+ return 0
+ }
+
+ //directoryPath is the path to the current directory from root
+ directoryPath := "/" + dn.Inode.Path(nil) + "/"
+ status := updateDir(ctx, &dn.Inode, dn.client, directoryPath)
+ dn.lastUpdate = time.Now()
+ return status
+}
+
+//The statNode structure represents counters
+type statNode struct {
+ fs.Inode
+ client *statsclient.StatsClient
+ path string
+}
+
+var _ = (fs.NodeOpener)((*statNode)(nil))
+
+//When a file is opened, the correpsonding counter value is dumped and a file handle is created
+func (sn *statNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
+ content, status := getCounterContent(sn.path, sn.client)
+ if status == syscall.ENOENT {
+ sn.Inode.ForgetPersistent()
+ }
+ return &statFH{data: []byte(content)}, fuse.FOPEN_DIRECT_IO, status
+}
+
+/* The statFH structure aims at dislaying the counters dynamically.
+ * It allows the Kernel to read data as I/O without having to specify files sizes, as they may evolve dynamically.
+ */
+type statFH struct {
+ data []byte
+}
+
+var _ = (fs.FileReader)((*statFH)(nil))
+
+func (fh *statFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadResult, syscall.Errno) {
+ end := int(off) + len(data)
+ if end > len(fh.data) {
+ end = len(fh.data)
+ }
+ return fuse.ReadResultData(fh.data[off:end]), fs.OK
+}
+
+//NewStatsFileSystem creates the fs for the stat segment.
+func NewStatsFileSystem(sc *statsclient.StatsClient) (root fs.InodeEmbedder, err error) {
+ return &rootNode{client: sc}, nil
+}
pan class="p">{ ip_adjacency_t *adj = adj_get (ai); ipip_tunnel_t *t; u32 sw_if_index = adj->rewrite_header.sw_if_index; t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index); if (!t) return; /* * find the adjacency that is contributed by the FIB entry * that this tunnel resolves via, and use it as the next adj * in the midchain */ if (vnet_hw_interface_get_flags (vnet_get_main (), t->hw_if_index) & VNET_HW_INTERFACE_FLAG_LINK_UP) { adj_nbr_midchain_stack_on_fib_entry (ai, fib_entry_index, FIB_FORW_CHAIN_TYPE_UNICAST_IP4); } else { adj_nbr_midchain_unstack (ai); } } static void sixrd_tunnel_stack (adj_index_t ai, u32 fib_index) { dpo_id_t dpo = DPO_INVALID; ip_adjacency_t *adj = adj_get (ai); u32 sw_if_index = adj->rewrite_header.sw_if_index; ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index); if (!t) return; lookup_dpo_add_or_lock_w_fib_index (fib_index, DPO_PROTO_IP4, LOOKUP_UNICAST, LOOKUP_INPUT_DST_ADDR, LOOKUP_TABLE_FROM_CONFIG, &dpo); adj_nbr_midchain_stack (ai, &dpo); dpo_reset (&dpo); } static void sixrd_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai) { ip_adjacency_t *adj = adj_get (ai); ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index); /* Not our tunnel */ if (!t) return; if (IP_LOOKUP_NEXT_BCAST == adj->lookup_next_index) { adj_nbr_midchain_update_rewrite (ai, sixrd_fixup, t, ADJ_FLAG_NONE, sixrd_build_rewrite (vnm, sw_if_index, adj_get_link_type (ai), NULL)); sixrd_tunnel_stack (ai, t->fib_index); } else { sixrd_adj_delegate_t *sixrd_ad; ip4_address_t da4; da4.as_u32 = sixrd_get_addr_net (t, adj->sub_type.nbr.next_hop.as_u64[0]); fib_prefix_t pfx = { .fp_proto = FIB_PROTOCOL_IP4, .fp_len = 32, .fp_addr = { .ip4 = da4, } , }; adj_nbr_midchain_update_rewrite (ai, ip6ip_fixup, t, ADJ_FLAG_NONE, sixrd_build_rewrite (vnm, sw_if_index, adj_get_link_type (ai), NULL)); sixrd_ad = sixrd_adj_from_base (adj_delegate_get (adj, sixrd_adj_delegate_type)); if (sixrd_ad == NULL) { pool_get (sixrd_adj_delegate_pool, sixrd_ad); fib_node_init (&sixrd_ad->sixrd_node, sixrd_fib_node_type); sixrd_ad->adj_index = ai; sixrd_ad->sixrd_fib_entry_index = fib_table_entry_special_add (t->fib_index, &pfx, FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE); sixrd_ad->sixrd_sibling = fib_entry_child_add (sixrd_ad->sixrd_fib_entry_index, sixrd_fib_node_type, sixrd_ad - sixrd_adj_delegate_pool); adj_delegate_add (adj, sixrd_adj_delegate_type, sixrd_ad - sixrd_adj_delegate_pool); ip6ip_tunnel_stack (ai, sixrd_ad->sixrd_fib_entry_index); } } } clib_error_t * sixrd_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) { /* Always up */ vnet_hw_interface_set_flags (vnm, hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP); return /* no error */ 0; } /* *INDENT-OFF* */ VNET_HW_INTERFACE_CLASS(sixrd_hw_interface_class) = { .name = "ip6ip-6rd", .build_rewrite = sixrd_build_rewrite, .update_adjacency = sixrd_update_adj, }; VNET_DEVICE_CLASS(sixrd_device_class) = { .name = "ip6ip-6rd", .admin_up_down_function = sixrd_interface_admin_up_down, #ifdef SOON .clear counter = 0; #endif } ; /* *INDENT-ON* */ int sixrd_add_tunnel (ip6_address_t * ip6_prefix, u8 ip6_prefix_len, ip4_address_t * ip4_prefix, u8 ip4_prefix_len, ip4_address_t * ip4_src, bool security_check, u32 ip4_fib_index, u32 ip6_fib_index, u32 * sw_if_index) { ipip_main_t *gm = &ipip_main; ipip_tunnel_t *t; if ((ip6_prefix_len + 32 - ip4_prefix_len) > 64) return VNET_API_ERROR_INVALID_VALUE; /* Tunnel already configured */ ip46_address_t src = ip46_address_initializer, dst = ip46_address_initializer; ip_set (&src, ip4_src, true); ipip_tunnel_key_t key = { .transport = IPIP_TRANSPORT_IP4, .fib_index = ip4_fib_index, .src = src, .dst = dst }; t = ipip_tunnel_db_find (&key); if (t) return VNET_API_ERROR_IF_ALREADY_EXISTS; /* Get tunnel index */ pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES); clib_memset (t, 0, sizeof (*t)); u32 t_idx = t - gm->tunnels; /* tunnel index (or instance) */ /* Init tunnel struct */ t->mode = IPIP_MODE_6RD; t->sixrd.ip4_prefix.as_u32 = ip4_prefix->as_u32; t->sixrd.ip4_prefix_len = ip4_prefix_len; t->sixrd.ip6_prefix = *ip6_prefix; t->sixrd.ip6_prefix_len = ip6_prefix_len; t->sixrd.ip6_fib_index = ip6_fib_index; t->tunnel_src = src; t->sixrd.security_check = security_check; t->sixrd.shift = (ip4_prefix_len < 32) ? 64 - ip6_prefix_len - (32 - ip4_prefix_len) : 0; /* Create interface */ u32 hw_if_index = vnet_register_interface (vnet_get_main (), sixrd_device_class.index, t_idx, sixrd_hw_interface_class.index, t_idx); /* Default the interface to up and enable IPv6 (payload) */ vnet_hw_interface_t *hi = vnet_get_hw_interface (vnet_get_main (), hw_if_index); t->hw_if_index = hw_if_index; t->fib_index = ip4_fib_index; t->sw_if_index = hi->sw_if_index; t->dev_instance = t_idx; t->user_instance = t_idx; vnet_sw_interface_set_mtu (vnet_get_main (), t->sw_if_index, 1480); ipip_tunnel_db_add (t, &key); vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, hi->sw_if_index, ~0); gm->tunnel_index_by_sw_if_index[hi->sw_if_index] = t_idx; vnet_hw_interface_set_flags (vnet_get_main (), hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP); vnet_sw_interface_set_flags (vnet_get_main (), hi->sw_if_index, VNET_SW_INTERFACE_FLAG_ADMIN_UP); ip6_sw_interface_enable_disable (t->sw_if_index, true); /* Create IPv6 route/adjacency */ /* *INDENT-OFF* */ fib_prefix_t pfx6 = { .fp_proto = FIB_PROTOCOL_IP6, .fp_len = t->sixrd.ip6_prefix_len, .fp_addr = { .ip6 = t->sixrd.ip6_prefix, }, }; /* *INDENT-ON* */ fib_table_lock (ip6_fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_6RD); fib_table_entry_update_one_path (ip6_fib_index, &pfx6, FIB_SOURCE_6RD, FIB_ENTRY_FLAG_ATTACHED, DPO_PROTO_IP6, &ADJ_BCAST_ADDR, t->sw_if_index, ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); *sw_if_index = t->sw_if_index; if (!gm->ip4_protocol_registered) { vlib_node_t *ipip4_input = vlib_get_node_by_name (gm->vlib_main, (u8 *) "ipip4-input"); ASSERT (ipip4_input); ip4_register_protocol (IP_PROTOCOL_IPV6, ipip4_input->index); } return 0; } /* * sixrd_del_tunnel */ int sixrd_del_tunnel (u32 sw_if_index) { ipip_main_t *gm = &ipip_main; ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index); if (!t) { clib_warning ("SIXRD tunnel delete: tunnel does not exist: %d", sw_if_index); return -1; } /* *INDENT-OFF* */ fib_prefix_t pfx6 = { .fp_proto = FIB_PROTOCOL_IP6, .fp_len = t->sixrd.ip6_prefix_len, .fp_addr = { .ip6 = t->sixrd.ip6_prefix, }, }; /* *INDENT-ON* */ fib_table_entry_path_remove (t->sixrd.ip6_fib_index, &pfx6, FIB_SOURCE_6RD, DPO_PROTO_IP6, &ADJ_BCAST_ADDR, t->sw_if_index, ~0, 1, FIB_ROUTE_PATH_FLAG_NONE); fib_table_unlock (t->sixrd.ip6_fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_6RD); vnet_sw_interface_set_flags (vnet_get_main (), t->sw_if_index, 0 /* down */ ); ip6_sw_interface_enable_disable (t->sw_if_index, false); gm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; vnet_delete_hw_interface (vnet_get_main (), t->hw_if_index); ipip_tunnel_db_remove (t); pool_put (gm->tunnels, t); return 0; } static void sixrd_adj_delegate_adj_deleted (adj_delegate_t * aed) { sixrd_adj_delegate_t *sixrd_ad; sixrd_ad = sixrd_adj_from_base (aed); fib_entry_child_remove (sixrd_ad->sixrd_fib_entry_index, sixrd_ad->sixrd_sibling); fib_table_entry_delete_index (sixrd_ad->sixrd_fib_entry_index, FIB_SOURCE_RR); pool_put (sixrd_adj_delegate_pool, sixrd_ad); } static u8 * sixrd_adj_delegate_format (const adj_delegate_t * aed, u8 * s) { const sixrd_adj_delegate_t *sixrd_ad; sixrd_ad = sixrd_adj_from_const_base (aed); s = format (s, "SIXRD:[fib-entry:%d]", sixrd_ad->sixrd_fib_entry_index); return (s); } static void sixrd_fib_node_last_lock_gone (fib_node_t * node) { /* top of the dependency tree, locks not managed here. */ } static sixrd_adj_delegate_t * sixrd_adj_delegate_from_fib_node (fib_node_t * node) { return ((sixrd_adj_delegate_t *) (((char *) node) - STRUCT_OFFSET_OF (sixrd_adj_delegate_t, sixrd_node))); } static fib_node_back_walk_rc_t sixrd_fib_node_back_walk_notify (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) { sixrd_adj_delegate_t *sixrd_ad; sixrd_ad = sixrd_adj_delegate_from_fib_node (node); ip6ip_tunnel_stack (sixrd_ad->adj_index, sixrd_ad->sixrd_fib_entry_index); return (FIB_NODE_BACK_WALK_CONTINUE); } /** * Function definition to get a FIB node from its index */ static fib_node_t * sixrd_fib_node_get (fib_node_index_t index) { sixrd_adj_delegate_t *sixrd_ad; sixrd_ad = pool_elt_at_index (sixrd_adj_delegate_pool, index); return (&sixrd_ad->sixrd_node); } /** * VFT registered with the adjacency delegate */ const static adj_delegate_vft_t sixrd_adj_delegate_vft = { .adv_adj_deleted = sixrd_adj_delegate_adj_deleted, .adv_format = sixrd_adj_delegate_format, }; /** * VFT registered with the FIB node for the adj delegate */ const static fib_node_vft_t sixrd_fib_node_vft = { .fnv_get = sixrd_fib_node_get, .fnv_last_lock = sixrd_fib_node_last_lock_gone, .fnv_back_walk = sixrd_fib_node_back_walk_notify, }; static clib_error_t * sixrd_init (vlib_main_t * vm) { clib_error_t *error = 0; /* Make sure the IPIP tunnel subsystem is initialised */ error = vlib_call_init_function (vm, ipip_init); sixrd_adj_delegate_type = adj_delegate_register_new_type (&sixrd_adj_delegate_vft); sixrd_fib_node_type = fib_node_register_new_type (&sixrd_fib_node_vft); return error; } VLIB_INIT_FUNCTION (sixrd_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */