diff --git a/Makefile b/Makefile index b561a9bb..e25d3922 100644 --- a/Makefile +++ b/Makefile @@ -52,10 +52,10 @@ RELEASE_VERSION_MAJOR_MINOR = $(shell echo $(RELEASE_VERSION_FULL) | cut -d '.' endif ifeq ($(VPP_VERSION),) -VPP_VERSION="22.10" +VPP_VERSION="23.06" endif ifeq ($(DEV_VERSION),) # for tagging in-development images -DEV_VERSION="22.10" +DEV_VERSION="23.06" endif REPO="ghcr.io/pantheontech" STONEWORK_VPP_IMAGE="stonework-vpp" @@ -253,6 +253,7 @@ generate-binapi: get-binapi-generator @cd plugins/binapi/vpp2106 && VPP_VERSION=21.06 go generate . @cd plugins/binapi/vpp2202 && VPP_VERSION=22.02 go generate . @cd plugins/binapi/vpp2210 && VPP_VERSION=22.10 go generate . + @cd plugins/binapi/vpp2306 && VPP_VERSION=23.06 go generate . generate-descriptor-adapters: get-descriptor-adapter-generator @echo "# generating descriptor adapters" diff --git a/docker/dev.Dockerfile b/docker/dev.Dockerfile index ec84c576..05b3d93d 100644 --- a/docker/dev.Dockerfile +++ b/docker/dev.Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -ARG VPP_IMAGE=vpp:22.10 +ARG VPP_IMAGE=vpp:23.06 ARG VPPAGENT_IMAGE=ligato/vpp-agent:v3.4.0 FROM $VPP_IMAGE as vpp @@ -116,8 +116,8 @@ RUN rm /tmp/legacy-nat.conf COPY ./docker/vpptrace.sh /usr/bin/vpptrace.sh RUN chmod u+x /usr/bin/vpptrace.sh -COPY ./plugins/binapi/vpp2210/api/abx.api.json /usr/share/vpp/api/plugins/ -COPY ./plugins/binapi/vpp2210/api/isisx.api.json /usr/share/vpp/api/plugins/ +COPY ./plugins/binapi/vpp2306/api/abx.api.json /usr/share/vpp/api/plugins/ +COPY ./plugins/binapi/vpp2306/api/isisx.api.json /usr/share/vpp/api/plugins/ CMD rm -f /dev/shm/db /dev/shm/global_vm /dev/shm/vpe-api && \ mkdir -p /run/vpp /run/stonework/vpp && \ diff --git a/docker/mockcnf/Dockerfile b/docker/mockcnf/Dockerfile index 16202fb8..f80705cc 100644 --- a/docker/mockcnf/Dockerfile +++ b/docker/mockcnf/Dockerfile @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG VPP_IMAGE=vpp:22.10 +ARG VPP_IMAGE=vpp:23.06 ARG VPPAGENT_IMAGE=ligato/vpp-agent:v3.4.0 FROM $VPP_IMAGE as vpp diff --git a/docker/prod.Dockerfile b/docker/prod.Dockerfile index 5d7138ae..48a67871 100644 --- a/docker/prod.Dockerfile +++ b/docker/prod.Dockerfile @@ -14,8 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG VPP_IMAGE=vpp:22.10 -ARG DEV_IMAGE=stonework-dev:22.10 +ARG VPP_IMAGE=vpp:23.06 +ARG DEV_IMAGE=stonework-dev:23.06 FROM $VPP_IMAGE as vpp FROM $DEV_IMAGE as dev diff --git a/docker/vpp-mellanox.Dockerfile b/docker/vpp-mellanox.Dockerfile index 46aef103..0cf47adc 100644 --- a/docker/vpp-mellanox.Dockerfile +++ b/docker/vpp-mellanox.Dockerfile @@ -27,7 +27,7 @@ # host network_mode (besides making it privileged and mounting /dev volume as # ussual with DPDK), to use StoneWork with this VPP under the hood. -ARG VPP_VERSION=22.10 +ARG VPP_VERSION=23.06 ARG VPP_IMAGE=ligato/vpp-base:$VPP_VERSION FROM ${VPP_IMAGE} AS base @@ -60,7 +60,7 @@ RUN cd vpp && yes | make install-dep install-ext-deps && make pkg-deb #----------------- # build ABX plugin -ARG VPP_VERSION=22.10 +ARG VPP_VERSION=23.06 COPY vpp/abx /tmp/abx RUN VPPVER=$(echo $VPP_VERSION | tr -d ".") && \ cp -r /tmp/abx/vpp${VPPVER} /opt/dev/abx diff --git a/docker/vpp-test.Dockerfile b/docker/vpp-test.Dockerfile index 7d1a91d0..9946ca06 100644 --- a/docker/vpp-test.Dockerfile +++ b/docker/vpp-test.Dockerfile @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG VPP_IMAGE=vpp:22.10 +ARG VPP_IMAGE=vpp:23.06 FROM ${VPP_IMAGE} diff --git a/docker/vpp.Dockerfile b/docker/vpp.Dockerfile index fd41a7f5..800e46a0 100644 --- a/docker/vpp.Dockerfile +++ b/docker/vpp.Dockerfile @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG VPP_VERSION=22.10 +ARG VPP_VERSION=23.06 ARG VPP_IMAGE=ligato/vpp-base:$VPP_VERSION FROM ${VPP_IMAGE} @@ -76,7 +76,7 @@ RUN set -ex; \ # there is a bug in VPP 21.06 that api files are not built on standard location # for external plugins, to reproduce it is enough to try to build sample-plugin RUN set -ex; \ - if [ "$VPP_VERSION" = "22.10" ] || [ "$VPP_VERSION" = "22.02" ]; \ + if [ "$VPP_VERSION" = "23.06" ] || [ "$VPP_VERSION" = "22.10" ] || [ "$VPP_VERSION" = "22.02" ]; \ then \ cp abx/build/CMakeFiles/vpp-api/vapi/* /usr/include/vapi/; \ elif [ "$VPP_VERSION" = "21.06" ]; \ diff --git a/docs/UPDATE_PROCEDURE.md b/docs/UPDATE_PROCEDURE.md index c3b0cefb..168a8bbb 100644 --- a/docs/UPDATE_PROCEDURE.md +++ b/docs/UPDATE_PROCEDURE.md @@ -11,7 +11,7 @@ StoneWork is based on *vpp-agent* and thus supports stable VPP versions, support Check whether `ligato/vpp-base`, with your desired version, already exists. To do so, just look at `ligato/vpp-base` tags on [DockerHub][dockerhub-tags]. Or directly by docker pull command, for example: -`docker pull ligato/vpp-base:22.10` +`docker pull ligato/vpp-base:23.06` If there is no such tagged version, you need to create it. @@ -80,16 +80,16 @@ StoneWork docker images are present on [GitHub Container Registry][ghcr]. To update images, create and push a git tag into image repository according to the following convention: `v..` (for example -`v22.10.0`), where `` may increase if VPP is updated by its patch version +`v23.06.0`), where `` may increase if VPP is updated by its patch version or if some change is submitted into the control-plane. This triggers build of the images in repository and tags StoneWork production image as `ghcr.io/pantheontech/stonework` with the following version tags: 1. Full git tag as-it-is, with trimmed leading '`v`', for example - `ghcr.io/pantheontech/stonework:22.10.0`. This tag is fixed + `ghcr.io/pantheontech/stonework:23.06.0`. This tag is fixed and should never be changed. 2. `.`, for example - `ghcr.io/pantheontech/stonework:22.10`. This tag points to the + `ghcr.io/pantheontech/stonework:23.06`. This tag points to the latest version with the same major and minor version number. 3. `latest`, for example `ghcr.io/pantheontech/stonework:latest`. @@ -99,7 +99,7 @@ while keeping the `latest` and `.` tags still updated. After that, all three tagged images are automatically pushed into GitHub Container Registry. -[dockerhub-tags]: https://hub.docker.com/r/ligato/vpp-base/tags?page=1&ordering=last_updated&name=22.10 +[dockerhub-tags]: https://hub.docker.com/r/ligato/vpp-base/tags?page=1&ordering=last_updated&name=23.06 [inspiration-pr]: https://github.com/ligato/vpp-base/pull/18 [agent-instructions]: https://github.com/ligato/vpp-agent/wiki/Guide-for-adding-new-VPP-version [ghcr]: https://github.com/orgs/PANTHEONtech/packages/container/package/stonework diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md index dd357d58..1f496ee4 100644 --- a/docs/USERGUIDE.md +++ b/docs/USERGUIDE.md @@ -20,7 +20,7 @@ StoneWork images are publicly available and can be pulled, [as described here](h ## Run To try out the StoneWork, we can simply run it as a Docker container: ``` -$ docker run -d --rm --name stonework -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:22.10 +$ docker run -d --rm --name stonework -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:23.06 ``` This will run a Docker container named *stonework* in the background. @@ -129,7 +129,7 @@ TAP Interface To create tap interface, we need to make the StoneWork container privileged, so running it as: ``` -$ docker run -d --rm --name stonework --privileged -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:22.10 +$ docker run -d --rm --name stonework --privileged -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:23.06 ``` Then we will use the configuration as: @@ -167,7 +167,7 @@ But this tap interface is present only inside our StoneWork container. What if w Then we need to use host network mode in our docker run one liner: ``` -$ docker run -it --rm --name stonework --privileged --network="host" -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:22.10 +$ docker run -it --rm --name stonework --privileged --network="host" -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:23.06 ``` For more details about tap interfaces and VPP, take a look [at this example][tap-example]. @@ -178,7 +178,7 @@ As our Docker run one-liner grows, it becomes better to use docker-compose, in t Lets rewrite the above mentioned docker run command, i.e.: ``` -$ docker run -it --rm --name stonework --privileged --network="host" -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:22.10 +$ docker run -it --rm --name stonework --privileged --network="host" -e ETCD_CONFIG="" ghcr.io/pantheontech/stonework:23.06 ``` into *docker-compose.yaml*: ``` @@ -187,7 +187,7 @@ version: '3.3' services: stonework: container_name: stonework - image: "ghcr.io/pantheontech/stonework:22.10" + image: "ghcr.io/pantheontech/stonework:23.06" privileged: true network_mode: "host" environment: @@ -258,7 +258,7 @@ version: '3.3' services: stonework: container_name: stonework - image: "ghcr.io/pantheontech/stonework:22.10" + image: "ghcr.io/pantheontech/stonework:23.06" privileged: true network_mode: "host" environment: @@ -435,7 +435,7 @@ volumes: # new services: stonework: container_name: stonework - image: "ghcr.io/pantheontech/stonework:22.10" + image: "ghcr.io/pantheontech/stonework:23.06" privileged: true network_mode: "host" environment: diff --git a/examples/getting-started/README.md b/examples/getting-started/README.md index 157d3f48..747e6900 100644 --- a/examples/getting-started/README.md +++ b/examples/getting-started/README.md @@ -66,7 +66,7 @@ The StoneWork container should be present almost immediately: ``` $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -83f034b16cd8 ghcr.io/pantheontech/stonework:22.10 "/bin/sh -c 'rm -f /…" 6 seconds ago Up 5 seconds stonework +83f034b16cd8 ghcr.io/pantheontech/stonework:23.06 "/bin/sh -c 'rm -f /…" 6 seconds ago Up 5 seconds stonework ``` It shouldn't take too long for StoneWork to initialize and apply the "day0" diff --git a/examples/getting-started/docker-compose.yaml b/examples/getting-started/docker-compose.yaml index d154210c..7115a3f5 100644 --- a/examples/getting-started/docker-compose.yaml +++ b/examples/getting-started/docker-compose.yaml @@ -21,7 +21,7 @@ volumes: services: stonework: - image: "ghcr.io/pantheontech/stonework:22.10" + image: "ghcr.io/pantheontech/stonework:23.06" privileged: true network_mode: "host" pid: "host" diff --git a/examples/nat-router/docker-compose.yaml b/examples/nat-router/docker-compose.yaml index a23286d6..e38e10de 100644 --- a/examples/nat-router/docker-compose.yaml +++ b/examples/nat-router/docker-compose.yaml @@ -21,7 +21,7 @@ volumes: services: stonework: - image: "ghcr.io/pantheontech/stonework:22.10" + image: "ghcr.io/pantheontech/stonework:23.06" privileged: true pid: "host" environment: diff --git a/examples/telemetry-monitoring/docker-compose.yaml b/examples/telemetry-monitoring/docker-compose.yaml index f1207b49..e530e6f7 100644 --- a/examples/telemetry-monitoring/docker-compose.yaml +++ b/examples/telemetry-monitoring/docker-compose.yaml @@ -18,7 +18,7 @@ version: '3.3' services: stonework: - image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:22.10} + image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:23.06} depends_on: - tester1 - tester2 @@ -37,7 +37,7 @@ services: - ./config:/etc/stonework/config tester1: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester1 @@ -48,7 +48,7 @@ services: ' tester2: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester2 diff --git a/examples/testing/010-xconnect/docker-compose.yaml b/examples/testing/010-xconnect/docker-compose.yaml index 2e20a127..4bfe80d7 100644 --- a/examples/testing/010-xconnect/docker-compose.yaml +++ b/examples/testing/010-xconnect/docker-compose.yaml @@ -18,7 +18,7 @@ version: '3.3' services: stonework: - image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:22.10} + image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:23.06} depends_on: - tester1 - tester2 @@ -36,7 +36,7 @@ services: - ./config:/etc/stonework/config tester1: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester1 @@ -47,7 +47,7 @@ services: ' tester2: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester2 diff --git a/examples/testing/020-switch/docker-compose.yaml b/examples/testing/020-switch/docker-compose.yaml index 2e20a127..4bfe80d7 100644 --- a/examples/testing/020-switch/docker-compose.yaml +++ b/examples/testing/020-switch/docker-compose.yaml @@ -18,7 +18,7 @@ version: '3.3' services: stonework: - image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:22.10} + image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:23.06} depends_on: - tester1 - tester2 @@ -36,7 +36,7 @@ services: - ./config:/etc/stonework/config tester1: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester1 @@ -47,7 +47,7 @@ services: ' tester2: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester2 diff --git a/examples/testing/030-router/docker-compose.yaml b/examples/testing/030-router/docker-compose.yaml index 6c93edb6..3674b828 100644 --- a/examples/testing/030-router/docker-compose.yaml +++ b/examples/testing/030-router/docker-compose.yaml @@ -18,7 +18,7 @@ version: '3.3' services: stonework: - image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:22.10} + image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:23.06} depends_on: - tester1 - tester2 @@ -37,7 +37,7 @@ services: - ./config:/etc/stonework/config tester1: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester1 @@ -52,7 +52,7 @@ services: ' tester2: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester2 @@ -63,7 +63,7 @@ services: ' tester3: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester3 diff --git a/examples/testing/040-router6/docker-compose.yaml b/examples/testing/040-router6/docker-compose.yaml index 6bb1314e..728d141a 100644 --- a/examples/testing/040-router6/docker-compose.yaml +++ b/examples/testing/040-router6/docker-compose.yaml @@ -18,7 +18,7 @@ version: '3.3' services: stonework: - image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:22.10} + image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:23.06} depends_on: - tester1 - tester2 @@ -37,7 +37,7 @@ services: - ./config:/etc/stonework/config tester1: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester1 @@ -48,7 +48,7 @@ services: ' tester2: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester2 @@ -61,7 +61,7 @@ services: ' tester3: - image: stonework-tester:22.10 + image: stonework-tester:23.06 privileged: true environment: MICROSERVICE_LABEL: tester3 diff --git a/examples/testing/100-mock-cnf-module/docker-compose.yaml b/examples/testing/100-mock-cnf-module/docker-compose.yaml index a7b06a26..e2ccbf1e 100644 --- a/examples/testing/100-mock-cnf-module/docker-compose.yaml +++ b/examples/testing/100-mock-cnf-module/docker-compose.yaml @@ -21,7 +21,7 @@ volumes: services: stonework: - image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:22.10} + image: ${STONEWORK_IMAGE:-ghcr.io/pantheontech/stonework:23.06} privileged: true pid: "host" environment: @@ -50,7 +50,7 @@ services: ' mockcnf1: - image: "stonework-mockcnf:22.10" + image: "stonework-mockcnf:23.06" depends_on: - stonework - router-ns @@ -68,7 +68,7 @@ services: network_mode: "service:router-ns" mockcnf2: - image: "stonework-mockcnf:22.10" + image: "stonework-mockcnf:23.06" depends_on: - stonework - router-ns diff --git a/examples/testing/110-mock-cnf-standalone/docker-compose.yaml b/examples/testing/110-mock-cnf-standalone/docker-compose.yaml index d3defd4d..dc87b0e9 100644 --- a/examples/testing/110-mock-cnf-standalone/docker-compose.yaml +++ b/examples/testing/110-mock-cnf-standalone/docker-compose.yaml @@ -18,7 +18,7 @@ version: '3.3' services: mockcnf1: - image: "stonework-mockcnf:22.10" + image: "stonework-mockcnf:23.06" privileged: true environment: INITIAL_LOGLVL: "debug" diff --git a/go.mod b/go.mod index 63a1eccf..b777e91b 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 go.fd.io/govpp v0.7.0 go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220211111933-3d9ff310b1fa - go.ligato.io/vpp-agent/v3 v3.5.0-alpha.0.20230602112701-a5dcb5a2d575 + go.ligato.io/vpp-agent/v3 v3.5.0-alpha.0.20230621123110-6c38360edf82 google.golang.org/grpc v1.47.0 google.golang.org/protobuf v1.28.2-0.20230222093303-bc1253ad3743 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index c433b431..ff42872b 100644 --- a/go.sum +++ b/go.sum @@ -929,8 +929,8 @@ go.fd.io/govpp v0.7.0 h1:8qideC7G0xPeYz2sjwni8GKWWbNk45Ev73oR1igKDYY= go.fd.io/govpp v0.7.0/go.mod h1:VxUPq8HGQH6/9IL9saMURL3UcHsUuN8XmETuao5HA7o= go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220211111933-3d9ff310b1fa h1:PbrmPqtS8K7jVVXbs9w2n0AzEqT3KGW/0bXWrSgEoOo= go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220211111933-3d9ff310b1fa/go.mod h1:N4rrtufgHchultyJuN8ClAS7szH3IhcfDcC3LX9cRRo= -go.ligato.io/vpp-agent/v3 v3.5.0-alpha.0.20230602112701-a5dcb5a2d575 h1:Oi54dXfcHFAMzFV/Mguy+lF5VAiHEpHFceXzVogo+vY= -go.ligato.io/vpp-agent/v3 v3.5.0-alpha.0.20230602112701-a5dcb5a2d575/go.mod h1:rbqXVq1y82p2rBzu++cW6ec1aSqZ3PxE1jPK5ywWefI= +go.ligato.io/vpp-agent/v3 v3.5.0-alpha.0.20230621123110-6c38360edf82 h1:vQSm2nTnWTk7OXI5crmEhofgmppB9vRe0t6zmAD6FxI= +go.ligato.io/vpp-agent/v3 v3.5.0-alpha.0.20230621123110-6c38360edf82/go.mod h1:rbqXVq1y82p2rBzu++cW6ec1aSqZ3PxE1jPK5ywWefI= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= diff --git a/plugins/abx/abxplugin.go b/plugins/abx/abxplugin.go index a24de687..0d59a24e 100644 --- a/plugins/abx/abxplugin.go +++ b/plugins/abx/abxplugin.go @@ -35,6 +35,7 @@ import ( _ "go.pantheon.tech/stonework/plugins/abx/vppcalls/vpp2106" _ "go.pantheon.tech/stonework/plugins/abx/vppcalls/vpp2202" _ "go.pantheon.tech/stonework/plugins/abx/vppcalls/vpp2210" + _ "go.pantheon.tech/stonework/plugins/abx/vppcalls/vpp2306" ) // ABXPlugin is a plugin that manages ACL-based forwarding. diff --git a/plugins/abx/vppcalls/vpp2306/abx_vppcalls.go b/plugins/abx/vppcalls/vpp2306/abx_vppcalls.go new file mode 100644 index 00000000..44880192 --- /dev/null +++ b/plugins/abx/vppcalls/vpp2306/abx_vppcalls.go @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + "fmt" + + "github.com/go-errors/errors" + + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/abx" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ethernet_types" +) + +// GetAbxVersion retrieves version of the VPP ABX plugin +func (h *ABXVppHandler) GetAbxVersion() (ver string, err error) { + req := &abx.AbxPluginGetVersion{} + reply := &abx.AbxPluginGetVersionReply{} + + if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { + return "", err + } + + return fmt.Sprintf("%d.%d", reply.Major, reply.Minor), nil +} + +// AddAbxPolicy creates new ABX entry +func (h *ABXVppHandler) AddAbxPolicy(policyID uint32, aclID uint32, txIf string, dstMac string) error { + if err := h.abxAddDelPolicy(policyID, aclID, txIf, dstMac, true); err != nil { + return errors.Errorf("failed to add ABX policy %d (ACL: %v): %v", policyID, aclID, err) + } + return nil +} + +// DeleteAbxPolicy removes existing ABX entry +func (h *ABXVppHandler) DeleteAbxPolicy(policyID uint32) error { + if err := h.abxAddDelPolicy(policyID, 0, "", "", false); err != nil { + return errors.Errorf("failed to delete ABX policy %d: %v", policyID, err) + } + return nil +} + +// AbxAttachInterface attaches interface to the ABF +func (h *ABXVppHandler) AbxAttachInterface(policyID, ifIdx, priority uint32) error { + if err := h.abxAttachDetachInterface(policyID, ifIdx, priority, true); err != nil { + return errors.Errorf("failed to attach interface %d to ABX policy %d: %v", ifIdx, policyID, err) + } + return nil +} + +// AbxDetachInterface detaches interface from the ABF +func (h *ABXVppHandler) AbxDetachInterface(policyID, ifIdx, priority uint32) error { + if err := h.abxAttachDetachInterface(policyID, ifIdx, priority, false); err != nil { + return errors.Errorf("failed to detach interface %d from ABX policy %d: %v", ifIdx, policyID, err) + } + return nil +} + +func (h *ABXVppHandler) abxAttachDetachInterface(policyID, ifIdx, priority uint32, isAttach bool) error { + req := &abx.AbxInterfaceAttachDetach{ + IsAttach: boolToUint(isAttach), + Attach: abx.AbxInterfaceAttach{ + PolicyID: policyID, + Priority: priority, + RxSwIfIndex: ifIdx, + }, + } + reply := &abx.AbxInterfaceAttachDetachReply{} + + return h.callsChannel.SendRequest(req).ReceiveReply(reply) +} + +func (h *ABXVppHandler) abxAddDelPolicy(policyID, aclID uint32, txInterface string, dstMac string, isAdd bool) (err error) { + var txSwIfIndex uint32 + if isAdd { + meta, found := h.ifIndexes.LookupByName(txInterface) + if !found { + return errors.Errorf("tx interface %s not found", txInterface) + } + txSwIfIndex = meta.SwIfIndex + } + var dstMacAddr ethernet_types.MacAddress + if dstMac != "" { + dstMacAddr, err = ethernet_types.ParseMacAddress(dstMac) + if err != nil { + return fmt.Errorf("parse dstMac error: %w", err) + } + } + + req := &abx.AbxPolicyAddDel{ + IsAdd: boolToUint(isAdd), + Policy: abx.AbxPolicy{ + PolicyID: policyID, + ACLIndex: aclID, + TxSwIfIndex: txSwIfIndex, + DstMac: dstMacAddr, + }, + } + reply := &abx.AbxPolicyAddDelReply{} + + return h.callsChannel.SendRequest(req).ReceiveReply(reply) +} + +func boolToUint(input bool) uint8 { + if input { + return 1 + } + return 0 +} diff --git a/plugins/abx/vppcalls/vpp2306/abx_vppcalls_test.go b/plugins/abx/vppcalls/vpp2306/abx_vppcalls_test.go new file mode 100644 index 00000000..e29556a0 --- /dev/null +++ b/plugins/abx/vppcalls/vpp2306/abx_vppcalls_test.go @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + "testing" + + . "github.com/onsi/gomega" + "go.ligato.io/cn-infra/v2/logging/logrus" + + "go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/aclidx" + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + "go.ligato.io/vpp-agent/v3/plugins/vpp/vppmock" + + "go.pantheon.tech/stonework/plugins/abx/vppcalls" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/abx" +) + +func abxTestSetup(t *testing.T) (*vppmock.TestCtx, vppcalls.ABXVppAPI, ifaceidx.IfaceMetadataIndexRW) { + ctx := vppmock.SetupTestCtx(t) + log := logrus.NewLogger("test-log") + aclIdx := aclidx.NewACLIndex(log, "acl-index") + ifIdx := ifaceidx.NewIfaceIndex(log, "if-index") + abxHandler := NewABXVppHandler(ctx.MockChannel, aclIdx, ifIdx, log) + return ctx, abxHandler, ifIdx +} + +func TestGetABXVersion(t *testing.T) { + ctx, abxHandler, _ := abxTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abx.AbxPluginGetVersionReply{ + Major: 1, + Minor: 0, + }) + version, err := abxHandler.GetAbxVersion() + + Expect(err).To(BeNil()) + Expect(version).To(Equal("1.0")) +} + +/* +func TestAddABXPolicy(t *testing.T) { + ctx, abxHandler, ifIndexes := abxTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbxPolicyAddDelReply{}) + + ifIndexes.Put("if1", &ifaceidx.IfaceMetadata{ + SwIfIndex: 5, + }) + ifIndexes.Put("if2", &ifaceidx.IfaceMetadata{ + SwIfIndex: 10, + }) + + err := abxHandler.AddAbxPolicy(1, 2, "if2", "aa:aa:aa:aa:aa:aa") + + Expect(err).To(BeNil()) + req, ok := ctx.MockChannel.Msg.(*abx.AbxPolicyAddDel) + Expect(ok).To(BeTrue()) + Expect(req.IsAdd).To(Equal(uint8(1))) + Expect(req.Policy.PolicyID).To(Equal(uint32(1))) + Expect(req.Policy.ACLIndex).To(Equal(uint32(2))) + // Expect(req.Policy.NPaths).To(Equal(uint8(2))) + // Expect(req.Policy.Paths[0].SwIfIndex).To(Equal(uint32(5))) + // Expect(req.Policy.Paths[0].NextHop[:4]).To(BeEquivalentTo(net.ParseIP("10.0.0.1").To4())) + // Expect(req.Policy.Paths[1].SwIfIndex).To(Equal(uint32(10))) + // Expect(req.Policy.Paths[1].NextHop).To(BeEquivalentTo(net.ParseIP("ffff::").To16())) +} + +func TestAddABXPolicyError(t *testing.T) { + ctx, abfHandler, _ := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfPolicyAddDelReply{ + Retval: 1, + }) + + err := abfHandler.AddAbfPolicy(1, 2, nil) + + Expect(err).ToNot(BeNil()) +} + +func TestDeleteABXPolicy(t *testing.T) { + ctx, abfHandler, ifIndexes := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfPolicyAddDelReply{}) + + ifIndexes.Put("if1", &ifaceidx.IfaceMetadata{ + SwIfIndex: 5, + }) + ifIndexes.Put("if2", &ifaceidx.IfaceMetadata{ + SwIfIndex: 10, + }) + + err := abfHandler.DeleteAbfPolicy(1, []*vpp_abf.ABF_ForwardingPath{ + { + InterfaceName: "if1", + NextHopIp: "10.0.0.1", + }, + { + InterfaceName: "if2", + NextHopIp: "ffff::", + }, + }) + + Expect(err).To(BeNil()) + req, ok := ctx.MockChannel.Msg.(*abf.AbfPolicyAddDel) + Expect(ok).To(BeTrue()) + Expect(req.IsAdd).To(Equal(uint8(0))) + Expect(req.Policy.PolicyID).To(Equal(uint32(1))) + Expect(req.Policy.NPaths).To(Equal(uint8(2))) + Expect(req.Policy.Paths[0].SwIfIndex).To(Equal(uint32(5))) + Expect(req.Policy.Paths[0].NextHop[:4]).To(BeEquivalentTo(net.ParseIP("10.0.0.1").To4())) + Expect(req.Policy.Paths[1].SwIfIndex).To(Equal(uint32(10))) + Expect(req.Policy.Paths[1].NextHop).To(BeEquivalentTo(net.ParseIP("ffff::").To16())) +} + +func TestDeleteABXPolicyError(t *testing.T) { + ctx, abfHandler, _ := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfPolicyAddDelReply{ + Retval: 1, + }) + + err := abfHandler.DeleteAbfPolicy(1, nil) + + Expect(err).ToNot(BeNil()) +} + +func TestAttachABXInterface(t *testing.T) { + ctx, abfHandler, _ := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfItfAttachAddDelReply{}) + + err := abfHandler.AbfAttachInterfaceIPv4(1, 2, 3) + + Expect(err).To(BeNil()) + req, ok := ctx.MockChannel.Msg.(*abf.AbfItfAttachAddDel) + Expect(ok).To(BeTrue()) + Expect(req.IsAdd).To(Equal(uint8(1))) + Expect(req.Attach.PolicyID).To(Equal(uint32(1))) + Expect(req.Attach.SwIfIndex).To(Equal(uint32(2))) + Expect(req.Attach.Priority).To(Equal(uint32(3))) + Expect(req.Attach.IsIPv6).To(Equal(uint8(0))) +} + +func TestAttachABXInterfaceError(t *testing.T) { + ctx, abfHandler, _ := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfItfAttachAddDelReply{ + Retval: -1, + }) + + err := abfHandler.AbfAttachInterfaceIPv4(1, 2, 3) + + Expect(err).ToNot(BeNil()) +} + +func TestDetachABXInterface(t *testing.T) { + ctx, abfHandler, _ := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfItfAttachAddDelReply{}) + + err := abfHandler.AbfDetachInterfaceIPv4(1, 2, 3) + + Expect(err).To(BeNil()) + req, ok := ctx.MockChannel.Msg.(*abf.AbfItfAttachAddDel) + Expect(ok).To(BeTrue()) + Expect(req.IsAdd).To(Equal(uint8(0))) + Expect(req.Attach.PolicyID).To(Equal(uint32(1))) + Expect(req.Attach.SwIfIndex).To(Equal(uint32(2))) + Expect(req.Attach.Priority).To(Equal(uint32(3))) + Expect(req.Attach.IsIPv6).To(Equal(uint8(0))) +} + +func TestDetachABXInterfaceError(t *testing.T) { + ctx, abfHandler, _ := abfTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&abf.AbfItfAttachAddDelReply{ + Retval: -1, + }) + + err := abfHandler.AbfDetachInterfaceIPv4(1, 2, 3) + + Expect(err).ToNot(BeNil()) +} +*/ diff --git a/plugins/abx/vppcalls/vpp2306/dump_abx_vppcalls.go b/plugins/abx/vppcalls/vpp2306/dump_abx_vppcalls.go new file mode 100644 index 00000000..3d49d738 --- /dev/null +++ b/plugins/abx/vppcalls/vpp2306/dump_abx_vppcalls.go @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + vpp_abx "go.pantheon.tech/stonework/proto/abx" + + "go.pantheon.tech/stonework/plugins/abx/vppcalls" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/abx" +) + +// placeholder for unknown names +const unknownName = "" + +// DumpABXPolicy retrieves VPP ABX configuration. +func (h *ABXVppHandler) DumpABXPolicy() ([]*vppcalls.ABXDetails, error) { + // retrieve ABX interfaces + attachedIfs, err := h.dumpABXInterfaces() + if err != nil { + return nil, err + } + + // retrieve ABX policy + abxPolicy, err := h.dumpABXPolicy() + if err != nil { + return nil, err + } + + // merge attached interfaces data to policy + for _, policy := range abxPolicy { + ifData, ok := attachedIfs[policy.Meta.PolicyID] + if ok { + policy.ABX.AttachedInterfaces = ifData + } + } + + return abxPolicy, nil +} + +func (h *ABXVppHandler) dumpABXInterfaces() (map[uint32][]*vpp_abx.ABX_AttachedInterface, error) { + // ABX index <-> attached interfaces + abxIfs := make(map[uint32][]*vpp_abx.ABX_AttachedInterface) + + req := &abx.AbxInterfaceAttachDump{} + reqCtx := h.callsChannel.SendMultiRequest(req) + + for { + reply := &abx.AbxInterfaceAttachDetails{} + last, err := reqCtx.ReceiveReply(reply) + if err != nil { + return nil, err + } + if last { + break + } + + // interface name + ifName, _, exists := h.ifIndexes.LookupBySwIfIndex(reply.Attach.RxSwIfIndex) + if !exists { + ifName = unknownName + } + + // attached interface entry + attached := &vpp_abx.ABX_AttachedInterface{ + InputInterface: ifName, + Priority: reply.Attach.Priority, + } + + _, ok := abxIfs[reply.Attach.PolicyID] + if !ok { + abxIfs[reply.Attach.PolicyID] = []*vpp_abx.ABX_AttachedInterface{} + } + abxIfs[reply.Attach.PolicyID] = append(abxIfs[reply.Attach.PolicyID], attached) + } + + return abxIfs, nil +} + +func (h *ABXVppHandler) dumpABXPolicy() ([]*vppcalls.ABXDetails, error) { + var abxs []*vppcalls.ABXDetails + + reqCtx := h.callsChannel.SendMultiRequest(&abx.AbxPolicyDump{}) + for { + reply := &abx.AbxPolicyDetails{} + last, err := reqCtx.ReceiveReply(reply) + if err != nil { + return nil, err + } + if last { + break + } + + // ACL name + aclName, _, exists := h.aclIndexes.LookupByIndex(reply.Policy.ACLIndex) + if !exists { + aclName = unknownName + } + + // output interface name + ifName, _, exists := h.ifIndexes.LookupBySwIfIndex(reply.Policy.TxSwIfIndex) + if !exists { + ifName = unknownName + } + dstMac := "" + if mac := reply.Policy.DstMac.String(); mac != "00:00:00:00:00:00" { + dstMac = mac + } + + abxData := &vppcalls.ABXDetails{ + ABX: &vpp_abx.ABX{ + Index: reply.Policy.PolicyID, + AclName: aclName, + OutputInterface: ifName, + DstMac: dstMac, + }, + Meta: &vppcalls.ABXMeta{ + PolicyID: reply.Policy.PolicyID, + }, + } + + abxs = append(abxs, abxData) + } + + return abxs, nil +} diff --git a/plugins/abx/vppcalls/vpp2306/vppcalls_handlers.go b/plugins/abx/vppcalls/vpp2306/vppcalls_handlers.go new file mode 100644 index 00000000..1a2992c9 --- /dev/null +++ b/plugins/abx/vppcalls/vpp2306/vppcalls_handlers.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + govppapi "go.fd.io/govpp/api" + "go.ligato.io/cn-infra/v2/logging" + + "go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/aclidx" + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + + "go.pantheon.tech/stonework/plugins/abx/vppcalls" + binapi "go.pantheon.tech/stonework/plugins/binapi/vpp2306" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/abx" +) + +func init() { + var msgs []govppapi.Message + msgs = append(msgs, abx.AllMessages()...) + + vppcalls.AddAbxHandlerVersion(binapi.Version, msgs, NewABXVppHandler) +} + +// ABXVppHandler is accessor for abx-related vppcalls methods +type ABXVppHandler struct { + callsChannel govppapi.Channel + aclIndexes aclidx.ACLMetadataIndex + ifIndexes ifaceidx.IfaceMetadataIndex + log logging.Logger +} + +// NewABXVppHandler returns new ABXVppHandler. +func NewABXVppHandler(calls govppapi.Channel, aclIdx aclidx.ACLMetadataIndex, ifIdx ifaceidx.IfaceMetadataIndex, + log logging.Logger) vppcalls.ABXVppAPI { + return &ABXVppHandler{ + callsChannel: calls, + aclIndexes: aclIdx, + ifIndexes: ifIdx, + log: log, + } +} diff --git a/plugins/bfd/bfdplugin.go b/plugins/bfd/bfdplugin.go index 739457c9..31517198 100644 --- a/plugins/bfd/bfdplugin.go +++ b/plugins/bfd/bfdplugin.go @@ -40,6 +40,7 @@ import ( _ "go.pantheon.tech/stonework/plugins/bfd/vppcalls/vpp2106" _ "go.pantheon.tech/stonework/plugins/bfd/vppcalls/vpp2202" _ "go.pantheon.tech/stonework/plugins/bfd/vppcalls/vpp2210" + _ "go.pantheon.tech/stonework/plugins/bfd/vppcalls/vpp2306" ) // BfdPlugin groups required BFD dependencies and descriptors diff --git a/plugins/bfd/vppcalls/vpp2306/bfd_vppcalls.go b/plugins/bfd/vppcalls/vpp2306/bfd_vppcalls.go new file mode 100644 index 00000000..e2559aba --- /dev/null +++ b/plugins/bfd/vppcalls/vpp2306/bfd_vppcalls.go @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + "context" + "errors" + "fmt" + "os" + "time" + + govppapi "go.fd.io/govpp/api" + + "go.pantheon.tech/stonework/plugins/bfd/vppcalls" + binapi "go.pantheon.tech/stonework/plugins/binapi/vpp2306/bfd" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/interface_types" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ip_types" + "go.pantheon.tech/stonework/proto/bfd" +) + +var ( + // EventDeliverTimeout defines maximum time to deliver event upstream. + EventDeliverTimeout = time.Second + + // NotificationChanBufferSize defines size of notification channel buffer. + NotificationChanBufferSize = 10 +) + +// AddBfd creates BFD session attached to the defined interface with given configuration ID. +func (h *BfdVppHandler) AddBfd(confID uint32, bfdEntry *bfd.BFD) error { + // interface + ifMeta, exists := h.ifIndexes.LookupByName(bfdEntry.Interface) + if !exists { + return fmt.Errorf("cannot configure BFD: interface %s is missing", bfdEntry.Interface) + } + + localAddr, err := ip_types.ParseAddress(bfdEntry.GetLocalIp()) + if err != nil { + return err + } + peerAddr, err := ip_types.ParseAddress(bfdEntry.GetPeerIp()) + if err != nil { + return err + } + if localAddr.Af != peerAddr.Af { + return fmt.Errorf("both IP addresses must be the same IP version") + } + req := &binapi.BfdUDPAdd{ + SwIfIndex: interface_types.InterfaceIndex(ifMeta.SwIfIndex), + DesiredMinTx: bfdEntry.GetMinTxInterval(), + RequiredMinRx: bfdEntry.GetMinRxInterval(), + LocalAddr: localAddr, + PeerAddr: peerAddr, + DetectMult: uint8(bfdEntry.GetDetectMultiplier()), + BfdKeyID: uint8(confID), + ConfKeyID: confID, + } + + resp := &binapi.BfdUDPAddReply{} + return h.callsChannel.SendRequest(req).ReceiveReply(resp) +} + +// DeletebfdEntry removes existing BFD session. +func (h *BfdVppHandler) DeleteBfd(bfdEntry *bfd.BFD) error { + ifMeta, exists := h.ifIndexes.LookupByName(bfdEntry.Interface) + if !exists { + return fmt.Errorf("cannot remove BFD: interface %s is missing", bfdEntry.Interface) + } + + localAddr, err := ip_types.ParseAddress(bfdEntry.GetLocalIp()) + if err != nil { + return err + } + peerAddr, err := ip_types.ParseAddress(bfdEntry.GetPeerIp()) + if err != nil { + return err + } + if localAddr.Af != peerAddr.Af { + return fmt.Errorf("both IP addresses must be the same IP version") + } + req := &binapi.BfdUDPDel{ + SwIfIndex: interface_types.InterfaceIndex(ifMeta.SwIfIndex), + LocalAddr: localAddr, + PeerAddr: peerAddr, + } + + resp := &binapi.BfdUDPDelReply{} + return h.callsChannel.SendRequest(req).ReceiveReply(resp) +} + +// DumpBfd returns retrieved BFD data together with BFD state. +func (h *BfdVppHandler) DumpBfd() ([]*vppcalls.BfdDetails, error) { + var bfdList []*vppcalls.BfdDetails + reqCtx := h.callsChannel.SendMultiRequest(&binapi.BfdUDPSessionDump{}) + for { + bfdEntryDetails := &binapi.BfdUDPSessionDetails{} + if stop, err := reqCtx.ReceiveReply(bfdEntryDetails); err != nil { + h.log.Error(err) + return nil, err + } else if stop { + break + } + ifName, _, exists := h.ifIndexes.LookupBySwIfIndex(uint32(bfdEntryDetails.SwIfIndex)) + if !exists { + return nil, fmt.Errorf("BFD interface with index %d is missing", bfdEntryDetails.SwIfIndex) + } + config := &bfd.BFD{ + Interface: ifName, + LocalIp: bfdEntryDetails.LocalAddr.String(), + PeerIp: bfdEntryDetails.PeerAddr.String(), + MinTxInterval: bfdEntryDetails.DesiredMinTx, + MinRxInterval: bfdEntryDetails.RequiredMinRx, + DetectMultiplier: uint32(bfdEntryDetails.DetectMult), + } + + bfdList = append(bfdList, &vppcalls.BfdDetails{ + Config: config, + State: stateToProto(bfdEntryDetails.State), + ConfKey: bfdEntryDetails.ConfKeyID, + BfdKey: bfdEntryDetails.BfdKeyID, + IsAuthenticated: bfdEntryDetails.IsAuthenticated, + }) + } + + return bfdList, nil +} + +// WatchBfdEvents starts BFD event watcher. +func (h *BfdVppHandler) WatchBfdEvents(ctx context.Context, eventChan chan<- *bfd.BFDEvent) error { + notificationChan := make(chan govppapi.Message, NotificationChanBufferSize) + + // subscribe to BFD notifications + sub, err := h.callsChannel.SubscribeNotification(notificationChan, &binapi.BfdUDPSessionDetails{}) + if err != nil { + return fmt.Errorf("subscribing to VPP notification (bfd_session_event) failed: %v", err) + } + unsubscribe := func() { + if err := sub.Unsubscribe(); err != nil { + h.log.Warnf("unsubscribing VPP notification (bfd_session_event) failed: %v", err) + } + } + + go func() { + h.log.Debugf("start watching BFD events") + defer h.log.Debugf("done watching BFD events (%v)", ctx.Err()) + + for { + select { + case e, open := <-notificationChan: + if !open { + h.log.Debugf("BFD events channel was closed") + unsubscribe() + return + } + + bfdEvent, ok := e.(*binapi.BfdUDPSessionDetails) + if !ok { + h.log.Debugf("unexpected notification type: %#v", bfdEvent) + continue + } + + event, err := h.toBfdEvent(bfdEvent) + if err != nil { + h.log.Warn(err) + continue + } + + select { + case eventChan <- event: + // ok + case <-ctx.Done(): + unsubscribe() + return + default: + // in case the channel is full + go func() { + select { + case eventChan <- event: + // sent ok + case <-time.After(EventDeliverTimeout): + h.log.Warnf("BFD (conf-ID: %d) event dropped, cannot deliver", bfdEvent.ConfKeyID) + } + }() + } + case <-ctx.Done(): + unsubscribe() + return + } + } + }() + + // enable BFD events from VPP + req := &binapi.WantBfdEvents{ + PID: uint32(os.Getpid()), + EnableDisable: true, + } + resp := &binapi.WantBfdEventsReply{} + err = h.callsChannel.SendRequest(req).ReceiveReply(resp) + // do not return error on repeated subscribe attempt + if errors.Is(err, govppapi.VPPApiError(govppapi.INVALID_REGISTRATION)) { + h.log.Debugf("already subscribed to BFD events: %v", err) + return nil + } + return err +} + +func (h *BfdVppHandler) toBfdEvent(bfdEvent *binapi.BfdUDPSessionDetails) (*bfd.BFDEvent, error) { + ifName, _, exists := h.ifIndexes.LookupBySwIfIndex(uint32(bfdEvent.SwIfIndex)) + if !exists { + return nil, fmt.Errorf("BFD event for unknown interface (sw_if_index: %d)", + bfdEvent.SwIfIndex) + } + event := &bfd.BFDEvent{ + Interface: ifName, + LocalIp: bfdEvent.LocalAddr.String(), + PeerIp: bfdEvent.PeerAddr.String(), + SessionState: stateToProto(bfdEvent.State), + } + return event, nil +} + +func stateToProto(state binapi.BfdState) bfd.BFDEvent_SessionState { + switch state { + case 1: + return bfd.BFDEvent_Down + case 2: + return bfd.BFDEvent_Init + case 3: + return bfd.BFDEvent_Up + } + return bfd.BFDEvent_Unknown +} diff --git a/plugins/bfd/vppcalls/vpp2306/vppcalls_handlers.go b/plugins/bfd/vppcalls/vpp2306/vppcalls_handlers.go new file mode 100644 index 00000000..d9487ee9 --- /dev/null +++ b/plugins/bfd/vppcalls/vpp2306/vppcalls_handlers.go @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + govppapi "go.fd.io/govpp/api" + "go.ligato.io/cn-infra/v2/logging" + + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + + "go.pantheon.tech/stonework/plugins/bfd/vppcalls" + binapi "go.pantheon.tech/stonework/plugins/binapi/vpp2306" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/bfd" +) + +func init() { + var msgs []govppapi.Message + msgs = append(msgs, bfd.AllMessages()...) + + vppcalls.AddBfdHandlerVersion(binapi.Version, msgs, NewBfdVppHandler) +} + +// BfdVppHandler is accessor for BFD-related vppcalls methods +type BfdVppHandler struct { + callsChannel govppapi.Channel + ifIndexes ifaceidx.IfaceMetadataIndex + log logging.Logger +} + +// NewBfdVppHandler creates new instance of BFD vppcalls handler +func NewBfdVppHandler(calls govppapi.Channel, ifIndexes ifaceidx.IfaceMetadataIndex, log logging.Logger) vppcalls.BfdVppAPI { + return &BfdVppHandler{ + callsChannel: calls, + ifIndexes: ifIndexes, + log: log, + } +} diff --git a/plugins/binapi/vpp2306/abx/abx.ba.go b/plugins/binapi/vpp2306/abx/abx.ba.go new file mode 100644 index 00000000..360d6a80 --- /dev/null +++ b/plugins/binapi/vpp2306/abx/abx.ba.go @@ -0,0 +1,442 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. +// versions: +// binapi-generator: v0.7.0 +// VPP: 23.06 +// source: /usr/share/vpp/api/plugins/abx.api.json + +// Package abx contains generated bindings for API file abx.api. +// +// Contents: +// - 2 structs +// - 10 messages +package abx + +import ( + api "go.fd.io/govpp/api" + codec "go.fd.io/govpp/codec" + ethernet_types "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ethernet_types" + _ "go.pantheon.tech/stonework/plugins/binapi/vpp2306/interface_types" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the GoVPP api package it is being compiled against. +// A compilation error at this line likely means your copy of the +// GoVPP api package needs to be updated. +const _ = api.GoVppAPIPackageIsVersion2 + +const ( + APIFile = "abx" + APIVersion = "0.1.0" + VersionCrc = 0x558e384 +) + +// AbxInterfaceAttach defines type 'abx_interface_attach'. +type AbxInterfaceAttach struct { + PolicyID uint32 `binapi:"u32,name=policy_id" json:"policy_id,omitempty"` + Priority uint32 `binapi:"u32,name=priority" json:"priority,omitempty"` + RxSwIfIndex uint32 `binapi:"u32,name=rx_sw_if_index" json:"rx_sw_if_index,omitempty"` +} + +// AbxPolicy defines type 'abx_policy'. +type AbxPolicy struct { + PolicyID uint32 `binapi:"u32,name=policy_id" json:"policy_id,omitempty"` + ACLIndex uint32 `binapi:"u32,name=acl_index" json:"acl_index,omitempty"` + TxSwIfIndex uint32 `binapi:"u32,name=tx_sw_if_index" json:"tx_sw_if_index,omitempty"` + DstMac ethernet_types.MacAddress `binapi:"mac_address,name=dst_mac" json:"dst_mac,omitempty"` +} + +// AbxInterfaceAttachDetach defines message 'abx_interface_attach_detach'. +// InProgress: the message form may change in the future versions +type AbxInterfaceAttachDetach struct { + IsAttach uint8 `binapi:"u8,name=is_attach" json:"is_attach,omitempty"` + Attach AbxInterfaceAttach `binapi:"abx_interface_attach,name=attach" json:"attach,omitempty"` +} + +func (m *AbxInterfaceAttachDetach) Reset() { *m = AbxInterfaceAttachDetach{} } +func (*AbxInterfaceAttachDetach) GetMessageName() string { return "abx_interface_attach_detach" } +func (*AbxInterfaceAttachDetach) GetCrcString() string { return "a09d5b0c" } +func (*AbxInterfaceAttachDetach) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *AbxInterfaceAttachDetach) Size() (size int) { + if m == nil { + return 0 + } + size += 1 // m.IsAttach + size += 4 // m.Attach.PolicyID + size += 4 // m.Attach.Priority + size += 4 // m.Attach.RxSwIfIndex + return size +} +func (m *AbxInterfaceAttachDetach) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint8(m.IsAttach) + buf.EncodeUint32(m.Attach.PolicyID) + buf.EncodeUint32(m.Attach.Priority) + buf.EncodeUint32(m.Attach.RxSwIfIndex) + return buf.Bytes(), nil +} +func (m *AbxInterfaceAttachDetach) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.IsAttach = buf.DecodeUint8() + m.Attach.PolicyID = buf.DecodeUint32() + m.Attach.Priority = buf.DecodeUint32() + m.Attach.RxSwIfIndex = buf.DecodeUint32() + return nil +} + +// AbxInterfaceAttachDetachReply defines message 'abx_interface_attach_detach_reply'. +// InProgress: the message form may change in the future versions +type AbxInterfaceAttachDetachReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *AbxInterfaceAttachDetachReply) Reset() { *m = AbxInterfaceAttachDetachReply{} } +func (*AbxInterfaceAttachDetachReply) GetMessageName() string { + return "abx_interface_attach_detach_reply" +} +func (*AbxInterfaceAttachDetachReply) GetCrcString() string { return "e8d4e804" } +func (*AbxInterfaceAttachDetachReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *AbxInterfaceAttachDetachReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *AbxInterfaceAttachDetachReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *AbxInterfaceAttachDetachReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// AbxInterfaceAttachDetails defines message 'abx_interface_attach_details'. +// InProgress: the message form may change in the future versions +type AbxInterfaceAttachDetails struct { + Attach AbxInterfaceAttach `binapi:"abx_interface_attach,name=attach" json:"attach,omitempty"` +} + +func (m *AbxInterfaceAttachDetails) Reset() { *m = AbxInterfaceAttachDetails{} } +func (*AbxInterfaceAttachDetails) GetMessageName() string { return "abx_interface_attach_details" } +func (*AbxInterfaceAttachDetails) GetCrcString() string { return "e7369b44" } +func (*AbxInterfaceAttachDetails) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *AbxInterfaceAttachDetails) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Attach.PolicyID + size += 4 // m.Attach.Priority + size += 4 // m.Attach.RxSwIfIndex + return size +} +func (m *AbxInterfaceAttachDetails) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.Attach.PolicyID) + buf.EncodeUint32(m.Attach.Priority) + buf.EncodeUint32(m.Attach.RxSwIfIndex) + return buf.Bytes(), nil +} +func (m *AbxInterfaceAttachDetails) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Attach.PolicyID = buf.DecodeUint32() + m.Attach.Priority = buf.DecodeUint32() + m.Attach.RxSwIfIndex = buf.DecodeUint32() + return nil +} + +// AbxInterfaceAttachDump defines message 'abx_interface_attach_dump'. +// InProgress: the message form may change in the future versions +type AbxInterfaceAttachDump struct{} + +func (m *AbxInterfaceAttachDump) Reset() { *m = AbxInterfaceAttachDump{} } +func (*AbxInterfaceAttachDump) GetMessageName() string { return "abx_interface_attach_dump" } +func (*AbxInterfaceAttachDump) GetCrcString() string { return "51077d14" } +func (*AbxInterfaceAttachDump) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *AbxInterfaceAttachDump) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *AbxInterfaceAttachDump) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *AbxInterfaceAttachDump) Unmarshal(b []byte) error { + return nil +} + +// AbxPluginGetVersion defines message 'abx_plugin_get_version'. +// InProgress: the message form may change in the future versions +type AbxPluginGetVersion struct{} + +func (m *AbxPluginGetVersion) Reset() { *m = AbxPluginGetVersion{} } +func (*AbxPluginGetVersion) GetMessageName() string { return "abx_plugin_get_version" } +func (*AbxPluginGetVersion) GetCrcString() string { return "51077d14" } +func (*AbxPluginGetVersion) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *AbxPluginGetVersion) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *AbxPluginGetVersion) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *AbxPluginGetVersion) Unmarshal(b []byte) error { + return nil +} + +// AbxPluginGetVersionReply defines message 'abx_plugin_get_version_reply'. +// InProgress: the message form may change in the future versions +type AbxPluginGetVersionReply struct { + Major uint32 `binapi:"u32,name=major" json:"major,omitempty"` + Minor uint32 `binapi:"u32,name=minor" json:"minor,omitempty"` +} + +func (m *AbxPluginGetVersionReply) Reset() { *m = AbxPluginGetVersionReply{} } +func (*AbxPluginGetVersionReply) GetMessageName() string { return "abx_plugin_get_version_reply" } +func (*AbxPluginGetVersionReply) GetCrcString() string { return "9b32cf86" } +func (*AbxPluginGetVersionReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *AbxPluginGetVersionReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Major + size += 4 // m.Minor + return size +} +func (m *AbxPluginGetVersionReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.Major) + buf.EncodeUint32(m.Minor) + return buf.Bytes(), nil +} +func (m *AbxPluginGetVersionReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Major = buf.DecodeUint32() + m.Minor = buf.DecodeUint32() + return nil +} + +// AbxPolicyAddDel defines message 'abx_policy_add_del'. +// InProgress: the message form may change in the future versions +type AbxPolicyAddDel struct { + IsAdd uint8 `binapi:"u8,name=is_add" json:"is_add,omitempty"` + Policy AbxPolicy `binapi:"abx_policy,name=policy" json:"policy,omitempty"` +} + +func (m *AbxPolicyAddDel) Reset() { *m = AbxPolicyAddDel{} } +func (*AbxPolicyAddDel) GetMessageName() string { return "abx_policy_add_del" } +func (*AbxPolicyAddDel) GetCrcString() string { return "f5ab75d9" } +func (*AbxPolicyAddDel) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *AbxPolicyAddDel) Size() (size int) { + if m == nil { + return 0 + } + size += 1 // m.IsAdd + size += 4 // m.Policy.PolicyID + size += 4 // m.Policy.ACLIndex + size += 4 // m.Policy.TxSwIfIndex + size += 1 * 6 // m.Policy.DstMac + return size +} +func (m *AbxPolicyAddDel) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint8(m.IsAdd) + buf.EncodeUint32(m.Policy.PolicyID) + buf.EncodeUint32(m.Policy.ACLIndex) + buf.EncodeUint32(m.Policy.TxSwIfIndex) + buf.EncodeBytes(m.Policy.DstMac[:], 6) + return buf.Bytes(), nil +} +func (m *AbxPolicyAddDel) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.IsAdd = buf.DecodeUint8() + m.Policy.PolicyID = buf.DecodeUint32() + m.Policy.ACLIndex = buf.DecodeUint32() + m.Policy.TxSwIfIndex = buf.DecodeUint32() + copy(m.Policy.DstMac[:], buf.DecodeBytes(6)) + return nil +} + +// AbxPolicyAddDelReply defines message 'abx_policy_add_del_reply'. +// InProgress: the message form may change in the future versions +type AbxPolicyAddDelReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *AbxPolicyAddDelReply) Reset() { *m = AbxPolicyAddDelReply{} } +func (*AbxPolicyAddDelReply) GetMessageName() string { return "abx_policy_add_del_reply" } +func (*AbxPolicyAddDelReply) GetCrcString() string { return "e8d4e804" } +func (*AbxPolicyAddDelReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *AbxPolicyAddDelReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *AbxPolicyAddDelReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *AbxPolicyAddDelReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// AbxPolicyDetails defines message 'abx_policy_details'. +// InProgress: the message form may change in the future versions +type AbxPolicyDetails struct { + Policy AbxPolicy `binapi:"abx_policy,name=policy" json:"policy,omitempty"` +} + +func (m *AbxPolicyDetails) Reset() { *m = AbxPolicyDetails{} } +func (*AbxPolicyDetails) GetMessageName() string { return "abx_policy_details" } +func (*AbxPolicyDetails) GetCrcString() string { return "1833567f" } +func (*AbxPolicyDetails) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *AbxPolicyDetails) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Policy.PolicyID + size += 4 // m.Policy.ACLIndex + size += 4 // m.Policy.TxSwIfIndex + size += 1 * 6 // m.Policy.DstMac + return size +} +func (m *AbxPolicyDetails) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.Policy.PolicyID) + buf.EncodeUint32(m.Policy.ACLIndex) + buf.EncodeUint32(m.Policy.TxSwIfIndex) + buf.EncodeBytes(m.Policy.DstMac[:], 6) + return buf.Bytes(), nil +} +func (m *AbxPolicyDetails) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Policy.PolicyID = buf.DecodeUint32() + m.Policy.ACLIndex = buf.DecodeUint32() + m.Policy.TxSwIfIndex = buf.DecodeUint32() + copy(m.Policy.DstMac[:], buf.DecodeBytes(6)) + return nil +} + +// AbxPolicyDump defines message 'abx_policy_dump'. +// InProgress: the message form may change in the future versions +type AbxPolicyDump struct{} + +func (m *AbxPolicyDump) Reset() { *m = AbxPolicyDump{} } +func (*AbxPolicyDump) GetMessageName() string { return "abx_policy_dump" } +func (*AbxPolicyDump) GetCrcString() string { return "51077d14" } +func (*AbxPolicyDump) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *AbxPolicyDump) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *AbxPolicyDump) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *AbxPolicyDump) Unmarshal(b []byte) error { + return nil +} + +func init() { file_abx_binapi_init() } +func file_abx_binapi_init() { + api.RegisterMessage((*AbxInterfaceAttachDetach)(nil), "abx_interface_attach_detach_a09d5b0c") + api.RegisterMessage((*AbxInterfaceAttachDetachReply)(nil), "abx_interface_attach_detach_reply_e8d4e804") + api.RegisterMessage((*AbxInterfaceAttachDetails)(nil), "abx_interface_attach_details_e7369b44") + api.RegisterMessage((*AbxInterfaceAttachDump)(nil), "abx_interface_attach_dump_51077d14") + api.RegisterMessage((*AbxPluginGetVersion)(nil), "abx_plugin_get_version_51077d14") + api.RegisterMessage((*AbxPluginGetVersionReply)(nil), "abx_plugin_get_version_reply_9b32cf86") + api.RegisterMessage((*AbxPolicyAddDel)(nil), "abx_policy_add_del_f5ab75d9") + api.RegisterMessage((*AbxPolicyAddDelReply)(nil), "abx_policy_add_del_reply_e8d4e804") + api.RegisterMessage((*AbxPolicyDetails)(nil), "abx_policy_details_1833567f") + api.RegisterMessage((*AbxPolicyDump)(nil), "abx_policy_dump_51077d14") +} + +// Messages returns list of all messages in this module. +func AllMessages() []api.Message { + return []api.Message{ + (*AbxInterfaceAttachDetach)(nil), + (*AbxInterfaceAttachDetachReply)(nil), + (*AbxInterfaceAttachDetails)(nil), + (*AbxInterfaceAttachDump)(nil), + (*AbxPluginGetVersion)(nil), + (*AbxPluginGetVersionReply)(nil), + (*AbxPolicyAddDel)(nil), + (*AbxPolicyAddDelReply)(nil), + (*AbxPolicyDetails)(nil), + (*AbxPolicyDump)(nil), + } +} diff --git a/plugins/binapi/vpp2306/abx/abx_rpc.ba.go b/plugins/binapi/vpp2306/abx/abx_rpc.ba.go new file mode 100644 index 00000000..31f8d236 --- /dev/null +++ b/plugins/binapi/vpp2306/abx/abx_rpc.ba.go @@ -0,0 +1,142 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. + +package abx + +import ( + "context" + "fmt" + "io" + + api "go.fd.io/govpp/api" + memclnt "go.pantheon.tech/stonework/plugins/binapi/vpp2306/memclnt" +) + +// RPCService defines RPC service abx. +type RPCService interface { + AbxInterfaceAttachDetach(ctx context.Context, in *AbxInterfaceAttachDetach) (*AbxInterfaceAttachDetachReply, error) + AbxInterfaceAttachDump(ctx context.Context, in *AbxInterfaceAttachDump) (RPCService_AbxInterfaceAttachDumpClient, error) + AbxPluginGetVersion(ctx context.Context, in *AbxPluginGetVersion) (*AbxPluginGetVersionReply, error) + AbxPolicyAddDel(ctx context.Context, in *AbxPolicyAddDel) (*AbxPolicyAddDelReply, error) + AbxPolicyDump(ctx context.Context, in *AbxPolicyDump) (RPCService_AbxPolicyDumpClient, error) +} + +type serviceClient struct { + conn api.Connection +} + +func NewServiceClient(conn api.Connection) RPCService { + return &serviceClient{conn} +} + +func (c *serviceClient) AbxInterfaceAttachDetach(ctx context.Context, in *AbxInterfaceAttachDetach) (*AbxInterfaceAttachDetachReply, error) { + out := new(AbxInterfaceAttachDetachReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) AbxInterfaceAttachDump(ctx context.Context, in *AbxInterfaceAttachDump) (RPCService_AbxInterfaceAttachDumpClient, error) { + stream, err := c.conn.NewStream(ctx) + if err != nil { + return nil, err + } + x := &serviceClient_AbxInterfaceAttachDumpClient{stream} + if err := x.Stream.SendMsg(in); err != nil { + return nil, err + } + if err = x.Stream.SendMsg(&memclnt.ControlPing{}); err != nil { + return nil, err + } + return x, nil +} + +type RPCService_AbxInterfaceAttachDumpClient interface { + Recv() (*AbxInterfaceAttachDetails, error) + api.Stream +} + +type serviceClient_AbxInterfaceAttachDumpClient struct { + api.Stream +} + +func (c *serviceClient_AbxInterfaceAttachDumpClient) Recv() (*AbxInterfaceAttachDetails, error) { + msg, err := c.Stream.RecvMsg() + if err != nil { + return nil, err + } + switch m := msg.(type) { + case *AbxInterfaceAttachDetails: + return m, nil + case *memclnt.ControlPingReply: + err = c.Stream.Close() + if err != nil { + return nil, err + } + return nil, io.EOF + default: + return nil, fmt.Errorf("unexpected message: %T %v", m, m) + } +} + +func (c *serviceClient) AbxPluginGetVersion(ctx context.Context, in *AbxPluginGetVersion) (*AbxPluginGetVersionReply, error) { + out := new(AbxPluginGetVersionReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) AbxPolicyAddDel(ctx context.Context, in *AbxPolicyAddDel) (*AbxPolicyAddDelReply, error) { + out := new(AbxPolicyAddDelReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) AbxPolicyDump(ctx context.Context, in *AbxPolicyDump) (RPCService_AbxPolicyDumpClient, error) { + stream, err := c.conn.NewStream(ctx) + if err != nil { + return nil, err + } + x := &serviceClient_AbxPolicyDumpClient{stream} + if err := x.Stream.SendMsg(in); err != nil { + return nil, err + } + if err = x.Stream.SendMsg(&memclnt.ControlPing{}); err != nil { + return nil, err + } + return x, nil +} + +type RPCService_AbxPolicyDumpClient interface { + Recv() (*AbxPolicyDetails, error) + api.Stream +} + +type serviceClient_AbxPolicyDumpClient struct { + api.Stream +} + +func (c *serviceClient_AbxPolicyDumpClient) Recv() (*AbxPolicyDetails, error) { + msg, err := c.Stream.RecvMsg() + if err != nil { + return nil, err + } + switch m := msg.(type) { + case *AbxPolicyDetails: + return m, nil + case *memclnt.ControlPingReply: + err = c.Stream.Close() + if err != nil { + return nil, err + } + return nil, io.EOF + default: + return nil, fmt.Errorf("unexpected message: %T %v", m, m) + } +} diff --git a/plugins/binapi/vpp2306/api/abx.api.json b/plugins/binapi/vpp2306/api/abx.api.json new file mode 100644 index 00000000..b7c8a0d0 --- /dev/null +++ b/plugins/binapi/vpp2306/api/abx.api.json @@ -0,0 +1,454 @@ +{ + "types": [ + [ + "abx_policy", + [ + "u32", + "policy_id" + ], + [ + "u32", + "acl_index" + ], + [ + "u32", + "tx_sw_if_index" + ], + [ + "vl_api_mac_address_t", + "dst_mac" + ] + ], + [ + "abx_interface_attach", + [ + "u32", + "policy_id" + ], + [ + "u32", + "priority" + ], + [ + "u32", + "rx_sw_if_index" + ] + ] + ], + "messages": [ + [ + "abx_plugin_get_version", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14", + "options": {} + } + ], + [ + "abx_plugin_get_version_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "u32", + "major" + ], + [ + "u32", + "minor" + ], + { + "crc": "0x9b32cf86", + "options": {} + } + ], + [ + "abx_policy_add_del", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u8", + "is_add" + ], + [ + "vl_api_abx_policy_t", + "policy" + ], + { + "crc": "0xf5ab75d9", + "options": {} + } + ], + [ + "abx_policy_add_del_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804", + "options": {} + } + ], + [ + "abx_interface_attach_detach", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u8", + "is_attach" + ], + [ + "vl_api_abx_interface_attach_t", + "attach" + ], + { + "crc": "0xa09d5b0c", + "options": {} + } + ], + [ + "abx_interface_attach_detach_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804", + "options": {} + } + ], + [ + "abx_policy_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_abx_policy_t", + "policy" + ], + { + "crc": "0x1833567f", + "options": {} + } + ], + [ + "abx_policy_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14", + "options": {} + } + ], + [ + "abx_interface_attach_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_abx_interface_attach_t", + "attach" + ], + { + "crc": "0xe7369b44", + "options": {} + } + ], + [ + "abx_interface_attach_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14", + "options": {} + } + ] + ], + "unions": [], + "enums": [ + [ + "if_status_flags", + [ + "IF_STATUS_API_FLAG_ADMIN_UP", + 1 + ], + [ + "IF_STATUS_API_FLAG_LINK_UP", + 2 + ], + { + "enumtype": "u32" + } + ], + [ + "mtu_proto", + [ + "MTU_PROTO_API_L3", + 0 + ], + [ + "MTU_PROTO_API_IP4", + 1 + ], + [ + "MTU_PROTO_API_IP6", + 2 + ], + [ + "MTU_PROTO_API_MPLS", + 3 + ], + { + "enumtype": "u32" + } + ], + [ + "link_duplex", + [ + "LINK_DUPLEX_API_UNKNOWN", + 0 + ], + [ + "LINK_DUPLEX_API_HALF", + 1 + ], + [ + "LINK_DUPLEX_API_FULL", + 2 + ], + { + "enumtype": "u32" + } + ], + [ + "sub_if_flags", + [ + "SUB_IF_API_FLAG_NO_TAGS", + 1 + ], + [ + "SUB_IF_API_FLAG_ONE_TAG", + 2 + ], + [ + "SUB_IF_API_FLAG_TWO_TAGS", + 4 + ], + [ + "SUB_IF_API_FLAG_DOT1AD", + 8 + ], + [ + "SUB_IF_API_FLAG_EXACT_MATCH", + 16 + ], + [ + "SUB_IF_API_FLAG_DEFAULT", + 32 + ], + [ + "SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY", + 64 + ], + [ + "SUB_IF_API_FLAG_INNER_VLAN_ID_ANY", + 128 + ], + [ + "SUB_IF_API_FLAG_MASK_VNET", + 254 + ], + [ + "SUB_IF_API_FLAG_DOT1AH", + 256 + ], + { + "enumtype": "u32" + } + ], + [ + "rx_mode", + [ + "RX_MODE_API_UNKNOWN", + 0 + ], + [ + "RX_MODE_API_POLLING", + 1 + ], + [ + "RX_MODE_API_INTERRUPT", + 2 + ], + [ + "RX_MODE_API_ADAPTIVE", + 3 + ], + [ + "RX_MODE_API_DEFAULT", + 4 + ], + { + "enumtype": "u32" + } + ], + [ + "if_type", + [ + "IF_API_TYPE_HARDWARE", + 0 + ], + [ + "IF_API_TYPE_SUB", + 1 + ], + [ + "IF_API_TYPE_P2P", + 2 + ], + [ + "IF_API_TYPE_PIPE", + 3 + ], + { + "enumtype": "u32" + } + ], + [ + "direction", + [ + "RX", + 0 + ], + [ + "TX", + 1 + ], + { + "enumtype": "u8" + } + ] + ], + "enumflags": [], + "services": { + "abx_plugin_get_version": { + "reply": "abx_plugin_get_version_reply" + }, + "abx_policy_add_del": { + "reply": "abx_policy_add_del_reply" + }, + "abx_interface_attach_detach": { + "reply": "abx_interface_attach_detach_reply" + }, + "abx_policy_dump": { + "reply": "abx_policy_details", + "stream": true + }, + "abx_interface_attach_dump": { + "reply": "abx_interface_attach_details", + "stream": true + } + }, + "options": { + "version": "0.1.0" + }, + "aliases": { + "interface_index": { + "type": "u32" + }, + "mac_address": { + "type": "u8", + "length": 6 + } + }, + "vl_api_version": "0x558e384", + "imports": [ + "vnet/interface_types.api", + "vnet/ethernet/ethernet_types.api" + ], + "counters": [], + "paths": [] +} diff --git a/plugins/binapi/vpp2306/api/isisx.api.json b/plugins/binapi/vpp2306/api/isisx.api.json new file mode 100644 index 00000000..ea51b8c3 --- /dev/null +++ b/plugins/binapi/vpp2306/api/isisx.api.json @@ -0,0 +1,335 @@ +{ + "types": [ + [ + "isisx_connection", + [ + "u32", + "rx_sw_if_index" + ], + [ + "u32", + "tx_sw_if_index" + ] + ] + ], + "messages": [ + [ + "isisx_plugin_get_version", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14", + "options": {} + } + ], + [ + "isisx_plugin_get_version_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "u32", + "major" + ], + [ + "u32", + "minor" + ], + { + "crc": "0x9b32cf86", + "options": {} + } + ], + [ + "isisx_connection_add_del", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u8", + "is_add" + ], + [ + "vl_api_isisx_connection_t", + "connection" + ], + { + "crc": "0x2bbf55c3", + "options": {} + } + ], + [ + "isisx_connection_add_del_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804", + "options": {} + } + ], + [ + "isisx_connection_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_isisx_connection_t", + "connection" + ], + { + "crc": "0x4b667522", + "options": {} + } + ], + [ + "isisx_connection_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14", + "options": {} + } + ] + ], + "unions": [], + "enums": [ + [ + "if_status_flags", + [ + "IF_STATUS_API_FLAG_ADMIN_UP", + 1 + ], + [ + "IF_STATUS_API_FLAG_LINK_UP", + 2 + ], + { + "enumtype": "u32" + } + ], + [ + "mtu_proto", + [ + "MTU_PROTO_API_L3", + 0 + ], + [ + "MTU_PROTO_API_IP4", + 1 + ], + [ + "MTU_PROTO_API_IP6", + 2 + ], + [ + "MTU_PROTO_API_MPLS", + 3 + ], + { + "enumtype": "u32" + } + ], + [ + "link_duplex", + [ + "LINK_DUPLEX_API_UNKNOWN", + 0 + ], + [ + "LINK_DUPLEX_API_HALF", + 1 + ], + [ + "LINK_DUPLEX_API_FULL", + 2 + ], + { + "enumtype": "u32" + } + ], + [ + "sub_if_flags", + [ + "SUB_IF_API_FLAG_NO_TAGS", + 1 + ], + [ + "SUB_IF_API_FLAG_ONE_TAG", + 2 + ], + [ + "SUB_IF_API_FLAG_TWO_TAGS", + 4 + ], + [ + "SUB_IF_API_FLAG_DOT1AD", + 8 + ], + [ + "SUB_IF_API_FLAG_EXACT_MATCH", + 16 + ], + [ + "SUB_IF_API_FLAG_DEFAULT", + 32 + ], + [ + "SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY", + 64 + ], + [ + "SUB_IF_API_FLAG_INNER_VLAN_ID_ANY", + 128 + ], + [ + "SUB_IF_API_FLAG_MASK_VNET", + 254 + ], + [ + "SUB_IF_API_FLAG_DOT1AH", + 256 + ], + { + "enumtype": "u32" + } + ], + [ + "rx_mode", + [ + "RX_MODE_API_UNKNOWN", + 0 + ], + [ + "RX_MODE_API_POLLING", + 1 + ], + [ + "RX_MODE_API_INTERRUPT", + 2 + ], + [ + "RX_MODE_API_ADAPTIVE", + 3 + ], + [ + "RX_MODE_API_DEFAULT", + 4 + ], + { + "enumtype": "u32" + } + ], + [ + "if_type", + [ + "IF_API_TYPE_HARDWARE", + 0 + ], + [ + "IF_API_TYPE_SUB", + 1 + ], + [ + "IF_API_TYPE_P2P", + 2 + ], + [ + "IF_API_TYPE_PIPE", + 3 + ], + { + "enumtype": "u32" + } + ], + [ + "direction", + [ + "RX", + 0 + ], + [ + "TX", + 1 + ], + { + "enumtype": "u8" + } + ] + ], + "enumflags": [], + "services": { + "isisx_plugin_get_version": { + "reply": "isisx_plugin_get_version_reply" + }, + "isisx_connection_add_del": { + "reply": "isisx_connection_add_del_reply" + }, + "isisx_connection_dump": { + "reply": "isisx_connection_details", + "stream": true + } + }, + "options": { + "version": "0.1.0" + }, + "aliases": { + "interface_index": { + "type": "u32" + } + }, + "vl_api_version": "0x8b24933", + "imports": [ + "vnet/interface_types.api" + ], + "counters": [], + "paths": [] +} diff --git a/plugins/binapi/vpp2306/bfd/bfd.ba.go b/plugins/binapi/vpp2306/bfd/bfd.ba.go new file mode 100644 index 00000000..8550da72 --- /dev/null +++ b/plugins/binapi/vpp2306/bfd/bfd.ba.go @@ -0,0 +1,1456 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. +// versions: +// binapi-generator: v0.7.0 +// VPP: 23.06 +// source: /usr/share/vpp/api/core/bfd.api.json + +// Package bfd contains generated bindings for API file bfd.api. +// +// Contents: +// - 1 enum +// - 31 messages +package bfd + +import ( + "strconv" + + api "go.fd.io/govpp/api" + codec "go.fd.io/govpp/codec" + interface_types "go.pantheon.tech/stonework/plugins/binapi/vpp2306/interface_types" + ip_types "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ip_types" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the GoVPP api package it is being compiled against. +// A compilation error at this line likely means your copy of the +// GoVPP api package needs to be updated. +const _ = api.GoVppAPIPackageIsVersion2 + +const ( + APIFile = "bfd" + APIVersion = "2.0.0" + VersionCrc = 0xe65443a6 +) + +// BfdState defines enum 'bfd_state'. +type BfdState uint32 + +const ( + BFD_STATE_API_ADMIN_DOWN BfdState = 0 + BFD_STATE_API_DOWN BfdState = 1 + BFD_STATE_API_INIT BfdState = 2 + BFD_STATE_API_UP BfdState = 3 +) + +var ( + BfdState_name = map[uint32]string{ + 0: "BFD_STATE_API_ADMIN_DOWN", + 1: "BFD_STATE_API_DOWN", + 2: "BFD_STATE_API_INIT", + 3: "BFD_STATE_API_UP", + } + BfdState_value = map[string]uint32{ + "BFD_STATE_API_ADMIN_DOWN": 0, + "BFD_STATE_API_DOWN": 1, + "BFD_STATE_API_INIT": 2, + "BFD_STATE_API_UP": 3, + } +) + +func (x BfdState) String() string { + s, ok := BfdState_name[uint32(x)] + if ok { + return s + } + return "BfdState(" + strconv.Itoa(int(x)) + ")" +} + +// BfdAuthDelKey defines message 'bfd_auth_del_key'. +type BfdAuthDelKey struct { + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` +} + +func (m *BfdAuthDelKey) Reset() { *m = BfdAuthDelKey{} } +func (*BfdAuthDelKey) GetMessageName() string { return "bfd_auth_del_key" } +func (*BfdAuthDelKey) GetCrcString() string { return "65310b22" } +func (*BfdAuthDelKey) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdAuthDelKey) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.ConfKeyID + return size +} +func (m *BfdAuthDelKey) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.ConfKeyID) + return buf.Bytes(), nil +} +func (m *BfdAuthDelKey) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.ConfKeyID = buf.DecodeUint32() + return nil +} + +// BfdAuthDelKeyReply defines message 'bfd_auth_del_key_reply'. +type BfdAuthDelKeyReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdAuthDelKeyReply) Reset() { *m = BfdAuthDelKeyReply{} } +func (*BfdAuthDelKeyReply) GetMessageName() string { return "bfd_auth_del_key_reply" } +func (*BfdAuthDelKeyReply) GetCrcString() string { return "e8d4e804" } +func (*BfdAuthDelKeyReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdAuthDelKeyReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdAuthDelKeyReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdAuthDelKeyReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdAuthKeysDetails defines message 'bfd_auth_keys_details'. +type BfdAuthKeysDetails struct { + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` + UseCount uint32 `binapi:"u32,name=use_count" json:"use_count,omitempty"` + AuthType uint8 `binapi:"u8,name=auth_type" json:"auth_type,omitempty"` +} + +func (m *BfdAuthKeysDetails) Reset() { *m = BfdAuthKeysDetails{} } +func (*BfdAuthKeysDetails) GetMessageName() string { return "bfd_auth_keys_details" } +func (*BfdAuthKeysDetails) GetCrcString() string { return "84130e9f" } +func (*BfdAuthKeysDetails) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdAuthKeysDetails) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.ConfKeyID + size += 4 // m.UseCount + size += 1 // m.AuthType + return size +} +func (m *BfdAuthKeysDetails) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.ConfKeyID) + buf.EncodeUint32(m.UseCount) + buf.EncodeUint8(m.AuthType) + return buf.Bytes(), nil +} +func (m *BfdAuthKeysDetails) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.ConfKeyID = buf.DecodeUint32() + m.UseCount = buf.DecodeUint32() + m.AuthType = buf.DecodeUint8() + return nil +} + +// BfdAuthKeysDump defines message 'bfd_auth_keys_dump'. +type BfdAuthKeysDump struct{} + +func (m *BfdAuthKeysDump) Reset() { *m = BfdAuthKeysDump{} } +func (*BfdAuthKeysDump) GetMessageName() string { return "bfd_auth_keys_dump" } +func (*BfdAuthKeysDump) GetCrcString() string { return "51077d14" } +func (*BfdAuthKeysDump) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdAuthKeysDump) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *BfdAuthKeysDump) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *BfdAuthKeysDump) Unmarshal(b []byte) error { + return nil +} + +// BfdAuthSetKey defines message 'bfd_auth_set_key'. +type BfdAuthSetKey struct { + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` + KeyLen uint8 `binapi:"u8,name=key_len" json:"key_len,omitempty"` + AuthType uint8 `binapi:"u8,name=auth_type" json:"auth_type,omitempty"` + Key []byte `binapi:"u8[20],name=key" json:"key,omitempty"` +} + +func (m *BfdAuthSetKey) Reset() { *m = BfdAuthSetKey{} } +func (*BfdAuthSetKey) GetMessageName() string { return "bfd_auth_set_key" } +func (*BfdAuthSetKey) GetCrcString() string { return "690b8877" } +func (*BfdAuthSetKey) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdAuthSetKey) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.ConfKeyID + size += 1 // m.KeyLen + size += 1 // m.AuthType + size += 1 * 20 // m.Key + return size +} +func (m *BfdAuthSetKey) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.ConfKeyID) + buf.EncodeUint8(m.KeyLen) + buf.EncodeUint8(m.AuthType) + buf.EncodeBytes(m.Key, 20) + return buf.Bytes(), nil +} +func (m *BfdAuthSetKey) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.ConfKeyID = buf.DecodeUint32() + m.KeyLen = buf.DecodeUint8() + m.AuthType = buf.DecodeUint8() + m.Key = make([]byte, 20) + copy(m.Key, buf.DecodeBytes(len(m.Key))) + return nil +} + +// BfdAuthSetKeyReply defines message 'bfd_auth_set_key_reply'. +type BfdAuthSetKeyReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdAuthSetKeyReply) Reset() { *m = BfdAuthSetKeyReply{} } +func (*BfdAuthSetKeyReply) GetMessageName() string { return "bfd_auth_set_key_reply" } +func (*BfdAuthSetKeyReply) GetCrcString() string { return "e8d4e804" } +func (*BfdAuthSetKeyReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdAuthSetKeyReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdAuthSetKeyReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdAuthSetKeyReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPAdd defines message 'bfd_udp_add'. +type BfdUDPAdd struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + DesiredMinTx uint32 `binapi:"u32,name=desired_min_tx" json:"desired_min_tx,omitempty"` + RequiredMinRx uint32 `binapi:"u32,name=required_min_rx" json:"required_min_rx,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + DetectMult uint8 `binapi:"u8,name=detect_mult" json:"detect_mult,omitempty"` + IsAuthenticated bool `binapi:"bool,name=is_authenticated" json:"is_authenticated,omitempty"` + BfdKeyID uint8 `binapi:"u8,name=bfd_key_id" json:"bfd_key_id,omitempty"` + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` +} + +func (m *BfdUDPAdd) Reset() { *m = BfdUDPAdd{} } +func (*BfdUDPAdd) GetMessageName() string { return "bfd_udp_add" } +func (*BfdUDPAdd) GetCrcString() string { return "939cd26a" } +func (*BfdUDPAdd) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPAdd) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 4 // m.DesiredMinTx + size += 4 // m.RequiredMinRx + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 1 // m.DetectMult + size += 1 // m.IsAuthenticated + size += 1 // m.BfdKeyID + size += 4 // m.ConfKeyID + return size +} +func (m *BfdUDPAdd) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint32(m.DesiredMinTx) + buf.EncodeUint32(m.RequiredMinRx) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(m.DetectMult) + buf.EncodeBool(m.IsAuthenticated) + buf.EncodeUint8(m.BfdKeyID) + buf.EncodeUint32(m.ConfKeyID) + return buf.Bytes(), nil +} +func (m *BfdUDPAdd) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.DesiredMinTx = buf.DecodeUint32() + m.RequiredMinRx = buf.DecodeUint32() + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.DetectMult = buf.DecodeUint8() + m.IsAuthenticated = buf.DecodeBool() + m.BfdKeyID = buf.DecodeUint8() + m.ConfKeyID = buf.DecodeUint32() + return nil +} + +// BfdUDPAddReply defines message 'bfd_udp_add_reply'. +type BfdUDPAddReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPAddReply) Reset() { *m = BfdUDPAddReply{} } +func (*BfdUDPAddReply) GetMessageName() string { return "bfd_udp_add_reply" } +func (*BfdUDPAddReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPAddReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPAddReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPAddReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPAddReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPAuthActivate defines message 'bfd_udp_auth_activate'. +type BfdUDPAuthActivate struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + IsDelayed bool `binapi:"bool,name=is_delayed" json:"is_delayed,omitempty"` + BfdKeyID uint8 `binapi:"u8,name=bfd_key_id" json:"bfd_key_id,omitempty"` + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` +} + +func (m *BfdUDPAuthActivate) Reset() { *m = BfdUDPAuthActivate{} } +func (*BfdUDPAuthActivate) GetMessageName() string { return "bfd_udp_auth_activate" } +func (*BfdUDPAuthActivate) GetCrcString() string { return "21fd1bdb" } +func (*BfdUDPAuthActivate) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPAuthActivate) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 1 // m.IsDelayed + size += 1 // m.BfdKeyID + size += 4 // m.ConfKeyID + return size +} +func (m *BfdUDPAuthActivate) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeBool(m.IsDelayed) + buf.EncodeUint8(m.BfdKeyID) + buf.EncodeUint32(m.ConfKeyID) + return buf.Bytes(), nil +} +func (m *BfdUDPAuthActivate) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.IsDelayed = buf.DecodeBool() + m.BfdKeyID = buf.DecodeUint8() + m.ConfKeyID = buf.DecodeUint32() + return nil +} + +// BfdUDPAuthActivateReply defines message 'bfd_udp_auth_activate_reply'. +type BfdUDPAuthActivateReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPAuthActivateReply) Reset() { *m = BfdUDPAuthActivateReply{} } +func (*BfdUDPAuthActivateReply) GetMessageName() string { return "bfd_udp_auth_activate_reply" } +func (*BfdUDPAuthActivateReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPAuthActivateReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPAuthActivateReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPAuthActivateReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPAuthActivateReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPAuthDeactivate defines message 'bfd_udp_auth_deactivate'. +type BfdUDPAuthDeactivate struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + IsDelayed bool `binapi:"bool,name=is_delayed" json:"is_delayed,omitempty"` +} + +func (m *BfdUDPAuthDeactivate) Reset() { *m = BfdUDPAuthDeactivate{} } +func (*BfdUDPAuthDeactivate) GetMessageName() string { return "bfd_udp_auth_deactivate" } +func (*BfdUDPAuthDeactivate) GetCrcString() string { return "9a05e2e0" } +func (*BfdUDPAuthDeactivate) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPAuthDeactivate) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 1 // m.IsDelayed + return size +} +func (m *BfdUDPAuthDeactivate) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeBool(m.IsDelayed) + return buf.Bytes(), nil +} +func (m *BfdUDPAuthDeactivate) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.IsDelayed = buf.DecodeBool() + return nil +} + +// BfdUDPAuthDeactivateReply defines message 'bfd_udp_auth_deactivate_reply'. +type BfdUDPAuthDeactivateReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPAuthDeactivateReply) Reset() { *m = BfdUDPAuthDeactivateReply{} } +func (*BfdUDPAuthDeactivateReply) GetMessageName() string { return "bfd_udp_auth_deactivate_reply" } +func (*BfdUDPAuthDeactivateReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPAuthDeactivateReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPAuthDeactivateReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPAuthDeactivateReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPAuthDeactivateReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPDel defines message 'bfd_udp_del'. +type BfdUDPDel struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` +} + +func (m *BfdUDPDel) Reset() { *m = BfdUDPDel{} } +func (*BfdUDPDel) GetMessageName() string { return "bfd_udp_del" } +func (*BfdUDPDel) GetCrcString() string { return "dcb13a89" } +func (*BfdUDPDel) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPDel) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + return size +} +func (m *BfdUDPDel) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + return buf.Bytes(), nil +} +func (m *BfdUDPDel) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + return nil +} + +// BfdUDPDelEchoSource defines message 'bfd_udp_del_echo_source'. +type BfdUDPDelEchoSource struct{} + +func (m *BfdUDPDelEchoSource) Reset() { *m = BfdUDPDelEchoSource{} } +func (*BfdUDPDelEchoSource) GetMessageName() string { return "bfd_udp_del_echo_source" } +func (*BfdUDPDelEchoSource) GetCrcString() string { return "51077d14" } +func (*BfdUDPDelEchoSource) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPDelEchoSource) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *BfdUDPDelEchoSource) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *BfdUDPDelEchoSource) Unmarshal(b []byte) error { + return nil +} + +// BfdUDPDelEchoSourceReply defines message 'bfd_udp_del_echo_source_reply'. +type BfdUDPDelEchoSourceReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPDelEchoSourceReply) Reset() { *m = BfdUDPDelEchoSourceReply{} } +func (*BfdUDPDelEchoSourceReply) GetMessageName() string { return "bfd_udp_del_echo_source_reply" } +func (*BfdUDPDelEchoSourceReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPDelEchoSourceReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPDelEchoSourceReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPDelEchoSourceReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPDelEchoSourceReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPDelReply defines message 'bfd_udp_del_reply'. +type BfdUDPDelReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPDelReply) Reset() { *m = BfdUDPDelReply{} } +func (*BfdUDPDelReply) GetMessageName() string { return "bfd_udp_del_reply" } +func (*BfdUDPDelReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPDelReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPDelReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPDelReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPDelReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPGetEchoSource defines message 'bfd_udp_get_echo_source'. +type BfdUDPGetEchoSource struct{} + +func (m *BfdUDPGetEchoSource) Reset() { *m = BfdUDPGetEchoSource{} } +func (*BfdUDPGetEchoSource) GetMessageName() string { return "bfd_udp_get_echo_source" } +func (*BfdUDPGetEchoSource) GetCrcString() string { return "51077d14" } +func (*BfdUDPGetEchoSource) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPGetEchoSource) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *BfdUDPGetEchoSource) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *BfdUDPGetEchoSource) Unmarshal(b []byte) error { + return nil +} + +// BfdUDPGetEchoSourceReply defines message 'bfd_udp_get_echo_source_reply'. +type BfdUDPGetEchoSourceReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + IsSet bool `binapi:"bool,name=is_set" json:"is_set,omitempty"` + HaveUsableIP4 bool `binapi:"bool,name=have_usable_ip4" json:"have_usable_ip4,omitempty"` + IP4Addr ip_types.IP4Address `binapi:"ip4_address,name=ip4_addr" json:"ip4_addr,omitempty"` + HaveUsableIP6 bool `binapi:"bool,name=have_usable_ip6" json:"have_usable_ip6,omitempty"` + IP6Addr ip_types.IP6Address `binapi:"ip6_address,name=ip6_addr" json:"ip6_addr,omitempty"` +} + +func (m *BfdUDPGetEchoSourceReply) Reset() { *m = BfdUDPGetEchoSourceReply{} } +func (*BfdUDPGetEchoSourceReply) GetMessageName() string { return "bfd_udp_get_echo_source_reply" } +func (*BfdUDPGetEchoSourceReply) GetCrcString() string { return "e3d736a1" } +func (*BfdUDPGetEchoSourceReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPGetEchoSourceReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + size += 4 // m.SwIfIndex + size += 1 // m.IsSet + size += 1 // m.HaveUsableIP4 + size += 1 * 4 // m.IP4Addr + size += 1 // m.HaveUsableIP6 + size += 1 * 16 // m.IP6Addr + return size +} +func (m *BfdUDPGetEchoSourceReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeBool(m.IsSet) + buf.EncodeBool(m.HaveUsableIP4) + buf.EncodeBytes(m.IP4Addr[:], 4) + buf.EncodeBool(m.HaveUsableIP6) + buf.EncodeBytes(m.IP6Addr[:], 16) + return buf.Bytes(), nil +} +func (m *BfdUDPGetEchoSourceReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.IsSet = buf.DecodeBool() + m.HaveUsableIP4 = buf.DecodeBool() + copy(m.IP4Addr[:], buf.DecodeBytes(4)) + m.HaveUsableIP6 = buf.DecodeBool() + copy(m.IP6Addr[:], buf.DecodeBytes(16)) + return nil +} + +// BfdUDPMod defines message 'bfd_udp_mod'. +type BfdUDPMod struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + DesiredMinTx uint32 `binapi:"u32,name=desired_min_tx" json:"desired_min_tx,omitempty"` + RequiredMinRx uint32 `binapi:"u32,name=required_min_rx" json:"required_min_rx,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + DetectMult uint8 `binapi:"u8,name=detect_mult" json:"detect_mult,omitempty"` +} + +func (m *BfdUDPMod) Reset() { *m = BfdUDPMod{} } +func (*BfdUDPMod) GetMessageName() string { return "bfd_udp_mod" } +func (*BfdUDPMod) GetCrcString() string { return "913df085" } +func (*BfdUDPMod) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPMod) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 4 // m.DesiredMinTx + size += 4 // m.RequiredMinRx + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 1 // m.DetectMult + return size +} +func (m *BfdUDPMod) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint32(m.DesiredMinTx) + buf.EncodeUint32(m.RequiredMinRx) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(m.DetectMult) + return buf.Bytes(), nil +} +func (m *BfdUDPMod) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.DesiredMinTx = buf.DecodeUint32() + m.RequiredMinRx = buf.DecodeUint32() + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.DetectMult = buf.DecodeUint8() + return nil +} + +// BfdUDPModReply defines message 'bfd_udp_mod_reply'. +type BfdUDPModReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPModReply) Reset() { *m = BfdUDPModReply{} } +func (*BfdUDPModReply) GetMessageName() string { return "bfd_udp_mod_reply" } +func (*BfdUDPModReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPModReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPModReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPModReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPModReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPSessionDetails defines message 'bfd_udp_session_details'. +type BfdUDPSessionDetails struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + State BfdState `binapi:"bfd_state,name=state" json:"state,omitempty"` + IsAuthenticated bool `binapi:"bool,name=is_authenticated" json:"is_authenticated,omitempty"` + BfdKeyID uint8 `binapi:"u8,name=bfd_key_id" json:"bfd_key_id,omitempty"` + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` + RequiredMinRx uint32 `binapi:"u32,name=required_min_rx" json:"required_min_rx,omitempty"` + DesiredMinTx uint32 `binapi:"u32,name=desired_min_tx" json:"desired_min_tx,omitempty"` + DetectMult uint8 `binapi:"u8,name=detect_mult" json:"detect_mult,omitempty"` +} + +func (m *BfdUDPSessionDetails) Reset() { *m = BfdUDPSessionDetails{} } +func (*BfdUDPSessionDetails) GetMessageName() string { return "bfd_udp_session_details" } +func (*BfdUDPSessionDetails) GetCrcString() string { return "09fb2f2d" } +func (*BfdUDPSessionDetails) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPSessionDetails) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 4 // m.State + size += 1 // m.IsAuthenticated + size += 1 // m.BfdKeyID + size += 4 // m.ConfKeyID + size += 4 // m.RequiredMinRx + size += 4 // m.DesiredMinTx + size += 1 // m.DetectMult + return size +} +func (m *BfdUDPSessionDetails) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint32(uint32(m.State)) + buf.EncodeBool(m.IsAuthenticated) + buf.EncodeUint8(m.BfdKeyID) + buf.EncodeUint32(m.ConfKeyID) + buf.EncodeUint32(m.RequiredMinRx) + buf.EncodeUint32(m.DesiredMinTx) + buf.EncodeUint8(m.DetectMult) + return buf.Bytes(), nil +} +func (m *BfdUDPSessionDetails) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.State = BfdState(buf.DecodeUint32()) + m.IsAuthenticated = buf.DecodeBool() + m.BfdKeyID = buf.DecodeUint8() + m.ConfKeyID = buf.DecodeUint32() + m.RequiredMinRx = buf.DecodeUint32() + m.DesiredMinTx = buf.DecodeUint32() + m.DetectMult = buf.DecodeUint8() + return nil +} + +// BfdUDPSessionDump defines message 'bfd_udp_session_dump'. +type BfdUDPSessionDump struct{} + +func (m *BfdUDPSessionDump) Reset() { *m = BfdUDPSessionDump{} } +func (*BfdUDPSessionDump) GetMessageName() string { return "bfd_udp_session_dump" } +func (*BfdUDPSessionDump) GetCrcString() string { return "51077d14" } +func (*BfdUDPSessionDump) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPSessionDump) Size() (size int) { + if m == nil { + return 0 + } + return size +} +func (m *BfdUDPSessionDump) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + return buf.Bytes(), nil +} +func (m *BfdUDPSessionDump) Unmarshal(b []byte) error { + return nil +} + +// BfdUDPSessionEvent defines message 'bfd_udp_session_event'. +type BfdUDPSessionEvent struct { + PID uint32 `binapi:"u32,name=pid" json:"pid,omitempty"` + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + State BfdState `binapi:"bfd_state,name=state" json:"state,omitempty"` + IsAuthenticated bool `binapi:"bool,name=is_authenticated" json:"is_authenticated,omitempty"` + BfdKeyID uint8 `binapi:"u8,name=bfd_key_id" json:"bfd_key_id,omitempty"` + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` + RequiredMinRx uint32 `binapi:"u32,name=required_min_rx" json:"required_min_rx,omitempty"` + DesiredMinTx uint32 `binapi:"u32,name=desired_min_tx" json:"desired_min_tx,omitempty"` + DetectMult uint8 `binapi:"u8,name=detect_mult" json:"detect_mult,omitempty"` +} + +func (m *BfdUDPSessionEvent) Reset() { *m = BfdUDPSessionEvent{} } +func (*BfdUDPSessionEvent) GetMessageName() string { return "bfd_udp_session_event" } +func (*BfdUDPSessionEvent) GetCrcString() string { return "8eaaf062" } +func (*BfdUDPSessionEvent) GetMessageType() api.MessageType { + return api.EventMessage +} + +func (m *BfdUDPSessionEvent) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.PID + size += 4 // m.SwIfIndex + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 4 // m.State + size += 1 // m.IsAuthenticated + size += 1 // m.BfdKeyID + size += 4 // m.ConfKeyID + size += 4 // m.RequiredMinRx + size += 4 // m.DesiredMinTx + size += 1 // m.DetectMult + return size +} +func (m *BfdUDPSessionEvent) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(m.PID) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint32(uint32(m.State)) + buf.EncodeBool(m.IsAuthenticated) + buf.EncodeUint8(m.BfdKeyID) + buf.EncodeUint32(m.ConfKeyID) + buf.EncodeUint32(m.RequiredMinRx) + buf.EncodeUint32(m.DesiredMinTx) + buf.EncodeUint8(m.DetectMult) + return buf.Bytes(), nil +} +func (m *BfdUDPSessionEvent) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.PID = buf.DecodeUint32() + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.State = BfdState(buf.DecodeUint32()) + m.IsAuthenticated = buf.DecodeBool() + m.BfdKeyID = buf.DecodeUint8() + m.ConfKeyID = buf.DecodeUint32() + m.RequiredMinRx = buf.DecodeUint32() + m.DesiredMinTx = buf.DecodeUint32() + m.DetectMult = buf.DecodeUint8() + return nil +} + +// BfdUDPSessionSetFlags defines message 'bfd_udp_session_set_flags'. +type BfdUDPSessionSetFlags struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + Flags interface_types.IfStatusFlags `binapi:"if_status_flags,name=flags" json:"flags,omitempty"` +} + +func (m *BfdUDPSessionSetFlags) Reset() { *m = BfdUDPSessionSetFlags{} } +func (*BfdUDPSessionSetFlags) GetMessageName() string { return "bfd_udp_session_set_flags" } +func (*BfdUDPSessionSetFlags) GetCrcString() string { return "04b4bdfd" } +func (*BfdUDPSessionSetFlags) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPSessionSetFlags) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 4 // m.Flags + return size +} +func (m *BfdUDPSessionSetFlags) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint32(uint32(m.Flags)) + return buf.Bytes(), nil +} +func (m *BfdUDPSessionSetFlags) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.Flags = interface_types.IfStatusFlags(buf.DecodeUint32()) + return nil +} + +// BfdUDPSessionSetFlagsReply defines message 'bfd_udp_session_set_flags_reply'. +type BfdUDPSessionSetFlagsReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPSessionSetFlagsReply) Reset() { *m = BfdUDPSessionSetFlagsReply{} } +func (*BfdUDPSessionSetFlagsReply) GetMessageName() string { return "bfd_udp_session_set_flags_reply" } +func (*BfdUDPSessionSetFlagsReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPSessionSetFlagsReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPSessionSetFlagsReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPSessionSetFlagsReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPSessionSetFlagsReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPSetEchoSource defines message 'bfd_udp_set_echo_source'. +type BfdUDPSetEchoSource struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` +} + +func (m *BfdUDPSetEchoSource) Reset() { *m = BfdUDPSetEchoSource{} } +func (*BfdUDPSetEchoSource) GetMessageName() string { return "bfd_udp_set_echo_source" } +func (*BfdUDPSetEchoSource) GetCrcString() string { return "f9e6675e" } +func (*BfdUDPSetEchoSource) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPSetEchoSource) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + return size +} +func (m *BfdUDPSetEchoSource) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + return buf.Bytes(), nil +} +func (m *BfdUDPSetEchoSource) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + return nil +} + +// BfdUDPSetEchoSourceReply defines message 'bfd_udp_set_echo_source_reply'. +type BfdUDPSetEchoSourceReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *BfdUDPSetEchoSourceReply) Reset() { *m = BfdUDPSetEchoSourceReply{} } +func (*BfdUDPSetEchoSourceReply) GetMessageName() string { return "bfd_udp_set_echo_source_reply" } +func (*BfdUDPSetEchoSourceReply) GetCrcString() string { return "e8d4e804" } +func (*BfdUDPSetEchoSourceReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPSetEchoSourceReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *BfdUDPSetEchoSourceReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *BfdUDPSetEchoSourceReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +// BfdUDPUpd defines message 'bfd_udp_upd'. +type BfdUDPUpd struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + DesiredMinTx uint32 `binapi:"u32,name=desired_min_tx" json:"desired_min_tx,omitempty"` + RequiredMinRx uint32 `binapi:"u32,name=required_min_rx" json:"required_min_rx,omitempty"` + LocalAddr ip_types.Address `binapi:"address,name=local_addr" json:"local_addr,omitempty"` + PeerAddr ip_types.Address `binapi:"address,name=peer_addr" json:"peer_addr,omitempty"` + DetectMult uint8 `binapi:"u8,name=detect_mult" json:"detect_mult,omitempty"` + IsAuthenticated bool `binapi:"bool,name=is_authenticated" json:"is_authenticated,omitempty"` + BfdKeyID uint8 `binapi:"u8,name=bfd_key_id" json:"bfd_key_id,omitempty"` + ConfKeyID uint32 `binapi:"u32,name=conf_key_id" json:"conf_key_id,omitempty"` +} + +func (m *BfdUDPUpd) Reset() { *m = BfdUDPUpd{} } +func (*BfdUDPUpd) GetMessageName() string { return "bfd_udp_upd" } +func (*BfdUDPUpd) GetCrcString() string { return "939cd26a" } +func (*BfdUDPUpd) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *BfdUDPUpd) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 4 // m.DesiredMinTx + size += 4 // m.RequiredMinRx + size += 1 // m.LocalAddr.Af + size += 1 * 16 // m.LocalAddr.Un + size += 1 // m.PeerAddr.Af + size += 1 * 16 // m.PeerAddr.Un + size += 1 // m.DetectMult + size += 1 // m.IsAuthenticated + size += 1 // m.BfdKeyID + size += 4 // m.ConfKeyID + return size +} +func (m *BfdUDPUpd) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeUint32(m.DesiredMinTx) + buf.EncodeUint32(m.RequiredMinRx) + buf.EncodeUint8(uint8(m.LocalAddr.Af)) + buf.EncodeBytes(m.LocalAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(uint8(m.PeerAddr.Af)) + buf.EncodeBytes(m.PeerAddr.Un.XXX_UnionData[:], 16) + buf.EncodeUint8(m.DetectMult) + buf.EncodeBool(m.IsAuthenticated) + buf.EncodeUint8(m.BfdKeyID) + buf.EncodeUint32(m.ConfKeyID) + return buf.Bytes(), nil +} +func (m *BfdUDPUpd) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.DesiredMinTx = buf.DecodeUint32() + m.RequiredMinRx = buf.DecodeUint32() + m.LocalAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.LocalAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.PeerAddr.Af = ip_types.AddressFamily(buf.DecodeUint8()) + copy(m.PeerAddr.Un.XXX_UnionData[:], buf.DecodeBytes(16)) + m.DetectMult = buf.DecodeUint8() + m.IsAuthenticated = buf.DecodeBool() + m.BfdKeyID = buf.DecodeUint8() + m.ConfKeyID = buf.DecodeUint32() + return nil +} + +// BfdUDPUpdReply defines message 'bfd_udp_upd_reply'. +type BfdUDPUpdReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` + StatsIndex uint32 `binapi:"u32,name=stats_index" json:"stats_index,omitempty"` +} + +func (m *BfdUDPUpdReply) Reset() { *m = BfdUDPUpdReply{} } +func (*BfdUDPUpdReply) GetMessageName() string { return "bfd_udp_upd_reply" } +func (*BfdUDPUpdReply) GetCrcString() string { return "1992deab" } +func (*BfdUDPUpdReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *BfdUDPUpdReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + size += 4 // m.StatsIndex + return size +} +func (m *BfdUDPUpdReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + buf.EncodeUint32(m.StatsIndex) + return buf.Bytes(), nil +} +func (m *BfdUDPUpdReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + m.StatsIndex = buf.DecodeUint32() + return nil +} + +// WantBfdEvents defines message 'want_bfd_events'. +type WantBfdEvents struct { + EnableDisable bool `binapi:"bool,name=enable_disable" json:"enable_disable,omitempty"` + PID uint32 `binapi:"u32,name=pid" json:"pid,omitempty"` +} + +func (m *WantBfdEvents) Reset() { *m = WantBfdEvents{} } +func (*WantBfdEvents) GetMessageName() string { return "want_bfd_events" } +func (*WantBfdEvents) GetCrcString() string { return "c5e2af94" } +func (*WantBfdEvents) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *WantBfdEvents) Size() (size int) { + if m == nil { + return 0 + } + size += 1 // m.EnableDisable + size += 4 // m.PID + return size +} +func (m *WantBfdEvents) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeBool(m.EnableDisable) + buf.EncodeUint32(m.PID) + return buf.Bytes(), nil +} +func (m *WantBfdEvents) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.EnableDisable = buf.DecodeBool() + m.PID = buf.DecodeUint32() + return nil +} + +// WantBfdEventsReply defines message 'want_bfd_events_reply'. +type WantBfdEventsReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *WantBfdEventsReply) Reset() { *m = WantBfdEventsReply{} } +func (*WantBfdEventsReply) GetMessageName() string { return "want_bfd_events_reply" } +func (*WantBfdEventsReply) GetCrcString() string { return "e8d4e804" } +func (*WantBfdEventsReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *WantBfdEventsReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *WantBfdEventsReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *WantBfdEventsReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +func init() { file_bfd_binapi_init() } +func file_bfd_binapi_init() { + api.RegisterMessage((*BfdAuthDelKey)(nil), "bfd_auth_del_key_65310b22") + api.RegisterMessage((*BfdAuthDelKeyReply)(nil), "bfd_auth_del_key_reply_e8d4e804") + api.RegisterMessage((*BfdAuthKeysDetails)(nil), "bfd_auth_keys_details_84130e9f") + api.RegisterMessage((*BfdAuthKeysDump)(nil), "bfd_auth_keys_dump_51077d14") + api.RegisterMessage((*BfdAuthSetKey)(nil), "bfd_auth_set_key_690b8877") + api.RegisterMessage((*BfdAuthSetKeyReply)(nil), "bfd_auth_set_key_reply_e8d4e804") + api.RegisterMessage((*BfdUDPAdd)(nil), "bfd_udp_add_939cd26a") + api.RegisterMessage((*BfdUDPAddReply)(nil), "bfd_udp_add_reply_e8d4e804") + api.RegisterMessage((*BfdUDPAuthActivate)(nil), "bfd_udp_auth_activate_21fd1bdb") + api.RegisterMessage((*BfdUDPAuthActivateReply)(nil), "bfd_udp_auth_activate_reply_e8d4e804") + api.RegisterMessage((*BfdUDPAuthDeactivate)(nil), "bfd_udp_auth_deactivate_9a05e2e0") + api.RegisterMessage((*BfdUDPAuthDeactivateReply)(nil), "bfd_udp_auth_deactivate_reply_e8d4e804") + api.RegisterMessage((*BfdUDPDel)(nil), "bfd_udp_del_dcb13a89") + api.RegisterMessage((*BfdUDPDelEchoSource)(nil), "bfd_udp_del_echo_source_51077d14") + api.RegisterMessage((*BfdUDPDelEchoSourceReply)(nil), "bfd_udp_del_echo_source_reply_e8d4e804") + api.RegisterMessage((*BfdUDPDelReply)(nil), "bfd_udp_del_reply_e8d4e804") + api.RegisterMessage((*BfdUDPGetEchoSource)(nil), "bfd_udp_get_echo_source_51077d14") + api.RegisterMessage((*BfdUDPGetEchoSourceReply)(nil), "bfd_udp_get_echo_source_reply_e3d736a1") + api.RegisterMessage((*BfdUDPMod)(nil), "bfd_udp_mod_913df085") + api.RegisterMessage((*BfdUDPModReply)(nil), "bfd_udp_mod_reply_e8d4e804") + api.RegisterMessage((*BfdUDPSessionDetails)(nil), "bfd_udp_session_details_09fb2f2d") + api.RegisterMessage((*BfdUDPSessionDump)(nil), "bfd_udp_session_dump_51077d14") + api.RegisterMessage((*BfdUDPSessionEvent)(nil), "bfd_udp_session_event_8eaaf062") + api.RegisterMessage((*BfdUDPSessionSetFlags)(nil), "bfd_udp_session_set_flags_04b4bdfd") + api.RegisterMessage((*BfdUDPSessionSetFlagsReply)(nil), "bfd_udp_session_set_flags_reply_e8d4e804") + api.RegisterMessage((*BfdUDPSetEchoSource)(nil), "bfd_udp_set_echo_source_f9e6675e") + api.RegisterMessage((*BfdUDPSetEchoSourceReply)(nil), "bfd_udp_set_echo_source_reply_e8d4e804") + api.RegisterMessage((*BfdUDPUpd)(nil), "bfd_udp_upd_939cd26a") + api.RegisterMessage((*BfdUDPUpdReply)(nil), "bfd_udp_upd_reply_1992deab") + api.RegisterMessage((*WantBfdEvents)(nil), "want_bfd_events_c5e2af94") + api.RegisterMessage((*WantBfdEventsReply)(nil), "want_bfd_events_reply_e8d4e804") +} + +// Messages returns list of all messages in this module. +func AllMessages() []api.Message { + return []api.Message{ + (*BfdAuthDelKey)(nil), + (*BfdAuthDelKeyReply)(nil), + (*BfdAuthKeysDetails)(nil), + (*BfdAuthKeysDump)(nil), + (*BfdAuthSetKey)(nil), + (*BfdAuthSetKeyReply)(nil), + (*BfdUDPAdd)(nil), + (*BfdUDPAddReply)(nil), + (*BfdUDPAuthActivate)(nil), + (*BfdUDPAuthActivateReply)(nil), + (*BfdUDPAuthDeactivate)(nil), + (*BfdUDPAuthDeactivateReply)(nil), + (*BfdUDPDel)(nil), + (*BfdUDPDelEchoSource)(nil), + (*BfdUDPDelEchoSourceReply)(nil), + (*BfdUDPDelReply)(nil), + (*BfdUDPGetEchoSource)(nil), + (*BfdUDPGetEchoSourceReply)(nil), + (*BfdUDPMod)(nil), + (*BfdUDPModReply)(nil), + (*BfdUDPSessionDetails)(nil), + (*BfdUDPSessionDump)(nil), + (*BfdUDPSessionEvent)(nil), + (*BfdUDPSessionSetFlags)(nil), + (*BfdUDPSessionSetFlagsReply)(nil), + (*BfdUDPSetEchoSource)(nil), + (*BfdUDPSetEchoSourceReply)(nil), + (*BfdUDPUpd)(nil), + (*BfdUDPUpdReply)(nil), + (*WantBfdEvents)(nil), + (*WantBfdEventsReply)(nil), + } +} diff --git a/plugins/binapi/vpp2306/bfd/bfd_rpc.ba.go b/plugins/binapi/vpp2306/bfd/bfd_rpc.ba.go new file mode 100644 index 00000000..6943b7a7 --- /dev/null +++ b/plugins/binapi/vpp2306/bfd/bfd_rpc.ba.go @@ -0,0 +1,242 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. + +package bfd + +import ( + "context" + "fmt" + "io" + + api "go.fd.io/govpp/api" + memclnt "go.pantheon.tech/stonework/plugins/binapi/vpp2306/memclnt" +) + +// RPCService defines RPC service bfd. +type RPCService interface { + BfdAuthDelKey(ctx context.Context, in *BfdAuthDelKey) (*BfdAuthDelKeyReply, error) + BfdAuthKeysDump(ctx context.Context, in *BfdAuthKeysDump) (RPCService_BfdAuthKeysDumpClient, error) + BfdAuthSetKey(ctx context.Context, in *BfdAuthSetKey) (*BfdAuthSetKeyReply, error) + BfdUDPAdd(ctx context.Context, in *BfdUDPAdd) (*BfdUDPAddReply, error) + BfdUDPAuthActivate(ctx context.Context, in *BfdUDPAuthActivate) (*BfdUDPAuthActivateReply, error) + BfdUDPAuthDeactivate(ctx context.Context, in *BfdUDPAuthDeactivate) (*BfdUDPAuthDeactivateReply, error) + BfdUDPDel(ctx context.Context, in *BfdUDPDel) (*BfdUDPDelReply, error) + BfdUDPDelEchoSource(ctx context.Context, in *BfdUDPDelEchoSource) (*BfdUDPDelEchoSourceReply, error) + BfdUDPGetEchoSource(ctx context.Context, in *BfdUDPGetEchoSource) (*BfdUDPGetEchoSourceReply, error) + BfdUDPMod(ctx context.Context, in *BfdUDPMod) (*BfdUDPModReply, error) + BfdUDPSessionDump(ctx context.Context, in *BfdUDPSessionDump) (RPCService_BfdUDPSessionDumpClient, error) + BfdUDPSessionSetFlags(ctx context.Context, in *BfdUDPSessionSetFlags) (*BfdUDPSessionSetFlagsReply, error) + BfdUDPSetEchoSource(ctx context.Context, in *BfdUDPSetEchoSource) (*BfdUDPSetEchoSourceReply, error) + BfdUDPUpd(ctx context.Context, in *BfdUDPUpd) (*BfdUDPUpdReply, error) + WantBfdEvents(ctx context.Context, in *WantBfdEvents) (*WantBfdEventsReply, error) +} + +type serviceClient struct { + conn api.Connection +} + +func NewServiceClient(conn api.Connection) RPCService { + return &serviceClient{conn} +} + +func (c *serviceClient) BfdAuthDelKey(ctx context.Context, in *BfdAuthDelKey) (*BfdAuthDelKeyReply, error) { + out := new(BfdAuthDelKeyReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdAuthKeysDump(ctx context.Context, in *BfdAuthKeysDump) (RPCService_BfdAuthKeysDumpClient, error) { + stream, err := c.conn.NewStream(ctx) + if err != nil { + return nil, err + } + x := &serviceClient_BfdAuthKeysDumpClient{stream} + if err := x.Stream.SendMsg(in); err != nil { + return nil, err + } + if err = x.Stream.SendMsg(&memclnt.ControlPing{}); err != nil { + return nil, err + } + return x, nil +} + +type RPCService_BfdAuthKeysDumpClient interface { + Recv() (*BfdAuthKeysDetails, error) + api.Stream +} + +type serviceClient_BfdAuthKeysDumpClient struct { + api.Stream +} + +func (c *serviceClient_BfdAuthKeysDumpClient) Recv() (*BfdAuthKeysDetails, error) { + msg, err := c.Stream.RecvMsg() + if err != nil { + return nil, err + } + switch m := msg.(type) { + case *BfdAuthKeysDetails: + return m, nil + case *memclnt.ControlPingReply: + err = c.Stream.Close() + if err != nil { + return nil, err + } + return nil, io.EOF + default: + return nil, fmt.Errorf("unexpected message: %T %v", m, m) + } +} + +func (c *serviceClient) BfdAuthSetKey(ctx context.Context, in *BfdAuthSetKey) (*BfdAuthSetKeyReply, error) { + out := new(BfdAuthSetKeyReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPAdd(ctx context.Context, in *BfdUDPAdd) (*BfdUDPAddReply, error) { + out := new(BfdUDPAddReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPAuthActivate(ctx context.Context, in *BfdUDPAuthActivate) (*BfdUDPAuthActivateReply, error) { + out := new(BfdUDPAuthActivateReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPAuthDeactivate(ctx context.Context, in *BfdUDPAuthDeactivate) (*BfdUDPAuthDeactivateReply, error) { + out := new(BfdUDPAuthDeactivateReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPDel(ctx context.Context, in *BfdUDPDel) (*BfdUDPDelReply, error) { + out := new(BfdUDPDelReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPDelEchoSource(ctx context.Context, in *BfdUDPDelEchoSource) (*BfdUDPDelEchoSourceReply, error) { + out := new(BfdUDPDelEchoSourceReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPGetEchoSource(ctx context.Context, in *BfdUDPGetEchoSource) (*BfdUDPGetEchoSourceReply, error) { + out := new(BfdUDPGetEchoSourceReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPMod(ctx context.Context, in *BfdUDPMod) (*BfdUDPModReply, error) { + out := new(BfdUDPModReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPSessionDump(ctx context.Context, in *BfdUDPSessionDump) (RPCService_BfdUDPSessionDumpClient, error) { + stream, err := c.conn.NewStream(ctx) + if err != nil { + return nil, err + } + x := &serviceClient_BfdUDPSessionDumpClient{stream} + if err := x.Stream.SendMsg(in); err != nil { + return nil, err + } + if err = x.Stream.SendMsg(&memclnt.ControlPing{}); err != nil { + return nil, err + } + return x, nil +} + +type RPCService_BfdUDPSessionDumpClient interface { + Recv() (*BfdUDPSessionDetails, error) + api.Stream +} + +type serviceClient_BfdUDPSessionDumpClient struct { + api.Stream +} + +func (c *serviceClient_BfdUDPSessionDumpClient) Recv() (*BfdUDPSessionDetails, error) { + msg, err := c.Stream.RecvMsg() + if err != nil { + return nil, err + } + switch m := msg.(type) { + case *BfdUDPSessionDetails: + return m, nil + case *memclnt.ControlPingReply: + err = c.Stream.Close() + if err != nil { + return nil, err + } + return nil, io.EOF + default: + return nil, fmt.Errorf("unexpected message: %T %v", m, m) + } +} + +func (c *serviceClient) BfdUDPSessionSetFlags(ctx context.Context, in *BfdUDPSessionSetFlags) (*BfdUDPSessionSetFlagsReply, error) { + out := new(BfdUDPSessionSetFlagsReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPSetEchoSource(ctx context.Context, in *BfdUDPSetEchoSource) (*BfdUDPSetEchoSourceReply, error) { + out := new(BfdUDPSetEchoSourceReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) BfdUDPUpd(ctx context.Context, in *BfdUDPUpd) (*BfdUDPUpdReply, error) { + out := new(BfdUDPUpdReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} + +func (c *serviceClient) WantBfdEvents(ctx context.Context, in *WantBfdEvents) (*WantBfdEventsReply, error) { + out := new(WantBfdEventsReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} diff --git a/plugins/binapi/vpp2306/ethernet_types/ethernet_types.ba.go b/plugins/binapi/vpp2306/ethernet_types/ethernet_types.ba.go new file mode 100644 index 00000000..5470b89a --- /dev/null +++ b/plugins/binapi/vpp2306/ethernet_types/ethernet_types.ba.go @@ -0,0 +1,63 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. +// versions: +// binapi-generator: v0.7.0 +// VPP: 23.06 +// source: /usr/share/vpp/api/core/ethernet_types.api.json + +// Package ethernet_types contains generated bindings for API file ethernet_types.api. +// +// Contents: +// - 1 alias +package ethernet_types + +import ( + "net" + + api "go.fd.io/govpp/api" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the GoVPP api package it is being compiled against. +// A compilation error at this line likely means your copy of the +// GoVPP api package needs to be updated. +const _ = api.GoVppAPIPackageIsVersion2 + +const ( + APIFile = "ethernet_types" + APIVersion = "1.0.0" + VersionCrc = 0xf24103d6 +) + +// MacAddress defines alias 'mac_address'. +type MacAddress [6]uint8 + +func ParseMacAddress(s string) (MacAddress, error) { + var macaddr MacAddress + mac, err := net.ParseMAC(s) + if err != nil { + return macaddr, err + } + copy(macaddr[:], mac[:]) + return macaddr, nil +} + +func (x MacAddress) ToMAC() net.HardwareAddr { + return net.HardwareAddr(x[:]) +} + +func (x MacAddress) String() string { + return x.ToMAC().String() +} + +func (x *MacAddress) MarshalText() ([]byte, error) { + return []byte(x.String()), nil +} + +func (x *MacAddress) UnmarshalText(text []byte) error { + mac, err := ParseMacAddress(string(text)) + if err != nil { + return err + } + *x = mac + return nil +} diff --git a/plugins/binapi/vpp2306/gen.go b/plugins/binapi/vpp2306/gen.go new file mode 100644 index 00000000..66766685 --- /dev/null +++ b/plugins/binapi/vpp2306/gen.go @@ -0,0 +1,10 @@ +package binapi + +//go:generate binapi-generator --output-dir=. abx +//go:generate binapi-generator --output-dir=. bfd +//go:generate binapi-generator --output-dir=. memclnt +//go:generate binapi-generator --output-dir=. nat64 +//go:generate binapi-generator --output-dir=. vpe +//go:generate binapi-generator --output-dir=. isisx + +const Version = "23.06" diff --git a/plugins/binapi/vpp2306/interface_types/interface_types.ba.go b/plugins/binapi/vpp2306/interface_types/interface_types.ba.go new file mode 100644 index 00000000..029839fd --- /dev/null +++ b/plugins/binapi/vpp2306/interface_types/interface_types.ba.go @@ -0,0 +1,308 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. +// versions: +// binapi-generator: v0.7.0 +// VPP: 23.06 +// source: /usr/share/vpp/api/core/interface_types.api.json + +// Package interface_types contains generated bindings for API file interface_types.api. +// +// Contents: +// - 1 alias +// - 7 enums +package interface_types + +import ( + "strconv" + + api "go.fd.io/govpp/api" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the GoVPP api package it is being compiled against. +// A compilation error at this line likely means your copy of the +// GoVPP api package needs to be updated. +const _ = api.GoVppAPIPackageIsVersion2 + +const ( + APIFile = "interface_types" + APIVersion = "1.0.0" + VersionCrc = 0x7f2ba79a +) + +// Direction defines enum 'direction'. +type Direction uint8 + +const ( + RX Direction = 0 + TX Direction = 1 +) + +var ( + Direction_name = map[uint8]string{ + 0: "RX", + 1: "TX", + } + Direction_value = map[string]uint8{ + "RX": 0, + "TX": 1, + } +) + +func (x Direction) String() string { + s, ok := Direction_name[uint8(x)] + if ok { + return s + } + return "Direction(" + strconv.Itoa(int(x)) + ")" +} + +// IfStatusFlags defines enum 'if_status_flags'. +type IfStatusFlags uint32 + +const ( + IF_STATUS_API_FLAG_ADMIN_UP IfStatusFlags = 1 + IF_STATUS_API_FLAG_LINK_UP IfStatusFlags = 2 +) + +var ( + IfStatusFlags_name = map[uint32]string{ + 1: "IF_STATUS_API_FLAG_ADMIN_UP", + 2: "IF_STATUS_API_FLAG_LINK_UP", + } + IfStatusFlags_value = map[string]uint32{ + "IF_STATUS_API_FLAG_ADMIN_UP": 1, + "IF_STATUS_API_FLAG_LINK_UP": 2, + } +) + +func (x IfStatusFlags) String() string { + s, ok := IfStatusFlags_name[uint32(x)] + if ok { + return s + } + str := func(n uint32) string { + s, ok := IfStatusFlags_name[uint32(n)] + if ok { + return s + } + return "IfStatusFlags(" + strconv.Itoa(int(n)) + ")" + } + for i := uint32(0); i <= 32; i++ { + val := uint32(x) + if val&(1< %s) due to: %v", + inputInterface, outputInterface, err) + } + return nil +} + +// DeleteISISXConnection deletes existing ISISX unidirectional cross-connection between 2 interfaces +func (h *ISISXVppHandler) DeleteISISXConnection(inputInterface, outputInterface string) error { + if err := h.addDeleteISISXConnection(false, inputInterface, outputInterface); err != nil { + return errors.Errorf("failed to delete ISISX cross connection(%s -> %s) due to: %v", + inputInterface, outputInterface, err) + } + return nil +} + +func (h *ISISXVppHandler) addDeleteISISXConnection(isAdd bool, inputInterface, outputInterface string) error { + // translate interface name to vpp interface indexes + meta, found := h.ifIndexes.LookupByName(inputInterface) + if !found { + return errors.Errorf("input interface %s not found", inputInterface) + } + inputSwIfIndex := meta.SwIfIndex + meta, found = h.ifIndexes.LookupByName(outputInterface) + if !found { + return errors.Errorf("output interface %s not found", outputInterface) + } + outputSwIfIndex := meta.SwIfIndex + + // construct request + req := &isisx.IsisxConnectionAddDel{ + IsAdd: boolToUint(isAdd), + Connection: isisx.IsisxConnection{ + RxSwIfIndex: inputSwIfIndex, + TxSwIfIndex: outputSwIfIndex, + }, + } + reply := &isisx.IsisxConnectionAddDelReply{} + + // send, wait for and handle reply + if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { + return err + } + if reply.Retval != 0 { + return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval) + } + + return nil +} + +func boolToUint(input bool) uint8 { + if input { + return 1 + } + return 0 +} diff --git a/plugins/isisx/vppcalls/vpp2306/isisx_vppcalls_test.go b/plugins/isisx/vppcalls/vpp2306/isisx_vppcalls_test.go new file mode 100644 index 00000000..e312ddcd --- /dev/null +++ b/plugins/isisx/vppcalls/vpp2306/isisx_vppcalls_test.go @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306_test + +import ( + "testing" + + . "github.com/onsi/gomega" + govppapi "go.fd.io/govpp/api" + "go.ligato.io/cn-infra/v2/logging/logrus" + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + "go.ligato.io/vpp-agent/v3/plugins/vpp/vppmock" + + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/isisx" + "go.pantheon.tech/stonework/plugins/isisx/vppcalls" + "go.pantheon.tech/stonework/plugins/isisx/vppcalls/vpp2306" +) + +func isisxTestSetup(t *testing.T) (*vppmock.TestCtx, vppcalls.ISISXVppAPI, ifaceidx.IfaceMetadataIndexRW) { + ctx := vppmock.SetupTestCtx(t) + log := logrus.NewLogger("test-log") + ifIdx := ifaceidx.NewIfaceIndex(log, "if-index") + isisxHandler := vpp2306.NewISISXVppHandler(ctx.MockChannel, ifIdx, log) + return ctx, isisxHandler, ifIdx +} + +func TestGetISISXVersion(t *testing.T) { + ctx, isisxHandler, _ := isisxTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply(&isisx.IsisxPluginGetVersionReply{ + Major: 1, + Minor: 0, + }) + version, err := isisxHandler.GetISISXVersion() + + Expect(err).To(BeNil()) + Expect(version).To(Equal("1.0")) +} + +func TestAddISISXConnection(t *testing.T) { + // Prepare different cases + cases := []struct { + Name string + InputInterface string + OutputInterface string + ExpectFailure bool + MockReply govppapi.Message + PrepareIfIndexes func(ifIndexes ifaceidx.IfaceMetadataIndexRW) + }{ + { + Name: "happy path", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: false, + MockReply: &isisx.IsisxConnectionAddDelReply{}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface1", &ifaceidx.IfaceMetadata{SwIfIndex: 11}) + ifIndexes.Put("interface2", &ifaceidx.IfaceMetadata{SwIfIndex: 12}) + }, + }, + { + Name: "no index for input interface", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: true, + MockReply: &isisx.IsisxConnectionAddDelReply{}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface2", &ifaceidx.IfaceMetadata{SwIfIndex: 12}) + }, + }, + { + Name: "no index for output interface", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: true, + MockReply: &isisx.IsisxConnectionAddDelReply{}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface1", &ifaceidx.IfaceMetadata{SwIfIndex: 11}) + }, + }, + { + Name: "error from vpp", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: true, + MockReply: &isisx.IsisxConnectionAddDelReply{Retval: 1}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface1", &ifaceidx.IfaceMetadata{SwIfIndex: 11}) + ifIndexes.Put("interface2", &ifaceidx.IfaceMetadata{SwIfIndex: 12}) + }, + }, + } + + // Run all cases + for _, td := range cases { + t.Run(td.Name, func(t *testing.T) { + ctx, isisxHandler, ifIndexes := isisxTestSetup(t) + defer ctx.TeardownTestCtx() + + // prepare for case + td.PrepareIfIndexes(ifIndexes) + ctx.MockVpp.MockReply(td.MockReply) + + // make the call and verify + err := isisxHandler.AddISISXConnection(td.InputInterface, td.OutputInterface) + if td.ExpectFailure { + Expect(err).Should(HaveOccurred()) + } else { + Expect(err).ShouldNot(HaveOccurred()) + } + }) + } +} + +func TestDeleteISISXConnection(t *testing.T) { + // Prepare different cases + cases := []struct { + Name string + InputInterface string + OutputInterface string + ExpectFailure bool + MockReply govppapi.Message + PrepareIfIndexes func(ifIndexes ifaceidx.IfaceMetadataIndexRW) + }{ + { + Name: "happy path", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: false, + MockReply: &isisx.IsisxConnectionAddDelReply{}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface1", &ifaceidx.IfaceMetadata{SwIfIndex: 11}) + ifIndexes.Put("interface2", &ifaceidx.IfaceMetadata{SwIfIndex: 12}) + }, + }, + { + Name: "no index for input interface", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: true, + MockReply: &isisx.IsisxConnectionAddDelReply{}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface2", &ifaceidx.IfaceMetadata{SwIfIndex: 12}) + }, + }, + { + Name: "no index for output interface", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: true, + MockReply: &isisx.IsisxConnectionAddDelReply{}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface1", &ifaceidx.IfaceMetadata{SwIfIndex: 11}) + }, + }, + { + Name: "error from vpp", + InputInterface: "interface1", + OutputInterface: "interface2", + ExpectFailure: true, + MockReply: &isisx.IsisxConnectionAddDelReply{Retval: 1}, + PrepareIfIndexes: func(ifIndexes ifaceidx.IfaceMetadataIndexRW) { + ifIndexes.Put("interface1", &ifaceidx.IfaceMetadata{SwIfIndex: 11}) + ifIndexes.Put("interface2", &ifaceidx.IfaceMetadata{SwIfIndex: 12}) + }, + }, + } + + // Run all cases + for _, td := range cases { + t.Run(td.Name, func(t *testing.T) { + ctx, isisxHandler, ifIndexes := isisxTestSetup(t) + defer ctx.TeardownTestCtx() + + // prepare for case + td.PrepareIfIndexes(ifIndexes) + ctx.MockVpp.MockReply(td.MockReply) + + // make the call and verify + err := isisxHandler.DeleteISISXConnection(td.InputInterface, td.OutputInterface) + if td.ExpectFailure { + Expect(err).Should(HaveOccurred()) + } else { + Expect(err).ShouldNot(HaveOccurred()) + } + }) + } +} diff --git a/plugins/isisx/vppcalls/vpp2306/vppcalls_handlers.go b/plugins/isisx/vppcalls/vpp2306/vppcalls_handlers.go new file mode 100644 index 00000000..2f985886 --- /dev/null +++ b/plugins/isisx/vppcalls/vpp2306/vppcalls_handlers.go @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + govppapi "go.fd.io/govpp/api" + "go.ligato.io/cn-infra/v2/logging" + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + + binapi "go.pantheon.tech/stonework/plugins/binapi/vpp2306" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/isisx" + "go.pantheon.tech/stonework/plugins/isisx/vppcalls" +) + +func init() { + var msgs []govppapi.Message + msgs = append(msgs, isisx.AllMessages()...) + + vppcalls.AddIsisxHandlerVersion(binapi.Version, msgs, NewISISXVppHandler) +} + +// ISISXVppHandler is accessor for isisx-related vppcalls methods +type ISISXVppHandler struct { + callsChannel govppapi.Channel + ifIndexes ifaceidx.IfaceMetadataIndex + log logging.Logger +} + +// NewISISXVppHandler returns new ISISXVppHandler. +func NewISISXVppHandler(calls govppapi.Channel, ifIdx ifaceidx.IfaceMetadataIndex, + log logging.Logger) vppcalls.ISISXVppAPI { + return &ISISXVppHandler{ + callsChannel: calls, + ifIndexes: ifIdx, + log: log, + } +} diff --git a/plugins/nat64/nat64plugin.go b/plugins/nat64/nat64plugin.go index a5727ce8..ad418969 100644 --- a/plugins/nat64/nat64plugin.go +++ b/plugins/nat64/nat64plugin.go @@ -36,6 +36,7 @@ import ( _ "go.pantheon.tech/stonework/plugins/nat64/vppcalls/vpp2106" _ "go.pantheon.tech/stonework/plugins/nat64/vppcalls/vpp2202" _ "go.pantheon.tech/stonework/plugins/nat64/vppcalls/vpp2210" + _ "go.pantheon.tech/stonework/plugins/nat64/vppcalls/vpp2306" ) // NAT64Plugin configures VPP NAT. diff --git a/plugins/nat64/vppcalls/vpp2306/dump_nat64_vppcalls.go b/plugins/nat64/vppcalls/vpp2306/dump_nat64_vppcalls.go new file mode 100644 index 00000000..b3680253 --- /dev/null +++ b/plugins/nat64/vppcalls/vpp2306/dump_nat64_vppcalls.go @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + "encoding/binary" + "fmt" + "net" + + natba "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat64" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat_types" + nat "go.pantheon.tech/stonework/proto/nat64" +) + +// Num protocol representation +const ( + ICMP uint8 = 1 + TCP uint8 = 6 + UDP uint8 = 17 +) + +// Nat64IPv6PrefixDump dumps all IPv6 prefixes configured for NAT64. +func (h *Nat64VppHandler) Nat64IPv6PrefixDump() (prefixes []*nat.Nat64IPv6Prefix, err error) { + req := &natba.Nat64PrefixDump{} + reqContext := h.callsChannel.SendMultiRequest(req) + + for { + msg := &natba.Nat64PrefixDetails{} + stop, err := reqContext.ReceiveReply(msg) + if err != nil { + return nil, fmt.Errorf("failed to dump NAT64 IPv6 prefixes: %v", err) + } + if stop { + break + } + prefixes = append(prefixes, &nat.Nat64IPv6Prefix{ + VrfId: msg.VrfID, + Prefix: msg.Prefix.ToIPNet().String(), + }) + } + + return prefixes, nil +} + +// Nat64InterfacesDump dumps NAT64 config of all NAT64-enabled interfaces. +func (h *Nat64VppHandler) Nat64InterfacesDump() (interfaces []*nat.Nat64Interface, err error) { + req := &natba.Nat64InterfaceDump{} + reqContext := h.callsChannel.SendMultiRequest(req) + + for { + msg := &natba.Nat64InterfaceDetails{} + stop, err := reqContext.ReceiveReply(msg) + if err != nil { + return nil, fmt.Errorf("failed to dump NAT64 interfaces: %v", err) + } + if stop { + break + } + ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) + if !found { + h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex) + continue + } + ifType := nat.Nat64Interface_IPV6_INSIDE + if msg.Flags&nat_types.NAT_IS_OUTSIDE != 0 { + ifType = nat.Nat64Interface_IPV4_OUTSIDE + } + interfaces = append(interfaces, &nat.Nat64Interface{ + Name: ifName, + Type: ifType, + }) + } + return interfaces, nil +} + +// Nat64AddressPoolsDump dumps all configured NAT64 address pools. +// Note that VPP returns configured addresses one-by-one, loosing information about address pools +// configured with multiple addresses through IP ranges. Provide expected configuration to group +// addresses from the same range. +func (h *Nat64VppHandler) Nat64AddressPoolsDump(correlateWith []*nat.Nat64AddressPool) (pools []*nat.Nat64AddressPool, err error) { + req := &natba.Nat64PoolAddrDump{} + reqContext := h.callsChannel.SendMultiRequest(req) + + for { + msg := &natba.Nat64PoolAddrDetails{} + stop, err := reqContext.ReceiveReply(msg) + if err != nil { + return nil, fmt.Errorf("failed to dump NAT64 address pools: %v", err) + } + if stop { + break + } + address := net.IP(msg.Address[:]).String() + pools = append(pools, &nat.Nat64AddressPool{ + VrfId: msg.VrfID, + FirstIp: address, + LastIp: address, + }) + } + return correlateAddressPools(pools, correlateWith), nil +} + +// Nat64StaticBIBsDump dumps NAT64 static bindings. +func (h *Nat64VppHandler) Nat64StaticBIBsDump() (bibs []*nat.Nat64StaticBIB, err error) { + req := &natba.Nat64BibDump{ + Proto: ^uint8(0), // ALL + } + reqContext := h.callsChannel.SendMultiRequest(req) + + for { + msg := &natba.Nat64BibDetails{} + stop, err := reqContext.ReceiveReply(msg) + if err != nil { + return nil, fmt.Errorf("failed to dump NAT64 static BIBs: %v", err) + } + if stop { + break + } + if msg.Flags&nat_types.NAT_IS_STATIC == 0 { + // dynamic entry + continue + } + var proto nat.Nat64StaticBIB_Protocol + switch msg.Proto { + case TCP: + proto = nat.Nat64StaticBIB_TCP + case UDP: + proto = nat.Nat64StaticBIB_UDP + case ICMP: + proto = nat.Nat64StaticBIB_ICMP + } + bibs = append(bibs, &nat.Nat64StaticBIB{ + VrfId: msg.VrfID, + Protocol: proto, + InsideIpv6Address: net.IP(msg.IAddr[:]).String(), + InsidePort: uint32(msg.IPort), + OutsideIpv4Address: net.IP(msg.OAddr[:]).String(), + OutsidePort: uint32(msg.OPort), + }) + } + return bibs, nil +} + +func correlateAddressPools(dumped, correlateWith []*nat.Nat64AddressPool) (correlated []*nat.Nat64AddressPool) { + if len(correlateWith) == 0 { + return dumped + } + dumpedMap := make(map[uint32]map[uint32]*nat.Nat64AddressPool) // VRF -> address as int -> pool + for _, pool := range dumped { + if _, initialized := dumpedMap[pool.VrfId]; !initialized { + dumpedMap[pool.VrfId] = make(map[uint32]*nat.Nat64AddressPool) + } + dumpedMap[pool.VrfId][ip2int(net.ParseIP(pool.FirstIp))] = pool + } + for _, correlate := range correlateWith { + var firstIP, lastIP net.IP + firstIP = net.ParseIP(correlate.FirstIp) + if correlate.LastIp != "" { + lastIP = net.ParseIP(correlate.LastIp) + } else { + lastIP = firstIP + } + if firstIP == nil || lastIP == nil { + continue + } + first := ip2int(firstIP) + last := ip2int(lastIP) + if first >= last { + continue + } + allDumped := true + for i := first; i <= last; i++ { + if _, isDumped := dumpedMap[correlate.VrfId][i]; !isDumped { + allDumped = false + break + } + } + if allDumped { + for i := first; i <= last; i++ { + delete(dumpedMap[correlate.VrfId], i) + } + correlated = append(correlated, correlate) + } + } + for vrf := range dumpedMap { + for _, pool := range dumpedMap[vrf] { + correlated = append(correlated, pool) + } + } + return correlated +} + +func ip2int(ip net.IP) uint32 { + return binary.BigEndian.Uint32(ip.To4()) +} diff --git a/plugins/nat64/vppcalls/vpp2306/dump_nat64_vppcalls_test.go b/plugins/nat64/vppcalls/vpp2306/dump_nat64_vppcalls_test.go new file mode 100644 index 00000000..d34aaa44 --- /dev/null +++ b/plugins/nat64/vppcalls/vpp2306/dump_nat64_vppcalls_test.go @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306_test + +import ( + "net" + "sort" + "testing" + + . "github.com/onsi/gomega" + + "go.ligato.io/cn-infra/v2/logging/logrus" + + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ip_types" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/memclnt" + natba "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat64" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat_types" + + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + "go.ligato.io/vpp-agent/v3/plugins/vpp/vppmock" + + "go.pantheon.tech/stonework/plugins/nat64/vppcalls" + "go.pantheon.tech/stonework/plugins/nat64/vppcalls/vpp2306" + "go.pantheon.tech/stonework/proto/nat64" +) + +func TestNat64IPv6PrefixDump(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply( + &natba.Nat64PrefixDetails{ + VrfID: 5, + Prefix: ipTo6Prefix("64:ff9b::/96"), + }, + &natba.Nat64PrefixDetails{ + VrfID: 3, + Prefix: ipTo6Prefix("2004::/32"), + }) + ctx.MockVpp.MockReply(&memclnt.ControlPingReply{}) + + prefixes, err := natHandler.Nat64IPv6PrefixDump() + Expect(err).To(Succeed()) + + Expect(prefixes).To(HaveLen(2)) + + Expect(prefixes[0].VrfId).To(BeEquivalentTo(5)) + Expect(prefixes[0].Prefix).To(Equal("64:ff9b::/96")) + + Expect(prefixes[1].VrfId).To(BeEquivalentTo(3)) + Expect(prefixes[1].Prefix).To(Equal("2004::/32")) +} + +func TestNat64InterfacesDump(t *testing.T) { + ctx, natHandler, swIfIndexes := natTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply( + &natba.Nat64InterfaceDetails{ + SwIfIndex: 1, + Flags: nat_types.NAT_IS_OUTSIDE, + }, + &natba.Nat64InterfaceDetails{ + SwIfIndex: 2, + Flags: nat_types.NAT_IS_INSIDE, + }) + ctx.MockVpp.MockReply(&memclnt.ControlPingReply{}) + + swIfIndexes.Put("if0", &ifaceidx.IfaceMetadata{SwIfIndex: 1}) + swIfIndexes.Put("if1", &ifaceidx.IfaceMetadata{SwIfIndex: 2}) + + interfaces, err := natHandler.Nat64InterfacesDump() + Expect(err).To(Succeed()) + + Expect(interfaces).To(HaveLen(2)) + + Expect(interfaces[0].Name).To(Equal("if0")) + Expect(interfaces[0].Type).To(Equal(nat64.Nat64Interface_IPV4_OUTSIDE)) + + Expect(interfaces[1].Name).To(Equal("if1")) + Expect(interfaces[1].Type).To(Equal(nat64.Nat64Interface_IPV6_INSIDE)) +} + +func TestNat64AddressPoolsDump(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + // address pool + ctx.MockVpp.MockReply( + &natba.Nat64PoolAddrDetails{ + Address: ipTo4Address("10.10.1.1"), + VrfID: 1, + }, + &natba.Nat64PoolAddrDetails{ + Address: ipTo4Address("80.80.80.2"), + VrfID: 2, + }, + &natba.Nat64PoolAddrDetails{ + Address: ipTo4Address("192.168.10.3"), + VrfID: 2, + }, + &natba.Nat64PoolAddrDetails{ + Address: ipTo4Address("192.168.10.4"), + VrfID: 2, + }, + &natba.Nat64PoolAddrDetails{ + Address: ipTo4Address("192.168.10.5"), + VrfID: 2, + }) + ctx.MockVpp.MockReply(&memclnt.ControlPingReply{}) + + pools, err := natHandler.Nat64AddressPoolsDump([]*nat64.Nat64AddressPool{ + { + VrfId: 2, + FirstIp: "192.168.10.3", + LastIp: "192.168.10.5", + }, + }) + Expect(err).To(Succeed()) + + Expect(pools).To(HaveLen(3)) + + sort.Slice(pools, func(i, j int) bool { + return pools[i].FirstIp < pools[j].FirstIp + }) + + Expect(pools[0].FirstIp).To(Equal("10.10.1.1")) + Expect(pools[0].LastIp).To(Equal("10.10.1.1")) + Expect(pools[0].VrfId).To(BeEquivalentTo(1)) + + Expect(pools[1].FirstIp).To(Equal("192.168.10.3")) + Expect(pools[1].LastIp).To(Equal("192.168.10.5")) + Expect(pools[1].VrfId).To(BeEquivalentTo(2)) + + Expect(pools[2].FirstIp).To(Equal("80.80.80.2")) + Expect(pools[2].LastIp).To(Equal("80.80.80.2")) + Expect(pools[2].VrfId).To(BeEquivalentTo(2)) +} + +func TestNat64StaticBIBsDump(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + ctx.MockVpp.MockReply( + &natba.Nat64BibDetails{ + IAddr: ipTo6Address("2000::3"), + IPort: 8080, + OAddr: ipTo4Address("172.16.2.3"), + OPort: 80, + VrfID: 5, + Proto: 6, // TCP + Flags: nat_types.NAT_IS_STATIC, + }, + &natba.Nat64BibDetails{ + IAddr: ipTo6Address("2000::8"), + IPort: 9090, + OAddr: ipTo4Address("10.10.5.5"), + OPort: 90, + VrfID: 0, + Proto: 17, // UDP + Flags: nat_types.NAT_IS_STATIC, + }, + &natba.Nat64BibDetails{ + IAddr: ipTo6Address("2000::10"), + OAddr: ipTo4Address("80.80.80.1"), + VrfID: 4, + Proto: 1, // ICMP + Flags: nat_types.NAT_IS_STATIC, + }) + ctx.MockVpp.MockReply(&memclnt.ControlPingReply{}) + + bibs, err := natHandler.Nat64StaticBIBsDump() + Expect(err).To(Succeed()) + + Expect(bibs).To(HaveLen(3)) + + Expect(bibs[0].InsideIpv6Address).To(Equal("2000::3")) + Expect(bibs[0].InsidePort).To(BeEquivalentTo(8080)) + Expect(bibs[0].OutsideIpv4Address).To(Equal("172.16.2.3")) + Expect(bibs[0].OutsidePort).To(BeEquivalentTo(80)) + Expect(bibs[0].VrfId).To(BeEquivalentTo(5)) + Expect(bibs[0].Protocol).To(Equal(nat64.Nat64StaticBIB_TCP)) + + Expect(bibs[1].InsideIpv6Address).To(Equal("2000::8")) + Expect(bibs[1].InsidePort).To(BeEquivalentTo(9090)) + Expect(bibs[1].OutsideIpv4Address).To(Equal("10.10.5.5")) + Expect(bibs[1].OutsidePort).To(BeEquivalentTo(90)) + Expect(bibs[1].VrfId).To(BeEquivalentTo(0)) + Expect(bibs[1].Protocol).To(Equal(nat64.Nat64StaticBIB_UDP)) + + Expect(bibs[2].InsideIpv6Address).To(Equal("2000::10")) + Expect(bibs[2].InsidePort).To(BeEquivalentTo(0)) + Expect(bibs[2].OutsideIpv4Address).To(Equal("80.80.80.1")) + Expect(bibs[2].OutsidePort).To(BeEquivalentTo(0)) + Expect(bibs[2].VrfId).To(BeEquivalentTo(4)) + Expect(bibs[2].Protocol).To(Equal(nat64.Nat64StaticBIB_ICMP)) +} + +func ipTo6Address(ipStr string) (addr ip_types.IP6Address) { + netIP := net.ParseIP(ipStr) + Expect(netIP).ToNot(BeNil()) + ip4 := netIP.To4() + Expect(ip4).To(BeNil()) + copy(addr[:], netIP.To16()) + return +} + +func ipTo6Prefix(ipStr string) (prefix ip_types.IP6Prefix) { + _, netIP, err := net.ParseCIDR(ipStr) + Expect(err).To(Succeed()) + ip4 := netIP.IP.To4() + Expect(ip4).To(BeNil()) + copy(prefix.Address[:], netIP.IP.To16()) + prefixLen, _ := netIP.Mask.Size() + prefix.Len = uint8(prefixLen) + return +} + +func ipTo4Address(ipStr string) (addr ip_types.IP4Address) { + netIP := net.ParseIP(ipStr) + if ip4 := netIP.To4(); ip4 != nil { + copy(addr[:], ip4) + } + return +} + +func natTestSetup(t *testing.T) (*vppmock.TestCtx, vppcalls.Nat64VppAPI, ifaceidx.IfaceMetadataIndexRW) { + ctx := vppmock.SetupTestCtx(t) + log := logrus.NewLogger("test-log") + swIfIndexes := ifaceidx.NewIfaceIndex(logrus.DefaultLogger(), "test-sw_if_indexes") + natHandler := vpp2306.NewNat64VppHandler(ctx.MockChannel, swIfIndexes, log) + return ctx, natHandler, swIfIndexes +} diff --git a/plugins/nat64/vppcalls/vpp2306/nat64_vppcalls.go b/plugins/nat64/vppcalls/vpp2306/nat64_vppcalls.go new file mode 100644 index 00000000..bfbc6a06 --- /dev/null +++ b/plugins/nat64/vppcalls/vpp2306/nat64_vppcalls.go @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + "fmt" + "net" + + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/interface_types" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ip_types" + natba "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat64" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat_types" + + "go.pantheon.tech/stonework/proto/nat64" +) + +// AddNat64IPv6Prefix adds IPv6 prefix for NAT64 (used to embed IPv4 address). +func (h *Nat64VppHandler) AddNat64IPv6Prefix(vrf uint32, prefix string) error { + return h.handleNat64IPv6Prefix(vrf, prefix, true) +} + +// DelNat64IPv6Prefix removes existing IPv6 prefix previously configured for NAT64. +func (h *Nat64VppHandler) DelNat64IPv6Prefix(vrf uint32, prefix string) error { + return h.handleNat64IPv6Prefix(vrf, prefix, false) +} + +// EnableNat64Interface enables NAT64 for provided interface. +func (h *Nat64VppHandler) EnableNat64Interface(iface string, natIfaceType nat64.Nat64Interface_Type) error { + return h.handleNat64Interface(iface, natIfaceType, true) +} + +// DisableNat64Interface disables NAT64 for provided interface. +func (h *Nat64VppHandler) DisableNat64Interface(iface string, natIfaceType nat64.Nat64Interface_Type) error { + return h.handleNat64Interface(iface, natIfaceType, false) +} + +// AddNat64AddressPool adds new IPV4 address pool into the NAT64 pools. +func (h *Nat64VppHandler) AddNat64AddressPool(vrf uint32, firstIP, lastIP string) error { + return h.handleNat64AddressPool(vrf, firstIP, lastIP, true) +} + +// DelNat64AddressPool removes existing IPv4 address pool from the NAT64 pools. +func (h *Nat64VppHandler) DelNat64AddressPool(vrf uint32, firstIP, lastIP string) error { + return h.handleNat64AddressPool(vrf, firstIP, lastIP, false) +} + +// AddNat64StaticBIB creates new NAT64 static binding. +func (h *Nat64VppHandler) AddNat64StaticBIB(bib *nat64.Nat64StaticBIB) error { + return h.handleNat64StaticBIB(bib, true) +} + +// DelNat64StaticBIB removes existing NAT64 static binding. +func (h *Nat64VppHandler) DelNat64StaticBIB(bib *nat64.Nat64StaticBIB) error { + return h.handleNat64StaticBIB(bib, false) +} + +// Calls VPP binary API to set/unset NAT64 IPv6 prefix. +func (h *Nat64VppHandler) handleNat64IPv6Prefix(vrf uint32, prefix string, isAdd bool) error { + ipv6Prefix, err := ipTo6Prefix(prefix) + if err != nil { + return fmt.Errorf("unable to parse IPv6 prefix %s: %v", prefix, err) + } + req := &natba.Nat64AddDelPrefix{ + VrfID: vrf, + Prefix: ipv6Prefix, + IsAdd: isAdd, + } + reply := &natba.Nat64AddDelPrefixReply{} + if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { + return err + } + return nil +} + +// Calls VPP binary API to set/unset interface NAT64 feature. +func (h *Nat64VppHandler) handleNat64Interface(iface string, natIfaceType nat64.Nat64Interface_Type, isAdd bool) error { + // get interface metadata + ifaceMeta, found := h.ifIndexes.LookupByName(iface) + if !found { + return fmt.Errorf("failed to get metadata for interface: %s", iface) + } + var flags nat_types.NatConfigFlags + switch natIfaceType { + case nat64.Nat64Interface_IPV6_INSIDE: + flags = nat_types.NAT_IS_INSIDE + case nat64.Nat64Interface_IPV4_OUTSIDE: + flags = nat_types.NAT_IS_OUTSIDE + } + req := &natba.Nat64AddDelInterface{ + SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex), + Flags: flags, + IsAdd: isAdd, + } + reply := &natba.Nat64AddDelInterfaceReply{} + if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { + return err + } + return nil +} + +// Calls VPP binary API to add/del NAT64 address pool. +func (h *Nat64VppHandler) handleNat64AddressPool(vrf uint32, firstIP, lastIP string, isAdd bool) error { + startAddr, err := ip_types.ParseIP4Address(firstIP) + if err != nil { + return fmt.Errorf("unable to parse address %s from the NAT64 pool: %v", firstIP, err) + } + endAddr := startAddr + if lastIP != "" { + endAddr, err = ip_types.ParseIP4Address(lastIP) + if err != nil { + return fmt.Errorf("unable to parse address %s from the NAT64 pool: %v", lastIP, err) + } + } + req := &natba.Nat64AddDelPoolAddrRange{ + StartAddr: startAddr, + EndAddr: endAddr, + VrfID: vrf, + IsAdd: isAdd, + } + reply := &natba.Nat64AddDelPoolAddrRangeReply{} + if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { + return err + } + return nil +} + +// Calls VPP binary API to add/del NAT64 static binding. +func (h *Nat64VppHandler) handleNat64StaticBIB(bib *nat64.Nat64StaticBIB, isAdd bool) error { + inAddr, err := ip_types.ParseIP6Address(bib.GetInsideIpv6Address()) + if err != nil { + return fmt.Errorf("unable to parse inside IPv6 address (%s): %v", bib.GetInsideIpv6Address(), err) + } + outAddr, err := ip_types.ParseIP4Address(bib.GetOutsideIpv4Address()) + if err != nil { + return fmt.Errorf("unable to parse outside IPv4 address (%s): %v", bib.GetOutsideIpv4Address(), err) + } + var proto uint8 + switch bib.GetProtocol() { + case nat64.Nat64StaticBIB_TCP: + proto = TCP + case nat64.Nat64StaticBIB_UDP: + proto = UDP + case nat64.Nat64StaticBIB_ICMP: + proto = ICMP + default: + h.log.Warnf("Unknown protocol %v, defaulting to TCP", bib.GetProtocol()) + proto = TCP + } + req := &natba.Nat64AddDelStaticBib{ + IAddr: inAddr, + OAddr: outAddr, + IPort: uint16(bib.GetInsidePort()), + OPort: uint16(bib.GetOutsidePort()), + Proto: proto, + VrfID: bib.GetVrfId(), + IsAdd: isAdd, + } + reply := &natba.Nat64AddDelStaticBibReply{} + if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { + return err + } + return nil +} + +func ipTo6Prefix(ipStr string) (prefix ip_types.IP6Prefix, err error) { + _, netIP, err := net.ParseCIDR(ipStr) + if err != nil { + return prefix, fmt.Errorf("invalid IP (%q): %v", ipStr, err) + } + if ip4 := netIP.IP.To4(); ip4 != nil { + return prefix, fmt.Errorf("required IPv6, provided IPv4 prefix: %q", ipStr) + } + copy(prefix.Address[:], netIP.IP.To16()) + prefixLen, _ := netIP.Mask.Size() + prefix.Len = uint8(prefixLen) + return +} diff --git a/plugins/nat64/vppcalls/vpp2306/nat64_vppcalls_test.go b/plugins/nat64/vppcalls/vpp2306/nat64_vppcalls_test.go new file mode 100644 index 00000000..f5a9cc7e --- /dev/null +++ b/plugins/nat64/vppcalls/vpp2306/nat64_vppcalls_test.go @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306_test + +import ( + "net" + "testing" + + . "github.com/onsi/gomega" + + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/ip_types" + natba "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat64" + "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat_types" + "go.pantheon.tech/stonework/proto/nat64" +) + +func TestAddNat64IPv6Prefix(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + _, prefix1, _ := net.ParseCIDR("64:ff9b::/96") + _, prefix2, _ := net.ParseCIDR("2004::/32") + + ctx.MockVpp.MockReply(&natba.Nat64AddDelPrefixReply{}) + err := natHandler.AddNat64IPv6Prefix(0, prefix1.String()) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelPrefix) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(ip6PrefixToIPNet(msg.Prefix)).To(BeEquivalentTo(prefix1.String())) + Expect(msg.VrfID).To(BeEquivalentTo(0)) + + ctx.MockVpp.MockReply(&natba.Nat64AddDelPrefixReply{}) + err = natHandler.AddNat64IPv6Prefix(5, prefix2.String()) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelPrefix) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(ip6PrefixToIPNet(msg.Prefix)).To(BeEquivalentTo(prefix2.String())) + Expect(msg.VrfID).To(BeEquivalentTo(5)) + + err = natHandler.AddNat64IPv6Prefix(1, "invalid prefix") + Expect(err).Should(HaveOccurred()) + err = natHandler.AddNat64IPv6Prefix(1, "192.168.1.0/24") + Expect(err).Should(HaveOccurred()) +} + +func TestDelNat64IPv6Prefix(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + _, prefix1, _ := net.ParseCIDR("64:ff9b::/96") + _, prefix2, _ := net.ParseCIDR("2004::/32") + + ctx.MockVpp.MockReply(&natba.Nat64AddDelPrefixReply{}) + err := natHandler.DelNat64IPv6Prefix(0, prefix1.String()) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelPrefix) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(ip6PrefixToIPNet(msg.Prefix)).To(BeEquivalentTo(prefix1.String())) + Expect(msg.VrfID).To(BeEquivalentTo(0)) + + ctx.MockVpp.MockReply(&natba.Nat64AddDelPrefixReply{}) + err = natHandler.DelNat64IPv6Prefix(5, prefix2.String()) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelPrefix) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(ip6PrefixToIPNet(msg.Prefix)).To(BeEquivalentTo(prefix2.String())) + Expect(msg.VrfID).To(BeEquivalentTo(5)) + + err = natHandler.DelNat64IPv6Prefix(1, "invalid prefix") + Expect(err).Should(HaveOccurred()) + err = natHandler.DelNat64IPv6Prefix(1, "192.168.1.0/24") + Expect(err).Should(HaveOccurred()) +} + +func TestEnableNat64Interface(t *testing.T) { + ctx, natHandler, swIfIndexes := natTestSetup(t) + defer ctx.TeardownTestCtx() + + swIfIndexes.Put("if1", &ifaceidx.IfaceMetadata{SwIfIndex: 2}) + + ctx.MockVpp.MockReply(&natba.Nat64AddDelInterfaceReply{}) + err := natHandler.EnableNat64Interface("if1", nat64.Nat64Interface_IPV4_OUTSIDE) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelInterface) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(msg.SwIfIndex).To(BeEquivalentTo(2)) + Expect(msg.Flags).To(BeEquivalentTo(nat_types.NAT_IS_OUTSIDE)) + + ctx.MockVpp.MockReply(&natba.Nat64AddDelInterfaceReply{}) + err = natHandler.EnableNat64Interface("if1", nat64.Nat64Interface_IPV6_INSIDE) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelInterface) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(msg.SwIfIndex).To(BeEquivalentTo(2)) + Expect(msg.Flags).To(BeEquivalentTo(nat_types.NAT_IS_INSIDE)) + + err = natHandler.EnableNat64Interface("non-existent interface", nat64.Nat64Interface_IPV4_OUTSIDE) + Expect(err).Should(HaveOccurred()) +} + +func TestDisableNat64Interface(t *testing.T) { + ctx, natHandler, swIfIndexes := natTestSetup(t) + defer ctx.TeardownTestCtx() + + swIfIndexes.Put("if1", &ifaceidx.IfaceMetadata{SwIfIndex: 2}) + + ctx.MockVpp.MockReply(&natba.Nat64AddDelInterfaceReply{}) + err := natHandler.DisableNat64Interface("if1", nat64.Nat64Interface_IPV4_OUTSIDE) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelInterface) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(msg.SwIfIndex).To(BeEquivalentTo(2)) + Expect(msg.Flags).To(BeEquivalentTo(nat_types.NAT_IS_OUTSIDE)) + + ctx.MockVpp.MockReply(&natba.Nat64AddDelInterfaceReply{}) + err = natHandler.DisableNat64Interface("if1", nat64.Nat64Interface_IPV6_INSIDE) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelInterface) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(msg.SwIfIndex).To(BeEquivalentTo(2)) + Expect(msg.Flags).To(BeEquivalentTo(nat_types.NAT_IS_INSIDE)) + + err = natHandler.DisableNat64Interface("non-existent interface", nat64.Nat64Interface_IPV4_OUTSIDE) + Expect(err).Should(HaveOccurred()) +} + +func TestAddNat64AddressPool(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + addr1 := net.ParseIP("10.0.0.1").To4() + addr2 := net.ParseIP("10.0.0.10").To4() + + // first IP only + ctx.MockVpp.MockReply(&natba.Nat64AddDelPoolAddrRangeReply{}) + err := natHandler.AddNat64AddressPool(0, addr1.String(), "") + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelPoolAddrRange) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(msg.StartAddr.String()).To(BeEquivalentTo(addr1.String())) + Expect(msg.EndAddr.String()).To(BeEquivalentTo(addr1.String())) + Expect(msg.VrfID).To(BeEquivalentTo(0)) + + // first IP + last IP + ctx.MockVpp.MockReply(&natba.Nat64AddDelPoolAddrRangeReply{}) + err = natHandler.AddNat64AddressPool(5, addr1.String(), addr2.String()) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelPoolAddrRange) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(msg.StartAddr.String()).To(BeEquivalentTo(addr1.String())) + Expect(msg.EndAddr.String()).To(BeEquivalentTo(addr2.String())) + Expect(msg.VrfID).To(BeEquivalentTo(5)) + + err = natHandler.AddNat64AddressPool(0, "invalid address", "") + Expect(err).Should(HaveOccurred()) + err = natHandler.AddNat64AddressPool(0, "invalid address", addr2.String()) + Expect(err).Should(HaveOccurred()) + err = natHandler.AddNat64AddressPool(0, addr1.String(), "invalid address") + Expect(err).Should(HaveOccurred()) +} + +func TestDelNat64AddressPool(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + addr1 := net.ParseIP("10.0.0.1").To4() + addr2 := net.ParseIP("10.0.0.10").To4() + + // first IP only + ctx.MockVpp.MockReply(&natba.Nat64AddDelPoolAddrRangeReply{}) + err := natHandler.DelNat64AddressPool(0, addr1.String(), "") + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelPoolAddrRange) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(msg.StartAddr.String()).To(BeEquivalentTo(addr1.String())) + Expect(msg.EndAddr.String()).To(BeEquivalentTo(addr1.String())) + Expect(msg.VrfID).To(BeEquivalentTo(0)) + + // first IP + last IP + ctx.MockVpp.MockReply(&natba.Nat64AddDelPoolAddrRangeReply{}) + err = natHandler.DelNat64AddressPool(5, addr1.String(), addr2.String()) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelPoolAddrRange) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(msg.StartAddr.String()).To(BeEquivalentTo(addr1.String())) + Expect(msg.EndAddr.String()).To(BeEquivalentTo(addr2.String())) + Expect(msg.VrfID).To(BeEquivalentTo(5)) + + err = natHandler.DelNat64AddressPool(0, "invalid address", "") + Expect(err).Should(HaveOccurred()) + err = natHandler.DelNat64AddressPool(0, "invalid address", addr2.String()) + Expect(err).Should(HaveOccurred()) + err = natHandler.DelNat64AddressPool(0, addr1.String(), "invalid address") + Expect(err).Should(HaveOccurred()) +} + +func TestAddNat64StaticBIB(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + inAddr := net.ParseIP("2000::3").To16() + outAddr := net.ParseIP("172.16.2.3").To4() + + // TCP + ctx.MockVpp.MockReply(&natba.Nat64AddDelStaticBibReply{}) + err := natHandler.AddNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 10, + InsideIpv6Address: inAddr.String(), + InsidePort: 8000, + OutsideIpv4Address: outAddr.String(), + OutsidePort: 80, + Protocol: nat64.Nat64StaticBIB_TCP, + }) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelStaticBib) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(msg.IAddr.String()).To(BeEquivalentTo(inAddr.String())) + Expect(msg.IPort).To(BeEquivalentTo(8000)) + Expect(msg.OAddr.String()).To(BeEquivalentTo(outAddr.String())) + Expect(msg.OPort).To(BeEquivalentTo(80)) + Expect(msg.VrfID).To(BeEquivalentTo(10)) + Expect(msg.Proto).To(BeEquivalentTo(6)) + + // UDP + ctx.MockVpp.MockReply(&natba.Nat64AddDelStaticBibReply{}) + err = natHandler.AddNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 0, + InsideIpv6Address: inAddr.String(), + InsidePort: 9000, + OutsideIpv4Address: outAddr.String(), + OutsidePort: 90, + Protocol: nat64.Nat64StaticBIB_UDP, + }) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelStaticBib) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeTrue()) + Expect(msg.IAddr.String()).To(BeEquivalentTo(inAddr.String())) + Expect(msg.IPort).To(BeEquivalentTo(9000)) + Expect(msg.OAddr.String()).To(BeEquivalentTo(outAddr.String())) + Expect(msg.OPort).To(BeEquivalentTo(90)) + Expect(msg.VrfID).To(BeEquivalentTo(0)) + Expect(msg.Proto).To(BeEquivalentTo(17)) + + // Invalid data + err = natHandler.AddNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 0, + InsideIpv6Address: "192.168.1.1", // expecting IPv6 + InsidePort: 9000, + OutsideIpv4Address: outAddr.String(), + OutsidePort: 90, + Protocol: nat64.Nat64StaticBIB_UDP, + }) + Expect(err).Should(HaveOccurred()) + err = natHandler.AddNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 0, + InsideIpv6Address: inAddr.String(), + InsidePort: 9000, + OutsideIpv4Address: "invalid IP address", + OutsidePort: 90, + Protocol: nat64.Nat64StaticBIB_UDP, + }) + Expect(err).Should(HaveOccurred()) +} + +func TestDelNat64StaticBIB(t *testing.T) { + ctx, natHandler, _ := natTestSetup(t) + defer ctx.TeardownTestCtx() + + inAddr := net.ParseIP("2000::3").To16() + outAddr := net.ParseIP("172.16.2.3").To4() + + // TCP + ctx.MockVpp.MockReply(&natba.Nat64AddDelStaticBibReply{}) + err := natHandler.DelNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 10, + InsideIpv6Address: inAddr.String(), + InsidePort: 8000, + OutsideIpv4Address: outAddr.String(), + OutsidePort: 80, + Protocol: nat64.Nat64StaticBIB_TCP, + }) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok := ctx.MockChannel.Msg.(*natba.Nat64AddDelStaticBib) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(msg.IAddr.String()).To(BeEquivalentTo(inAddr.String())) + Expect(msg.IPort).To(BeEquivalentTo(8000)) + Expect(msg.OAddr.String()).To(BeEquivalentTo(outAddr.String())) + Expect(msg.OPort).To(BeEquivalentTo(80)) + Expect(msg.VrfID).To(BeEquivalentTo(10)) + Expect(msg.Proto).To(BeEquivalentTo(6)) + + // UDP + ctx.MockVpp.MockReply(&natba.Nat64AddDelStaticBibReply{}) + err = natHandler.DelNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 0, + InsideIpv6Address: inAddr.String(), + InsidePort: 9000, + OutsideIpv4Address: outAddr.String(), + OutsidePort: 90, + Protocol: nat64.Nat64StaticBIB_UDP, + }) + Expect(err).ShouldNot(HaveOccurred()) + + msg, ok = ctx.MockChannel.Msg.(*natba.Nat64AddDelStaticBib) + Expect(ok).To(BeTrue()) + Expect(msg.IsAdd).To(BeFalse()) + Expect(msg.IAddr.String()).To(BeEquivalentTo(inAddr.String())) + Expect(msg.IPort).To(BeEquivalentTo(9000)) + Expect(msg.OAddr.String()).To(BeEquivalentTo(outAddr.String())) + Expect(msg.OPort).To(BeEquivalentTo(90)) + Expect(msg.VrfID).To(BeEquivalentTo(0)) + Expect(msg.Proto).To(BeEquivalentTo(17)) + + // Invalid data + err = natHandler.DelNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 0, + InsideIpv6Address: "192.168.1.1", // expecting IPv6 + InsidePort: 9000, + OutsideIpv4Address: outAddr.String(), + OutsidePort: 90, + Protocol: nat64.Nat64StaticBIB_UDP, + }) + Expect(err).Should(HaveOccurred()) + err = natHandler.DelNat64StaticBIB(&nat64.Nat64StaticBIB{ + VrfId: 0, + InsideIpv6Address: inAddr.String(), + InsidePort: 9000, + OutsideIpv4Address: "invalid IP address", + OutsidePort: 90, + Protocol: nat64.Nat64StaticBIB_UDP, + }) + Expect(err).Should(HaveOccurred()) +} + +func ip6PrefixToIPNet(prefix ip_types.IP6Prefix) string { + ipNet := &net.IPNet{} + ipNet.IP = make(net.IP, net.IPv6len) + copy(ipNet.IP[:], prefix.Address[:]) + ipNet.Mask = net.CIDRMask(int(prefix.Len), 8*net.IPv6len) + return ipNet.String() +} diff --git a/plugins/nat64/vppcalls/vpp2306/vppcalls_handler.go b/plugins/nat64/vppcalls/vpp2306/vppcalls_handler.go new file mode 100644 index 00000000..83855bcd --- /dev/null +++ b/plugins/nat64/vppcalls/vpp2306/vppcalls_handler.go @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2022 PANTHEON.tech +// +// 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. + +package vpp2306 + +import ( + govppapi "go.fd.io/govpp/api" + "go.ligato.io/cn-infra/v2/logging" + + "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" + + binapi "go.pantheon.tech/stonework/plugins/binapi/vpp2306" + natba "go.pantheon.tech/stonework/plugins/binapi/vpp2306/nat64" + "go.pantheon.tech/stonework/plugins/nat64/vppcalls" +) + +func init() { + var msgs []govppapi.Message + msgs = append(msgs, natba.AllMessages()...) + + vppcalls.AddNat64HandlerVersion(binapi.Version, msgs, NewNat64VppHandler) +} + +// Nat64VppHandler is accessor for NAT64-related vppcalls methods. +type Nat64VppHandler struct { + callsChannel govppapi.Channel + ifIndexes ifaceidx.IfaceMetadataIndex + log logging.Logger +} + +// NewNat64VppHandler creates new instance of NAT64 vppcalls handler. +func NewNat64VppHandler(callsChan govppapi.Channel, + ifIndexes ifaceidx.IfaceMetadataIndex, log logging.Logger, +) vppcalls.Nat64VppAPI { + return &Nat64VppHandler{ + callsChannel: callsChan, + ifIndexes: ifIndexes, + log: log, + } +} diff --git a/scripts/dev/mockcnf.Dockerfile b/scripts/dev/mockcnf.Dockerfile index 9cb0bc86..bc7530d9 100644 --- a/scripts/dev/mockcnf.Dockerfile +++ b/scripts/dev/mockcnf.Dockerfile @@ -14,6 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM stonework-mockcnf:22.10 +FROM stonework-mockcnf:23.06 COPY mockcnf /usr/local/bin/mockcnf diff --git a/scripts/dev/replace-mockcnf.sh b/scripts/dev/replace-mockcnf.sh index 302cb1c6..33552359 100755 --- a/scripts/dev/replace-mockcnf.sh +++ b/scripts/dev/replace-mockcnf.sh @@ -23,6 +23,6 @@ set -e cp ../../cmd/mockcnf/mockcnf . # rebuild the production image with replaced agent binary -docker build -f mockcnf.Dockerfile -t stonework-mockcnf:22.10 --no-cache --force-rm=true . +docker build -f mockcnf.Dockerfile -t stonework-mockcnf:23.06 --no-cache --force-rm=true . rm -f ./mockcnf diff --git a/scripts/dev/replace-stonework.sh b/scripts/dev/replace-stonework.sh index 80c2048a..0bafbab8 100755 --- a/scripts/dev/replace-stonework.sh +++ b/scripts/dev/replace-stonework.sh @@ -23,6 +23,6 @@ set -e cp ../../cmd/stonework/stonework . # rebuild the production image with replaced agent binary -docker build -f stonework.Dockerfile -t stonework:22.10 --no-cache --force-rm=true . +docker build -f stonework.Dockerfile -t stonework:23.06 --no-cache --force-rm=true . rm -f ./stonework diff --git a/scripts/dev/stonework.Dockerfile b/scripts/dev/stonework.Dockerfile index 9bdf603a..c5fe03cc 100644 --- a/scripts/dev/stonework.Dockerfile +++ b/scripts/dev/stonework.Dockerfile @@ -14,6 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM stonework:22.10 +FROM stonework:23.06 COPY stonework /usr/local/bin/stonework diff --git a/scripts/gen-docs.sh b/scripts/gen-docs.sh index 93db6092..538670a4 100755 --- a/scripts/gen-docs.sh +++ b/scripts/gen-docs.sh @@ -54,17 +54,17 @@ while IFS= read -r line; do done # generate root proto message -docker run --rm -v ${TMPDIR}:/api stonework-proto-rootgen:22.10 proto-rootgen --cnf-name ${CNFNAME} +docker run --rm -v ${TMPDIR}:/api stonework-proto-rootgen:23.06 proto-rootgen --cnf-name ${CNFNAME} # generate json schema -docker run --rm -v ${TMPDIR}:/api stonework-proto-rootgen:22.10 \ +docker run --rm -v ${TMPDIR}:/api stonework-proto-rootgen:23.06 \ protoc \ --jsonschema_out="json_fieldnames:/api" \ --proto_path=/api /api/${CNFNAME,,}-root.proto cp ${TMPDIR}/Root.jsonschema ${OUTDIR}/${CNFNAME^^}-CONFIG.jsonschema # generate docs (as markdown & pdf) -docker run --rm -v ${TMPDIR}:/api stonework-proto-rootgen:22.10 \ +docker run --rm -v ${TMPDIR}:/api stonework-proto-rootgen:23.06 \ bash -x -c "\ sed -i 's/{{.CnfName}}/${CNFNAME}/g' /gendoc/markdown.tmpl && protos=\$(find /api -name \"*.proto\" | grep -vF \"/api/${CNFNAME,,}-root.proto\") && diff --git a/vpp/abx/vpp2306/CMakeLists.txt b/vpp/abx/vpp2306/CMakeLists.txt new file mode 100644 index 00000000..91159691 --- /dev/null +++ b/vpp/abx/vpp2306/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.5) + +project(abx-plugin) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +find_package(VPP) + +add_subdirectory(abx) diff --git a/vpp/abx/vpp2306/abx/CMakeLists.txt b/vpp/abx/vpp2306/abx/CMakeLists.txt new file mode 100644 index 00000000..25bca676 --- /dev/null +++ b/vpp/abx/vpp2306/abx/CMakeLists.txt @@ -0,0 +1,37 @@ + +# Copyright (c) 2022 PANTHEON.tech. +# 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. + +include_directories(${CMAKE_SOURCE_DIR}) +message(STATUS "VPP_WORKSPACE: ${VPP_WORKSPACE}") +include_directories(${VPP_WORKSPACE}/src/) + +# for generated API headers: +include_directories(${CMAKE_BINARY_DIR}) + +add_vpp_plugin(abx + SOURCES + node.c + abx.c + abx_api.c + abx_policy.c + abx_if_attach.c + + MULTIARCH_SOURCES + node.c + + API_FILES + abx.api + + COMPONENT vpp-plugin-abx +) diff --git a/vpp/abx/vpp2306/abx/abx.api b/vpp/abx/vpp2306/abx/abx.api new file mode 100644 index 00000000..f957d180 --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx.api @@ -0,0 +1,137 @@ +/* + * abx.api - binary API skeleton + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +/** + * @file abx.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane binary API messages which are generally + * called through a shared memory interface. + */ + +/* Version and type recitations */ + +option version = "0.1.0"; +import "vnet/interface_types.api"; +import "vnet/ethernet/ethernet_types.api"; + +/** \brief Get the plugin version + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define abx_plugin_get_version +{ + u32 client_index; + u32 context; +}; + +/** \brief Reply to get the plugin version + @param context - returned sender context, to match reply w/ request + @param major - Incremented every time a known breaking behavior change is introduced + @param minor - Incremented with small changes, may be used to avoid buggy versions +*/ +define abx_plugin_get_version_reply +{ + u32 context; + u32 major; + u32 minor; +}; + +/** \brief A description of an ABX policy + @param policy_id User chosen Identifier for the policy + @param acl_index The ACL that the policy will match against + @param tx_sw_if_index Tx interface index to be used for forwarding + */ +typedef abx_policy +{ + u32 policy_id; + u32 acl_index; + u32 tx_sw_if_index; + vl_api_mac_address_t dst_mac; +}; + +/** \brief A description of a policy attachment to an interface + @param The policy ID to attach + @param priority The priority of the attachment, w.r.t. to other attachments + on this interface. lower value is 'better' + @param rx_sw_if_index The interface to attach to + */ +typedef abx_interface_attach +{ + u32 policy_id; + u32 priority; + u32 rx_sw_if_index; +}; + +/** \brief A description of an ABX policy + @param is_add Is this the addition or removal of paths from the policy + If the policy does not exist it is created. If the last path + Is being removed, the policy is deleted + @param policy The ABX policy + */ +autoreply define abx_policy_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_abx_policy_t policy; +}; + +/** \brief A description of an ABX interface attach +*/ +autoreply define abx_interface_attach_detach +{ + u32 client_index; + u32 context; + u8 is_attach; + vl_api_abx_interface_attach_t attach; +}; + +/** \brief Dump ABX policies response + @param policy - the ABX policy +*/ +define abx_policy_details { + u32 context; + vl_api_abx_policy_t policy; +}; + +/** \brief Dump ABX policies + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define abx_policy_dump { + u32 client_index; + u32 context; +}; + +/** \brief Dump interfaces with attached ABX policies response + @param priority - priority of ABX policy + @param policy_id - id of ABX policy +*/ +define abx_interface_attach_details { + u32 context; + vl_api_abx_interface_attach_t attach; +}; + +/** \brief Dump interfaces with attached ABX policies + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define abx_interface_attach_dump { + u32 client_index; + u32 context; +}; diff --git a/vpp/abx/vpp2306/abx/abx.c b/vpp/abx/vpp2306/abx/abx.c new file mode 100644 index 00000000..372fdd87 --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx.c @@ -0,0 +1,82 @@ +/* + * abx.c - skeleton vpp engine plug-in + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include +#include +#include + +#include +#include + +/* define message IDs */ +#include +#include +#include + +abx_main_t abx_main; + +static clib_error_t * +abx_init (vlib_main_t * vm) +{ + abx_main_t *abx = &abx_main; + clib_error_t *error = 0; + + abx->vlib_main = vm; + abx->vnet_main = vnet_get_main (); + + return error; +} + +VLIB_INIT_FUNCTION (abx_init); + +/* *INDENT-OFF* */ +VNET_FEATURE_INIT (abx_ip4_unicast, static) = { + .arc_name = "ip4-unicast", + .node_name = "abx-ip4", + .runs_before = VNET_FEATURES("nat-pre-out2in", "nat-pre-in2out", "nat44-ed-out2in", "nat44-ed-in2out"), +}; + +VNET_FEATURE_INIT (abx_ip4_multicast, static) = { + .arc_name = "ip4-multicast", + .node_name = "abx-ip4", +}; + +VNET_FEATURE_INIT (abx_ip6_unicast, static) = { + .arc_name = "ip6-unicast", + .node_name = "abx-ip6", +}; + +VNET_FEATURE_INIT (abx_ip6_multicast, static) = { + .arc_name = "ip6-multicast", + .node_name = "abx-ip6", +}; +/* *INDENT-ON */ + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = +{ + .description = "ACL Based Xconnect", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/abx/vpp2306/abx/abx.h b/vpp/abx/vpp2306/abx/abx.h new file mode 100644 index 00000000..445ea8da --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx.h @@ -0,0 +1,56 @@ + +/* + * abx.h - skeleton vpp engine plug-in header file + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ +#ifndef __included_abx_h__ +#define __included_abx_h__ + +#include +#include +#include +#include +#include + +#include +#include + +#define ABX_PLUGIN_VERSION_MAJOR 1 +#define ABX_PLUGIN_VERSION_MINOR 2 + +typedef struct +{ + /* API message ID base */ + u16 msg_id_base; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + ethernet_main_t *ethernet_main; +} abx_main_t; + +extern abx_main_t abx_main; + +extern vlib_node_registration_t abx_node; + +#endif /* __included_abx_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/abx/vpp2306/abx/abx_api.c b/vpp/abx/vpp2306/abx/abx_api.c new file mode 100644 index 00000000..1291e19a --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx_api.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +/* define message IDs */ +#include +#include +#include + +/** + * Base message ID for the plugin + */ +static u32 abx_base_msg_id; + +#define REPLY_MSG_ID_BASE (abx_base_msg_id) +#include + +/* API message handler */ + +static void +vl_api_abx_plugin_get_version_t_handler (vl_api_abx_plugin_get_version_t * mp) +{ + vl_api_abx_plugin_get_version_reply_t *rmp; + vl_api_registration_t *rp; + + rp = vl_api_client_index_to_registration (mp->client_index); + if (rp == 0) + return; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_ABX_PLUGIN_GET_VERSION_REPLY + abx_base_msg_id); + rmp->context = mp->context; + rmp->major = htonl (ABX_PLUGIN_VERSION_MAJOR); + rmp->minor = htonl (ABX_PLUGIN_VERSION_MINOR); + + vl_api_send_msg (rp, (u8 *) rmp); +} + +static void +vl_api_abx_policy_add_del_t_handler (vl_api_abx_policy_add_del_t * mp) +{ + vl_api_abx_policy_add_del_reply_t *rmp; + int rv = 0; + u32 policy_id = htonl (mp->policy.policy_id); + mac_address_t mac; + + if (mp->is_add) + { + u32 acl_index = htonl (mp->policy.acl_index); + u32 tx_sw_if_index = htonl (mp->policy.tx_sw_if_index); + mac_address_decode (mp->policy.dst_mac, &mac); + + abx_policy_update (policy_id, acl_index, tx_sw_if_index, &mac); + } + else + { + abx_policy_delete (policy_id); + } + REPLY_MACRO (VL_API_ABX_POLICY_ADD_DEL_REPLY); +} + +static void +vl_api_abx_interface_attach_detach_t_handler ( + vl_api_abx_interface_attach_detach_t * mp) +{ + vl_api_abx_interface_attach_detach_reply_t *rmp; + int rv = 0; + u32 rx_sw_if_index = htonl (mp->attach.rx_sw_if_index); + u32 priority = htonl (mp->attach.priority); + u32 policy_id = htonl (mp->attach.policy_id); + + if (mp->is_attach) + { + rv = abx_if_attach (policy_id, priority, rx_sw_if_index); + } + else + { + rv = abx_if_detach (policy_id, rx_sw_if_index); + } + REPLY_MACRO (VL_API_ABX_INTERFACE_ATTACH_DETACH_REPLY); +} + +typedef struct abx_policy_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} abx_policy_walk_ctx_t; + +static walk_rc_t +abx_policy_send_details ( + u32 api, void *args) +{ + vl_api_abx_policy_details_t *mp; + abx_policy_walk_ctx_t *ctx; + abx_policy_t *ap; + + ctx = args; + ap = abx_policy_get (api); + + mp = vl_msg_api_alloc (sizeof (*mp)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = htons (VL_API_ABX_POLICY_DETAILS + abx_base_msg_id); + + mp->context = ctx->context; + mp->policy.policy_id = htonl (ap->ap_id); + mp->policy.acl_index = htonl (ap->ap_acl); + mp->policy.tx_sw_if_index = htonl (ap->ap_tx_sw_if_index); + mac_address_encode (&ap->ap_dst_mac, mp->policy.dst_mac); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_abx_policy_dump_t_handler (vl_api_abx_policy_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + abx_policy_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + abx_policy_walk (abx_policy_send_details, &ctx); +} + +typedef struct abx_if_attach_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} abx_if_attach_walk_ctx_t; + +static walk_rc_t +abx_interface_attach_details ( + u32 aiai, void *args) +{ + vl_api_abx_interface_attach_details_t *mp; + abx_if_attach_walk_ctx_t *ctx; + abx_if_attach_t *aia; + abx_policy_t *ap; + + ctx = args; + aia = abx_if_attach_get (aiai); + ap = abx_policy_get (aia->aia_abx); + + mp = vl_msg_api_alloc (sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_ABX_INTERFACE_ATTACH_DETAILS + abx_base_msg_id); + + mp->context = ctx->context; + mp->attach.policy_id = htonl (ap->ap_id); + mp->attach.priority = htonl (aia->aia_priority); + mp->attach.rx_sw_if_index = htonl (aia->aia_sw_if_index); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_abx_interface_attach_dump_t_handler (vl_api_abx_interface_attach_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + abx_if_attach_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + abx_if_attach_walk (abx_interface_attach_details, &ctx); +} + +#include + +static clib_error_t * +abx_init_api (vlib_main_t * vm) +{ + /* Ask for a correctly-sized block of API message decode slots */ + abx_base_msg_id = setup_message_id_table (); + + return 0; +} + +VLIB_INIT_FUNCTION (abx_init_api); diff --git a/vpp/abx/vpp2306/abx/abx_if_attach.c b/vpp/abx/vpp2306/abx/abx_if_attach.c new file mode 100644 index 00000000..6e32e128 --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx_if_attach.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include +#include + +/** + * Pool of ABX interface attachment objects + */ +abx_if_attach_t *abx_if_attach_pool; + +/** + * A per interface vector of attached policies. Used in the data-plane + */ +u32 **abx_per_if; + +/** + * Per interface values of ACL lookup context IDs. Used in the data-plane + */ +static u32 *abx_alctx_per_if; + +/** + * ABX ACL module user id returned during the initialization + */ +static u32 abx_acl_user_id; + +/* + * ACL plugin method vtable + */ + +static acl_plugin_methods_t acl_plugin; + +/** + * A DB of attachments; key={abx_index,sw_if_index} + */ +static uword *abx_if_attach_db; + +static u64 +abx_if_attach_mk_key (u32 abx_index, u32 sw_if_index) +{ + u64 key; + + key = abx_index; + key = key << 32; + key |= sw_if_index; + + return (key); +} + +static abx_if_attach_t * +abx_if_attach_db_find (u32 abx_index, u32 sw_if_index) +{ + uword *p; + u64 key; + + key = abx_if_attach_mk_key (abx_index, sw_if_index); + + p = hash_get (abx_if_attach_db, key); + + if (NULL != p) + return (pool_elt_at_index (abx_if_attach_pool, p[0])); + + return (NULL); +} + +static void +abx_if_attach_db_add (u32 abx_index, u32 sw_if_index, abx_if_attach_t * aia) +{ + u64 key; + + key = abx_if_attach_mk_key (abx_index, sw_if_index); + + hash_set (abx_if_attach_db, key, aia - abx_if_attach_pool); +} + +static void +abx_if_attach_db_del (u32 abx_index, u32 sw_if_index) +{ + u64 key; + + key = abx_if_attach_mk_key (abx_index, sw_if_index); + + hash_unset (abx_if_attach_db, key); +} + +static int +abx_cmp_attach_for_sort (void *v1, void *v2) +{ + const abx_if_attach_t *aia1; + const abx_if_attach_t *aia2; + + aia1 = abx_if_attach_get (*(u32 *) v1); + aia2 = abx_if_attach_get (*(u32 *) v2); + + return (aia1->aia_priority - aia2->aia_priority); +} + +void +abx_setup_acl_lc (u32 sw_if_index) +{ + u32 *acl_vec = 0; + u32 *aiai; + abx_if_attach_t *aia; + + if (~0 == abx_alctx_per_if[sw_if_index]) + return; + + vec_foreach (aiai, abx_per_if[sw_if_index]) + { + aia = abx_if_attach_get (*aiai); + vec_add1 (acl_vec, aia->aia_acl); + } + acl_plugin.set_acl_vec_for_context (abx_alctx_per_if[sw_if_index], acl_vec); + vec_free (acl_vec); +} + +static int +abx_if_enable_disable (u32 sw_if_index, u8 enable_disable) +{ + vnet_feature_enable_disable ("ip4-unicast", "abx-ip4", + sw_if_index, enable_disable, NULL, 0); + vnet_feature_enable_disable ("ip4-multicast", "abx-ip4", + sw_if_index, enable_disable, NULL, 0); + vnet_feature_enable_disable ("ip6-unicast", "abx-ip6", + sw_if_index, enable_disable, NULL, 0); + vnet_feature_enable_disable ("ip6-multicast", "abx-ip6", + sw_if_index, enable_disable, NULL, 0); + return 0; +} + +int +abx_if_attach (u32 policy_id, u32 priority, u32 sw_if_index) +{ + abx_if_attach_t *aia; + abx_policy_t *ap; + u32 api; + + api = abx_policy_find (policy_id); + if (INDEX_INVALID == api) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + ap = abx_policy_get (api); + + /* + * check this is not a duplicate + */ + aia = abx_if_attach_db_find (policy_id, sw_if_index); + if (NULL != aia) + return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS); + + /* + * construct a new attachment object + */ + pool_get (abx_if_attach_pool, aia); + + aia->aia_priority = priority; + aia->aia_acl = ap->ap_acl; + aia->aia_abx = api; + aia->aia_sw_if_index = sw_if_index; + abx_if_attach_db_add (policy_id, sw_if_index, aia); + + /* + * Insert the policy on the interfaces list. + */ + vec_validate_init_empty (abx_per_if, sw_if_index, NULL); + vec_add1 (abx_per_if[sw_if_index], aia - abx_if_attach_pool); + if (1 == vec_len (abx_per_if[sw_if_index])) + { + abx_if_enable_disable (sw_if_index, 1); + + /* if this is the first ABX policy, we need to acquire an ACL lookup context */ + vec_validate_init_empty (abx_alctx_per_if, sw_if_index, ~0); + + abx_alctx_per_if[sw_if_index] = + acl_plugin.get_lookup_context_index (abx_acl_user_id, sw_if_index, 0); + } + else + { + vec_sort_with_function (abx_per_if[sw_if_index], + abx_cmp_attach_for_sort); + } + + /* Prepare and set the list of ACLs for lookup within the context */ + abx_setup_acl_lc (sw_if_index); + + return (0); +} + +int +abx_if_detach (u32 policy_id, u32 sw_if_index) +{ + abx_if_attach_t *aia; + u32 index; + vnet_main_t *vnm = vnet_get_main (); + + /* + * check this is a valid interface + */ + if (pool_is_free_index (vnm->interface_main.sw_interfaces, + sw_if_index)) + return (VNET_API_ERROR_INVALID_SW_IF_INDEX); + + /* + * check this is a valid attachment + */ + aia = abx_if_attach_db_find (policy_id, sw_if_index); + + if (NULL == aia) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + /* + * first remove from the interface's vector + */ + ASSERT (abx_per_if[sw_if_index]); + + index = vec_search (abx_per_if[sw_if_index], aia - abx_if_attach_pool); + + ASSERT (index != ~0); + vec_del1 (abx_per_if[sw_if_index], index); + + if (0 == vec_len (abx_per_if[sw_if_index])) + { + abx_if_enable_disable (sw_if_index, 0); + + /* Return the lookup context, invalidate its id in our records */ + acl_plugin.put_lookup_context_index (abx_alctx_per_if[sw_if_index]); + abx_alctx_per_if[sw_if_index] = ~0; + } + else + { + vec_sort_with_function (abx_per_if[sw_if_index], + abx_cmp_attach_for_sort); + } + + /* Prepare and set the list of ACLs for lookup within the context */ + abx_setup_acl_lc (sw_if_index); + + /* + * remove the attachment from the DB + */ + abx_if_attach_db_del (policy_id, sw_if_index); + + /* + * return the object + */ + pool_put (abx_if_attach_pool, aia); + + return (0); +} + +static clib_error_t * +abx_if_attach_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 policy_id, sw_if_index; + u32 is_del, priority; + vnet_main_t *vnm; + + is_del = 0; + sw_if_index = policy_id = ~0; + vnm = vnet_get_main (); + priority = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_del = 1; + else if (unformat (input, "add")) + is_del = 0; + else if (unformat (input, "policy %d", &policy_id)) + ; + else if (unformat (input, "priority %d", &priority)) + ; + else if (unformat (input, "%U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + if (~0 == sw_if_index) + return (clib_error_return (0, "invalid interface name")); + if (~0 == policy_id) + return (clib_error_return (0, "invalid policy ID:%d", policy_id)); + if (~0 == abx_policy_find (policy_id)) + return (clib_error_return (0, "invalid policy ID:%d", policy_id)); + + if (is_del) + abx_if_detach (policy_id, sw_if_index); + else + abx_if_attach (policy_id, priority, sw_if_index); + + return (NULL); +} + +/* *INDENT-OFF* */ +/** + * Attach an ABX policy to an interface. + */ +VLIB_CLI_COMMAND (abx_if_attach_cmd_node, static) = { + .path = "abx attach", + .function = abx_if_attach_cmd, + .short_help = "abx attach [del] policy priority ", + // this is not MP safe +}; +/* *INDENT-ON* */ + +static u8 * +format_abx_if_attach (u8 * s, va_list * args) +{ + abx_if_attach_t *aia = va_arg (*args, abx_if_attach_t *); + abx_policy_t *ap; + + ap = abx_policy_get (aia->aia_abx); + s = format (s, " abx-interface-attach: policy:%d priority:%d", + ap->ap_id, aia->aia_priority); + + return (s); +} + +static clib_error_t * +abx_show_attach_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + const abx_if_attach_t *aia; + u32 sw_if_index, *aiai; + vnet_main_t *vnm; + + sw_if_index = ~0; + vnm = vnet_get_main (); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + if (~0 == sw_if_index) + return (clib_error_return (0, "specify an interface")); + + vec_validate_init_empty (abx_per_if, sw_if_index, NULL); + if (0 < vec_len (abx_per_if[sw_if_index])) + { + /* *INDENT-OFF* */ + vec_foreach(aiai, abx_per_if[sw_if_index]) + { + aia = pool_elt_at_index(abx_if_attach_pool, *aiai); + vlib_cli_output(vm, "%U", format_abx_if_attach, aia); + } + /* *INDENT-ON* */ + } + + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (abx_show_attach_cmd_node, static) = { + .path = "show abx attach", + .function = abx_show_attach_cmd, + .short_help = "show abx attach ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +static clib_error_t * +abx_if_bond_init (vlib_main_t * vm) +{ + clib_error_t *acl_init_res = acl_plugin_exports_init (&acl_plugin); + if (acl_init_res) + return (acl_init_res); + + abx_acl_user_id = + acl_plugin.register_user_module ("ABX plugin", "sw_if_index", NULL); + + return (NULL); +} + +VLIB_INIT_FUNCTION (abx_if_bond_init) = +{ + .runs_after = VLIB_INITS("acl_init"), +}; + +inline u32 get_abx_alctx_per_if(u32 sw_if_index) +{ + /* + * check this is a valid interface + */ + if (pool_is_free_index (vnet_get_main ()->interface_main.sw_interfaces, + sw_if_index)) + return INDEX_INVALID; + + if (vec_len (abx_alctx_per_if) < sw_if_index) + return INDEX_INVALID; + + return abx_alctx_per_if[sw_if_index]; +} + +inline void *get_abx_acl_main() +{ + return acl_plugin.p_acl_main; +} + +void +abx_if_attach_walk (abx_if_attach_cb_t cb, void *ctx) +{ + u32 aia; + + /* *INDENT-OFF* */ + pool_foreach_index(aia, abx_if_attach_pool) + { + if (!cb(aia, ctx)) + break; + } + /* *INDENT-ON* */ +} diff --git a/vpp/abx/vpp2306/abx/abx_if_attach.h b/vpp/abx/vpp2306/abx/abx_if_attach.h new file mode 100644 index 00000000..38c89b70 --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx_if_attach.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#ifndef __ABX_ITF_ATTACH_H__ +#define __ABX_ITF_ATTACH_H__ + +#include + +/** + * Attachment data for an ABX policy to an interface + */ +typedef struct abx_if_attach_t_ +{ + CLIB_CACHE_LINE_ALIGN_MARK (marker); + /** + * ACL index to match + */ + u32 aia_acl; + + /** + * The VPP index of the ABX policy + */ + u32 aia_abx; + + /** + * The interface for the attachment + */ + u32 aia_sw_if_index; + + /** + * The priority of this policy for attachment. + * The lower the value the higher the priority. + * The higher priority policies are matched first. + */ + u32 aia_priority; +} abx_if_attach_t; + +extern abx_if_attach_t *abx_if_attach_pool; +extern u32 **abx_per_if; + +static inline abx_if_attach_t * +abx_if_attach_get (u32 index) +{ + return (pool_elt_at_index (abx_if_attach_pool, index)); +} + +static inline u32 * +abx_attachments_per_if_get (u32 index) +{ + return (abx_per_if[index]); +} + +extern int abx_if_attach (u32 policy_id, u32 priority, u32 sw_if_index); + +extern int abx_if_detach (u32 policy_id, u32 sw_if_index); + +extern u32 get_abx_alctx_per_if (u32 sw_if_index); + +extern void *get_abx_acl_main (); + +typedef walk_rc_t (*abx_if_attach_cb_t) (u32 aiai, void *ctx); + +extern void abx_if_attach_walk (abx_if_attach_cb_t cb, void *ctx); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/vpp/abx/vpp2306/abx/abx_policy.c b/vpp/abx/vpp2306/abx/abx_policy.c new file mode 100644 index 00000000..7837aeb1 --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx_policy.c @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include + +#include +#include +#include +#include + +/** + * Pool of ABX objects + */ +static abx_policy_t *abx_policy_pool; + +/** + * DB of ABX policy objects + * - policy ID to index conversion. + */ +static uword *abx_policy_db; + +inline abx_policy_t * +abx_policy_get (u32 index) +{ + return (pool_elt_at_index (abx_policy_pool, index)); +} + +static abx_policy_t * +abx_policy_find_id (u32 policy_id) +{ + u32 api; + + api = abx_policy_find (policy_id); + + if (INDEX_INVALID != api) + return (abx_policy_get (api)); + + return (NULL); +} + +u32 +abx_policy_find (u32 policy_id) +{ + uword *p; + + p = hash_get (abx_policy_db, policy_id); + + if (NULL != p) + return (p[0]); + + return (INDEX_INVALID); +} + +void +abx_policy_update (const u32 policy_id, const u32 acl_index, + const u32 tx_sw_if_index, const mac_address_t * mac) +{ + abx_policy_t *ap; + u32 api; + + api = abx_policy_find (policy_id); + if (INDEX_INVALID == api) + { + /* + * create a new policy + */ + pool_get (abx_policy_pool, ap); + /* + * add this new policy to the DB + */ + api = ap - abx_policy_pool; + hash_set (abx_policy_db, policy_id, api); + } + else + { + /* + * get existing policy + */ + ap = abx_policy_get (api); + } + + ap->ap_acl = acl_index; + ap->ap_id = policy_id; + ap->ap_tx_sw_if_index = tx_sw_if_index; + + if (NULL != mac && !mac_address_is_zero (mac)) + { + memcpy (ap->ap_dst_mac.bytes, mac, 6); + } + else + { + clib_memset (&ap->ap_dst_mac, 0, sizeof (mac_address_t)); + } +} + +int +abx_policy_delete (const u32 policy_id) +{ + abx_policy_t *ap; + u32 api; + + api = abx_policy_find (policy_id); + + if (INDEX_INVALID == api) + { + /* + * no such policy + */ + return (-1); + } + else + { + ap = abx_policy_get (api); + hash_unset (abx_policy_db, ap->ap_id); + pool_put (abx_policy_pool, ap); + } + + return (0); +} + +static clib_error_t * +abx_policy_cmd (vlib_main_t * vm, + unformat_input_t * main_input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + mac_address_t mac = ZERO_MAC_ADDRESS; + u32 acl_index, policy_id, tx_sw_if_index; + u32 is_del; + vnet_main_t *vnm = vnet_get_main (); + + is_del = 0; + acl_index = INDEX_INVALID; + policy_id = INDEX_INVALID; + tx_sw_if_index = INDEX_INVALID; + + /* Get a line of input. */ + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "acl %d", &acl_index)) + ; + else if (unformat (line_input, "id %d", &policy_id)) + ; + else if (unformat (line_input, "del")) + is_del = 1; + else if (unformat (line_input, "add")) + is_del = 0; + else if (unformat (line_input, "via %U", + unformat_vnet_sw_interface, vnm, &tx_sw_if_index)) + ; + else if (unformat (line_input, "dst-mac-rewrite %U", + unformat_mac_address, &mac)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input)); + } + + if (INDEX_INVALID == policy_id) + { + vlib_cli_output (vm, "Specify a Policy ID"); + return 0; + } + + if (!is_del) + { + if (INDEX_INVALID == acl_index) + { + vlib_cli_output (vm, "ACL index must be set"); + return 0; + } + if (INDEX_INVALID == tx_sw_if_index) + { + vlib_cli_output (vm, "TX sw_if_index index must be set"); + return 0; + } + abx_policy_update (policy_id, acl_index, tx_sw_if_index, &mac); + } + else + { + abx_policy_delete (policy_id); + } + + unformat_free (line_input); + return (NULL); +} + +/* *INDENT-OFF* */ +/** + * Create an ABX policy. + */ +VLIB_CLI_COMMAND (abx_policy_cmd_node, static) = { + .path = "abx policy", + .function = abx_policy_cmd, + .short_help = "abx policy [add|del] id acl via [dst-mac-rewrite ]", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +static u8 * +abx_format_mac_address (u8 * s, va_list * args) +{ + u8 *a = va_arg (*args, u8 *); + return format (s, "%02x:%02x:%02x:%02x:%02x:%02x", + a[0], a[1], a[2], a[3], a[4], a[5]); +} + +static u8 * +format_abx_policy (u8 * s, va_list * args) +{ + abx_policy_t *ap = va_arg (*args, abx_policy_t *); + + s = format (s, " abx[%d]: policy:%d acl:%d via-sw-if-index: %d", + ap - abx_policy_pool, ap->ap_id, ap->ap_acl, + ap->ap_tx_sw_if_index); + if (!mac_address_is_zero (&ap->ap_dst_mac)) + { + s = format (s, " dst-mac-rewrite: %U", + abx_format_mac_address, &ap->ap_dst_mac); + } + return (s); +} + +static clib_error_t * +abx_show_policy_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 policy_id; + abx_policy_t *ap; + + policy_id = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%d", &policy_id)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + if (INDEX_INVALID == policy_id) + { + /* *INDENT-OFF* */ + pool_foreach(ap, abx_policy_pool) + { + vlib_cli_output(vm, "%U", format_abx_policy, ap); + } + /* *INDENT-ON* */ + } + else + { + ap = abx_policy_find_id (policy_id); + + if (NULL != ap) + vlib_cli_output (vm, "%U", format_abx_policy, ap); + else + vlib_cli_output (vm, "Invalid policy ID:%d", policy_id); + } + + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (abx_policy_show_policy_cmd_node, static) = { + .path = "show abx policy", + .function = abx_show_policy_cmd, + .short_help = "show abx policy ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +void +abx_policy_walk (abx_policy_cb_t cb, void *ctx) +{ + u32 api; + + pool_foreach_index (api, abx_policy_pool) + { + if (!cb (api, ctx)) + break; + } + /* *INDENT-ON* */ +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/abx/vpp2306/abx/abx_policy.h b/vpp/abx/vpp2306/abx/abx_policy.h new file mode 100644 index 00000000..bf13644b --- /dev/null +++ b/vpp/abx/vpp2306/abx/abx_policy.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#ifndef __ABX_POLICY_H__ +#define __ABX_POLICY_H__ + +#include +#include + +/** + * An ACL based Xconnect 'policy'. + * This comprises the ACL index to match against and forward to interface. + * + * ABX policies are then 'attached' to interfaces. An input feature + * will run through the list of policies a match will divert the packet, + * if all miss then we continues down the interface's feature arc + */ +typedef struct abx_policy_t_ +{ + /** + * The policy ID - as configured by the client + */ + u32 ap_id; + /** + * ACL index to match + */ + u32 ap_acl; + + /** + * Tx interface + */ + u32 ap_tx_sw_if_index; + + /** + * Tx destination mac address + */ + mac_address_t ap_dst_mac; +} abx_policy_t; + +/** + * Get an ABX object from its VPP index + */ +abx_policy_t *abx_policy_get (u32 index); + +/** + * Find a ABX object from the client's policy IDwalk_rc_t + * + * @param policy_id Client's defined policy ID + * @return VPP's object index + */ +u32 abx_policy_find (u32 policy_id); + +/** + * Create or update an ABX Policy + * + * @param policy_id User defined Policy ID + * @param acl_index The ACL the policy with match on + */ +void abx_policy_update (const u32 policy_id, const u32 acl_index, + const u32 tx_sw_if_index, const mac_address_t * mac); + +/** + * Delete paths from an ABX Policy. If no more paths exist, the policy + * is deleted. + * + * @param policy_id User defined Policy ID + */ +int abx_policy_delete (const u32 policy_id); + +/** + * Callback function invoked during a walk of ABX all policies + */ +typedef walk_rc_t (*abx_policy_cb_t) (u32 api, void *ctx); + +/** + * Walk/visit each of the ABX policies + */ +extern void abx_policy_walk (abx_policy_cb_t cb, void *ctx); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/vpp/abx/vpp2306/abx/node.c b/vpp/abx/vpp2306/abx/node.c new file mode 100644 index 00000000..2215059b --- /dev/null +++ b/vpp/abx/vpp2306/abx/node.c @@ -0,0 +1,340 @@ +/* + * node.c - skeleton vpp engine plug-in dual-loop node skeleton + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ +#include +#include + +#include +#include +#include +#include +#include + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + + bool matched; + u32 acl_pos; + u32 aia_sw_if_index; + u32 ap_id; +} abx_trace_t; + +#ifndef CLIB_MARCH_VARIANT +/* packet trace format function */ +static u8 * +format_abx_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + abx_trace_t *t = va_arg (*args, abx_trace_t *); + + s = format (s, "ABX: %s, sw_if_index %d, next index %d", + (t->matched) ? "match" : "no-match", + t->sw_if_index, t->next_index); + if (t->matched) + { + s = format (s, ", acl_pos %d, attached_if %d, policy_id %d", + t->acl_pos, t->aia_sw_if_index, t->ap_id); + } + return s; +} + +vlib_node_registration_t abx_node; + +#endif /* CLIB_MARCH_VARIANT */ + +#define foreach_abx_error \ +_(FWD, "ABX forwarded packets") + +typedef enum +{ +#define _(sym,str) ABX_ERROR_##sym, + foreach_abx_error +#undef _ + ABX_N_ERROR, +} abx_error_t; + +#ifndef CLIB_MARCH_VARIANT +static char *abx_error_strings[] = { +#define _(sym,string) string, + foreach_abx_error +#undef _ +}; +#endif /* CLIB_MARCH_VARIANT */ + +typedef enum +{ + ABX_NEXT_INTERFACE_OUTPUT, + ABX_N_NEXT, +} abx_next_t; + +static inline uword +abx_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + u8 is_ip6) +{ + u32 n_left_from, *from, *to_next; + abx_next_t next_index; + u32 pkts_abx_fwd = 0; + + vnet_main_t *vnm = vnet_get_main (); + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 match_acl_index = ~0; + u32 match_acl_pos = ~0; + u32 match_rule_index = ~0; + u32 trace_bitmap = 0; + u8 action; + u32 lc_index = ~0; + fa_5tuple_opaque_t fa_5tuple0; + bool matched = false; + const u32 *attachments0; + const abx_if_attach_t *aia0 = NULL; + const abx_policy_t *ap0 = NULL; + + u32 bi0; + vlib_buffer_t *b0; + u32 next0, arc_next0; + u32 sw_if_index0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + vnet_feature_next (&arc_next0, b0); + next0 = arc_next0; + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + attachments0 = abx_attachments_per_if_get (sw_if_index0); + /* + * check if any of the policies attached to this interface matches. + */ + lc_index = get_abx_alctx_per_if (sw_if_index0); + if (INDEX_INVALID != lc_index) + { + acl_plugin_fill_5tuple_inline (get_abx_acl_main (), lc_index, + b0, is_ip6, 1, 0, &fa_5tuple0); + ip4_header_t *ip4 = vlib_buffer_get_current (b0); + ip6_header_t *ip6 = vlib_buffer_get_current (b0); + + if (acl_plugin_match_5tuple_inline + (get_abx_acl_main (), lc_index, &fa_5tuple0, is_ip6, + &action, &match_acl_pos, &match_acl_index, + &match_rule_index, &trace_bitmap)) + { + ethernet_header_t *new_eth, *orig_eth; + u8 l2off_valid = b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID; + u16 l2off = l2off_valid ? vnet_buffer (b0)->l2_hdr_offset : 0; + orig_eth = (ethernet_header_t *)(b0->data + l2off); + + next0 = ABX_NEXT_INTERFACE_OUTPUT; + matched = true; + + aia0 = abx_if_attach_get (attachments0[match_acl_pos]); + if (aia0) + { + ap0 = abx_policy_get (aia0->aia_abx); + if (ap0) + { + vlib_buffer_advance (b0, + -sizeof (ethernet_header_t)); + + u32 outer_vlan = ~0, inner_vlan = ~0; + + vnet_sw_interface_t *si0 = vnet_get_sw_interface ( + vnm, ap0->ap_tx_sw_if_index); + if (si0->type == VNET_SW_INTERFACE_TYPE_SUB) + { + if (si0->sub.eth.flags.one_tag == 1) + { + vlib_buffer_advance ( + b0, -4 /* space for 1 VLAN tag. */); + outer_vlan = + (si0->sub.eth.outer_vlan_id << 16) | + 0x0800; + } + else if (si0->sub.eth.flags.two_tags == 1) + { + vlib_buffer_advance ( + b0, -8 /* space for 2 VLAN tags. */); + outer_vlan = + (si0->sub.eth.outer_vlan_id << 16) | + 0x8100; + inner_vlan = + (si0->sub.eth.inner_vlan_id << 16) | + 0x0800; + } + } + + new_eth = vlib_buffer_get_current (b0); + + if (ethernet_frame_is_tagged ( + clib_net_to_host_u16 (orig_eth->type)) || + si0->type == VNET_SW_INTERFACE_TYPE_SUB) + { + /* Remove VLAN tags from old frame. */ + memmove (new_eth->dst_address, + orig_eth->dst_address, 6); + memmove (new_eth->src_address, + orig_eth->src_address, 6); + vnet_buffer (b0)->l2_hdr_offset = + b0->current_data; + } + + if (si0->type == VNET_SW_INTERFACE_TYPE_SUB && + outer_vlan != (u32) ~0) + { + new_eth->type = + (si0->sub.eth.flags.dot1ad == 1) ? + clib_net_to_host_u16 (0x88a8) : + clib_net_to_host_u16 (0x8100); + u32 *vlan_tag = (u32 *) (new_eth + 1); + *vlan_tag = clib_host_to_net_u32 (outer_vlan); + if (inner_vlan != (u32) ~0) + { + u32 *inner_vlan_tag = (u32 *) (vlan_tag + 1); + *inner_vlan_tag = + clib_host_to_net_u32 (inner_vlan); + } + } + + /* Overwrite dest mac address if not multicast */ + if (!mac_address_is_zero (&ap0->ap_dst_mac)) + { + if ((!is_ip6 + && (ip4->dst_address.as_u8[0] >> 4 != 0xe)) + || (is_ip6 + && (ip6-> + dst_address.as_u16[0] != 0xff))) + { + clib_memcpy (new_eth->dst_address, ap0->ap_dst_mac.bytes, 6); + } + } + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = + ap0->ap_tx_sw_if_index; + pkts_abx_fwd++; + } + } + } + } + /* Send pkt back out the RX interface */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + abx_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->matched = matched; + if (matched && aia0 && ap0) + { + t->acl_pos = match_acl_pos; + t->aia_sw_if_index = aia0->aia_sw_if_index; + t->ap_id = ap0->ap_id; + } + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, bi0, + next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, abx_node.index, ABX_ERROR_FWD, + pkts_abx_fwd); + + return frame->n_vectors; +} + +VLIB_NODE_FN (abx_ip4_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return abx_node_fn_inline (vm, node, frame, 0 /* is_ip6 */); +} + +VLIB_NODE_FN (abx_ip6_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return abx_node_fn_inline (vm, node, frame, 1 /* is_ip6 */); +} + +/* *INDENT-OFF* */ +#ifndef CLIB_MARCH_VARIANT +VLIB_REGISTER_NODE (abx_ip4_node) = +{ + .name = "abx-ip4", + .vector_size = sizeof (u32), + .format_trace = format_abx_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(abx_error_strings), + .error_strings = abx_error_strings, + + .n_next_nodes = ABX_N_NEXT, + .next_nodes = { + [ABX_NEXT_INTERFACE_OUTPUT] = "interface-output", + }, +}; + +VLIB_REGISTER_NODE (abx_ip6_node) = +{ + .name = "abx-ip6", + .vector_size = sizeof (u32), + .format_trace = format_abx_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(abx_error_strings), + .error_strings = abx_error_strings, + + .n_next_nodes = ABX_N_NEXT, + .next_nodes = { + [ABX_NEXT_INTERFACE_OUTPUT] = "interface-output", + }, +}; +#endif /* CLIB_MARCH_VARIANT */ +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/abx/vpp2306/build.sh b/vpp/abx/vpp2306/build.sh new file mode 100755 index 00000000..164fdc07 --- /dev/null +++ b/vpp/abx/vpp2306/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# SPDX-License-Identifier: Apache-2.0 + +# Copyright 2022 PANTHEON.tech +# +# 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. + +set -e -o pipefail + +if [ $# -lt 1 ] +then + echo "usage: $0 /path/to/vpp/workspace" + exit 1 +fi + +rm -rf build +mkdir -p build +pushd build +cmake -GNinja -DVPP_WORKSPACE=$1 .. +ninja +popd # build diff --git a/vpp/abx/vpp2306/test/test_abx.py b/vpp/abx/vpp2306/test/test_abx.py new file mode 100644 index 00000000..7d3db61a --- /dev/null +++ b/vpp/abx/vpp2306/test/test_abx.py @@ -0,0 +1,617 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: Apache-2.0 + +# Copyright 2022 PANTHEON.tech +# +# 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. + +import unittest + +from framework import VppTestCase, VppTestRunner +from vpp_sub_interface import VppDot1QSubint +from vpp_acl import AclRule, VppAcl, VppAclInterface + +from ipaddress import IPv4Network, IPv6Network + +from scapy.layers.l2 import Ether, Dot1Q +from scapy.layers.inet import IP, TCP + + +class TestABX(VppTestCase): + """ + Test ABX plugin + + Testing following scenarios: + 1. Adding, updating and removing of ABX policies + 2. Adding and removing of ABX attach configs + 3. Basic ABX test + 4. Basic ABX test - change destination MAC + 5. ABX with destination sub-interface + 6. ABX with destination sub-interface - change destination MAC + 7. ABX with source sub-interface + 8. ABX with source sub-interface - change MAC + 9. ABX with source and destination sub-interface + 10. ABX with source and destination sub-interface + - change destination MAC + + TODO: + 1. Double VLAN tagged packets forwarding scenarios + 2. IPv6 scenarios + + """ + + # Traffic type + IP = 0 + + # IP version + IPRANDOM = -1 + IPV4 = 0 + IPV6 = 1 + + # rule types + DENY = 0 + PERMIT = 1 + + # Transport layer + TCP = 0 + UDP = 1 + + # Supported protocols + proto = [[6, 17], [1, 58]] + + # Ether types + ETHER_TYPE_IPV4 = 0x0800 + ETHER_TYPE_DOT1Q = 0x8100 + + # VLAN IDs + PG0_VLAN_100 = 100 + PG1_VLAN_200 = 200 + + @classmethod + def setUpClass(cls): + super(TestABX, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestABX, cls).tearDownClass() + + def setUp(self): + super(TestABX, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(4)) + + # setup all interfaces + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.config_ip6() + i.resolve_arp() + i.generate_remote_hosts(3) + + # Create sub-interfaces with VLANs + self.pg0_vlan_100 = VppDot1QSubint(self, self.pg0, self.PG0_VLAN_100) + self.pg0_vlan_100.admin_up() + self.pg0_vlan_100.config_ip4() + self.pg0_vlan_100.resolve_arp() + self.pg0_vlan_100.config_ip6() + + self.pg1_vlan_200 = VppDot1QSubint(self, self.pg1, self.PG1_VLAN_200) + self.pg1_vlan_200.admin_up() + self.pg1_vlan_200.config_ip4() + self.pg1_vlan_200.resolve_arp() + self.pg1_vlan_200.config_ip6() + + def tearDown(self): + for i in self.pg_interfaces: + i.unconfig_ip4() + i.unconfig_ip6() + i.admin_down() + + self.pg0_vlan_100.unconfig_ip4() + self.pg0_vlan_100.unconfig_ip6() + self.pg0_vlan_100.admin_down() + self.pg0_vlan_100.remove_vpp_config() + + self.pg1_vlan_200.unconfig_ip4() + self.pg1_vlan_200.unconfig_ip6() + self.pg1_vlan_200.admin_down() + self.pg1_vlan_200.remove_vpp_config() + + super(TestABX, self).tearDown() + + def create_rule( + self, + ip=0, + permit_deny=0, + ports=-1, + proto=-1, + s_prefix=0, + s_ip=0, + d_prefix=0, + d_ip=0, + ): + if ip: + src_prefix = IPv6Network((s_ip, s_prefix)) + dst_prefix = IPv6Network((d_ip, d_prefix)) + else: + src_prefix = IPv4Network((s_ip, s_prefix)) + dst_prefix = IPv4Network((d_ip, d_prefix)) + return AclRule( + is_permit=permit_deny, + ports=ports, + proto=proto, + src_prefix=src_prefix, + dst_prefix=dst_prefix, + ) + + def apply_rules(self, rules, tag=None): + acl = VppAcl(self, rules, tag=tag) + acl.add_vpp_config() + self.logger.debug("Dumped ACL: " + str(acl.dump())) + # Apply a ACL on the interface as inbound + for i in self.pg_interfaces: + acl_if = VppAclInterface( + self, sw_if_index=i.sw_if_index, n_input=1, acls=[acl] + ) + acl_if.add_vpp_config() + return acl.acl_index + + def create_stream( + self, + in_if, + out_if, + vlan_id=0, + ttl=64, + ): + """ + Create input packet stream for defined interface. + If VLAN tag if other than 0. + """ + pkts = [] + if vlan_id == 0: + p = ( + Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(dst=out_if.remote_ip4, src=in_if.remote_ip4, ttl=ttl) / + TCP( + sport=3000, + dport=3000, + ) + ) + else: + p = ( + Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + Dot1Q(vlan=vlan_id) / + IP(dst=out_if.remote_ip4, src=in_if.remote_ip4, ttl=ttl) / + TCP( + sport=3000, + dport=3000, + ) + ) + + pkts.append(p) + return pkts + + def create_apply_acl(self): + """ + Create and apply ACL rule + Return ACL rule index + """ + + # Create ACL rule + rules = [] + rules.append( + self.create_rule( + self.IPV4, self.PERMIT, 3000, self.proto[self.IP][self.TCP] + ) + ) + + # Apply rules + return self.apply_rules(rules, "permit all") + + def create_abx_policy(self, policy_id, acl_idx, dest_if, change_mac=True): + """ + Create and add ABX policy + Returns ABX config as map. + """ + + policy = { + "policy_id": policy_id, + "acl_index": acl_idx, + "tx_sw_if_index": dest_if.sw_if_index, + "dst_mac": dest_if.local_mac, + } + + if change_mac is False: + del policy["dst_mac"] + + self.vapi.abx_policy_add_del(1, policy) + return policy + + def attach_abx_policy(self, policy_id, rx_sw_if_index): + """ + Attach ABX config to interface. + Returns ABX attach config. + """ + + interface_attach = { + "policy_id": policy_id, + "priority": 0, + "rx_sw_if_index": rx_sw_if_index, + } + + self.vapi.abx_interface_attach_detach( + is_attach=1, attach=interface_attach) + return interface_attach + + def cleanup_abx(self, abx_policy_config, abx_attach_config): + """ + Cleanup ABX attach config and policy. + """ + + self.vapi.abx_interface_attach_detach( + is_attach=0, attach=abx_attach_config) + self.vapi.abx_policy_add_del(0, abx_policy_config) + + def send_and_expect( + self, + intf, + pkts, + output, + n_rx=None, + worker=None, + trace=True, + ): + return super().send_and_expect(intf, pkts, output, n_rx, worker, trace) + + def test_abx_basic_acl_mac_dst_sub_if(self): + """Destination sub-interface - change destination MAC""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1_vlan_200 + ) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0.sw_if_index) + + pkts = self.create_stream(self.pg0, self.pg1) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_DOT1Q) + self.assertEqual(pkt_out[Ether].dst, self.pg1.local_mac) + self.assertEqual(pkt_out[Dot1Q].vlan, self.PG1_VLAN_200) + self.assertEqual(pkt_out[Dot1Q].type, self.ETHER_TYPE_IPV4) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl_dst_sub_if(self): + """Destination sub-interface""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1_vlan_200, False + ) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0.sw_if_index) + + pkts = self.create_stream(self.pg0, self.pg1) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_DOT1Q) + self.assertEqual(pkt_out[Ether].dst, pkt_in[Ether].dst) + self.assertEqual(pkt_out[Dot1Q].vlan, self.PG1_VLAN_200) + self.assertEqual(pkt_out[Dot1Q].type, self.ETHER_TYPE_IPV4) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl_mac(self): + """Change destination MAC""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0.sw_if_index) + + pkts = self.create_stream(self.pg0, self.pg1) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_IPV4) + self.assertEqual(pkt_out[Ether].dst, self.pg1.local_mac) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl(self): + """Basic test""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1, False) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0.sw_if_index) + + pkts = self.create_stream(self.pg0, self.pg1) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_IPV4) + self.assertEqual(pkt_out[Ether].dst, pkt_in[Ether].dst) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl_mac_src_dst_sub_if(self): + """Source and destination sub-interface - change destination MAC""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1_vlan_200 + ) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0_vlan_100.sw_if_index + ) + + pkts = self.create_stream( + self.pg0_vlan_100, self.pg1_vlan_200, self.PG0_VLAN_100 + ) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_DOT1Q) + self.assertEqual(pkt_out[Ether].dst, self.pg1.local_mac) + self.assertEqual(pkt_out[Dot1Q].vlan, self.PG1_VLAN_200) + self.assertEqual(pkt_out[Dot1Q].type, self.ETHER_TYPE_IPV4) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl_src_dst_sub_if(self): + """Source and destination sub-interface""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1_vlan_200, False + ) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0_vlan_100.sw_if_index + ) + + pkts = self.create_stream( + self.pg0_vlan_100, self.pg1_vlan_200, self.PG0_VLAN_100 + ) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_DOT1Q) + self.assertEqual(pkt_out[Ether].dst, pkt_in[Ether].dst) + self.assertEqual(pkt_out[Dot1Q].vlan, self.PG1_VLAN_200) + self.assertEqual(pkt_out[Dot1Q].type, self.ETHER_TYPE_IPV4) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl_src_sub_if(self): + """Source sub-interface""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1, False) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0_vlan_100.sw_if_index + ) + + pkts = self.create_stream( + self.pg0_vlan_100, self.pg1, self.PG0_VLAN_100) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_IPV4) + self.assertEqual(pkt_out[Ether].dst, pkt_in[Ether].dst) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_abx_basic_acl_mac_src_sub_if(self): + """Source sub-interface - change MAC""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0_vlan_100.sw_if_index + ) + + pkts = self.create_stream( + self.pg0_vlan_100, self.pg1, self.PG0_VLAN_100) + pkt_in = pkts[0] + self.logger.debug( + "\nIncoming Packet\n {0} \nIncoming Packet\n".format( + pkt_in.show(dump=True)) + ) + + pkts = self.send_and_expect(self.pg0, pkts, self.pg1) + + pkt_out = pkts[0] + self.logger.debug( + "\nOutcoming Packet\n {0} \nOutcoming Packet\n".format( + pkt_out.show(dump=True) + ) + ) + + self.assertEqual(pkt_out[Ether].type, self.ETHER_TYPE_IPV4) + self.assertEqual(pkt_out[Ether].dst, self.pg1.local_mac) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + def test_add_remove_policy(self): + """Adding, updating and removing of policies""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg0) + + policies = self.vapi.abx_policy_dump() + self.assertEqual(len(policies), 1) + policy = policies[0].policy + + self.assertEqual(policy.policy_id, policy_id) + self.assertEqual(policy.acl_index, acl_idx) + self.assertEqual(policy.tx_sw_if_index, self.pg0.sw_if_index) + self.assertEqual(policy.dst_mac, self.pg0.local_mac) + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1, False) + + policies = self.vapi.abx_policy_dump() + self.assertEqual(len(policies), 1) + policy = policies[0].policy + + self.assertEqual(policy.policy_id, policy_id) + self.assertEqual(policy.acl_index, acl_idx) + self.assertEqual(policy.tx_sw_if_index, self.pg1.sw_if_index) + self.assertEqual(policy.dst_mac, "00:00:00:00:00:00") + + self.vapi.abx_policy_add_del(0, abx_policy_config) + + policies = self.vapi.abx_policy_dump() + self.assertEqual(len(policies), 0) + + def test_add_remove_attach(self): + """Adding and removing of attach configs""" + + acl_idx = self.create_apply_acl() + policy_id = 1 + + abx_policy_config = self.create_abx_policy( + policy_id, acl_idx, self.pg1) + abx_attach_config = self.attach_abx_policy( + policy_id, self.pg0.sw_if_index) + + attach_configs = self.vapi.abx_interface_attach_dump() + self.assertEqual(len(attach_configs), 1) + attach = attach_configs[0].attach + + self.assertEqual(attach.policy_id, policy_id) + self.assertEqual(attach.priority, 0) + self.assertEqual(attach.rx_sw_if_index, self.pg0.sw_if_index) + + self.cleanup_abx(abx_policy_config, abx_attach_config) + + attach_configs = self.vapi.abx_interface_attach_dump() + self.assertEqual(len(attach_configs), 0) + + +if __name__ == "__main__": + unittest.main(testRunner=VppTestRunner) diff --git a/vpp/isisx/vpp2306/CMakeLists.txt b/vpp/isisx/vpp2306/CMakeLists.txt new file mode 100644 index 00000000..6cf55b97 --- /dev/null +++ b/vpp/isisx/vpp2306/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.5) + +project(isisx-plugin) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +find_package(VPP) + +add_subdirectory(isisx) diff --git a/vpp/isisx/vpp2306/build.sh b/vpp/isisx/vpp2306/build.sh new file mode 100755 index 00000000..164fdc07 --- /dev/null +++ b/vpp/isisx/vpp2306/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# SPDX-License-Identifier: Apache-2.0 + +# Copyright 2022 PANTHEON.tech +# +# 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. + +set -e -o pipefail + +if [ $# -lt 1 ] +then + echo "usage: $0 /path/to/vpp/workspace" + exit 1 +fi + +rm -rf build +mkdir -p build +pushd build +cmake -GNinja -DVPP_WORKSPACE=$1 .. +ninja +popd # build diff --git a/vpp/isisx/vpp2306/isisx/CMakeLists.txt b/vpp/isisx/vpp2306/isisx/CMakeLists.txt new file mode 100644 index 00000000..0c6d4c5a --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/CMakeLists.txt @@ -0,0 +1,36 @@ + +# Copyright (c) 2022 PANTHEON.tech s.r.o. +# 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. + +include_directories(${CMAKE_SOURCE_DIR}) +message(STATUS "VPP_WORKSPACE: ${VPP_WORKSPACE}") +include_directories(${VPP_WORKSPACE}/src/) + +# for generated API headers: +include_directories(${CMAKE_BINARY_DIR}) + +add_vpp_plugin(isisx + SOURCES + isisx.c + node.c + isisx.h + isisx_api.c + isisx_connect.c + isisx_cli.c + + MULTIARCH_SOURCES + node.c + + API_FILES + isisx.api +) diff --git a/vpp/isisx/vpp2306/isisx/isisx.api b/vpp/isisx/vpp2306/isisx/isisx.api new file mode 100644 index 00000000..419a68af --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx.api @@ -0,0 +1,87 @@ +/* + * isisx.api - binary API skeleton + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +/** + * @file isisx.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane binary API messages which are generally + * called through a shared memory interface. + */ + +/* Version and type recitations */ + +option version = "0.1.0"; +import "vnet/interface_types.api"; + +define isisx_plugin_get_version +{ + u32 client_index; + u32 context; +}; + +/** \brief Reply to get the plugin version + @param context - returned sender context, to match reply w/ request + @param major - Incremented every time a known breaking behavior change is introduced + @param minor - Incremented with small changes, may be used to avoid buggy versions +*/ +define isisx_plugin_get_version_reply +{ + u32 context; + u32 major; + u32 minor; +}; +/** \brief A description of an ISISx connection + @param rx_sw_if_index Rx interface index to be used for matching + @param tx_sw_if_index Tx interface index to be used for forwarding + */ +typedef isisx_connection +{ + u32 rx_sw_if_index; + u32 tx_sw_if_index; +}; + +/** \brief A description of an ISISx connection + @param is_add Is this the addition or removal of paths from the connect + If the connect does not exist it is created. If the last path + Is being removed, the connect is deleted + @param connection The ISISx connection + */ +autoreply define isisx_connection_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_isisx_connection_t connection; +}; + +/** \brief Dump ISISx connections response + @param policy - the ABX policy +*/ +define isisx_connection_details { + u32 context; + vl_api_isisx_connection_t connection; +}; + +/** \brief Dump ISISx connections + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define isisx_connection_dump { + u32 client_index; + u32 context; +}; \ No newline at end of file diff --git a/vpp/isisx/vpp2306/isisx/isisx.c b/vpp/isisx/vpp2306/isisx/isisx.c new file mode 100644 index 00000000..3f152ea3 --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx.c @@ -0,0 +1,75 @@ +/* + * isisx.c - skeleton vpp engine plug-in + * + * Copyright (c) + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define REPLY_MSG_ID_BASE pmp->msg_id_base +#include + +isisx_main_t isisx_main; + +static clib_error_t * isisx_init (vlib_main_t * vm) +{ + isisx_main_t * pmp = &isisx_main; + clib_error_t * error = 0; + + pmp->vlib_main = vm; + pmp->vnet_main = vnet_get_main(); + + /* Register ISIS protocol to be handled by this plugin node */ + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "isisx"); + osi_register_input_protocol (OSI_PROTOCOL_isis, node->index); + + pmp->log_class = vlib_log_register_class("isisx_plugin", 0); + return error; +} + +VLIB_INIT_FUNCTION (isisx_init); + +/* *INDENT-OFF* */ +VNET_FEATURE_INIT (isisx, static) = +{ + .arc_name = "device-input", + .node_name = "isisx", + .runs_before = VNET_FEATURES ("ethernet-input"), + +}; +/* *INDENT-ON */ + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = +{ + .description = "A simple ISIS protocol based Xconnect", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/isisx/vpp2306/isisx/isisx.h b/vpp/isisx/vpp2306/isisx/isisx.h new file mode 100644 index 00000000..5d9647b4 --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx.h @@ -0,0 +1,70 @@ + +/* + * isisx.h - skeleton vpp engine plug-in header file + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ +#ifndef __included_isisx_h__ +#define __included_isisx_h__ + +#include +#include +#include +#include + +#include +#include + +#define ISISX_PLUGIN_VERSION_MAJOR 1 +#define ISISX_PLUGIN_VERSION_MINOR 0 + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + + /* on/off switch for the periodic function */ + u8 periodic_timer_enabled; + /* Node index, non-zero if the periodic process has been created */ + u32 periodic_node_index; + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; + ethernet_main_t * ethernet_main; + + /* Logger class */ + vlib_log_class_t log_class; + + /* Hash table of ISISx connections */ + uword * isisx_connection_db; +} isisx_main_t; + +extern isisx_main_t isisx_main; + +extern vlib_node_registration_t isisx_node; + +#define isisx_log_warn(f, ...) do { \ + vlib_log(VLIB_LOG_LEVEL_WARNING, isisx_main.log_class, f, \ + ##__VA_ARGS__); \ +} while (0) + +#endif /* __included_isisx_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/isisx/vpp2306/isisx/isisx_api.c b/vpp/isisx/vpp2306/isisx/isisx_api.c new file mode 100644 index 00000000..85ebf025 --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx_api.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include +#include + +#include +#include + +#include + +/* define message IDs */ +#include +#include +#include + +#include + +#undef REPLY_MSG_ID_BASE +#define REPLY_MSG_ID_BASE isisx_main.msg_id_base + +/* API message handler */ + +static void +vl_api_isisx_plugin_get_version_t_handler ( + vl_api_isisx_plugin_get_version_t *mp) +{ + vl_api_isisx_plugin_get_version_reply_t *rmp; + REPLY_MACRO_DETAILS2 (VL_API_ISISX_PLUGIN_GET_VERSION_REPLY, ({ + rmp->major = htonl (ISISX_PLUGIN_VERSION_MAJOR); + rmp->minor = htonl (ISISX_PLUGIN_VERSION_MINOR); + })); +} + +static void +vl_api_isisx_connection_add_del_t_handler ( + vl_api_isisx_connection_add_del_t *mp) +{ + vl_api_isisx_connection_add_del_reply_t *rmp; + int rv = 0; + + u32 rx_sw_if_index = htonl (mp->connection.rx_sw_if_index); + if (mp->is_add) + { + u32 tx_sw_if_index = htonl (mp->connection.tx_sw_if_index); + + isisx_connection_update (rx_sw_if_index, tx_sw_if_index); + } + else + { + isisx_connection_delete (rx_sw_if_index); + } + REPLY_MACRO (VL_API_ISISX_CONNECTION_ADD_DEL_REPLY); +} + +typedef struct isisx_connection_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} isisx_connection_walk_ctx_t; + +static walk_rc_t +isisx_connection_send_details (u32 rx_sw_interface_index, + u32 tx_sw_interface_index, void *args) +{ + vl_api_isisx_connection_details_t *rmp; + isisx_connection_walk_ctx_t *ctx = args; + vl_api_registration_t *reg = ctx->reg; + u32 context = ctx->context; + + REPLY_MACRO_DETAILS4 (VL_API_ISISX_CONNECTION_DETAILS, reg, context, ({ + rmp->connection.rx_sw_if_index + = htonl (rx_sw_interface_index); + rmp->connection.tx_sw_if_index + = htonl (tx_sw_interface_index); + })); + + return WALK_CONTINUE; +} + +static void +vl_api_isisx_connection_dump_t_handler (vl_api_isisx_connection_dump_t *mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + isisx_connection_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + isisx_connection_walk (isisx_connection_send_details, &ctx); +} + +/* Set up the API message handling tables */ + +#include +static clib_error_t * +isisx_plugin_api_hookup (vlib_main_t *vm) +{ + isisx_main_t *pm = &isisx_main; + + /* Ask for a correctly-sized block of API message decode slots */ + pm->msg_id_base = setup_message_id_table (); + + return 0; +} + +VLIB_API_INIT_FUNCTION (isisx_plugin_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/isisx/vpp2306/isisx/isisx_cli.c b/vpp/isisx/vpp2306/isisx/isisx_cli.c new file mode 100644 index 00000000..11e37442 --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx_cli.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include +#include + +static clib_error_t * +isisx_add_del_connection (vlib_main_t * vm, + unformat_input_t * main_input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 rx_sw_if_index = INDEX_INVALID, tx_sw_if_index = INDEX_INVALID, is_del = 0; + vnet_main_t *vnm = vnet_get_main (); + + /* Get a line of input. */ + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + is_del = 1; + else if (unformat (line_input, "add")) + is_del = 0; + else if (unformat (line_input, " %U", + unformat_vnet_sw_interface, vnm, &rx_sw_if_index)) + ; + else if (unformat (line_input, "via %U", + unformat_vnet_sw_interface, vnm, &tx_sw_if_index)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input)); + } + + if (rx_sw_if_index == INDEX_INVALID) + { + vlib_cli_output (vm, "Specify a RX interface"); + return 0; + } + + if (!is_del) + { + if (rx_sw_if_index == INDEX_INVALID) + { + vlib_cli_output (vm, "rx_sw_if_index must be set"); + return 0; + } + if (tx_sw_if_index == INDEX_INVALID) + { + vlib_cli_output (vm, "tx_sw_if_index must be set"); + return 0; + } + isisx_connection_update (rx_sw_if_index, tx_sw_if_index); + } + else + { + isisx_connection_delete (rx_sw_if_index); + } + + unformat_free (line_input); + return NULL; +} + +/* *INDENT-OFF* */ +/** + * Create an ISISx connection. + */ +VLIB_CLI_COMMAND (cli_isisx_add_del_connection, static) = { + .path = "isisx connection", + .function = isisx_add_del_connection, + .short_help = "isisx connection [add|del] via ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +static u8 * +format_isisx_connection (u8 * s, va_list * args) +{ + u32 * rx_sw_if_index = va_arg (*args, u32 *); + u32 * tx_sw_if_index = va_arg (*args, u32 *); + + s = format (s, " rx interface: %U, sw_if_index:%d -> tx interface %U, sw_if_index:%d", + format_vnet_sw_if_index_name, isisx_main.vnet_main, rx_sw_if_index, rx_sw_if_index, + format_vnet_sw_if_index_name, isisx_main.vnet_main, tx_sw_if_index, tx_sw_if_index); + + return s; +} + +static clib_error_t * +show_isisx_connection (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 + rx_sw_if_index = INDEX_INVALID, + tx_sw_if_index = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%d", &rx_sw_if_index)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + vlib_cli_output (vm, "ISISx connections:"); + if (rx_sw_if_index == INDEX_INVALID) + { + /* *INDENT-OFF* */ + hash_foreach (rx_sw_if_index, tx_sw_if_index, (&isisx_main)->isisx_connection_db, + { + vlib_cli_output (vm, "%U", format_isisx_connection, rx_sw_if_index, tx_sw_if_index); + }); + /* *INDENT-ON* */ + } + else + { + tx_sw_if_index = isisx_get_tx_by_rx (rx_sw_if_index); + if (tx_sw_if_index != INDEX_INVALID) + vlib_cli_output (vm, "%U", format_isisx_connection, rx_sw_if_index, tx_sw_if_index); + else + vlib_cli_output (vm, "Invalid rx interface index: %d", rx_sw_if_index); + } + return NULL; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cli_show_isisx_connection, static) = { + .path = "show isisx connection", + .function = show_isisx_connection, + .short_help = "show isisx connection [rx-interface-id]", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ diff --git a/vpp/isisx/vpp2306/isisx/isisx_connect.c b/vpp/isisx/vpp2306/isisx/isisx_connect.c new file mode 100644 index 00000000..537390fc --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx_connect.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#include +#include + +#include +#include +#include +#include + +const u32 isisx_get_tx_by_rx (const u32 rx_sw_if_index) +{ + uword *ptr = NULL; + ptr = hash_get ((&isisx_main)->isisx_connection_db, rx_sw_if_index); + if (ptr) + { + return ptr[0]; + } + return INDEX_INVALID; +} + +inline void +isisx_connection_update (const u32 rx_sw_if_index, const u32 tx_sw_if_index) +{ + hash_set ((&isisx_main)->isisx_connection_db, rx_sw_if_index, tx_sw_if_index); +} + +int +isisx_connection_delete (const u32 rx_sw_if_index) +{ + const u32 tx_sw_if_index = isisx_get_tx_by_rx (rx_sw_if_index); + if (tx_sw_if_index == INDEX_INVALID) + { + /* + * no such connection + */ + return 0; + } + hash_unset ((&isisx_main)->isisx_connection_db, rx_sw_if_index); + return 1; +} + +void +isisx_connection_walk (isisx_connection_cb_t cb, void *ctx) +{ + u32 rx_sw_if_index, tx_sw_if_index; + + /* *INDENT-OFF* */ + hash_foreach (rx_sw_if_index, tx_sw_if_index, (&isisx_main)->isisx_connection_db, + { + if (!cb (rx_sw_if_index, tx_sw_if_index, ctx)) break; + }); + /* *INDENT-ON* */ +} + +static clib_error_t * +isisx_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) +{ + if (is_add) + return 0; + + u32 rx_sw_if_index, tx_sw_if_index; + + /* *INDENT-OFF* */ + hash_foreach (rx_sw_if_index, tx_sw_if_index, (&isisx_main)->isisx_connection_db, + { + if (rx_sw_if_index == sw_if_index || tx_sw_if_index == sw_if_index) + { + isisx_connection_delete(rx_sw_if_index); + } + }); + /* *INDENT-ON* */ + + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (isisx_sw_interface_add_del); \ No newline at end of file diff --git a/vpp/isisx/vpp2306/isisx/isisx_connect.h b/vpp/isisx/vpp2306/isisx/isisx_connect.h new file mode 100644 index 00000000..7354e0f8 --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/isisx_connect.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ + +#ifndef __isisx_connect_H__ +#define __isisx_connect_H__ + +#include + +/** + * An ISIS protocol based Xconnect. + * This comprises the RX interface index to match against and forward to the interface. + * + * ISISx connections consist of two interfaces 'attached' to each other. An input feature + * will select the connection from the database of connections and a match will divert the packet, + * if all miss then the packet dropped + */ + +/** + * Get an ISISx tx_sw_if_index from its rx_sw_if_index key + * + * @param rx_sw_if_index Client defined RX interface ID + * @return TX interface ID + */ +const u32 isisx_get_tx_by_rx (const u32 rx_sw_if_index); + +/** + * Create or update an ISISx connection + * + * @param rx_sw_if_index Client defined RX interface ID + * @param tx_sw_if_index The connection TX interface to match on + */ +void isisx_connection_update (const u32 rx_sw_if_index, + const u32 tx_sw_if_index); + +/** + * Remove a connection from an ISISx + * + * @param connect_id Client defined RX interface ID + */ +int isisx_connection_delete (const u32 rx_sw_if_index); + +/** + * Callback function invoked during a walk of all ISISx connections + */ +typedef walk_rc_t (*isisx_connection_cb_t) (u32 rx_sw_if_index, u32 tx_sw_if_index, void *ctx); + +/** + * Walk/visit each of the ISISx connections + */ +extern void isisx_connection_walk (isisx_connection_cb_t cb, void *ctx); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/vpp/isisx/vpp2306/isisx/node.c b/vpp/isisx/vpp2306/isisx/node.c new file mode 100644 index 00000000..79b2b63b --- /dev/null +++ b/vpp/isisx/vpp2306/isisx/node.c @@ -0,0 +1,183 @@ +/* + * node.c - skeleton vpp engine plug-in dual-loop node skeleton + * + * Copyright (c) 2022 PANTHEON.tech s.r.o. + * 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. + */ +#include +#include +#include +#include +#include + +#include +#include + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 new_src_mac[6]; + u8 new_dst_mac[6]; +} isisx_trace_t; + +#ifndef CLIB_MARCH_VARIANT + +/* packet trace format function */ +static u8 * format_isisx_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + isisx_trace_t * t = va_arg (*args, isisx_trace_t *); + + s = format (s, "isisx: rx_sw_if_index: %d (%U)", + t->sw_if_index, + format_vnet_sw_if_index_name, isisx_main.vnet_main, t->sw_if_index); + + return s; +} + +vlib_node_registration_t isisx_node; + +#endif /* CLIB_MARCH_VARIANT */ + +#define foreach_isisx_error \ +_(FORWARDED, "isisx forwarded packets") + +typedef enum { +#define _(sym,str) ISISX_ERROR_##sym, + foreach_isisx_error +#undef _ + isisx_N_ERROR, +} isisx_error_t; + +#ifndef CLIB_MARCH_VARIANT +static char * isisx_error_strings[] = +{ +#define _(sym,string) string, + foreach_isisx_error +#undef _ +}; +#endif /* CLIB_MARCH_VARIANT */ + +typedef enum +{ + ISISX_NEXT_DROP, + ISISX_NEXT_INTERFACE_OUTPUT, + ISISX_N_NEXT, +} isisx_next_t; + +VLIB_NODE_FN (isisx_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + isisx_next_t next_index; + u32 pkts_forwarded = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + + u32 bi0; + vlib_buffer_t * b0; + u32 next0 = ISISX_NEXT_DROP; + u32 sw_if_index0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + /* Tracing */ + isisx_trace_t *t = NULL; + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + + u32 tx_sw_if_index = isisx_get_tx_by_rx(sw_if_index0); + if(tx_sw_if_index != INDEX_INVALID) + { + /* Send pkt back out the RX interface */ + next0 = ISISX_NEXT_INTERFACE_OUTPUT; + /* reset buffer offset */ + vlib_buffer_advance (b0, (sizeof (ethernet_header_t) + sizeof (llc_header_t)) * (-1)); + //t->next_index = next0; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = tx_sw_if_index; + pkts_forwarded++; + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, isisx_node.index, + ISISX_ERROR_FORWARDED, pkts_forwarded); + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +#ifndef CLIB_MARCH_VARIANT +VLIB_REGISTER_NODE (isisx_node) = +{ + .name = "isisx", + .vector_size = sizeof (u32), + .format_trace = format_isisx_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(isisx_error_strings), + .error_strings = isisx_error_strings, + + .n_next_nodes = ISISX_N_NEXT, + + .next_nodes = { + [ISISX_NEXT_DROP] = "error-drop", + [ISISX_NEXT_INTERFACE_OUTPUT] = "interface-output", + }, +}; +#endif /* CLIB_MARCH_VARIANT */ + +/* *INDENT-ON* */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/isisx/vpp2306/test/test_isisx.py b/vpp/isisx/vpp2306/test/test_isisx.py new file mode 100644 index 00000000..2853dbce --- /dev/null +++ b/vpp/isisx/vpp2306/test/test_isisx.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: Apache-2.0 + +# Copyright 2022 PANTHEON.tech +# +# 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. + +import unittest + +from framework import tag_fixme_vpp_workers +from framework import VppTestCase, VppTestRunner + +import sys + +from vpp_isis import VppIsis +from scapy.all import * +from scapy.contrib.isis import * + + +@tag_fixme_vpp_workers +class TestIsisx(VppTestCase): + """ ISIS Test Case """ + + @classmethod + def setUpClass(cls): + super(TestIsisx, cls).setUpClass() + @classmethod + def tearDownClass(cls): + super(TestIsisx, cls).tearDownClass() + + def setUp(self): + super(TestIsisx, self).setUp() + + self.create_pg_interfaces(range(3)) + + for pg in self.pg_interfaces: + pg.admin_up() + pg.resolve_arp() + + def tearDown(self): + for pg in self.pg_interfaces: + pg.admin_down() + + super(TestIsisx, self).tearDown() + + def send(self, ti, pkts): + ti.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + def test_isisx_connection_create(self): + vpp = VppIsis(self, + self.pg0.sw_if_index, + self.pg1.sw_if_index) + + vpp.add_vpp_config() + + dump = vpp.query_vpp_config() + self.assertTrue(dump) + + def test_isisx_connection_delete(self): + vpp = VppIsis(self, + self.pg0.sw_if_index, + self.pg1.sw_if_index) + + vpp.add_vpp_config() + vpp.remove_vpp_config() + dump = vpp.query_vpp_config() + self.assertFalse(dump) + + def test_isisx_connection_deleted_if(self): + pass + + def test_isisx_connection_send_packets(self): + vpp = VppIsis(self, + self.pg0.sw_if_index, + self.pg1.sw_if_index) + + vpp.add_vpp_config() + + self.vapi.cli("trace add virtio-input 1") + + p_isis1 = (Dot3(dst=self.pg0.local_mac, src=self.pg0.remote_mac, len=1500) / + LLC(dsap=0xfe, ssap=0xfe, ctrl=3) / + ISIS_CommonHdr() / + ISIS_L1_LAN_Hello()) + + self.send(self.pg0, p_isis1) + capture = self.pg1.get_capture(1, timeout=10) + + self.assertEqual(capture[0][LLC].dsap, 0xfe) + self.assertEqual(capture[0][LLC].ssap, 0xfe) + self.assertEqual(capture[0][LLC].ctrl, 3) + + trace = self.vapi.cli("show trace") + self.assertTrue("OSI isis" in trace) + self.assertTrue("isisx: rx_sw_if_index: {}".format(self.pg0.sw_if_index) in trace) + self.assertTrue("{}-tx".format(self.pg1._name) in trace) + + self.vapi.cli("clear trace") + self.vapi.cli("trace add virtio-input 1") + + p_isis2 = (Dot3(dst=self.pg0.local_mac, src=self.pg0.remote_mac, len=1500) / + LLC(dsap=0xfe, ssap=0xfe, ctrl=3) / + ISIS_CommonHdr() / + ISIS_L2_LAN_Hello()) + + self.send(self.pg0, p_isis2) + capture = self.pg1.get_capture(1, timeout=10) + + self.assertEqual(capture[0][LLC].dsap, 0xfe) + self.assertEqual(capture[0][LLC].ssap, 0xfe) + self.assertEqual(capture[0][LLC].ctrl, 3) + + trace = self.vapi.cli("show trace") + self.assertTrue("OSI isis" in trace) + self.assertTrue("isisx: rx_sw_if_index: {}".format(self.pg0.sw_if_index) in trace) + self.assertTrue("{}-tx".format(self.pg1._name) in trace) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/vpp/isisx/vpp2306/test/vpp_isis.py b/vpp/isisx/vpp2306/test/vpp_isis.py new file mode 100644 index 00000000..9fddb380 --- /dev/null +++ b/vpp/isisx/vpp2306/test/vpp_isis.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Copyright 2022 PANTHEON.tech +# +# 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. + +from vpp_object import VppObject + +def get_if_dump(dump, rx_sw_if_index): + for d in dump: + if (d.connection.rx_sw_if_index == rx_sw_if_index): + return True + return False + +class VppIsis(VppObject): + def __init__(self, test, rx_sw_if_index, tx_sw_if_index): + self._test = test + self.rx_sw_if_index = rx_sw_if_index + self.tx_sw_if_index = tx_sw_if_index + + def add_vpp_config(self): + self._test.vapi.isisx_connection_add_del(is_add=1, connection={'rx_sw_if_index': self.rx_sw_if_index, 'tx_sw_if_index': self.tx_sw_if_index}) + + def remove_vpp_config(self): + self._test.vapi.isisx_connection_add_del(is_add=0, connection={'rx_sw_if_index': self.rx_sw_if_index}) + + def object_id(self): + return "%s:%d" % (self.rx_sw_if_index, self.tx_sw_if_index) + + def query_vpp_config(self): + dump = self._test.vapi.isisx_connection_dump() + return get_if_dump(dump, self.rx_sw_if_index)