From 2cecba0a4aed400c7334eb09984166747312c0c0 Mon Sep 17 00:00:00 2001 From: atulpatel261194 Date: Wed, 3 Apr 2024 22:32:14 -0700 Subject: [PATCH] feat(evpn): complete fastpath implementation of new arch Co-authored-by: Dimitrios Markou Co-authored-by: Saikumar Banoth Co-authored-by: Patel Atul Co-authored-by: Vemula Venkatesh Co-authored-by: Jambekar Vishakha Signed-off-by: atulpatel261194 --- cmd/main.go | 64 +- config-intel-e2000.yaml | 28 +- go.mod | 7 +- go.sum | 25 +- pkg/LinuxCIModule/lci.go | 7 + pkg/LinuxGeneralModule/lgm.go | 100 +- .../intele2000/intelE2000.go | 6 + pkg/config/config.go | 3 - pkg/frr/frr.go | 34 +- pkg/infradb/bridge.go | 5 + .../subscriberframework/eventbus/eventbus.go | 20 + pkg/infradb/vrf.go | 5 + pkg/netlink/eventbus/eventbus.go | 64 + pkg/netlink/netlink_watcher.go | 1988 +++++++++++ pkg/utils/helpers.go | 33 + .../p4runtime/p4driverapi/p4ctl.go | 305 ++ .../p4runtime/p4translation/dcgw.go | 3026 +++++++++++++++++ .../p4runtime/p4translation/p4trans.go | 1089 ++++++ testscript/auto_create_100_vrfs.sh | 45 - testscript/auto_create_delete_vrf_nosleep.sh | 31 - 20 files changed, 6699 insertions(+), 186 deletions(-) create mode 100644 pkg/netlink/eventbus/eventbus.go create mode 100644 pkg/netlink/netlink_watcher.go create mode 100644 pkg/vendor_plugins/intel-e2000/p4runtime/p4driverapi/p4ctl.go create mode 100644 pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/dcgw.go create mode 100644 pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/p4trans.go delete mode 100755 testscript/auto_create_100_vrfs.sh delete mode 100755 testscript/auto_create_delete_vrf_nosleep.sh diff --git a/cmd/main.go b/cmd/main.go index 7ce10bb3..7559ee9b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,9 +17,6 @@ import ( "path/filepath" "time" - "github.com/spf13/cobra" - "github.com/spf13/viper" - pc "github.com/opiproject/opi-api/inventory/v1/gen/go" pe "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" "github.com/opiproject/opi-evpn-bridge/pkg/bridge" @@ -31,6 +28,8 @@ import ( "github.com/opiproject/opi-evpn-bridge/pkg/utils" "github.com/opiproject/opi-evpn-bridge/pkg/vrf" "github.com/opiproject/opi-smbios-bridge/pkg/inventory" + "github.com/spf13/cobra" + "github.com/spf13/viper" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -42,9 +41,15 @@ import ( gen_linux "github.com/opiproject/opi-evpn-bridge/pkg/LinuxGeneralModule" intel_e2000_linux "github.com/opiproject/opi-evpn-bridge/pkg/LinuxVendorModule/intele2000" frr "github.com/opiproject/opi-evpn-bridge/pkg/frr" + netlink "github.com/opiproject/opi-evpn-bridge/pkg/netlink" + ipu_vendor "github.com/opiproject/opi-evpn-bridge/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" ) +const ( + intelStr = "intel_e2000" +) + var rootCmd = &cobra.Command{ Use: "opi-evpn-bridge", Short: "evpn bridge", @@ -61,10 +66,11 @@ var rootCmd = &cobra.Command{ go runGatewayServer(config.GlobalConfig.GRPCPort, config.GlobalConfig.HTTPPort) switch config.GlobalConfig.Buildenv { - case "intel_e2000": + case intelStr: gen_linux.Init() intel_e2000_linux.Init() frr.Init() + ipu_vendor.Init() case "ci": gen_linux.Init() ci_linux.Init() @@ -77,8 +83,14 @@ var rootCmd = &cobra.Command{ if err := createGrdVrf(); err != nil { log.Panicf("Error: %v", err) } + switch config.GlobalConfig.Buildenv { + case intelStr: + netlink.Init() + default: + } runGrpcServer(config.GlobalConfig.GRPCPort, config.GlobalConfig.TLSFiles) + }, } @@ -118,6 +130,30 @@ func setupLogger(filename string) { log.SetOutput(logger.Writer()) } +func cleanUp() { + log.Println("Defer function called") + if err := deleteGrdVrf(); err != nil { + log.Println("Failed to delete GRD vrf") + } + + switch config.GlobalConfig.Buildenv { + case intelStr: + gen_linux.DeInit() + intel_e2000_linux.DeInit() + frr.DeInit() + case "ci": + gen_linux.DeInit() + ci_linux.DeInit() + frr.DeInit() + default: + log.Panic(" ERROR: Could not find Build env ") + } + + if err := infradb.Close(); err != nil { + log.Println("Failed to close infradb") + } +} + // main function func main() { // setup file and console logger @@ -133,12 +169,7 @@ func main() { if err := rootCmd.Execute(); err != nil { log.Panicf("Error in Execute(): %v", err) } - - defer func() { - if err := infradb.Close(); err != nil { - log.Panicf("Error in close(): %v", err) - } - }() + defer cleanUp() } // runGrpcServer start the grpc server for all the components @@ -251,3 +282,16 @@ func createGrdVrf() error { return nil } + +// deleteGrdVrf creates the grd vrf with vni 0 +func deleteGrdVrf() error { + log.Printf("DeleteGrdVrf(): deleted GRD VRF object\n") + + err := infradb.DeleteVrf("//network.opiproject.org/vrfs/GRD") + if err != nil { + log.Printf("CreateGrdVrf(): Error in deleting GRD VRF object %+v\n", err) + return err + } + + return nil +} diff --git a/config-intel-e2000.yaml b/config-intel-e2000.yaml index 4441bab7..7a73549d 100644 --- a/config-intel-e2000.yaml +++ b/config-intel-e2000.yaml @@ -16,16 +16,42 @@ subscribers: events: ["vrf", "svi"] - name: "intel_e2000" priority: 4 - events: ["vrf"] + events: ["vrf", "logical-bridge", "bridge-port", "svi"] grpc: server_addresses: - 0.0.0.0 server_port: 51703 num_threads: 10 static_external_macs: [] +p4: + enabled: true + representors: + port_mux: "port-mux" + vrf_mux: "vrf-mux" + grpc_acc: "host" + grpc_host: "00:20:00:00:14:48" + phy0_rep: "port0" + phy1_rep: "port1" + config: + p4infofile: /root/p4files/opi_ln.p4info.txt + binfile: /root/p4files/opi_ln.pb.bin linuxfrr: enabled: true defaultvtep: "vxlan-vtep" portmux: "enp0s1f0d5" vrfmux: "enp0s1f0d4" ipmtu: 2962 +netlink: + enabled: true + pollinterval: 1 + phyports: + - name: "enp0s1f0d1" + vsi: 0 + - name: "enp0s1f0d2" + vsi: 1 +loglevel: + db: INFO + grpc: INFO + linux: INFO + netlink: INFO + p4: DEBUG diff --git a/go.mod b/go.mod index d28cb87f..910c2274 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/opiproject/opi-evpn-bridge go 1.19 require ( + github.com/antoninbas/p4runtime-go-client v0.0.0-20231025232350-c5ccfd6512c8 + github.com/golang/protobuf v1.5.3 github.com/golangci/golangci-lint v1.55.2 github.com/google/uuid v1.5.0 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 @@ -10,9 +12,12 @@ require ( github.com/onsi/ginkgo/v2 v2.14.0 github.com/opiproject/opi-api v0.0.0-20240304222410-5dba226aaa9e github.com/opiproject/opi-smbios-bridge v0.1.3-0.20240113044816-4401aa6a3d1a + github.com/p4lang/p4runtime v1.4.0-rc.5 github.com/philippgille/gokv v0.6.0 github.com/philippgille/gokv/gomap v0.6.0 github.com/philippgille/gokv/redis v0.6.0 + github.com/roman-kachanovsky/go-binary-pack v0.0.0-20170214094030-e260e0dc6732 + github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 @@ -95,7 +100,6 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.8.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect @@ -178,7 +182,6 @@ require ( github.com/sashamelentyev/usestdlibvars v1.24.0 // indirect github.com/securego/gosec/v2 v2.18.2 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/nosnakecase v1.7.0 // indirect github.com/sivchari/tenv v1.7.1 // indirect diff --git a/go.sum b/go.sum index 86d99c9a..6d3d121b 100644 --- a/go.sum +++ b/go.sum @@ -79,13 +79,13 @@ github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pO github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antoninbas/p4runtime-go-client v0.0.0-20231025232350-c5ccfd6512c8 h1:XcwLFZZzDCD08/cQVX4/hCBBDJkY6j/DdO2PJfv3cM4= +github.com/antoninbas/p4runtime-go-client v0.0.0-20231025232350-c5ccfd6512c8/go.mod h1:2NyBT5g9J8C9BpV+c+uk7acGBZDQmYXqLbtxr679REs= github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -465,6 +465,8 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/p4lang/p4runtime v1.4.0-rc.5 h1:zztZGEkRM09Hf25SIX0p0ML07dmRCgsy0oC8uafmjtg= +github.com/p4lang/p4runtime v1.4.0-rc.5/go.mod h1:m9laObIMXM9N1ElGXijc66/MSM5eheZJLRLxg/TG+fU= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/philippgille/gokv v0.0.0-20191001201555-5ac9a20de634/go.mod h1:OCoWPt+mbYuTO1FUVrQ2SxQU0oaaHBsn6lRhFX3JHOc= @@ -521,9 +523,10 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/roman-kachanovsky/go-binary-pack v0.0.0-20170214094030-e260e0dc6732 h1:cu91qu1+Yzl3BC0BUKKuiPkT5O2E4MaVTUAkh9Xeneg= +github.com/roman-kachanovsky/go-binary-pack v0.0.0-20170214094030-e260e0dc6732/go.mod h1:4aW4O8uyDJOg9waxhoBpMtBTjVFT+m8NUoawVIAJMso= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= @@ -658,22 +661,16 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= @@ -788,8 +785,6 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -881,8 +876,6 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -891,8 +884,7 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1061,6 +1053,7 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= @@ -1068,8 +1061,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/pkg/LinuxCIModule/lci.go b/pkg/LinuxCIModule/lci.go index f52eadb7..f2a0bb05 100644 --- a/pkg/LinuxCIModule/lci.go +++ b/pkg/LinuxCIModule/lci.go @@ -215,3 +215,10 @@ func Init() { ctx = context.Background() nlink = utils.NewNetlinkWrapper() } + +// DeInit function handles stops functionality +func DeInit() { + // Unsubscribe to InfraDB notifications + eb := eventbus.EBus + eb.UnsubscribeModule("lci") +} diff --git a/pkg/LinuxGeneralModule/lgm.go b/pkg/LinuxGeneralModule/lgm.go index 8c0c19e5..12b2bc02 100644 --- a/pkg/LinuxGeneralModule/lgm.go +++ b/pkg/LinuxGeneralModule/lgm.go @@ -15,7 +15,6 @@ import ( "os/exec" "reflect" "strconv" - "strings" "time" "github.com/opiproject/opi-evpn-bridge/pkg/config" @@ -300,9 +299,6 @@ func handlevrf(objectData *eventbus.ObjectData) { } } -// defaultVtep variable string -var defaultVtep string - // ipMtu variable int var ipMtu int @@ -326,7 +322,6 @@ func Init() { } } brTenant = "br-tenant" - defaultVtep = config.GlobalConfig.LinuxFrr.DefaultVtep ipMtu = config.GlobalConfig.LinuxFrr.IPMtu ctx = context.Background() nlink = utils.NewNetlinkWrapper() @@ -337,6 +332,15 @@ func Init() { } } +// DeInit function handles stops functionality +func DeInit() { + eb := eventbus.EBus + err := TearDownTenantBridge() + if err != nil { + log.Printf("LGM: Failed to tear down br-tenant: %v\n", err) + } + eb.UnsubscribeModule("lgm") +} func setUpTenantBridge() { brTenantMtu := ipMtu + 20 vlanfiltering := true @@ -376,10 +380,6 @@ func setUpBridge(lb *infradb.LogicalBridge) bool { log.Printf("LGM: Failed to get link information for %s: %v\n", brTenant, err) return false } - if reflect.ValueOf(lb.Spec.VtepIP).IsZero() { - tmpVtepIP := getIPAddress(defaultVtep) - lb.Spec.VtepIP = &tmpVtepIP - } vxlan := &netlink.Vxlan{LinkAttrs: netlink.LinkAttrs{Name: link, MTU: ipMtu}, VxlanId: int(*lb.Spec.Vni), Port: 4789, Learning: false, SrcAddr: lb.Spec.VtepIP.IP} if err := nlink.LinkAdd(ctx, vxlan); err != nil { log.Printf("LGM: Failed to create Vxlan linki %s: %v\n", link, err) @@ -415,10 +415,7 @@ func setUpBridge(lb *infradb.LogicalBridge) bool { //nolint:funlen,gocognit func setUpVrf(vrf *infradb.Vrf) (string, bool) { IPMtu := fmt.Sprintf("%+v", ipMtu) - Ifname := strings.Split(vrf.Name, "/") - ifwlen := len(Ifname) - vrf.Name = Ifname[ifwlen-1] - if vrf.Name == "GRD" { + if path.Base(vrf.Name) == "GRD" { vrf.Metadata.RoutingTable = make([]*uint32, 2) vrf.Metadata.RoutingTable[0] = new(uint32) vrf.Metadata.RoutingTable[1] = new(uint32) @@ -452,18 +449,12 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { log.Printf(" LGM: VTEP IP not found: %+v\n", vrf.Spec.VtepIP) return "", false } - } else { - // Pick the IP of interface default VTEP interface - // log.Printf("LGM: VTEP iP %+v\n",getIPAddress(defaultVtep)) - tmpVtepIP := getIPAddress(defaultVtep) - vrf.Spec.VtepIP = &tmpVtepIP - vtip = fmt.Sprintf("%+v", vrf.Spec.VtepIP.IP) } log.Printf("setUpVrf: %s %d\n", vtip, routingTable) // Create the vrf interface for the specified routing table and add loopback address linkAdderr := nlink.LinkAdd(ctx, &netlink.Vrf{ - LinkAttrs: netlink.LinkAttrs{Name: vrf.Name}, + LinkAttrs: netlink.LinkAttrs{Name: path.Base(vrf.Name)}, Table: routingTable, }) if linkAdderr != nil { @@ -473,7 +464,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { log.Printf("LGM: vrf link %s Added with table id %d\n", vrf.Name, routingTable) - link, linkErr := nlink.LinkByName(ctx, vrf.Name) + link, linkErr := nlink.LinkByName(ctx, path.Base(vrf.Name)) if linkErr != nil { log.Printf("LGM : Link %s not found\n", vrf.Name) return "", false @@ -529,7 +520,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { // intel e2000 servers. brErr := nlink.LinkAdd(ctx, &netlink.Bridge{ - LinkAttrs: netlink.LinkAttrs{Name: brStr + vrf.Name}, + LinkAttrs: netlink.LinkAttrs{Name: brStr + path.Base(vrf.Name)}, }) if brErr != nil { log.Printf("LGM : Error in added bridge port\n") @@ -540,7 +531,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { rmac := fmt.Sprintf("%+v", GenerateMac()) // str(macaddress.MAC(b'\x00'+random.randbytes(5))).replace("-", ":") hw, _ := net.ParseMAC(rmac) - linkBr, brErr := nlink.LinkByName(ctx, brStr+vrf.Name) + linkBr, brErr := nlink.LinkByName(ctx, brStr+path.Base(vrf.Name)) if brErr != nil { log.Printf("LGM : Error in getting the br-%s\n", vrf.Name) return "", false @@ -557,7 +548,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { return "", false } - linkMaster, errMaster := nlink.LinkByName(ctx, vrf.Name) + linkMaster, errMaster := nlink.LinkByName(ctx, path.Base(vrf.Name)) if errMaster != nil { log.Printf("LGM : Error in getting the %s\n", vrf.Name) return "", false @@ -580,7 +571,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { SrcVtep := vrf.Spec.VtepIP.IP vxlanErr := nlink.LinkAdd(ctx, &netlink.Vxlan{ - LinkAttrs: netlink.LinkAttrs{Name: vxlanStr + vrf.Name, MTU: ipMtu}, VxlanId: int(*vrf.Spec.Vni), SrcAddr: SrcVtep, Learning: false, Proxy: true, Port: 4789}) + LinkAttrs: netlink.LinkAttrs{Name: vxlanStr + path.Base(vrf.Name), MTU: ipMtu}, VxlanId: int(*vrf.Spec.Vni), SrcAddr: SrcVtep, Learning: false, Proxy: true, Port: 4789}) if vxlanErr != nil { log.Printf("LGM : Error in added vxlan port\n") return "", false @@ -588,7 +579,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { log.Printf("LGM : link added vxlan-%s type vxlan id %d local %s dstport 4789 nolearning proxy\n", vrf.Name, *vrf.Spec.Vni, vtip) - linkVxlan, vxlanErr := nlink.LinkByName(ctx, vxlanStr+vrf.Name) + linkVxlan, vxlanErr := nlink.LinkByName(ctx, vxlanStr+path.Base(vrf.Name)) if vxlanErr != nil { log.Printf("LGM : Error in getting the %s\n", vxlanStr+vrf.Name) return "", false @@ -734,54 +725,21 @@ func NetMaskToInt(mask int) (netmaskint [4]int64) { return netmaskint } -// getIPAddress gets the ip address from link -func getIPAddress(dev string) net.IPNet { - link, err := nlink.LinkByName(ctx, dev) - if err != nil { - log.Printf("LGM: Error in LinkByName %+v\n", err) - return net.IPNet{ - IP: net.ParseIP("0.0.0.0"), - } - } - - addrs, err := nlink.AddrList(ctx, link, netlink.FAMILY_V4) // ip address show - if err != nil { - log.Printf("LGM: Error in AddrList\n") - return net.IPNet{ - IP: net.ParseIP("0.0.0.0"), - } - } - var address = &net.IPNet{ - IP: net.IPv4(127, 0, 0, 0), - Mask: net.CIDRMask(8, 32)} - var addr = &netlink.Addr{IPNet: address} - var validIps []netlink.Addr - for index := 0; index < len(addrs); index++ { - if !addr.Equal(addrs[index]) { - validIps = append(validIps, addrs[index]) - } - } - return *validIps[0].IPNet -} - // tearDownVrf tears down the vrf func tearDownVrf(vrf *infradb.Vrf) bool { - Ifname := strings.Split(vrf.Name, "/") - ifwlen := len(Ifname) - vrf.Name = Ifname[ifwlen-1] - link, err1 := nlink.LinkByName(ctx, vrf.Name) + link, err1 := nlink.LinkByName(ctx, path.Base(vrf.Name)) if err1 != nil { log.Printf("LGM : Link %s not found %+v\n", vrf.Name, err1) return true } - if vrf.Name == "GRD" { + if path.Base(vrf.Name) == "GRD" { return true } routingTable := *vrf.Metadata.RoutingTable[0] // Delete the Linux networking artefacts in reverse order if !reflect.ValueOf(vrf.Spec.Vni).IsZero() { - linkVxlan, linkErr := nlink.LinkByName(ctx, vxlanStr+vrf.Name) + linkVxlan, linkErr := nlink.LinkByName(ctx, vxlanStr+path.Base(vrf.Name)) if linkErr != nil { log.Printf("LGM : Link vxlan-%s not found %+v\n", vrf.Name, linkErr) return false @@ -793,7 +751,7 @@ func tearDownVrf(vrf *infradb.Vrf) bool { } log.Printf("LGM : Delete vxlan-%s\n", vrf.Name) - linkBr, linkbrErr := nlink.LinkByName(ctx, brStr+vrf.Name) + linkBr, linkbrErr := nlink.LinkByName(ctx, brStr+path.Base(vrf.Name)) if linkbrErr != nil { log.Printf("LGM : Link br-%s not found %+v\n", vrf.Name, linkbrErr) return false @@ -878,3 +836,19 @@ func tearDownBridge(lb *infradb.LogicalBridge) bool { } return true } + +// TearDownTenantBridge tears down the bridge +func TearDownTenantBridge() error { + Intf, err := nlink.LinkByName(ctx, brTenant) + if err != nil { + log.Printf("LGM: Failed to get br-tenant %s: %v\n", Intf, err) + return err + } + if err = nlink.LinkDel(ctx, Intf); err != nil { + log.Printf("LGM : Failed to delete br-tenant %s: %v\n", Intf, err) + return err + } + log.Printf("LGM: Executed ip link delete %s", brTenant) + + return nil +} diff --git a/pkg/LinuxVendorModule/intele2000/intelE2000.go b/pkg/LinuxVendorModule/intele2000/intelE2000.go index a1119e67..4c528f7e 100644 --- a/pkg/LinuxVendorModule/intele2000/intelE2000.go +++ b/pkg/LinuxVendorModule/intele2000/intelE2000.go @@ -397,3 +397,9 @@ func Init() { ctx = context.Background() nlink = utils.NewNetlinkWrapper() } + +// DeInit function handles stops functionality +func DeInit() { + eb := eventbus.EBus + eb.UnsubscribeModule(lvmComp) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 954c65f0..ff771826 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -26,8 +26,6 @@ type SubscriberConfig struct { type P4FilesConfig struct { P4infoFile string `yaml:"p4infofile"` BinFile string `yaml:"binfile"` - ConfFile string `yaml:"conffile"` - SdePath string `yaml:"sdepath"` } // RepresentorsConfig Representors config structure @@ -43,7 +41,6 @@ type RepresentorsConfig struct { // P4Config p4 config structure type P4Config struct { Enabled bool `yaml:"enabled"` - Driver string `yaml:"driver"` Representors map[string]interface{} `yaml:"representors"` Config P4FilesConfig `yaml:"config"` } diff --git a/pkg/frr/frr.go b/pkg/frr/frr.go index 3ea9f568..2e3cba47 100644 --- a/pkg/frr/frr.go +++ b/pkg/frr/frr.go @@ -265,6 +265,18 @@ func Init() { } } +// DeInit function handles stops functionality +func DeInit() { + frrEnabled := config.GlobalConfig.LinuxFrr.Enabled + if !frrEnabled { + log.Println("FRR Module disabled") + return + } + // Unsubscribe to InfraDB notifications + eb := eventbus.EBus + eb.UnsubscribeModule(frrComp) +} + // routingTableBusy function checks the routing table /*func routingTableBusy(table uint32) bool { cp, err := run([]string{"ip", "route", "show", "table", strconv.Itoa(int(table))}, false) @@ -320,15 +332,12 @@ type BgpVrfCmd struct { // setUpVrf sets up the vrf func setUpVrf(vrf *infradb.Vrf) (string, bool) { // This function must not be executed for the vrf representing the GRD - Ifname := strings.Split(vrf.Name, "/") - ifwlen := len(Ifname) - vrf.Name = Ifname[ifwlen-1] - if vrf.Name == "GRD" { + if path.Base(vrf.Name) == "GRD" { return "", true } if !reflect.ValueOf(vrf.Spec.Vni).IsZero() { // Configure the vrf in FRR and set up BGP EVPN for it - vrfName := fmt.Sprintf("vrf %s", vrf.Name) + vrfName := fmt.Sprintf("vrf %s", path.Base(vrf.Name)) vniID := fmt.Sprintf("vni %s", strconv.Itoa(int(*vrf.Spec.Vni))) _, err := Frr.FrrZebraCmd(ctx, fmt.Sprintf("configure terminal\n %s\n %s\n exit-vrf\n exit", vrfName, vniID)) // fmt.Printf("FrrZebraCmd: %v:%v", data, err) @@ -343,7 +352,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { } else { LbiP = fmt.Sprintf("%+v", vrf.Spec.LoopbackIP.IP) } - _, err = Frr.FrrBgpCmd(ctx, fmt.Sprintf("configure terminal\n router bgp 65000 vrf %s\n bgp router-id %s\n no bgp ebgp-requires-policy\n no bgp hard-administrative-reset\n no bgp graceful-restart notification\n address-family ipv4 unicast\n redistribute connected\n redistribute static\n exit-address-family\n address-family l2vpn evpn\n advertise ipv4 unicast\n exit-address-family\n exit", vrf.Name, LbiP)) + _, err = Frr.FrrBgpCmd(ctx, fmt.Sprintf("configure terminal\n router bgp 65000 vrf %s\n bgp router-id %s\n no bgp ebgp-requires-policy\n no bgp hard-administrative-reset\n no bgp graceful-restart notification\n address-family ipv4 unicast\n redistribute connected\n redistribute static\n exit-address-family\n address-family l2vpn evpn\n advertise ipv4 unicast\n exit-address-family\n exit", path.Base(vrf.Name), LbiP)) if err != nil { return "", false } @@ -371,7 +380,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { if err1 != nil { log.Printf("error-%v", err) } - cmd = fmt.Sprintf("show bgp vrf %s json", vrf.Name) + cmd = fmt.Sprintf("show bgp vrf %s json", path.Base(vrf.Name)) cp, err = Frr.FrrBgpCmd(ctx, cmd) if err != nil { log.Printf("error-%v", err) @@ -461,14 +470,11 @@ func tearDownSvi(svi *infradb.Svi) bool { // tearDownVrf tears down vrf func tearDownVrf(vrf *infradb.Vrf) bool { // This function must not be executed for the vrf representing the GRD - Ifname := strings.Split(vrf.Name, "/") - ifwlen := len(Ifname) - vrf.Name = Ifname[ifwlen-1] - if vrf.Name == "GRD" { + if path.Base(vrf.Name) == "GRD" { return true } - data, err := Frr.FrrZebraCmd(ctx, fmt.Sprintf("show vrf %s vni\n", vrf.Name)) + data, err := Frr.FrrZebraCmd(ctx, fmt.Sprintf("show vrf %s vni\n", path.Base(vrf.Name))) if err != nil { log.Printf("tearDownVrf : failed to run the command") } @@ -479,8 +485,8 @@ func tearDownVrf(vrf *infradb.Vrf) bool { // Clean up FRR last if !reflect.ValueOf(vrf.Spec.Vni).IsZero() { log.Printf("FRR Deleted event") - delCmd1 := fmt.Sprintf("no router bgp 65000 vrf %s", vrf.Name) - delCmd2 := fmt.Sprintf("no vrf %s", vrf.Name) + delCmd1 := fmt.Sprintf("no router bgp 65000 vrf %s", path.Base(vrf.Name)) + delCmd2 := fmt.Sprintf("no vrf %s", path.Base(vrf.Name)) _, err = Frr.FrrBgpCmd(ctx, fmt.Sprintf("configure terminal\n %s\n exit\n", delCmd1)) if err != nil { return false diff --git a/pkg/infradb/bridge.go b/pkg/infradb/bridge.go index becdca15..1a3d7e44 100644 --- a/pkg/infradb/bridge.go +++ b/pkg/infradb/bridge.go @@ -14,8 +14,10 @@ import ( "net" pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" + "github.com/opiproject/opi-evpn-bridge/pkg/config" "github.com/opiproject/opi-evpn-bridge/pkg/infradb/common" "github.com/opiproject/opi-evpn-bridge/pkg/infradb/subscriberframework/eventbus" + "github.com/opiproject/opi-evpn-bridge/pkg/utils" ) // LogicalBridgeOperStatus operational Status for Logical Bridges @@ -73,6 +75,9 @@ func NewLogicalBridge(in *pb.LogicalBridge) (*LogicalBridge, error) { vtepip := make(net.IP, 4) binary.BigEndian.PutUint32(vtepip, in.Spec.VtepIpPrefix.Addr.GetV4Addr()) vip = &net.IPNet{IP: vtepip, Mask: net.CIDRMask(int(in.Spec.VtepIpPrefix.Len), 32)} + } else { + tmpVtepIP := utils.GetIPAddress(config.GlobalConfig.LinuxFrr.DefaultVtep) + vip = &tmpVtepIP } subscribers := eventbus.EBus.GetSubscribers("logical-bridge") diff --git a/pkg/infradb/subscriberframework/eventbus/eventbus.go b/pkg/infradb/subscriberframework/eventbus/eventbus.go index 2d8a23d8..47384474 100644 --- a/pkg/infradb/subscriberframework/eventbus/eventbus.go +++ b/pkg/infradb/subscriberframework/eventbus/eventbus.go @@ -66,6 +66,7 @@ func (e *EventBus) StartSubscriber(moduleName, eventType string, priority int, e subscriber.Ch <- "error: no event handler found" } case <-subscriber.Quit: + log.Printf("\nSubscriber %s quit \n", subscriber.Name) close(subscriber.Ch) return } @@ -115,6 +116,7 @@ func (e *EventBus) GetSubscribers(eventType string) []*Subscriber { return e.subscribers[eventType] } +// subscriberExist checks if the subscriber exist func (e *EventBus) subscriberExist(eventType string, moduleName string) bool { subList := e.GetSubscribers(eventType) if len(subList) != 0 { @@ -127,6 +129,24 @@ func (e *EventBus) subscriberExist(eventType string, moduleName string) bool { return false } +// UnsubscribeModule unsubs the whole module +func (e *EventBus) UnsubscribeModule(moduleName string) bool { + for eventName, subs := range e.subscribers { + if len(subs) != 0 { + for i, sub := range subs { + if sub.Name == moduleName { + sub.Quit <- true + e.subscribers[eventName] = append(subs[:i], subs[i+1:]...) + e.eventHandlers[moduleName+"."+eventName] = nil + log.Printf("\n Module %s is unsubscribed for event %s", sub.Name, eventName) + } + } + } + } + log.Printf("\nSubscriber %s is unsubscribed for all events\n", moduleName) + return false +} + // Publish api notifies the subscribers with certain eventType func (e *EventBus) Publish(objectData *ObjectData, subscriber *Subscriber) { e.publishL.RLock() diff --git a/pkg/infradb/vrf.go b/pkg/infradb/vrf.go index 6e099996..0d0665ea 100644 --- a/pkg/infradb/vrf.go +++ b/pkg/infradb/vrf.go @@ -14,8 +14,10 @@ import ( // "time" pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" + "github.com/opiproject/opi-evpn-bridge/pkg/config" "github.com/opiproject/opi-evpn-bridge/pkg/infradb/common" "github.com/opiproject/opi-evpn-bridge/pkg/infradb/subscriberframework/eventbus" + "github.com/opiproject/opi-evpn-bridge/pkg/utils" ) // VrfOperStatus operational Status for VRFs @@ -133,6 +135,9 @@ func NewVrf(in *pb.Vrf) (*Vrf, error) { vtepip := make(net.IP, 4) binary.BigEndian.PutUint32(vtepip, in.Spec.VtepIpPrefix.Addr.GetV4Addr()) vip = &net.IPNet{IP: vtepip, Mask: net.CIDRMask(int(in.Spec.VtepIpPrefix.Len), 32)} + } else { + tmpVtepIP := utils.GetIPAddress(config.GlobalConfig.LinuxFrr.DefaultVtep) + vip = &tmpVtepIP } subscribers := eventbus.EBus.GetSubscribers("vrf") diff --git a/pkg/netlink/eventbus/eventbus.go b/pkg/netlink/eventbus/eventbus.go new file mode 100644 index 00000000..cd8fea84 --- /dev/null +++ b/pkg/netlink/eventbus/eventbus.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries. + +// Package eventbus handles pub sub +package eventbus + +import ( + "sync" +) + +// EventBus holds the event bus info +type EventBus struct { + subscribers map[string][]*Subscriber + mutex sync.RWMutex +} + +// Subscriber holds the info for each subscriber +type Subscriber struct { + Ch chan interface{} + Quit chan bool +} + +// NewEventBus initializes ann EventBus object +func NewEventBus() *EventBus { + return &EventBus{ + subscribers: make(map[string][]*Subscriber), + } +} + +// Subscribe api provides registration of a subscriber to the given eventType +func (e *EventBus) Subscribe(eventType string) *Subscriber { + e.mutex.Lock() + defer e.mutex.Unlock() + + subscriber := &Subscriber{ + Ch: make(chan interface{}), + Quit: make(chan bool), + } + + e.subscribers[eventType] = append(e.subscribers[eventType], subscriber) + + return subscriber +} + +// Publish api notifies the subscribers with certain eventType +func (e *EventBus) Publish(eventType string, data interface{}) { + e.mutex.RLock() + defer e.mutex.RUnlock() + + subscribers, ok := e.subscribers[eventType] + if !ok { + return + } + + for _, sub := range subscribers { + sub.Ch <- data + } +} + +// Unsubscribe the subscriber, which delete the subscriber(all resources will be washed out) +func (s *Subscriber) Unsubscribe() { + close(s.Ch) + s.Quit <- true +} diff --git a/pkg/netlink/netlink_watcher.go b/pkg/netlink/netlink_watcher.go new file mode 100644 index 00000000..b91c9da0 --- /dev/null +++ b/pkg/netlink/netlink_watcher.go @@ -0,0 +1,1988 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries. +// Copyright (C) 2023 Nordix Foundation. + +// Package netlink handles the netlink related functionality +package netlink + +import ( + "context" + "fmt" + "log" + "os" + + "regexp" + "strconv" + "strings" + "time" + + "encoding/binary" + "encoding/json" + "net" + "reflect" + + "golang.org/x/sys/unix" + + vn "github.com/vishvananda/netlink" + + "path" + + "github.com/opiproject/opi-evpn-bridge/pkg/config" + "github.com/opiproject/opi-evpn-bridge/pkg/infradb" + eb "github.com/opiproject/opi-evpn-bridge/pkg/netlink/eventbus" + "github.com/opiproject/opi-evpn-bridge/pkg/utils" +) + +var ctx context.Context +var nlink utils.Netlink + +// dbLock variable +// var dbLock int + +// GRD variable +var GRD int + +// pollInterval variable +var pollInterval int + +// dbphyPortslock variable +var phyPorts = make(map[string]int) + +// stopMonitoring variable +var stopMonitoring bool + +// EventBus variable +var EventBus = eb.NewEventBus() + +// strNone variable +var strNone = "NONE" +var zebraStr = "zebra" + +// Route Direction +const ( // Route direction + None int = iota + RX + TX + RXTX +) + +// Nexthop type +const ( // NexthopStruct TYPE & L2NEXTHOP TYPE & FDBentry + PHY = iota + SVI + ACC + VXLAN + BRIDGEPORT + OTHER +) + +// RTNNeighbor +const ( + RTNNeighbor = 1111 +) + +// NeighKey strcture of neighbor +type NeighKey struct { + Dst string + VrfName string + Dev int +} + +// RouteKey structure of route description +type RouteKey struct { + Table int + Dst string +} + +// NexthopKey structure of nexthop +type NexthopKey struct { + VrfName string + Dst string + Dev int + Local bool +} + +// NeighIPStruct nighbor ip structure +type NeighIPStruct struct { + Dst string + Dev string + Lladdr string + ExternLearn string + State []string + Protocol string +} + +// FDBKey structure key for sorting theFDB entries +type FDBKey struct { + VlanID int + Mac string +} + +// L2NexthopKey is l2 neighbor key +type L2NexthopKey struct { + Dev string + VlanID int + Dst string +} + +// FdbIPStruct fdb ip structure +type FdbIPStruct struct { + Mac string + Ifname string + Vlan int + Flags []string + Master string + State string + Dst string +} + +// Routes Variable +var Routes = make(map[RouteKey]RouteStruct) + +// Nexthops Variable +var Nexthops = make(map[NexthopKey]NexthopStruct) + +// Neighbors Variable +var Neighbors = make(map[NeighKey]NeighStruct) + +// FDB Variable +var FDB = make(map[FDBKey]FdbEntryStruct) + +// L2Nexthops Variable +var L2Nexthops = make(map[L2NexthopKey]L2NexthopStruct) + +// LatestRoutes Variable +var LatestRoutes = make(map[RouteKey]RouteStruct) + +// LatestNexthop Variable +var LatestNexthop = make(map[NexthopKey]NexthopStruct) + +// LatestNeighbors Variable +var LatestNeighbors = make(map[NeighKey]NeighStruct) + +// LatestFDB Variable +var LatestFDB = make(map[FDBKey]FdbEntryStruct) + +// LatestL2Nexthop Variable +var LatestL2Nexthop = make(map[L2NexthopKey]L2NexthopStruct) + +/*-------------------------------------------------------------------------- +### Route Database Entries +### +### In the internal Route table, there is one entry per VRF and IP prefix +### to be installed in the routing table of the P4 pipeline. If there are +### multiple routes in the Linux route database for the same VRF and +### prefix, we pick the one with the lowest metric (as does the Linux +### forwarding plane). +### The key of the internal Route table consists of (vrf, dst prefix) and +### corresponds to the match fields in the P4 routing table. The rx/tx +### direction match field of the MEV P4 pipeline and the necessary +### duplication of some route entries is a technicality the MEV P4 pipeline +### and must be handled by the p4ctrl module. +--------------------------------------------------------------------------*/ + +// Route structure has route info +type Route interface { + Route_store(*infradb.Vrf, map[string]string) +} + +// RouteStruct structure has route info +type RouteStruct struct { + Route0 vn.Route + Vrf *infradb.Vrf + Nexthops []NexthopStruct + Metadata map[interface{}]interface{} + NlType string + Key RouteKey + Err error +} + +// RouteList list has route info +type RouteList struct { + RS []RouteStruct +} + +// NexthopStruct contains nexthop structure +type NexthopStruct struct { + nexthop vn.NexthopInfo + Vrf *infradb.Vrf + Local bool + Weight int + Metric int + ID int + Scope int + Protocol int + RouteRefs []RouteStruct + Key NexthopKey + Resolved bool + Neighbor *NeighStruct // ??? + NhType int + Metadata map[interface{}]interface{} +} + +// NetMaskToInt convert network mask to int +func NetMaskToInt(mask int) (netmaskint [4]int64) { + var binarystring string + + for ii := 1; ii <= mask; ii++ { + binarystring += "1" + } + for ii := 1; ii <= (32 - mask); ii++ { + binarystring += "0" + } + oct1 := binarystring[0:8] + oct2 := binarystring[8:16] + oct3 := binarystring[16:24] + oct4 := binarystring[24:] + netmaskint[0], _ = strconv.ParseInt(oct1, 2, 64) + netmaskint[1], _ = strconv.ParseInt(oct2, 2, 64) + netmaskint[2], _ = strconv.ParseInt(oct3, 2, 64) + netmaskint[3], _ = strconv.ParseInt(oct4, 2, 64) + + return netmaskint +} + +// RtnType map of string key as RTN Type +var RtnType = map[string]int{ + "unspec": unix.RTN_UNSPEC, + "unicast": unix.RTN_UNICAST, + "local": unix.RTN_LOCAL, + "broadcast": unix.RTN_BROADCAST, + "anycast": unix.RTN_ANYCAST, + "multicast": unix.RTN_MULTICAST, + "blackhole": unix.RTN_BLACKHOLE, + "unreachable": unix.RTN_UNREACHABLE, + "prohibit": unix.RTN_PROHIBIT, + "throw": unix.RTN_THROW, + "nat": unix.RTN_NAT, + "xresolve": unix.RTN_XRESOLVE, + "neighbor": RTNNeighbor, +} + +// RtnProto map of string key as RTN Type +var RtnProto = map[string]int{ + "unspec": unix.RTPROT_UNSPEC, + "redirect": unix.RTPROT_REDIRECT, + "kernel": unix.RTPROT_KERNEL, + "boot": unix.RTPROT_BOOT, + "static": unix.RTPROT_STATIC, + "bgp": int('B'), + "ipu_infra_mgr": int('I'), + "196": 196, +} + +// RtnScope map of string key as RTN scope +var RtnScope = map[string]int{ + "global": unix.RT_SCOPE_UNIVERSE, + "site": unix.RT_SCOPE_SITE, + "link": unix.RT_SCOPE_LINK, + "local": unix.RT_SCOPE_HOST, + "nowhere": unix.RT_SCOPE_NOWHERE, +} + +// flagstring strucure +type flagstring struct { + f int + s string +} + +// testFlag array of flag string +var testFlag = []flagstring{ + {f: unix.RTNH_F_ONLINK, s: "onlink"}, + {f: unix.RTNH_F_PERVASIVE, s: "pervasive"}, +} + +// getFlags gets the flag +func getFlags(s string) int { + f := 0 + for _, F := range testFlag { + if s == F.s { + f |= F.f + } + } + return f +} + +// getFlagString return flag of type string +func getFlagString(flag int) string { + f := "" + for _, F := range testFlag { + if F.f == flag { + str := F.s + return str + } + } + return f +} + +// NhIDCache Variable +var NhIDCache = make(map[NexthopKey]int) + +// NhNextID Variable +var NhNextID = 16 + +// NHAssignID returns the nexthop id +func NHAssignID(key NexthopKey) int { + id := NhIDCache[key] + if id == 0 { + // Assigne a free id and insert it into the cache + id = NhNextID + NhIDCache[key] = id + NhNextID++ + } + return id +} + +// NHParse parses the neighbor +func NHParse(v *infradb.Vrf, rc RouteCmdInfo) NexthopStruct { + var nh NexthopStruct + nh.Weight = 1 + nh.Vrf = v + if !reflect.ValueOf(rc.Dev).IsZero() { + vrf, _ := vn.LinkByName(rc.Dev) + nh.nexthop.LinkIndex = vrf.Attrs().Index + NameIndex[nh.nexthop.LinkIndex] = vrf.Attrs().Name + } + if len(rc.Flags) != 0 { + nh.nexthop.Flags = getFlags(rc.Flags[0]) + } + if !reflect.ValueOf(rc.Gateway).IsZero() { + nIP := &net.IPNet{ + IP: net.ParseIP(rc.Gateway), + } + nh.nexthop.Gw = nIP.IP + } + if !reflect.ValueOf(rc.Protocol).IsZero() { + nh.Protocol = RtnProto[rc.Protocol] + } + if !reflect.ValueOf(rc.Scope).IsZero() { + nh.Scope = RtnScope[rc.Scope] + } + if !reflect.ValueOf(rc.Type).IsZero() { + nh.NhType = RtnType[rc.Type] + if nh.NhType == unix.RTN_LOCAL { + nh.Local = true + } else { + nh.Local = false + } + } + if !reflect.ValueOf(rc.Weight).IsZero() { + nh.Weight = rc.Weight + } + nh.Key = NexthopKey{nh.Vrf.Name, nh.nexthop.Gw.String(), nh.nexthop.LinkIndex, nh.Local} + return nh +} + +// checkRtype checks the route type +func checkRtype(rType string) bool { + var Types = [6]string{"connected", "evpn-vxlan", "static", "bgp", "local", "neighbor"} + for _, v := range Types { + if v == rType { + return true + } + } + return false +} + +// preFilterRoute pre filter the routes +func preFilterRoute(r RouteStruct) bool { + if checkRtype(r.NlType) && !r.Route0.Dst.IP.IsLoopback() && strings.Compare(r.Route0.Dst.IP.String(), "0.0.0.0") != 0 { + return true + } + + return false +} + +// checkProto checks the proto type +func checkProto(proto int) bool { + var protos = [3]int{unix.RTPROT_BOOT, unix.RTPROT_STATIC, 196} + for _, v := range protos { + if proto == v { + return true + } + } + return false +} + +// annotate function annonates the entries +func (route RouteStruct) annotate() RouteStruct { + route.Metadata = make(map[interface{}]interface{}) + for i := 0; i < len(route.Nexthops); i++ { + nexthop := route.Nexthops[i] + route.Metadata["nh_ids"] = nexthop.ID + } + if route.Vrf.Spec.Vni != nil { + route.Metadata["vrf_id"] = *route.Vrf.Spec.Vni + } else { + route.Metadata["vrf_id"] = 0 + } + if len(route.Nexthops) != 0 { + nexthop := route.Nexthops[0] + if route.Vrf.Spec.Vni == nil { // GRD + switch nexthop.NhType { + case PHY: + route.Metadata["direction"] = RXTX + case ACC: + route.Metadata["direction"] = RX + default: + route.Metadata["direction"] = None + } + } else { + switch nexthop.NhType { + case VXLAN: + route.Metadata["direction"] = RXTX + case SVI, ACC: + route.Metadata["direction"] = RXTX + default: + route.Metadata["direction"] = None + } + } + } else { + route.Metadata["direction"] = None + } + return route +} + +//nolint +func setRouteType(rs RouteStruct, v *infradb.Vrf) string { + if rs.Route0.Type == unix.RTN_UNICAST && rs.Route0.Protocol == unix.RTPROT_KERNEL && rs.Route0.Scope == unix.RT_SCOPE_LINK && len(rs.Nexthops) == 1 { + // Connected routes are proto=kernel and scope=link with a netdev as single nexthop + return "connected" + } else if rs.Route0.Type == unix.RTN_UNICAST && int(rs.Route0.Protocol) == int('B') && rs.Route0.Scope == unix.RT_SCOPE_UNIVERSE { + // EVPN routes to remote destinations are proto=bgp, scope global withipu_infra_mgr_db + // all Nexthops residing on the br- bridge interface of the VRF. + var devs []string + if len(rs.Nexthops) != 0 { + for _, d := range rs.Nexthops { + devs = append(devs, NameIndex[d.nexthop.LinkIndex]) + } + if len(devs) == 1 && devs[0] == "br-"+v.Name { + return "evpn-vxlan" + } + return "bgp" + } + } else if rs.Route0.Type == unix.RTN_UNICAST && checkProto(int(rs.Route0.Protocol)) && rs.Route0.Scope == unix.RT_SCOPE_UNIVERSE { + return "static" + } else if rs.Route0.Type == unix.RTN_LOCAL { + return "local" + } else if rs.Route0.Type == RTNNeighbor { + // Special /32 or /128 routes for Resolved neighbors on connected subnets + return "neighbor" + } + return "unknown" +} + +// RouteSlice empty route structure slice +var RouteSlice []RouteStruct + +// Parse_Route parse the routes +//nolint +func Parse_Route(v *infradb.Vrf, Rm []RouteCmdInfo, t int) RouteList { + var route RouteList + for _, Ro := range Rm { + if reflect.ValueOf(Ro.Type).IsZero() && (!reflect.ValueOf(Ro.Dev).IsZero() || !reflect.ValueOf(Ro.Gateway).IsZero()) { + Ro.Type = "local" + } + var rs RouteStruct + rs.Vrf = v + if !reflect.ValueOf(Ro.Nhid).IsZero() || !reflect.ValueOf(Ro.Gateway).IsZero() || !reflect.ValueOf(Ro.Dev).IsZero() { + rs.Nexthops = append(rs.Nexthops, NHParse(v, Ro)) + } + rs.NlType = "unknown" + rs.Route0.Table = t + rs.Route0.Priority = 1 + if !reflect.ValueOf(Ro.Dev).IsZero() { + dev, _ := vn.LinkByName(Ro.Dev) + rs.Route0.LinkIndex = dev.Attrs().Index + } + if !reflect.ValueOf(Ro.Dst).IsZero() { + var Mask int + split := Ro.Dst + if strings.Contains(Ro.Dst, "/") { + split4 := strings.Split(Ro.Dst, "/") + Mask, _ = strconv.Atoi(split4[1]) + split = split4[0] + } else { + Mask = 32 + } + var nIP *net.IPNet + if Ro.Dst == "default" { + nIP = &net.IPNet{ + IP: net.ParseIP("0.0.0.0"), + Mask: net.IPv4Mask(0, 0, 0, 0), + } + } else { + mtoip := NetMaskToInt(Mask) + b3 := make([]byte, 8) // Converting int64 to byte + binary.LittleEndian.PutUint64(b3, uint64(mtoip[3])) + b2 := make([]byte, 8) + binary.LittleEndian.PutUint64(b2, uint64(mtoip[2])) + b1 := make([]byte, 8) + binary.LittleEndian.PutUint64(b1, uint64(mtoip[1])) + b0 := make([]byte, 8) + binary.LittleEndian.PutUint64(b0, uint64(mtoip[0])) + nIP = &net.IPNet{ + IP: net.ParseIP(split), + Mask: net.IPv4Mask(b0[0], b1[0], b2[0], b3[0]), + } + } + rs.Route0.Dst = nIP + } + if !reflect.ValueOf(Ro.Metric).IsZero() { + rs.Route0.Priority = Ro.Metric + } + if !reflect.ValueOf(Ro.Protocol).IsZero() { + if RtnProto[Ro.Protocol] != 0 { + rs.Route0.Protocol = vn.RouteProtocol(RtnProto[Ro.Protocol]) + } else { + rs.Route0.Protocol = 0 + } + } + if !reflect.ValueOf(Ro.Type).IsZero() { + rs.Route0.Type = RtnType[Ro.Type] + } + if len(Ro.Flags) != 0 { + rs.Route0.Flags = getFlags(Ro.Flags[0]) + } + if !reflect.ValueOf(Ro.Scope).IsZero() { + rs.Route0.Scope = vn.Scope(RtnScope[Ro.Scope]) + } + if !reflect.ValueOf(Ro.Prefsrc).IsZero() { + nIP := &net.IPNet{ + IP: net.ParseIP(Ro.Prefsrc), + } + rs.Route0.Src = nIP.IP + } + if !reflect.ValueOf(Ro.Gateway).IsZero() { + nIP := &net.IPNet{ + IP: net.ParseIP(Ro.Gateway), + } + rs.Route0.Gw = nIP.IP + } + if !reflect.ValueOf(Ro.VRF).IsZero() { + rs.Vrf, _ = infradb.GetVrf(Ro.VRF.Name) + } + if !reflect.ValueOf(Ro.Table).IsZero() { + rs.Route0.Table = Ro.Table + } + rs.NlType = setRouteType(rs, v) + rs.Key = RouteKey{Table: rs.Route0.Table, Dst: rs.Route0.Dst.String()} + if preFilterRoute(rs) { + route.RS = append(route.RS, rs) + } + } + return route +} + +/*func comparekey(i, j int) bool { + return RouteSlice[i].Key.Table > RouteSlice[j].Key.Table && RouteSlice[i].Key.Dst > RouteSlice[j].Key.Dst +}*/ + +//-------------------------------------------------------------------------- +//### NexthopStruct Database Entries +//-------------------------------------------------------------------------- + +// TryResolve function +type TryResolve func(map[string]string) + +// -------------------------------------------------------------------------- +// ### Bridge MAC Address Database +// ### +// ### We split the Linux FDB entries into DMAC and L2 NexthopStruct tables similar +// ### to routes and L3 nexthops, Thus, all remote EVPN DMAC entries share a +// ### single VXLAN L2 nexthop table entry. +// ### +// ### TODO: Support for dynamically learned MAC addresses on BridgePorts +// ### (e.g. for pod interfaces operating in promiscuous mode). +// -------------------------------------------------------------------------- + +// L2NexthopStruct structure +type L2NexthopStruct struct { + Dev string + VlanID int + Dst net.IP + Key L2NexthopKey + lb *infradb.LogicalBridge + bp *infradb.BridgePort + ID int + FdbRefs []FdbEntryStruct + Resolved bool + Type int + Metadata map[interface{}]interface{} +} + +// FdbEntryStruct structure +type FdbEntryStruct struct { + VlanID int + Mac string + Key FDBKey + State string + lb *infradb.LogicalBridge + bp *infradb.BridgePort + Nexthop L2NexthopStruct + Type int + Metadata map[interface{}]interface{} + Err error +} + +// FDBEntryList list +type FDBEntryList struct { + FS []FdbEntryStruct +} + +// ParseFdb parse the fdb +func ParseFdb(fdbIP FdbIPStruct, fdbentry FdbEntryStruct) FdbEntryStruct { + fdbentry.VlanID = fdbIP.Vlan + fdbentry.Mac = fdbIP.Mac + fdbentry.Key = FDBKey{fdbIP.Vlan, fdbIP.Mac} + fdbentry.State = fdbIP.State + lbs, _ := infradb.GetAllLBs() + for _, lb := range lbs { + if lb.Spec.VlanID == uint32(fdbentry.VlanID) { + fdbentry.lb = lb + break + } + } + if !(reflect.ValueOf(fdbentry.lb).IsZero()) { + bp := fdbentry.lb.MacTable[fdbentry.Mac] + if bp != "" { + fdbentry.bp, _ = infradb.GetBP(bp) + } + } + Dev := fdbIP.Ifname + dst := fdbIP.Dst + fdbentry.Nexthop = fdbentry.Nexthop.ParseL2NH(fdbentry.VlanID, Dev, dst, fdbentry.lb, fdbentry.bp) + fdbentry.Type = fdbentry.Nexthop.Type + return fdbentry +} +//nolint +// ParseL2NH parse the l2hn +func (l2n L2NexthopStruct) ParseL2NH(vlanID int, dev string, dst string, LB *infradb.LogicalBridge, BP *infradb.BridgePort) L2NexthopStruct { + l2n.Dev = dev + l2n.VlanID = vlanID + l2n.Dst = net.IP(dst) + l2n.Key = L2NexthopKey{l2n.Dev, l2n.VlanID, string(l2n.Dst)} + l2n.lb = LB + l2n.bp = BP + l2n.Resolved = true + if l2n.Dev == fmt.Sprintf("svi-%d", l2n.VlanID) { + l2n.Type = SVI + } else if l2n.Dev == fmt.Sprintf("vxlan-%d", l2n.VlanID) { + l2n.Type = VXLAN + } else if !(reflect.ValueOf(l2n.bp).IsZero()) { + l2n.Type = BRIDGEPORT + } else { + l2n.Type = None + } + return l2n +} + +// l2nexthopID +var l2nexthopID = 16 + +// l2NhIDCache +var l2NhIDCache = make(map[L2NexthopKey]int) + +// L2NHAssignID get nexthop id +func L2NHAssignID(key L2NexthopKey) int { + id := l2NhIDCache[key] + if id == 0 { + // Assigne a free id and insert it into the cache + id = l2nexthopID + l2NhIDCache[key] = id + l2nexthopID++ + } + return id +} + +// addFdbEntry add fdb entries +func addFdbEntry(m FdbEntryStruct) { + m = addL2Nexthop(m) + // TODO + // logger.debug(f"Adding {m.format()}.") + LatestFDB[m.Key] = m +} + +// addL2Nexthop add the l2 nexthop +func addL2Nexthop(m FdbEntryStruct) FdbEntryStruct { + if reflect.ValueOf(LatestL2Nexthop).IsZero() { + log.Fatal("netlink: L2Nexthop DB empty\n") + return FdbEntryStruct{} + } + latestNexthops := LatestL2Nexthop[m.Nexthop.Key] + if !(reflect.ValueOf(latestNexthops).IsZero()) { + latestNexthops.FdbRefs = append(latestNexthops.FdbRefs, m) + m.Nexthop = latestNexthops + } else { + latestNexthops = m.Nexthop + latestNexthops.FdbRefs = append(latestNexthops.FdbRefs, m) + latestNexthops.ID = L2NHAssignID(latestNexthops.Key) + LatestL2Nexthop[latestNexthops.Key] = latestNexthops + m.Nexthop = latestNexthops + } + return m +} + +//-------------------------------------------------------------------------- +//### Neighbor Database Entries +//-------------------------------------------------------------------------- + +// NeighInit neighbor init function +type NeighInit func(int, map[string]string) + +// linkTable wg sync.WaitGroup +var linkTable []vn.Link + +// vrfList netlink libarary var +var vrfList []vn.Link + +// deviceList netlink libarary var +var deviceList []vn.Link + +// vlanList netlink libarary var +var vlanList []vn.Link + +// bridgeList netlink libarary var +var bridgeList []vn.Link + +// vxlanList netlink libarary var +var vxlanList []vn.Link + +// linkList netlink libarary var +var linkList []vn.Link + +// NameIndex netlink libarary var +var NameIndex = make(map[int]string) + +// getlink get the link +func getlink() { + links, err := vn.LinkList() + if err != nil { + log.Fatal("netlink:", err) + } + for i := 0; i < len(links); i++ { + linkTable = append(linkTable, links[i]) + NameIndex[links[i].Attrs().Index] = links[i].Attrs().Name + switch links[i].Type() { + case "vrf": + vrfList = append(vrfList, links[i]) + case "device": + deviceList = append(deviceList, links[i]) + case "vlan": + vlanList = append(vlanList, links[i]) + case "bridge": + bridgeList = append(bridgeList, links[i]) + case "vxlan": + vxlanList = append(vxlanList, links[i]) + default: + } + linkList = append(linkList, links[i]) + } +} + +// readLatestNetlinkState reads the latest netlink state +func readLatestNetlinkState() { + vrfs, _ := infradb.GetAllVrfs() + for _, v := range vrfs { + readNeighbors(v) // viswanantha library + readRoutes(v) // Viswantha library + } + m := readFDB() + for i := 0; i < len(m); i++ { + addFdbEntry(m[i]) + } + dumpDBs() +} + +// dumpDBs dumps the databse +func dumpDBs() { + file, err := os.OpenFile("netlink_dump", os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + panic(err) + } + if err := os.Truncate("netlink_dump", 0); err != nil { + log.Printf("netlink: Failed to truncate: %v", err) + } + str := dumpRouteDB() + log.Printf("\n") + str += dumpNexthDB() + log.Printf("\n") + str += dumpNeighDB() + log.Printf("\n") + str += dumpFDB() + log.Printf("\n") + str += dumpL2NexthDB() + _, err = file.WriteString(str) + if err != nil { + log.Printf("netlink: %v", err) + } + err = file.Close() + if err != nil { + log.Printf("netlink: error closing file: %v", err) + } +} + +// NeighStruct structure +type NeighStruct struct { + Neigh0 vn.Neigh + Protocol string + VrfName string + Type int + Dev string + Err error + Key NeighKey + Metadata map[interface{}]interface{} +} + +// NeighList structure +type NeighList struct { + NS []NeighStruct +} + +// nolint +func neighborAnnotate(neighbor NeighStruct) NeighStruct { + neighbor.Metadata = make(map[interface{}]interface{}) + if strings.HasPrefix(neighbor.Dev, path.Base(neighbor.VrfName)) && neighbor.Protocol != zebraStr { + pattern := fmt.Sprintf(`%s-\d+$`, path.Base(neighbor.VrfName)) + mustcompile := regexp.MustCompile(pattern) + s := mustcompile.FindStringSubmatch(neighbor.Dev) + var LB *infradb.LogicalBridge + var BP *infradb.BridgePort + vID := strings.Split(s[0], "-")[1] + lbs, _ := infradb.GetAllLBs() + vlanID, err := strconv.ParseUint(vID,10,32) + if err != nil { + panic(err) + } + for _, lb := range lbs { + if lb.Spec.VlanID == uint32(vlanID) { + LB = lb + break + } + } + if !(reflect.ValueOf(LB).IsZero()) { + bp := LB.MacTable[neighbor.Neigh0.HardwareAddr.String()] + if bp != "" { + BP, _ = infradb.GetBP(bp) + } + } + if !(reflect.ValueOf(BP).IsZero()) { + neighbor.Type = SVI + neighbor.Metadata["vport_id"] = BP.Metadata.VPort + neighbor.Metadata["vlanID"] = vlanID + neighbor.Metadata["portType"] = BP.Spec.Ptype + } else { + neighbor.Type = None + } + } else if strings.HasPrefix(neighbor.Dev, path.Base(neighbor.VrfName)) && neighbor.Protocol == zebraStr { + pattern := fmt.Sprintf(`%s-\d+$`, path.Base(neighbor.VrfName)) + mustcompile := regexp.MustCompile(pattern) + s := mustcompile.FindStringSubmatch(neighbor.Dev) + var LB *infradb.LogicalBridge + vID := strings.Split(s[0], "-")[1] + lbs, _ := infradb.GetAllLBs() + vlanID, err := strconv.ParseUint(vID,10,32) + if err != nil { + panic(err) + } + for _, lb := range lbs { + if lb.Spec.VlanID == uint32(vlanID) { + LB = lb + break + } + } + if LB.Spec.Vni != nil { + vid, err := strconv.ParseInt(vID, 10, 64) + if err != nil { + panic(err) + } + fdbEntry := LatestFDB[FDBKey{int(vid), neighbor.Neigh0.HardwareAddr.String()}] + neighbor.Metadata["l2_nh"] = fdbEntry.Nexthop + neighbor.Type = VXLAN // confirm this later + } + } else if path.Base(neighbor.VrfName) == "GRD" && neighbor.Protocol != zebraStr { + VRF, _ := infradb.GetVrf("//network.opiproject.org/vrfs/GRD") + r := lookupRoute(neighbor.Neigh0.IP, VRF) + if !(reflect.ValueOf(r).IsZero()) { + if r.Nexthops[0].nexthop.LinkIndex == neighbor.Neigh0.LinkIndex { + neighbor.Type = PHY + neighbor.Metadata["vport_id"] = phyPorts[NameIndex[neighbor.Neigh0.LinkIndex]] + } else { + neighbor.Type = None + } + } else { + neighbor.Type = None + } + } + return neighbor +} + +// CheckNdup checks the duplication of neighbor +func CheckNdup(tmpKey NeighKey) bool { + var dup = false + for k := range LatestNeighbors { + if k == tmpKey { + dup = true + break + } + } + return dup +} + +// CheckRdup checks the duplication of routes +func CheckRdup(tmpKey RouteKey) bool { + var dup = false + for j := range LatestRoutes { + if j == tmpKey { + dup = true + break + } + } + return dup +} + +// addNeigh adds the neigh +func addNeigh(dump NeighList) { + for _, n := range dump.NS { + n = neighborAnnotate(n) + if len(LatestNeighbors) == 0 { + LatestNeighbors[n.Key] = n + } else if !CheckNdup(n.Key) { + LatestNeighbors[n.Key] = n + } + } +} + +// getStateStr gets the state from int +func getStateStr(s int) string { + neighState := map[int]string{ + vn.NUD_NONE: "NONE", + vn.NUD_INCOMPLETE: "INCOMPLETE", + vn.NUD_REACHABLE: "REACHABLE", + vn.NUD_STALE: "STALE", + vn.NUD_DELAY: "DELAY", + vn.NUD_PROBE: "PROBE", + vn.NUD_FAILED: "FAILED", + vn.NUD_NOARP: "NOARP", + vn.NUD_PERMANENT: "PERMANENT", + } + return neighState[s] +} + +func printNeigh(neigh *NeighStruct) string { + var Proto string + if neigh == nil { + return strNone + } + if neigh.Protocol == "" { + Proto = strNone + } else { + Proto = neigh.Protocol + } + str := fmt.Sprintf("Neighbor(vrf=%s dst=%s lladdr=%s dev=%s proto=%s state=%s) ", neigh.VrfName, neigh.Neigh0.IP.String(), neigh.Neigh0.HardwareAddr.String(), NameIndex[neigh.Neigh0.LinkIndex], Proto, getStateStr(neigh.Neigh0.State)) + return str +} + +// dumpRouteDB dump the route database +func dumpRouteDB() string { + var s string + log.Printf("netlink: Dump Route table:\n") + s = "Route table:\n" + for _, n := range LatestRoutes { + var via string + if n.Route0.Gw.String() == "" { + via = strNone + } else { + via = n.Route0.Gw.String() + } + str := fmt.Sprintf("Route(vrf=%s dst=%s type=%s proto=%s metric=%d via=%s dev=%s nhid= %d Table= %d)", n.Vrf.Name, n.Route0.Dst.String(), n.NlType, getProto(n), n.Route0.Priority, via, NameIndex[n.Route0.LinkIndex], n.Nexthops[0].ID, n.Route0.Table) + log.Println(str) + s += str + s += "\n" + } + log.Printf("\n\n\n") + s += "\n\n" + return s +} + +// dumpL2NexthDB dump the l2 nexthop entries +func dumpL2NexthDB() string { + var s string + log.Printf("netlink: Dump L2 Nexthop table:\n") + s = "L2 Nexthop table:\n" + var ip string + for _, n := range LatestL2Nexthop { + if n.Dst.String() == "" { + ip = strNone + } else { + ip = n.Dst.String() + } + str := fmt.Sprintf("L2Nexthop(id=%d dev=%s vlan=%d dst=%s type=%d #FDB entries=%d Resolved=%t) ", n.ID, n.Dev, n.VlanID, ip, n.Type, len(n.FdbRefs), n.Resolved) + log.Println(str) + s += str + s += "\n" + } + log.Printf("\n\n\n") + s += "\n\n" + return s +} + +// dumpFDB dump the fdb entries +func dumpFDB() string { + var s string + log.Printf("netlink: Dump FDB table:\n") + s = "FDB table:\n" + for _, n := range LatestFDB { + str := fmt.Sprintf("MacAddr(vlan=%d mac=%s state=%s type=%d l2nh_id=%d) ", n.VlanID, n.Mac, n.State, n.Type, n.Nexthop.ID) + log.Println(str) + s += str + s += "\n" + } + log.Printf("\n\n\n") + s += "\n\n" + return s +} + +// dumpNexthDB dump the nexthop entries +func dumpNexthDB() string { + var s string + log.Printf("netlink: Dump Nexthop table:\n") + s = "Nexthop table:\n" + for _, n := range LatestNexthop { + str := fmt.Sprintf("Nexthop(id=%d vrf=%s dst=%s dev=%s Local=%t weight=%d flags=[%s] #routes=%d Resolved=%t neighbor=%s) ", n.ID, n.Vrf.Name, n.nexthop.Gw.String(), NameIndex[n.nexthop.LinkIndex], n.Local, n.Weight, getFlagString(n.nexthop.Flags), len(n.RouteRefs), n.Resolved, printNeigh(n.Neighbor)) + log.Println(str) + s += str + s += "\n" + } + log.Printf("\n\n\n") + s += "\n\n" + return s +} + +// dumpNeighDB dump the neighbor entries +func dumpNeighDB() string { + var s string + log.Printf("netlink: Dump Neighbor table:\n") + s = "Neighbor table:\n" + for _, n := range LatestNeighbors { + var Proto string + if n.Protocol == "" { + Proto = strNone + } else { + Proto = n.Protocol + } + str := fmt.Sprintf("Neighbor(vrf=%s dst=%s lladdr=%s dev=%s proto=%s state=%s Type : %d) ", n.VrfName, n.Neigh0.IP.String(), n.Neigh0.HardwareAddr.String(), NameIndex[n.Neigh0.LinkIndex], Proto, getStateStr(n.Neigh0.State), n.Type) + log.Println(str) + s += str + s += "\n" + } + s += "\n\n" + return s +} + +// getProto gets the route protocol +func getProto(n RouteStruct) string { + for p, i := range RtnProto { + if i == int(n.Route0.Protocol) { + return p + } + } + return "0" +} + +// checkNeigh checks the nighbor +func checkNeigh(nk NeighKey) bool { + for k := range LatestNeighbors { + if k == nk { + return true + } + } + return false +} + +// tryResolve resolves the neighbor +func tryResolve(nexhthopSt NexthopStruct) NexthopStruct { + if len(nexhthopSt.nexthop.Gw) != 0 { + // Nexthops with a gateway IP need resolution of that IP + neighborKey := NeighKey{Dst: nexhthopSt.nexthop.Gw.String(), VrfName: nexhthopSt.Vrf.Name, Dev: nexhthopSt.nexthop.LinkIndex} + ch := checkNeigh(neighborKey) + if ch && LatestNeighbors[neighborKey].Neigh0.Type != 0 { + nexhthopSt.Resolved = true + nh := LatestNeighbors[neighborKey] + nexhthopSt.Neighbor = &nh + } else { + nexhthopSt.Resolved = false + } + } else { + nexhthopSt.Resolved = true + } + return nexhthopSt +} + +// checkNhDB checks the neighbor database +func checkNhDB(nhKey NexthopKey) bool { + for k := range LatestNexthop { + if k == nhKey { + return true + } + } + return false +} + +// addNexthop adds the nexthop +func addNexthop(nexthop NexthopStruct, r RouteStruct) RouteStruct { + ch := checkNhDB(nexthop.Key) + if ch { + NH0 := LatestNexthop[nexthop.Key] + // Links route with existing nexthop + NH0.RouteRefs = append(NH0.RouteRefs, r) + r.Nexthops = append(r.Nexthops, NH0) + } else { + // Create a new nexthop entry + nexthop.RouteRefs = append(nexthop.RouteRefs, r) + nexthop.ID = NHAssignID(nexthop.Key) + nexthop = tryResolve(nexthop) + LatestNexthop[nexthop.Key] = nexthop + r.Nexthops = append(r.Nexthops, nexthop) + } + return r +} + +// checkRoute checks the route +func checkRoute(r RouteStruct) bool { + Rk := r.Key + for k := range LatestRoutes { + if k == Rk { + return true + } + } + return false +} + +// deleteNH deletes the neighbor +func deleteNH(nexthop []NexthopStruct) []NexthopStruct { + index := len(nexthop) + if index == 1 { + nexthop = append(nexthop[:0], nexthop[1:]...) + } else { + for i := 0; i < index-1; i++ { + nexthop = append(nexthop[:0], nexthop[1:]...) + } + } + return nexthop +} + +// addRoute add the route +func addRoute(r RouteStruct) { + ch := checkRoute(r) + if ch { + R0 := LatestRoutes[r.Key] + if r.Route0.Priority >= R0.Route0.Priority { + // Route with lower metric exists and takes precedence + log.Printf("netlink: Ignoring %+v with higher metric than %+v\n", r, R0) + } else { + log.Printf("netlink: conflicts %+v with higher metric %+v. Will ignore it", r, R0) + } + } else { + Nexthops := r.Nexthops + r.Nexthops = deleteNH(r.Nexthops) + for _, nexthop := range Nexthops { + r = addNexthop(nexthop, r) + } + LatestRoutes[r.Key] = r + } +} + +// cmdProcessNb process the neighbor command +func cmdProcessNb(nb string, v string) NeighList { + var nbs []NeighIPStruct + CPs := strings.Split(nb[2:len(nb)-3], "},{") + for i := 0; i < len(CPs); i++ { + var ni NeighIPStruct + log.Println(CPs[i]) + err := json.Unmarshal([]byte(fmt.Sprintf("{%v}", CPs[i])), &ni) + if err != nil { + log.Println("netlink: error-", err) + } + nbs = append(nbs, ni) + } + Neigh := ParseNeigh(nbs, v) + return Neigh +} + +// getState gets the state for the neighbor +func getState(s string) int { + neighState := map[string]int{ + "NONE": vn.NUD_NONE, + "INCOMPLETE": vn.NUD_INCOMPLETE, + "REACHABLE": vn.NUD_REACHABLE, + "STALE": vn.NUD_STALE, + "DELAY": vn.NUD_DELAY, + "PROBE": vn.NUD_PROBE, + "FAILED": vn.NUD_FAILED, + "NOARP": vn.NUD_NOARP, + "PERMANENT": vn.NUD_PERMANENT, + } + return neighState[s] +} + +// preFilterNeighbor pre filter the neighbors +func preFilterNeighbor(n NeighStruct) bool { + if n.Neigh0.State != vn.NUD_NONE && n.Neigh0.State != vn.NUD_INCOMPLETE && n.Neigh0.State != vn.NUD_FAILED && NameIndex[n.Neigh0.LinkIndex] != "lo" { + return true + } + + return false +} + +// ParseNeigh parses the neigh +func ParseNeigh(nm []NeighIPStruct, v string) NeighList { + var NL NeighList + for _, ND := range nm { + var ns NeighStruct + ns.Neigh0.Type = OTHER + ns.VrfName = v + if !reflect.ValueOf(ND.Dev).IsZero() { + vrf, _ := vn.LinkByName(ND.Dev) + ns.Neigh0.LinkIndex = vrf.Attrs().Index + } + if !reflect.ValueOf(ND.Dst).IsZero() { + ipnet := &net.IPNet{ + IP: net.ParseIP(ND.Dst), + } + ns.Neigh0.IP = ipnet.IP + } + if !reflect.ValueOf(ND.State).IsZero() { + ns.Neigh0.State = getState(ND.State[0]) + } + if !reflect.ValueOf(ND.Lladdr).IsZero() { + ns.Neigh0.HardwareAddr, _ = net.ParseMAC(ND.Lladdr) + } + if !reflect.ValueOf(ND.Protocol).IsZero() { + ns.Protocol = ND.Protocol + } + // ns = neighborAnnotate(ns) /* Need InfraDB to finish for fetching LB/BP information */ + ns.Key = NeighKey{VrfName: v, Dst: ns.Neigh0.IP.String(), Dev: ns.Neigh0.LinkIndex} + if preFilterNeighbor(ns) { + NL.NS = append(NL.NS, ns) + } + } + return NL +} + +// getNeighborRoutes gets the nighbor routes +func getNeighborRoutes() []RouteCmdInfo { // []map[string]string{ + // Return a list of /32 or /128 routes & Nexthops to be inserted into + // the routing tables for Resolved neighbors on connected subnets + // on physical and SVI interfaces. + var neighborRoutes []RouteCmdInfo // []map[string]string + for _, N := range LatestNeighbors { + if (NameIndex[N.Neigh0.LinkIndex] == "enp0s1f0d1" || NameIndex[N.Neigh0.LinkIndex] == "enp0s1f0d3") && N.Neigh0.State == vn.NUD_REACHABLE { + vrf, _ := infradb.GetVrf(N.VrfName) + table := int(*vrf.Metadata.RoutingTable[0]) + + //# Create a special route with dst == gateway to resolve + //# the nexthop to the existing neighbor + R0 := RouteCmdInfo{Type: "neighbor", Dst: N.Neigh0.IP.String(), Protocol: "ipu_infra_mgr", Scope: "global", Gateway: N.Neigh0.IP.String(), Dev: NameIndex[N.Neigh0.LinkIndex], VRF: vrf, Table: table} + neighborRoutes = append(neighborRoutes, R0) + } + } + return neighborRoutes +} + +// readNeighbors reads the nighbors +func readNeighbors(v *infradb.Vrf) { + var N NeighList + var err error + var Nb string + if v.Spec.Vni == nil { + /* No support for "ip neighbor show" command in netlink library Raised ticket https://github.com/vishvananda/netlink/issues/913 , + so using ip command as WA */ + Nb, err = nlink.ReadNeigh(ctx, "") + } else { + Nb, err = nlink.ReadNeigh(ctx, path.Base(v.Name)) + } + if len(Nb) != 3 && err == nil { + N = cmdProcessNb(Nb, v.Name) + } + addNeigh(N) +} + +// NHRouteInfo neighbor route info +type NHRouteInfo struct { + ID int + Gateway string + Dev string + Scope string + Protocol string + Flags []string +} + +// RouteCmdInfo structure +type RouteCmdInfo struct { + Type string + Dst string + Nhid int + Gateway string + Dev string + Protocol string + Scope string + Prefsrc string + Metric int + Flags []string + Weight int + VRF *infradb.Vrf + Table int + NhInfo NHRouteInfo // {id gateway Dev scope protocol flags} +} + +// preFilterMac filter the mac +func preFilterMac(f FdbEntryStruct) bool { + // TODO m.nexthop.dst + if f.VlanID != 0 || !(reflect.ValueOf(f.Nexthop.Dst).IsZero()) { + log.Printf("netlink: %d vlan \n", len(f.Nexthop.Dst.String())) + return true + } + return false +} + +// cmdProcessRt process the route command +func cmdProcessRt(v *infradb.Vrf, r string, t int) RouteList { + var RouteData []RouteCmdInfo + if len(r) <= 3 { + log.Println("netlink: Error in the cmd:", r) + var route RouteList + return route + } + CPs := strings.Split(r[2:len(r)-3], "},{") + for i := 0; i < len(CPs); i++ { + var ri RouteCmdInfo + log.Println(CPs[i]) + err := json.Unmarshal([]byte(fmt.Sprintf("{%v}", CPs[i])), &ri) + if err != nil { + log.Println("error-", err) + } + RouteData = append(RouteData, ri) + } + route := Parse_Route(v, RouteData, t) + return route +} + +// readRouteFromIP reads the routes from ip +func readRouteFromIP(v *infradb.Vrf) { + var Rl RouteList + var rm []RouteCmdInfo + var Rt1 int + for _, routeSt := range v.Metadata.RoutingTable { + Rt1 = int(*routeSt) + Raw, err := nlink.ReadRoute(ctx, strconv.Itoa(Rt1)) + if err != nil { + log.Printf("netlink: Err Command route\n") + return + } + Rl = cmdProcessRt(v, Raw, Rt1) + for _, r := range Rl.RS { + addRoute(r) + } + } + nl := getNeighborRoutes() // Add extra routes for Resolved neighbors on connected subnets + for i := 0; i < len(nl); i++ { + rm = append(rm, nl[i]) + } + nr := Parse_Route(v, rm, 0) + for _, r := range nr.RS { + addRoute(r) + } +} + +// readRoutes reads the routes +func readRoutes(v *infradb.Vrf) { + readRouteFromIP(v) +} + +func notifyAddDel(r interface{}, event string) { + log.Printf("netlink: Notify event: %s\n", event) + EventBus.Publish(event, r) +} + +// notifyEvents array +var notifyEvents = []string{"_added", "_updated", "_deleted"} + +//nolint +func notify_changes(new_db map[interface{}]interface{}, old_db map[interface{}]interface{}, event []string) { + DB2 := old_db + DB1 := new_db + /* Checking the Updated entries in the netlink db by comparing the individual keys and their corresponding values in old and new db copies + entries with same keys with different values and send the notification to vendor specific module */ + for k1, v1 := range DB1 { + for k2, v2 := range DB2 { + if k1 == k2 { + if !reflect.DeepEqual(v1, v2) { + // To Avoid in-correct update notification due to race condition in which metadata is nil in new entry and crashing in dcgw module + if strings.Contains(event[1], "route") || strings.HasPrefix(event[1], "nexthop") { + var Rv RouteStruct + var Nv NexthopStruct + if strings.Contains(event[1], "route") { + Rv = v1.(RouteStruct) + if Rv.Vrf.Status.VrfOperStatus == infradb.VrfOperStatusToBeDeleted { + notifyAddDel(Rv, event[2]) + delete(new_db, k1) + delete(old_db, k2) + break + } + } else if strings.Contains(event[1], "nexthop") { + Nv = v1.(NexthopStruct) + if Nv.Vrf.Status.VrfOperStatus == infradb.VrfOperStatusToBeDeleted { + notifyAddDel(Nv, event[2]) + delete(new_db, k1) + delete(old_db, k2) + break + } + } + } + notifyAddDel(v1, event[1]) + } + delete(new_db, k1) + delete(old_db, k2) + break + } + } + } + for _, r := range new_db { // Added entries notification cases + notifyAddDel(r, event[0]) + } + for _, r := range old_db { // Deleted entires notification cases + notifyAddDel(r, event[2]) + } +} + +// readFDB read the fdb from db +func readFDB() []FdbEntryStruct { + var fdbs []FdbIPStruct + var macs []FdbEntryStruct + var fs FdbEntryStruct + + CP, err := nlink.ReadFDB(ctx) + if err != nil || len(CP) == 3 { + return macs + } + + CPs := strings.Split(CP[2:len(CP)-3], "},{") + for i := 0; i < len(CPs); i++ { + var fi FdbIPStruct + err := json.Unmarshal([]byte(fmt.Sprintf("{%v}", CPs[i])), &fi) + if err != nil { + log.Printf("netlink: error-%v", err) + } + fdbs = append(fdbs, fi) + } + for _, m := range fdbs { + fs = ParseFdb(m, fs) + if preFilterMac(fs) { + macs = append(macs, fs) + } + } + return macs +} + +// lookupRoute check the routes +func lookupRoute(dst net.IP, v *infradb.Vrf) RouteStruct { + // FIXME: If the semantic is to return the current entry of the NetlinkDB + // routing table, a direct lookup in Linux should only be done as fallback + // if there is no match in the DB. + var CP string + var err error + if v.Spec.Vni != nil { + CP, err = nlink.RouteLookup(ctx, dst.String(), path.Base(v.Name)) + } else { + CP, err = nlink.RouteLookup(ctx, dst.String(), "") + } + if err != nil { + log.Fatal("netlink : Command error \n", err) + return RouteStruct{} + } + r := cmdProcessRt(v, CP, int(*v.Metadata.RoutingTable[0])) + log.Printf("netlink: %+v\n", r) + if len(r.RS) != 0 { + R1 := r.RS[0] + // ### Search the LatestRoutes DB snapshot if that exists, else + // ### the current DB Route table. + var RouteTable map[RouteKey]RouteStruct + if len(LatestRoutes) != 0 { + RouteTable = LatestRoutes + } else { + RouteTable = Routes + } + RDB := RouteTable[R1.Key] + if !reflect.ValueOf(RDB).IsZero() { + // Return the existing route in the DB + return RDB + } + // Return the just constructed non-DB route + return R1 + } + + log.Printf("netlink: Failed to lookup route %v in VRF %v", dst, v) + return RouteStruct{} +} + +//nolint +func (nexthop NexthopStruct) annotate() NexthopStruct { + nexthop.Metadata = make(map[interface{}]interface{}) + var phyFlag bool + phyFlag = false + for k := range phyPorts { + if NameIndex[nexthop.nexthop.LinkIndex] == k { + phyFlag = true + } + } + if (!reflect.ValueOf(nexthop.nexthop.Gw).IsZero()) && nexthop.nexthop.LinkIndex != 0 && strings.HasPrefix(NameIndex[nexthop.nexthop.LinkIndex], path.Base(nexthop.Vrf.Name)+"-") && !nexthop.Local { + nexthop.NhType = SVI + link, _ := vn.LinkByName(NameIndex[nexthop.nexthop.LinkIndex]) + if !reflect.ValueOf(nexthop.Neighbor).IsZero() { + if nexthop.Neighbor.Type == SVI { + nexthop.NhType = SVI + nexthop.Metadata["direction"] = RX + nexthop.Metadata["smac"] = link.Attrs().HardwareAddr.String() + nexthop.Metadata["dmac"] = nexthop.Neighbor.Neigh0.HardwareAddr.String() + nexthop.Metadata["egress_vport"] = nexthop.Neighbor.Metadata["vport_id"] + nexthop.Metadata["vlanID"] = nexthop.Neighbor.Metadata["vlanID"] + nexthop.Metadata["portType"] = nexthop.Neighbor.Metadata["portType"] + } else if nexthop.Neighbor.Type == VXLAN { + nexthop.NhType = VXLAN + nexthop.Metadata["direction"] = TX + nexthop.Metadata["inner_dmac"] = nexthop.Neighbor.Neigh0.HardwareAddr.String() + nexthop.Metadata["inner_smac"] = link.Attrs().HardwareAddr.String() + L2N := nexthop.Neighbor.Metadata["l2_nh"].(L2NexthopStruct) + if L2N.Resolved { + nexthop.Metadata["local_vtep_ip"] = L2N.Metadata["local_vtep_ip"] + nexthop.Metadata["remote_vtep_ip"] = L2N.Metadata["remote_vtep_ip"] + nexthop.Metadata["vni"] = L2N.Metadata["vni"] + nexthop.Metadata["phy_smac"] = L2N.Metadata["phy_smac"] + nexthop.Metadata["phy_dmac"] = L2N.Metadata["phy_dmac"] + nexthop.Metadata["egress_vport"] = L2N.Metadata["egress_vport"] + } else { + nexthop.Resolved = false + } + } else { + nexthop.Resolved = false + log.Printf("netlink: Failed to gather data for nexthop on physical port\n") + } + } + } else if (!reflect.ValueOf(nexthop.nexthop.Gw).IsZero()) && phyFlag && !nexthop.Local { + nexthop.NhType = PHY + link1, _ := vn.LinkByName(NameIndex[nexthop.nexthop.LinkIndex]) + if link1 == nil { + return nexthop + } + nexthop.Metadata["direction"] = TX + nexthop.Metadata["smac"] = link1.Attrs().HardwareAddr.String() + nexthop.Metadata["egress_vport"] = phyPorts[nexthop.nexthop.Gw.String()] + if !reflect.ValueOf(nexthop.Neighbor).IsZero() { + if nexthop.Neighbor.Type == PHY { + nexthop.Metadata["dmac"] = nexthop.Neighbor.Neigh0.HardwareAddr.String() + } + } else { + nexthop.Resolved = false + log.Printf("netlink: Failed to gather data for nexthop on physical port") + } + } else if (!reflect.ValueOf(nexthop.nexthop.Gw).IsZero()) && NameIndex[nexthop.nexthop.LinkIndex] == fmt.Sprintf("br-%s", path.Base(nexthop.Vrf.Name)) && !nexthop.Local { + nexthop.NhType = VXLAN + G, _ := infradb.GetVrf(nexthop.Vrf.Name) + var detail map[string]interface{} + var Rmac net.HardwareAddr + for _, com := range G.Status.Components { + if com.Name == "frr" { + err := json.Unmarshal([]byte(com.Details), &detail) + if err != nil { + log.Printf("netlink: Error: %v", err) + } + rmac, found := detail["rmac"].(string) + if !found { + log.Printf("netlink: Key 'rmac' not found") + break + } + Rmac, err = net.ParseMAC(rmac) + if err != nil { + log.Printf("netlink: Error parsing MAC address: %v", err) + } + } + } + nexthop.Metadata["direction"] = TX + nexthop.Metadata["inner_smac"] = Rmac.String() + if reflect.ValueOf(Rmac).IsZero() { + nexthop.Resolved = false + } + vtepip := G.Spec.VtepIP.IP + nexthop.Metadata["local_vtep_ip"] = vtepip.String() + nexthop.Metadata["remote_vtep_ip"] = nexthop.nexthop.Gw.String() + nexthop.Metadata["vni"] = *nexthop.Vrf.Spec.Vni + if !reflect.ValueOf(nexthop.Neighbor).IsZero() { + nexthop.Metadata["inner_dmac"] = nexthop.Neighbor.Neigh0.HardwareAddr.String() + G, _ := infradb.GetVrf("//network.opiproject.org/vrfs/GRD") + r := lookupRoute(nexthop.nexthop.Gw, G) + if !reflect.ValueOf(r).IsZero() { + // For now pick the first physical nexthop (no ECMP yet) + phyNh := r.Nexthops[0] + link, _ := vn.LinkByName(NameIndex[phyNh.nexthop.LinkIndex]) + nexthop.Metadata["phy_smac"] = link.Attrs().HardwareAddr.String() + nexthop.Metadata["egress_vport"] = phyPorts[NameIndex[phyNh.nexthop.LinkIndex]] + if !reflect.ValueOf(phyNh.Neighbor).IsZero() { + nexthop.Metadata["phy_dmac"] = phyNh.Neighbor.Neigh0.HardwareAddr.String() + } else { + // The VXLAN nexthop can only be installed when the phy_nexthops are Resolved. + nexthop.Resolved = false + } + } + } else { + nexthop.Resolved = false + } + } else { + nexthop.NhType = ACC + link1, err := vn.LinkByName("rep-" + path.Base(nexthop.Vrf.Name)) + if err != nil { + log.Printf("netlink: Error in getting rep information: %v\n", err) + } + if link1 == nil { + return nexthop + } + nexthop.Metadata["direction"] = RX + nexthop.Metadata["dmac"] = link1.Attrs().HardwareAddr.String() + nexthop.Metadata["egress_vport"] = (int((link1.Attrs().HardwareAddr)[0]) << 8) + int((link1.Attrs().HardwareAddr)[1]) + if reflect.ValueOf(nexthop.Vrf.Spec.Vni).IsZero() { + nexthop.Metadata["vlanID"] = uint32(4089) + } else { + nexthop.Metadata["vlanID"] = *nexthop.Vrf.Metadata.RoutingTable[0]//*nexthop.Vrf.Spec.Vni + } + } + return nexthop +} + +//nolint +func (l2n L2NexthopStruct) annotate() L2NexthopStruct { + // Annotate certain L2 Nexthops with additional information from LB and GRD + l2n.Metadata = make(map[interface{}]interface{}) + LB := l2n.lb + if !(reflect.ValueOf(LB).IsZero()) { + if l2n.Type == SVI { + l2n.Metadata["vrf_id"] = *LB.Spec.Vni + } else if l2n.Type == VXLAN { + //# Remote EVPN MAC address learned on the VXLAN interface + //# The L2 nexthop must have a destination IP address in dst + l2n.Resolved = false + l2n.Metadata["local_vtep_ip"] = *LB.Spec.VtepIP + l2n.Metadata["remote_vtep_ip"] = l2n.Dst + l2n.Metadata["vni"] = *LB.Spec.Vni + //# The below physical nexthops are needed to transmit the VXLAN-encapsuleted packets + //# directly from the nexthop table to a physical port (and avoid another recirculation + //# for route lookup in the GRD table.) + VRF, _ := infradb.GetVrf("//network.opiproject.org/vrfs/GRD") + r := lookupRoute(l2n.Dst, VRF) + if !reflect.ValueOf(r).IsZero() { + // # For now pick the first physical nexthop (no ECMP yet) + phyNh := r.Nexthops[0] + link, _ := vn.LinkByName(NameIndex[phyNh.nexthop.LinkIndex]) + l2n.Metadata["phy_smac"] = link.Attrs().HardwareAddr.String() + l2n.Metadata["egress_vport"] = phyPorts[NameIndex[phyNh.nexthop.LinkIndex]] + if !reflect.ValueOf(phyNh.Neighbor).IsZero() { + if phyNh.Neighbor.Type == PHY { + l2n.Metadata["phy_dmac"] = phyNh.Neighbor.Neigh0.HardwareAddr.String() + l2n.Resolved = true + } else { + log.Printf("netlink: Error: Neighbor type not PHY\n") + } + } + } + } else if l2n.Type == BRIDGEPORT { + // BridgePort as L2 nexthop + l2n.Metadata["vport_id"] = l2n.bp.Metadata.VPort + l2n.Metadata["portType"] = l2n.bp.Spec.Ptype + } + } + return l2n +} + +// annotate the route +func (fdb FdbEntryStruct) annotate() FdbEntryStruct { + if fdb.VlanID == 0 { + return fdb + } + if reflect.ValueOf(fdb.lb).IsZero() { + return fdb + } + + fdb.Metadata = make(map[interface{}]interface{}) + l2n := fdb.Nexthop + if !reflect.ValueOf(l2n).IsZero() { + fdb.Metadata["nh_id"] = l2n.ID + if l2n.Type == VXLAN { + fdbEntry := LatestFDB[FDBKey{None, fdb.Mac}] + l2n.Dst = fdbEntry.Nexthop.Dst + } + switch l2n.Type { + case VXLAN: + fdb.Metadata["direction"] = TX + case BRIDGEPORT, SVI: + fdb.Metadata["direction"] = RXTX + + default: + fdb.Metadata["direction"] = None + } + } + return fdb +} + +// annotateDBEntries annonates the database entries +func annotateDBEntries() { + for _, nexthop := range LatestNexthop { + nexthop = nexthop.annotate() + LatestNexthop[nexthop.Key] = nexthop + } + for _, r := range LatestRoutes { + r = r.annotate() + LatestRoutes[r.Key] = r + } + + for _, m := range LatestFDB { + m = m.annotate() + LatestFDB[m.Key] = m + } + for _, l2n := range LatestL2Nexthop { + l2n = l2n.annotate() + LatestL2Nexthop[l2n.Key] = l2n + } +} + +// installFilterRoute install the route filter +func installFilterRoute(routeSt *RouteStruct) bool { + var nh []NexthopStruct + for _, n := range routeSt.Nexthops { + if n.Resolved { + nh = append(nh, n) + } + } + routeSt.Nexthops = nh + keep := checkRtype(routeSt.NlType) && len(nh) != 0 && strings.Compare(routeSt.Route0.Dst.IP.String(), "0.0.0.0") != 0 + return keep +} + +// checkNhType checks the nighbor type +func checkNhType(nType int) bool { + ntype := []int{PHY, SVI, ACC, VXLAN} + for _, i := range ntype { + if i == nType { + return true + } + } + return false +} + +// installFilterNH install the neighbor filter +func installFilterNH(nh NexthopStruct) bool { + check := checkNhType(nh.NhType) + keep := check && nh.Resolved && len(nh.RouteRefs) != 0 + return keep +} + +func checkFdbType(fdbtype int) bool { + var portType = []int{BRIDGEPORT, VXLAN} + for _, port := range portType { + if port == fdbtype { + return true + } + } + return false +} + +// installFilterFDB install fdb filer +func installFilterFDB(fdb FdbEntryStruct) bool { + // Drop entries w/o VLAN ID or associated LogicalBridge ... + // ... other than with L2 nexthops of type VXLAN and BridgePort ... + // ... and VXLAN entries with unresolved underlay nextop. + keep := !reflect.ValueOf(fdb.VlanID).IsZero() && !reflect.ValueOf(fdb.lb).IsZero() && checkFdbType(fdb.Type) && fdb.Nexthop.Resolved + if !keep { + log.Printf("netlink: install_filter: dropping {%v}", fdb) + } + return keep +} + +// installFilterL2N install the l2 filter +func installFilterL2N(l2n L2NexthopStruct) bool { + keep := !(reflect.ValueOf(l2n.Type).IsZero() && l2n.Resolved && reflect.ValueOf(l2n.FdbRefs).IsZero()) + if !keep { + log.Printf("netlink: install_filter FDB: dropping {%+v}", l2n) + } + return keep +} + +//nolint +func applyInstallFilters() { + for K, r := range LatestRoutes { + if !installFilterRoute(&r) { + // Remove route from its nexthop(s) + delete(LatestRoutes, K) + } + } + + for k, nexthop := range LatestNexthop { + if !installFilterNH(nexthop) { + delete(LatestNexthop, k) + } + } + + for k, m := range LatestFDB { + if !installFilterFDB(m) { + delete(LatestFDB, k) + } + } + for k, L2 := range LatestL2Nexthop { + if !installFilterL2N(L2) { + delete(LatestL2Nexthop, k) + } + } +} + +// oldgenmap old map +var oldgenmap = make(map[interface{}]interface{}) + +// latestgenmap latest map +var latestgenmap = make(map[interface{}]interface{}) + +// notifyDBChanges notify the database changes +func notifyDBChanges() { + var routeEventStr = make([]string, 0) + var nexthopEventStr = make([]string, 0) + var fdbEventStr = make([]string, 0) + var l2nexthopEventStr = make([]string, 0) + + for _, s := range notifyEvents { + routeEventStr = append(routeEventStr, "route"+s) + nexthopEventStr = append(nexthopEventStr, "nexthop"+s) + fdbEventStr = append(fdbEventStr, "fdb_entry"+s) + l2nexthopEventStr = append(l2nexthopEventStr, "l2_nexthop"+s) + } + type NlDBCopy struct { + RDB map[RouteKey]RouteStruct + NDB map[NexthopKey]NexthopStruct + FBDB map[FDBKey]FdbEntryStruct + L2NDB map[L2NexthopKey]L2NexthopStruct + } + latestdb := NlDBCopy{RDB: LatestRoutes, NDB: LatestNexthop, FBDB: LatestFDB, L2NDB: LatestL2Nexthop} + olddb := NlDBCopy{RDB: Routes, NDB: Nexthops, FBDB: FDB, L2NDB: L2Nexthops} + var eventStr []interface{} + eventStr = append(eventStr, routeEventStr) + eventStr = append(eventStr, nexthopEventStr) + eventStr = append(eventStr, fdbEventStr) + eventStr = append(eventStr, l2nexthopEventStr) + // Routes + oldgenmap = make(map[interface{}]interface{}) + latestgenmap = make(map[interface{}]interface{}) + for k, v := range latestdb.RDB { + latestgenmap[k] = v + } + for k, v := range olddb.RDB { + oldgenmap[k] = v + } + notify_changes(latestgenmap, oldgenmap, eventStr[0].([]string)) + // Nexthops + oldgenmap = make(map[interface{}]interface{}) + latestgenmap = make(map[interface{}]interface{}) + for k, v := range latestdb.NDB { + latestgenmap[k] = v + } + for k, v := range olddb.NDB { + oldgenmap[k] = v + } + notify_changes(latestgenmap, oldgenmap, eventStr[1].([]string)) + // FDB + oldgenmap = make(map[interface{}]interface{}) + latestgenmap = make(map[interface{}]interface{}) + for k, v := range latestdb.FBDB { + latestgenmap[k] = v + } + for k, v := range olddb.FBDB { + oldgenmap[k] = v + } + notify_changes(latestgenmap, oldgenmap, eventStr[2].([]string)) + // L2Nexthop + oldgenmap = make(map[interface{}]interface{}) + latestgenmap = make(map[interface{}]interface{}) + for k, v := range latestdb.L2NDB { + latestgenmap[k] = v + } + for k, v := range olddb.L2NDB { + oldgenmap[k] = v + } + notify_changes(latestgenmap, oldgenmap, eventStr[3].([]string)) +} + +// resyncWithKernel fun resyncs with kernal db +func resyncWithKernel() { + // Build a new DB snapshot from netlink and other sources + readLatestNetlinkState() + // Annotate the latest DB entries + annotateDBEntries() + // Filter the latest DB to retain only entries to be installed + applyInstallFilters() + // Compute changes between current and latest DB versions and inform subscribers about the changes + notifyDBChanges() + Routes = LatestRoutes + Nexthops = LatestNexthop + Neighbors = LatestNeighbors + FDB = LatestFDB + L2Nexthops = LatestL2Nexthop + DeleteLatestDB() +} + +// DeleteLatestDB deletes the latest db snap +func DeleteLatestDB() { + LatestRoutes = make(map[RouteKey]RouteStruct) + LatestNeighbors = make(map[NeighKey]NeighStruct) + LatestNexthop = make(map[NexthopKey]NexthopStruct) + LatestFDB = make(map[FDBKey]FdbEntryStruct) + LatestL2Nexthop = make(map[L2NexthopKey]L2NexthopStruct) +} + +// monitorNetlink moniters the netlink +func monitorNetlink(_ bool) { + for !stopMonitoring { + log.Printf("netlink: Polling netlink databases.") + resyncWithKernel() + log.Printf("netlink: Polling netlink databases completed.") + time.Sleep(time.Duration(pollInterval) * time.Second) + } + log.Printf("netlink: Stopped periodic polling. Waiting for Infra DB cleanup to finish") + time.Sleep(2 * time.Second) + log.Printf("netlink: One final netlink poll to identify what's still left.") + resyncWithKernel() + // Inform subscribers to delete configuration for any still remaining Netlink DB objects. + log.Printf("netlink: Delete any residual objects in DB") + for _, r := range Routes { + notifyAddDel(r, "route_deleted") + } + for _, nexthop := range Nexthops { + notifyAddDel(nexthop, "nexthop_deleted") + } + for _, m := range FDB { + notifyAddDel(m, "FDB_entry_deleted") + } + log.Printf("netlink: DB cleanup completed.") +} + +// Init function intializes config +func Init() { + pollInterval = config.GlobalConfig.Netlink.PollInterval + log.Printf("netlink: poll interval: %v", pollInterval) + nlEnabled := config.GlobalConfig.Netlink.Enabled + if !nlEnabled { + log.Printf("netlink: netlink_monitor disabled") + return + } + for i := 0; i < len(config.GlobalConfig.Netlink.PhyPorts); i++ { + phyPorts[config.GlobalConfig.Netlink.PhyPorts[i].Name] = config.GlobalConfig.Netlink.PhyPorts[i].Vsi + } + getlink() + ctx = context.Background() + nlink = utils.NewNetlinkWrapper() + go monitorNetlink(config.GlobalConfig.P4.Enabled) // monitor Thread started +} diff --git a/pkg/utils/helpers.go b/pkg/utils/helpers.go index 1a9a5950..07eaf337 100644 --- a/pkg/utils/helpers.go +++ b/pkg/utils/helpers.go @@ -5,8 +5,11 @@ package utils import ( + "log" + "net" "regexp" + "github.com/vishvananda/netlink" "go.einride.tech/aip/fieldmask" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/fieldmaskpb" @@ -28,3 +31,33 @@ func ValidateMacAddress(b []byte) error { } return nil } + +// GetIPAddress gets the ip address from link +func GetIPAddress(dev string) net.IPNet { + link, err := netlink.LinkByName(dev) + if err != nil { + log.Printf("Error in LinkByName %+v\n", err) + return net.IPNet{ + IP: net.ParseIP("0.0.0.0"), + } + } + + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) // ip address show + if err != nil { + log.Printf("Error in AddrList\n") + return net.IPNet{ + IP: net.ParseIP("0.0.0.0"), + } + } + var address = &net.IPNet{ + IP: net.IPv4(127, 0, 0, 0), + Mask: net.CIDRMask(8, 32)} + var addr = &netlink.Addr{IPNet: address} + var validIps []netlink.Addr + for index := 0; index < len(addrs); index++ { + if !addr.Equal(addrs[index]) { + validIps = append(validIps, addrs[index]) + } + } + return *validIps[0].IPNet +} diff --git a/pkg/vendor_plugins/intel-e2000/p4runtime/p4driverapi/p4ctl.go b/pkg/vendor_plugins/intel-e2000/p4runtime/p4driverapi/p4ctl.go new file mode 100644 index 00000000..d4ee046f --- /dev/null +++ b/pkg/vendor_plugins/intel-e2000/p4runtime/p4driverapi/p4ctl.go @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries. +// Copyright (C) 2023 Nordix Foundation. + +// Package p4driverapi handles p4 driver realted functionality +package p4driverapi + +import ( + "bytes" + "context" + "encoding/binary" + + // "encoding/hex" + "fmt" + "net" + + // "strings" + "log" + "time" + + logr "github.com/sirupsen/logrus" + "google.golang.org/grpc" + + p4_v1 "github.com/p4lang/p4runtime/go/p4/v1" + + "github.com/antoninbas/p4runtime-go-client/pkg/client" + "github.com/antoninbas/p4runtime-go-client/pkg/signals" +) + +const ( + defaultDeviceID = 1 + lpmStr = "lpm" + ternaryStr = "ternary" +) + +var ( + // Ctx var of type context + Ctx context.Context + + // P4RtC var of \p4 runtime client + P4RtC *client.Client +) + +// TableEntry p4 table entry type +type TableEntry struct { + Tablename string + TableField + Action +} + +// Action p4 table action type +type Action struct { + ActionName string + Params []interface{} +} + +// TableField p4 table field type +type TableField struct { + FieldValue map[string][2]interface{} + Priority int32 +} + +// uint16toBytes convert uint16 to bytes +func uint16toBytes(val uint16) []byte { + return []byte{byte(val >> 8), byte(val)} +} + +// boolToBytes convert bool to bytes +func boolToBytes(val bool) []byte { + if val { + return []byte{1} + } + return []byte{0} +} + +// uint32toBytes convert uint32 to bytes +func uint32toBytes(num uint32) []byte { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, num) + return bytes +} + +// Buildmfs builds the match fields +func Buildmfs(tablefield TableField) (map[string]client.MatchInterface, bool, error) { + var isTernary bool + isTernary = false + mfs := map[string]client.MatchInterface{} + for key, value := range tablefield.FieldValue { + switch v := value[0].(type) { + case net.HardwareAddr: + mfs[key] = &client.ExactMatch{Value: value[0].(net.HardwareAddr)} + case uint16: + // if value[1].(string) == lpmStr { + switch value[1].(string) { + case lpmStr: + mfs[key] = &client.LpmMatch{Value: uint16toBytes(value[0].(uint16)), PLen: 31} + // } else if value[1].(string) == ternaryStr { + case ternaryStr: + isTernary = true + mfs[key] = &client.TernaryMatch{Value: uint16toBytes(value[0].(uint16)), Mask: uint32toBytes(4294967295)} + // } else { + default: + mfs[key] = &client.ExactMatch{Value: uint16toBytes(value[0].(uint16))} + } + case *net.IPNet: + maskSize, _ := v.Mask.Size() + ip := v.IP.To4() + // if value[1].(string) == lpmStr { + switch value[1].(string) { + case lpmStr: + mfs[key] = &client.LpmMatch{Value: v.IP.To4(), PLen: int32(maskSize)} + // } else if value[1].(string) == ternaryStr { + case ternaryStr: + isTernary = true + mfs[key] = &client.TernaryMatch{Value: []byte(ip), Mask: uint32toBytes(4294967295)} + // } else { + default: + mfs[key] = &client.ExactMatch{Value: []byte(ip)} + } + case net.IP: + + switch value[1].(string) { + case lpmStr: + + mfs[key] = &client.LpmMatch{Value: value[0].(net.IP).To4(), PLen: 24} + // } else if value[1].(string) == ternaryStr { + case ternaryStr: + isTernary = true + mfs[key] = &client.TernaryMatch{Value: []byte(v), Mask: uint32toBytes(4294967295)} + // } else { + default: + mfs[key] = &client.ExactMatch{Value: []byte(v)} + } + case bool: + mfs[key] = &client.ExactMatch{Value: boolToBytes(value[0].(bool))} + case uint32: + switch value[1].(string) { + case lpmStr: + + mfs[key] = &client.LpmMatch{Value: uint32toBytes(value[0].(uint32)), PLen: 31} + // } else if value[1].(string) == ternaryStr { + case ternaryStr: + isTernary = true + mfs[key] = &client.TernaryMatch{Value: uint32toBytes(value[0].(uint32)), Mask: uint32toBytes(4294967295)} + // } else { + default: + mfs[key] = &client.ExactMatch{Value: uint32toBytes(value[0].(uint32))} + } + default: + log.Println("intel-e2000: Unknown field ", v) + return mfs, false, fmt.Errorf("invalid inputtype %d for %s", v, key) + } + } + return mfs, isTernary, nil +} + +// GetEntry get the entry +func GetEntry(table string) ([]*p4_v1.TableEntry, error) { + entry, err1 := P4RtC.ReadTableEntryWildcard(Ctx, table) + return entry, err1 +} + +// DelEntry deletes the entry +func DelEntry(entry TableEntry) error { + Options := &client.TableEntryOptions{ + Priority: entry.TableField.Priority, + } + mfs, isTernary, err := Buildmfs(entry.TableField) + if err != nil { + log.Fatalf("intel-e2000: Error in Building mfs: %v", err) + return err + } + if isTernary { + entry := P4RtC.NewTableEntry(entry.Tablename, mfs, nil, Options) + return P4RtC.DeleteTableEntry(Ctx, entry) + } + + entryP := P4RtC.NewTableEntry(entry.Tablename, mfs, nil, nil) + return P4RtC.DeleteTableEntry(Ctx, entryP) +} + +/*// mustMarshal marshal the msg +func mustMarshal(msg proto.Message) []byte { + data, err := proto.Marshal(msg) + if err != nil { + panic(err) // You should handle errors appropriately in your code + } + return data +}*/ + +// AddEntry adds an entry +func AddEntry(entry TableEntry) error { + Options := &client.TableEntryOptions{ + Priority: entry.TableField.Priority, + } + mfs, isTernary, err := Buildmfs(entry.TableField) + if err != nil { + log.Fatalf("intel-e2000: Error in Building mfs: %v", err) + return err + } + params := make([][]byte, len(entry.Action.Params)) + for i := 0; i < len(entry.Action.Params); i++ { + switch v := entry.Action.Params[i].(type) { + case uint16: + buf := new(bytes.Buffer) + err1 := binary.Write(buf, binary.BigEndian, v) + if err1 != nil { + log.Println("intel-e2000: binary.Write failed:", err1) + return err1 + } + params[i] = buf.Bytes() + case uint32: + buf := new(bytes.Buffer) + err1 := binary.Write(buf, binary.BigEndian, v) + if err1 != nil { + log.Println("inte-e2000: binary.Write failed:", err1) + return err1 + } + params[i] = buf.Bytes() + case net.HardwareAddr: + params[i] = v + case net.IP: + params[i] = v + default: + log.Println("intel-e2000: Unknown actionparam", v) + return nil + } + } + + actionSet := P4RtC.NewTableActionDirect(entry.Action.ActionName, params) + + if isTernary { + entryP := P4RtC.NewTableEntry(entry.Tablename, mfs, actionSet, Options) + return P4RtC.InsertTableEntry(Ctx, entryP) + } + entryP := P4RtC.NewTableEntry(entry.Tablename, mfs, actionSet, nil) + return P4RtC.InsertTableEntry(Ctx, entryP) +} + +/*// encodeMac encodes the mac from string +func encodeMac(macAddrString string) []byte { + str := strings.Replace(macAddrString, ":", "", -1) + decoded, _ := hex.DecodeString(str) + return decoded +}*/ + +// NewP4RuntimeClient get the p4 runtime client +func NewP4RuntimeClient(binPath string, p4infoPath string, conn *grpc.ClientConn) error { + Ctx = context.Background() + c := p4_v1.NewP4RuntimeClient(conn) + resp, err := c.Capabilities(Ctx, &p4_v1.CapabilitiesRequest{}) + if err != nil { + logr.Fatalf("intel-e2000: Error in Capabilities RPC: %v", err) + return err + } + logr.Infof("intel-e2000: P4Runtime server version is %s", resp.P4RuntimeApiVersion) + + stopCh := signals.RegisterSignalHandlers() + + electionID := &p4_v1.Uint128{High: 0, Low: 1} + + P4RtC = client.NewClient(c, defaultDeviceID, electionID) + arbitrationCh := make(chan bool) + + errs := make(chan error, 1) + go func() { + errs <- P4RtC.Run(stopCh, arbitrationCh, nil) + }() + + waitCh := make(chan struct{}) + + go func() { + sent := false + for isPrimary := range arbitrationCh { + if isPrimary { + logr.Infof("We are the primary client!") + if !sent { + waitCh <- struct{}{} + sent = true + } + } else { + logr.Infof("We are not the primary client!") + } + } + }() + + func() { + timeout := 5 * time.Second + Ctx2, cancel := context.WithTimeout(Ctx, timeout) + defer cancel() + select { + case <-Ctx2.Done(): + logr.Fatalf("Could not become the primary client within %v", timeout) + case <-errs: + logr.Fatalf("Could not get the client within %v", timeout) + case <-waitCh: + } + }() + logr.Info("Setting forwarding pipe") + if _, err := P4RtC.SetFwdPipe(Ctx, binPath, p4infoPath, 0); err != nil { + logr.Fatalf("Error when setting forwarding pipe: %v", err) + return err + } + return nil +} diff --git a/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/dcgw.go b/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/dcgw.go new file mode 100644 index 00000000..b1334df1 --- /dev/null +++ b/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/dcgw.go @@ -0,0 +1,3026 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries. +// Copyright (C) 2023 Nordix Foundation. + +package p4translation + +import ( + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "log" + "math" + "net" + "path" + "reflect" + "strconv" + "strings" + + "github.com/opiproject/opi-evpn-bridge/pkg/infradb" + netlink_polling "github.com/opiproject/opi-evpn-bridge/pkg/netlink" + p4client "github.com/opiproject/opi-evpn-bridge/pkg/vendor_plugins/intel-e2000/p4runtime/p4driverapi" + binarypack "github.com/roman-kachanovsky/go-binary-pack/binary-pack" +) + +// TcamPrefix structure of tcam type +var TcamPrefix = struct { + GRD, VRF, P2P uint32 +}{ + GRD: 0, + VRF: 2, // taking const for now as not imported VRF + P2P: 0x78654312, +} + +// Direction structure of type rx, tx or rxtx +var Direction = struct { + Rx, Tx int +}{ + Rx: 0, + Tx: 1, +} + +// Vlan structure of type grd phy port +var Vlan = struct { + GRD, PHY0, PHY1, PHY2, PHY3 uint16 +}{ + GRD: 4089, + PHY0: 4090, + PHY1: 4091, + PHY2: 4092, + PHY3: 4093, +} +var trueStr = "TRUE" +var grdStr = "GRD" +var intele2000Str = "intel-e2000" + +// PortID structure of type phy port +var PortID = struct { + PHY0, PHY1, PHY2, PHY3 int +}{ + PHY0: 0, + PHY1: 1, + PHY2: 2, + PHY3: 3, +} + +// EntryType structure of entry type +var EntryType = struct { + BP, l3NH, l2Nh, trieIn uint32 +}{ + BP: 0, + l3NH: 1, + l2Nh: 2, + trieIn: 3, +} + +// ModPointer structure of mod ptr definitions +var ModPointer = struct { + ignorePtr, l2FloodingPtr, ptrMinRange, ptrMaxRange uint32 +}{ + ignorePtr: 0, + l2FloodingPtr: 1, + ptrMinRange: 2, + ptrMaxRange: uint32(math.Pow(2, 16)) - 1, +} + +// TrieIndex structure of tri index definitions +var TrieIndex = struct { + triIdxMinRange, triIdxMaxRange uint32 +}{ + triIdxMinRange: 1, + triIdxMaxRange: uint32(math.Pow(2, 16)) - 1, +} + +// RefCountOp structure of reference count definitions +var RefCountOp = struct { + RESET, INCREMENT, DECREMENT int +}{ + RESET: 0, + INCREMENT: 1, + DECREMENT: 2, +} + +// ipuDB structure of ipu db port type +var ipuDB = struct { + TRUNK, ACCESS int +}{ + TRUNK: 0, + ACCESS: 1, +} + +// IDPool structure maintaining mod ptr pool +type IDPool struct { + _inUseIDs map[interface{}]uint32 + _refCount map[interface{}]uint32 + _availableIDs []uint32 +} + +// IDPoolInit initialize mod ptr pool +func (i IDPool) IDPoolInit(min uint32, max uint32) IDPool { + for j := min; j <= (max + 1); j++ { + i._availableIDs = append(i._availableIDs, j) + } + return i +} + +// PtrPool of type IDPool +var PtrPool IDPool + +// ptrPool initialized variable +var ptrPool = PtrPool.IDPoolInit(ModPointer.ptrMinRange, ModPointer.ptrMaxRange) + +// trieIndexPool initialized variable +var trieIndexPool = PtrPool.IDPoolInit(TrieIndex.triIdxMinRange, TrieIndex.triIdxMaxRange) + +// getID get the mod ptr id from pool +func (i IDPool) getID(keyType uint32, key []interface{}) uint32 { + // var fullKey interface{} + var fullKey interface{} = fmt.Sprintf("%d%d", keyType, key) + ptrID := ptrPool._inUseIDs[fullKey] + if ptrID == 0 { + ptrID = ptrPool._availableIDs[0] + ptrPool._availableIDs = ptrPool._availableIDs[1:] + if ptrPool._inUseIDs == nil { + ptrPool._inUseIDs = make(map[interface{}]uint32) + } + ptrPool._inUseIDs[fullKey] = ptrID + } + return ptrID +} + +// getUsedID get the mod ptr id from pool +func (i IDPool) getUsedID(keyType uint32, key []interface{}) uint32 { + // var fullKey interface{} + var fullKey interface{} = fmt.Sprintf("%d%d", keyType, key) + ptrID := ptrPool._inUseIDs[fullKey] + return ptrID +} + +// putID replaces the mod ptr +func (i IDPool) putID(keyType uint32, key []interface{}) error { + // var fullKey interface{} + var fullKey interface{} = fmt.Sprintf("%d%d", keyType, key) + ptrID := ptrPool._inUseIDs[fullKey] + if ptrID == 0 { + return fmt.Errorf("TODO") // or log + } + delete(ptrPool._inUseIDs, fullKey) + ptrPool._availableIDs = append(ptrPool._availableIDs, ptrID) + return nil +} + +// refCount get the reference count +func (i IDPool) refCount(keyType uint32, key []interface{}, op int) uint32 { + // var fullKey interface{} + var refCount uint32 + var fullKey interface{} = fmt.Sprintf("%d%d", keyType, key) + for key := range i._refCount { + if key == fullKey { + refCount = i._refCount[fullKey] + switch op { + case RefCountOp.RESET: + refCount = 1 + case RefCountOp.INCREMENT: + refCount++ + case RefCountOp.DECREMENT: + refCount-- + } + i._refCount[fullKey] = refCount + } else { + i._refCount[fullKey] = 1 + return uint32(1) + } + } + return refCount +} + +// Table of type string +type Table string + +const ( + + // l3Rt evpn p4 table name + l3Rt = "linux_networking_control.l3_routing_table" // VRFs routing table in LPM + // TableKeys ( + // ipv4_table_lpm_root2, // Exact + // vrf, // LPM + // direction, // LPM + // dst_ip, // LPM + // ) + // Actions ( + // set_neighbor(neighbor), + // ) + + // l3RtHost evpn p4 table name + l3RtHost = "linux_networking_control.l3_lem_table" + // TableKeys ( + // vrf, // Exact + // direction, // Exact + // dst_ip, // Exact + // ) + // Actions ( + // set_neighbor(neighbor) + // ) + + // l3P2PRt evpn p4 table name + l3P2PRt = "linux_networking_control.l3_p2p_routing_table" // Special GRD routing table for VXLAN packets + // TableKeys ( + // ipv4_table_lpm_root2, # Exact + // dst_ip, # LPM + // ) + // Actions ( + // set_p2p_neighbor(neighbor), + // + + // l3P2PRtHost evpn p4 table name + l3P2PRtHost = "linux_networking_control.l3_p2p_lem_table" + // Special LEM table for VXLAN packets + // TableKeys ( + // vrf, # Exact + // direction, # Exact + // dst_ip, # Exact + // ) + // Actions ( + // set_p2p_neighbor(neighbor) + // ) + + // l3NH evpn p4 table name + l3NH = "linux_networking_control.l3_nexthop_table" // VRFs next hop table + // TableKeys ( + // neighbor, // Exact + // bit32_zeros, // Exact + // ) + // Actions ( + // push_dmac_vlan(mod_ptr, vport) + // push_vlan(mod_ptr, vport) + // push_mac(mod_ptr, vport) + // push_outermac_vxlan_innermac(mod_ptr, vport) + // push_mac_vlan(mod_ptr, vport) + // ) + + // p2pIn evpn p4 table name + p2pIn = "linux_networking_control.ingress_p2p_table" + // TableKeys ( + // neighbor, # Exact + // bit32_zeros, # Exact + // ) + // Actions( + // fwd_to_port(port) + // + + // phyInIP evpn p4 table name + phyInIP = "linux_networking_control.phy_ingress_ip_table" // PHY ingress table - IP traffic + // TableKeys( + // port_id, // Exact + // bit32_zeros, // Exact + // ) + // Actions( + // set_vrf_id(tcam_prefix, vport, vrf), + // ) + + // phyInArp evpn p4 table name + phyInArp = "linux_networking_control.phy_ingress_arp_table" // PHY ingress table - ARP traffic + // TableKeys( + // port_id, // Exact + // bit32_zeros, // Exact + // ) + // Actions( + // fwd_to_port(port) + // ) + + // phyInVxlan evpn p4 table name + phyInVxlan = "linux_networking_control.phy_ingress_vxlan_table" // PHY ingress table - VXLAN traffic + // TableKeys( + // dst_ip + // vni, + // da + // ) + // Actions( + // pop_vxlan_set_vrf_id(mod_ptr, tcam_prefix, vport, vrf), + // ) + + // phyInVxlanL2 evpn p4 table name + phyInVxlanL2 = "linux_networking_control.phy_ingress_vxlan_vlan_table" + // Keys { + // dst_ip // Exact + // vni // Exact + // } + // Actions( + // pop_vxlan_set_vlan_id(mod_ptr, vlan_id, vport) + // ) + + // podInArpAccess evpn p4 table name + podInArpAccess = "linux_networking_control.vport_arp_ingress_table" + // Keys { + // vsi, // Exact + // bit32_zeros // Exact + // } + // Actions( + // fwd_to_port(port), + // send_to_port_mux_access(mod_ptr, vport) + // ) + + // podInArpTrunk evpn p4 table name + podInArpTrunk = "linux_networking_control.tagged_vport_arp_ingress_table" + // Key { + // vsi, // Exact + // vid // Exact + // } + // Actions( + // send_to_port_mux_trunk(mod_ptr, vport), + // fwd_to_port(port), + // pop_vlan(mod_ptr, vport) + // ) + + // podInIPAccess evpn p4 table name + podInIPAccess = "linux_networking_control.vport_ingress_table" + // Key { + // vsi, // Exact + // bit32_zeros // Exact + // } + // Actions( + // fwd_to_port(port) + // set_vlan(vlan_id, vport) + // ) + + // podInIPTrunk evpn p4 table name + podInIPTrunk = "linux_networking_control.tagged_vport_ingress_table" + // Key { + // vsi, // Exact + // vid // Exact + // } + // Actions( + // //pop_vlan(mod_ptr, vport) + // //pop_vlan_set_vrfid(mod_ptr, vport, tcam_prefix, vrf) + // set_vlan_and_pop_vlan(mod_ptr, vlan_id, vport) + // ) + + // portInSviAccess evpn p4 table name + portInSviAccess = "linux_networking_control.vport_svi_ingress_table" + // Key { + // vsi, // Exact + // da // Exact + // } + // Actions( + // set_vrf_id_tx(tcam_prefix, vport, vrf) + // fwd_to_port(port) + // ) + + // portInSviTrunk evpn p4 table name + portInSviTrunk = "linux_networking_control.tagged_vport_svi_ingress_table" + // Key { + // vsi, // Exact + // vid, // Exact + // da // Exact + // } + // Actions( + // pop_vlan_set_vrf_id(tcam_prefix, mod_ptr, vport, vrf) + // ) + + // portMuxIn evpn p4 table name + portMuxIn = "linux_networking_control.port_mux_ingress_table" + // Key { + // vsi, // Exact + // vid // Exact + // } + // Actions( + // set_def_vsi_loopback() + // pop_ctag_stag_vlan(mod_ptr, vport), + // pop_stag_vlan(mod_ptr, vport) + // ) + // PORT_MUX_RX = "linux_networking_control.port_mux_rx_table" + // Key { + // vid, // Exact + // bit32_zeros // Exact + // } + // Actions( + // pop_ctag_stag_vlan(mod_ptr, vport), + // pop_stag_vlan(mod_ptr, vport) + // ) + + // portMuxFwd evpn p4 table name + portMuxFwd = "linux_networking_control.port_mux_fwd_table" + // Key { + // bit32_zeros // Exact + // } + // Actions( + // "linux_networking_control.send_to_port_mux(vport)" + // ) + + // l2FwdLoop evpn p4 table name + l2FwdLoop = "linux_networking_control.l2_fwd_rx_table" + // Key { + // da // Exact (MAC) + // } + // Actions( + // l2_fwd(port) + // ) + + // l2Fwd evpn p4 table name + l2Fwd = "linux_networking_control.l2_dmac_table" + // Key { + // vlan_id, // Exact + // da, // Exact + // direction // Exact + // } + // Actions( + // set_neighbor(neighbor) + // ) + + // l2Nh evpn p4 table name + l2Nh = "linux_networking_control.l2_nexthop_table" + // Key { + // neighbor // Exact + // bit32_zeros // Exact + // } + // Actions( + // //push_dmac_vlan(mod_ptr, vport) + // push_stag_ctag(mod_ptr, vport) + // push_vlan(mod_ptr, vport) + // fwd_to_port(port) + // push_outermac_vxlan(mod_ptr, vport) + // ) + + // tcamEntries evpn p4 table name + tcamEntries = "linux_networking_control.ecmp_lpm_root_lut1" + + // Key { + // tcam_prefix, // Exact + // MATCH_PRIORITY, // Exact + // } + // Actions( + // None(ipv4_table_lpm_root1) + // ) + + // tcamEntries2 evpn p4 table name + tcamEntries2 = "linux_networking_control.ecmp_lpm_root_lut2" + // Key { + // tcamPrefix, # Exact + // MATCH_PRIORITY, # Exact + // } + // Actions( + // None(ipv4_table_lpm_root2) + // + +) + +// ModTable string var of mod table +type ModTable string + +const ( + + // pushVlan evpn p4 table name + pushVlan = "linux_networking_control.vlan_push_mod_table" + // src_action="push_vlan" + // Actions( + // vlan_push(pcp, dei, vlan_id), + // ) + + // pushMacVlan evpn p4 table name + pushMacVlan = "linux_networking_control.mac_vlan_push_mod_table" + // src_action="" + // Actions( + // update_smac_dmac_vlan(src_mac_addr, dst_mac_addr, pcp, dei, vlan_id) + + // pushDmacVlan evpn p4 table name + pushDmacVlan = "linux_networking_control.dmac_vlan_push_mod_table" + // src_action="push_dmac_vlan", + // Actions( + // dmac_vlan_push(pcp, dei, vlan_id, dst_mac_addr), + // ) + + // macMod evpn p4 table name + macMod = "linux_networking_control.mac_mod_table" + // src_action="push_mac" + // Actions( + // update_smac_dmac(src_mac_addr, dst_mac_addr), + // ) + + // pushVxlanHdr evpn p4 table name + pushVxlanHdr = "linux_networking_control.omac_vxlan_imac_push_mod_table" + // src_action="push_outermac_vxlan_innermac" + // Actions( + // omac_vxlan_imac_push(outer_smac_addr, + // outer_dmac_addr, + // src_addr, + // dst_addr, + // dst_port, + // vni, + // inner_smac_addr, + // inner_dmac_addr) + // ) + + // podOutAccess evpn p4 table name + podOutAccess = "linux_networking_control.vlan_encap_ctag_stag_mod_table" + // src_actions="send_to_port_mux_access" + // Actions( + // vlan_push_access(pcp, dei, ctag_id, pcp_s, dei_s, stag_id, dst_mac) + // ) + + // podOutTrunk evpn p4 table name + podOutTrunk = "linux_networking_control.vlan_encap_stag_mod_table" + // src_actions="send_to_port_mux_trunk" + // Actions( + // vlan_push_trunk(pcp, dei, stag_id, dst_mac) + // ) + + // popCtagStag evpn p4 table name + popCtagStag = "linux_networking_control.vlan_ctag_stag_pop_mod_table" + // src_actions="" + // Actions( + // vlan_ctag_stag_pop() + // ) + + // popStag evpn p4 table name + popStag = "linux_networking_control.vlan_stag_pop_mod_table" + // src_actions="" + // Actions( + // vlan_stag_pop() + // ) + + // pushQnQFlood evpn p4 table name + pushQnQFlood = "linux_networking_control.vlan_encap_ctag_stag_flood_mod_table" + // src_action="l2_nexthop_table.push_stag_ctag()" + // Action( + // vlan_push_stag_ctag_flood() + // ) + + // pushVxlanOutHdr evpn p4 table name + pushVxlanOutHdr = "linux_networking_control.omac_vxlan_push_mod_table" + +// src_action="l2_nexthop_table.push_outermac_vxlan()" +// Action( +// omac_vxlan_push(outer_smac_addr, outer_dmac_addr, src_addr, dst_addr, dst_port, vni) +// ) + +) + +/*func setMuxVsi(representors map[string]string) string{ + var muxVsi:= representors["vrf_mux"][0] + return muxVsi +}*/ +// _isL3vpnEnabled check if l3 enabled +func _isL3vpnEnabled(vrf *infradb.Vrf) bool { + return vrf.Spec.Vni != nil +} + +// bigEndian16 convert uint32 to big endian number +func bigEndian16(id uint32) interface{} { + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(id)) + unpackedData := binary.BigEndian.Uint16(buf) + return unpackedData +} + +// _bigEndian16 convert to big endian 16bit +func _bigEndian16(id interface{}) interface{} { + var bp = new(binarypack.BinaryPack) + var packFormat = []string{"H"} + var value = []interface{}{id} + var packedData, err = bp.Pack(packFormat, value) + if err != nil { + log.Printf("intel-e2000: error: %v\n", err) + } + var unpackedData = binary.BigEndian.Uint16(packedData) + return unpackedData +} + +/*// _bigEndian32 convert to big endian 32bit +func _bigEndian32(id interface{}) interface{} { + var bp = new(binarypack.BinaryPack) + var packFormat = []string{"I"} + var value = []interface{}{id} + var packedData, err = bp.Pack(packFormat, value) + if err != nil { + log.Printf("intel-e2000: error: %v\n",err) + } + var unpackedData = binary.BigEndian.Uint32(packedData) + return unpackedData +}*/ + +// _toEgressVsi convert to vsi+16 +func _toEgressVsi(vsiID int) int { + return vsiID + 16 +} + +// _directionsOf get the direction +func _directionsOf(entry interface{}) []int { + var directions []int + var direction int + + switch e := entry.(type) { + case netlink_polling.RouteStruct: + direction, _ = e.Metadata["direction"].(int) + case netlink_polling.FdbEntryStruct: + direction, _ = e.Metadata["direction"].(int) + } + if direction == netlink_polling.TX || direction == netlink_polling.RXTX { + directions = append(directions, Direction.Tx) + } + if direction == netlink_polling.RX || direction == netlink_polling.RXTX { + directions = append(directions, Direction.Rx) + } + return directions +} + +// _addTcamEntry adds the tcam entry +func _addTcamEntry(vrfID uint32, direction int) (p4client.TableEntry, uint32) { + tcamPrefix := fmt.Sprintf("%d%d", vrfID, direction) + var tblentry p4client.TableEntry + var tcam, err = strconv.ParseUint(tcamPrefix, 10, 32) + if err != nil { + panic(err) + } + var tidx = trieIndexPool.getUsedID(EntryType.trieIn, []interface{}{tcam}) + if tidx == 0 { + tidx = trieIndexPool.getID(EntryType.trieIn, []interface{}{tcam}) + trieIndexPool.refCount(EntryType.trieIn, []interface{}{tcam}, RefCountOp.RESET) + tblentry = p4client.TableEntry{ + Tablename: tcamEntries, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "user_meta.cmeta.tcam_prefix": {uint32(tcam), "ternary"}, + }, + Priority: int32(tidx), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.ecmp_lpm_root_lut1_action", + Params: []interface{}{tidx}, + }, + } + } else { + trieIndexPool.refCount(EntryType.trieIn, []interface{}{tcam}, RefCountOp.INCREMENT) + } + return tblentry, tidx +} + +// _getTcamPrefix get the tcam prefix value +func _getTcamPrefix(vrfID uint32, direction int) (int, error) { + tcamPrefix := fmt.Sprintf("%d%d", vrfID, direction) + val, err := strconv.ParseInt(tcamPrefix, 10, 32) + return int(val), err +} + +// _deleteTcamEntry deletes the tcam entry +func _deleteTcamEntry(vrfID uint32, direction int) ([]interface{}, uint32) { + tcamPrefix := fmt.Sprintf("%d%d", vrfID, direction) + var tblentry []interface{} + var tcam, err = strconv.ParseUint(tcamPrefix, 10, 32) + if err != nil { + panic(err) + } + var tidx = trieIndexPool.getUsedID(EntryType.trieIn, []interface{}{tcam}) + var refCount uint32 + if tidx != 0 { + refCount = trieIndexPool.refCount(EntryType.trieIn, []interface{}{tcam}, RefCountOp.DECREMENT) + if refCount == 0 { + err := trieIndexPool.putID(EntryType.trieIn, []interface{}{tcam}) + if err != nil { + log.Println(err) + } + tblentry = append(tblentry, p4client.TableEntry{ + Tablename: tcamEntries, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "user_meta.cmeta.tcam_prefix": {uint32(tcam), "ternary"}, + }, + Priority: int32(1), + }, + }) + } + } + return tblentry, tidx +} + +// PhyPort structure of phy ports +type PhyPort struct { + id int + vsi int + mac string +} + +// PhyPortInit initializes the phy port +func (p PhyPort) PhyPortInit(id int, vsi string, mac string) PhyPort { + p.id = id + val, err := strconv.ParseInt(vsi, 10, 32) + if err != nil { + panic(err) + } + p.vsi = int(val) + p.mac = mac + + return p +} + +// _p4NexthopID get the p4 nexthop id +func _p4NexthopID(nh netlink_polling.NexthopStruct, direction int) int { + nhID := nh.ID << 1 + if direction == Direction.Rx && (nh.NhType == netlink_polling.PHY || nh.NhType == netlink_polling.VXLAN) { + nhID++ + } + return nhID +} + +// _p2pQid get the qid for p2p port +func _p2pQid(pID int) int { + if pID == PortID.PHY0 { + return 0x87 + } else if pID == PortID.PHY1 { + return 0x8b + } + + return 0 +} + +// GrpcPairPort structure +type GrpcPairPort struct { + vsi int + mac string + peer map[string]string +} + +// GrpcPairPortInit get the vsi+16 +func (g GrpcPairPort) GrpcPairPortInit(vsi string, mac string) GrpcPairPort { + val, err := strconv.ParseInt(vsi, 10, 32) + if err != nil { + panic(err) + } + g.vsi = int(val) + g.mac = mac + return g +} + +// setRemotePeer set the remote peer +func (g GrpcPairPort) setRemotePeer(peer [2]string) GrpcPairPort { + g.peer = make(map[string]string) + g.peer["vsi"] = peer[0] + g.peer["mac"] = peer[1] + return g +} + +// L3Decoder structure +type L3Decoder struct { + _muxVsi uint16 + _defaultVsi int + _phyPorts []PhyPort + _grpcPorts []GrpcPairPort + PhyPort + GrpcPairPort +} + +// L3DecoderInit initialize the l3 decoder +func (l L3Decoder) L3DecoderInit(representors map[string][2]string) L3Decoder { + s := L3Decoder{ + _muxVsi: l.setMuxVsi(representors), + _defaultVsi: 0x6, + _phyPorts: l._getPhyInfo(representors), + _grpcPorts: l._getGrpcInfo(representors), + } + return s +} + +// setMuxVsi set the mux vsi +func (l L3Decoder) setMuxVsi(representors map[string][2]string) uint16 { + a := representors["vrf_mux"][0] + var muxVsi, err = strconv.ParseUint(a, 10, 16) + if err != nil { + panic(err) + } + return uint16(muxVsi) +} + +// _getPhyInfo get the phy port info +func (l L3Decoder) _getPhyInfo(representors map[string][2]string) []PhyPort { + var enabledPorts []PhyPort + var vsi string + var mac string + var p = reflect.TypeOf(PortID) + for i := 0; i < p.NumField(); i++ { + var k = p.Field(i).Name + var key = strings.ToLower(k) + "_rep" + for k = range representors { + if key == k { + vsi = representors[key][0] + mac = representors[key][1] + enabledPorts = append(enabledPorts, l.PhyPortInit(i, vsi, mac)) + } + } + } + return enabledPorts // should return tuple +} + +// _getGrpcInfo get the grpc information +func (l L3Decoder) _getGrpcInfo(representors map[string][2]string) []GrpcPairPort { + var accHost GrpcPairPort + var hostPort GrpcPairPort + var grpcPorts []GrpcPairPort + + accVsi := representors["grpc_acc"][0] + accMac := representors["grpc_acc"][1] + accHost = accHost.GrpcPairPortInit(accVsi, accMac) // ?? + + hostVsi := representors["grpc_host"][0] + hostMac := representors["grpc_host"][1] + hostPort = hostPort.GrpcPairPortInit(hostVsi, hostMac) // ?? + + accPeer := representors["grpc_host"] + hostPeer := representors["grpc_acc"] + accHost = accHost.setRemotePeer(accPeer) + + hostPort = hostPort.setRemotePeer(hostPeer) + + grpcPorts = append(grpcPorts, accHost, hostPort) + return grpcPorts +} + +// getVrfID get the vrf id from vni +func (l L3Decoder) getVrfID(route netlink_polling.RouteStruct) uint32 { + if route.Vrf.Spec.Vni == nil { + return 0 + } + + return *route.Vrf.Spec.Vni +} + +// _l3HostRoute gets the l3 host route +func (l L3Decoder) _l3HostRoute(route netlink_polling.RouteStruct, delete string) []interface{} { + var vrfID = l.getVrfID(route) + var directions = _directionsOf(route) + var host = route.Route0.Dst + var entries = make([]interface{}, 0) + + if delete == trueStr { + for _, dir := range directions { + entries = append(entries, p4client.TableEntry{ + Tablename: l3RtHost, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vrf": {_bigEndian16(vrfID), "exact"}, + "direction": {uint16(dir), "exact"}, + "dst_ip": {host, "exact"}, + }, + Priority: int32(0), + }, + }) + } + } else { + for _, dir := range directions { + entries = append(entries, p4client.TableEntry{ + Tablename: l3RtHost, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vrf": {bigEndian16(vrfID), "exact"}, + "direction": {uint16(dir), "exact"}, + "dst_ip": {host, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_neighbor", + Params: []interface{}{uint16(_p4NexthopID(route.Nexthops[0], dir))}, + }, + }) + } + } + if path.Base(route.Vrf.Name) == grdStr && route.Nexthops[0].NhType == netlink_polling.PHY { + if delete == trueStr { + entries = append(entries, p4client.TableEntry{ + Tablename: l3P2PRtHost, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vrf": {_bigEndian16(vrfID), "exact"}, + "direction": {uint16(Direction.Rx), "exact"}, + "dst_ip": {host, "exact"}, + }, + Priority: int32(0), + }, + }) + } else { + entries = append(entries, p4client.TableEntry{ + Tablename: l3P2PRtHost, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vrf": {_bigEndian16(vrfID), "exact"}, + "direction": {uint16(Direction.Rx), "exact"}, + "dst_ip": {host, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_p2p_neighbor", + Params: []interface{}{uint16(_p4NexthopID(route.Nexthops[0], Direction.Rx))}, + }, + }) + } + } + return entries +} + +// _l3Route generate the l3 route entries +func (l L3Decoder) _l3Route(route netlink_polling.RouteStruct, delete string) []interface{} { + var vrfID = l.getVrfID(route) + var directions = _directionsOf(route) + var addr = route.Route0.Dst.IP.String() + var entries = make([]interface{}, 0) + + for _, dir := range directions { + if delete == trueStr { + var tblEntry, tIdx = _deleteTcamEntry(vrfID, dir) + if !reflect.ValueOf(tblEntry).IsZero() { + entries = append(entries, tblEntry) + } + entries = append(entries, p4client.TableEntry{ + Tablename: l3Rt, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "ipv4_table_lpm_root1": {tIdx, "ternary"}, + "dst_ip": {net.ParseIP(addr), "lpm"}, + }, + Priority: int32(0), + }, + }) + } else { + var tblEntry, tIdx = _addTcamEntry(vrfID, dir) + if !reflect.ValueOf(tblEntry).IsZero() { + entries = append(entries, tblEntry) + } + entries = append(entries, p4client.TableEntry{ + Tablename: l3Rt, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "ipv4_table_lpm_root1": {tIdx, "ternary"}, + "dst_ip": {net.ParseIP(addr), "lpm"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_neighbor", + Params: []interface{}{uint16(_p4NexthopID(route.Nexthops[0], Direction.Rx))}, + }, + }) + } + } + if path.Base(route.Vrf.Name) == grdStr && route.Nexthops[0].NhType == netlink_polling.PHY { + tidx := trieIndexPool.getUsedID(EntryType.trieIn, []interface{}{TcamPrefix.P2P}) + if delete == trueStr { + entries = append(entries, p4client.TableEntry{ + Tablename: l3P2PRt, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "ipv4_table_lpm_root2": {tidx, "ternary"}, + "dst_ip": {net.ParseIP(addr), "lpm"}, + }, + Priority: int32(0), + }, + }) + } else { + entries = append(entries, p4client.TableEntry{ + Tablename: l3P2PRt, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "ipv4_table_lpm_root2": {tidx, "ternary"}, + "dst_ip": {net.ParseIP(addr), "lpm"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_p2p_neighbor", + Params: []interface{}{uint16(_p4NexthopID(route.Nexthops[0], Direction.Rx))}, + }, + }) + } + } + return entries +} + +// translateAddedRoute translate the added route to p4 entries +func (l L3Decoder) translateAddedRoute(route netlink_polling.RouteStruct) []interface{} { + var ipv4Net = route.Route0.Dst + if net.IP(ipv4Net.Mask).String() == "255.255.255.255" { + return l._l3HostRoute(route, "False") + } + + return l._l3Route(route, "False") +} + +/*// translateChangedRoute translate the changed route to p4 entries +func (l L3Decoder) translateChangedRoute(route netlink_polling.RouteStruct) []interface{} { + return l.translateAddedRoute(route) +}*/ + +// translateDeletedRoute translate the deleted route to p4 entries +func (l L3Decoder) translateDeletedRoute(route netlink_polling.RouteStruct) []interface{} { + var ipv4Net = route.Route0.Dst + if net.IP(ipv4Net.Mask).String() == "255.255.255.255" { + return l._l3HostRoute(route, "True") + } + + return l._l3Route(route, "True") +} + +// translateAddedNexthop translate the added nexthop to p4 entries +// +//nolint:funlen +func (l L3Decoder) translateAddedNexthop(nexthop netlink_polling.NexthopStruct) []interface{} { + if nexthop.NhType == netlink_polling.VXLAN { + var entries []interface{} + return entries + } + var key []interface{} + key = append(key, nexthop.Key.VrfName, nexthop.Key.Dst, nexthop.Key.Dev, nexthop.Key.Local) + var modPtr = ptrPool.getID(EntryType.l3NH, key) + nhID := _p4NexthopID(nexthop, Direction.Tx) + + var entries = make([]interface{}, 0) + + switch nexthop.NhType { + case netlink_polling.PHY: + // if nexthop.NhType == netlink_polling.PHY { + var smac, _ = net.ParseMAC(nexthop.Metadata["smac"].(string)) + var dmac, _ = net.ParseMAC(nexthop.Metadata["dmac"].(string)) + var portID = nexthop.Metadata["egress_vport"] + + entries = append(entries, p4client.TableEntry{ + Tablename: macMod, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.update_smac_dmac", + Params: []interface{}{smac, dmac}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_mac", + Params: []interface{}{modPtr, uint16(portID.(int))}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.send_p2p_push_mac", + Params: []interface{}{modPtr, uint16(portID.(int)), uint16(_p2pQid(portID.(int)))}, + }, + }, + p4client.TableEntry{ + Tablename: p2pIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.fwd_to_port", + Params: []interface{}{uint16(portID.(int))}, + }, + }) + // } else if nexthop.NhType == netlink_polling.ACC { + case netlink_polling.ACC: + var dmac, _ = net.ParseMAC(nexthop.Metadata["dmac"].(string)) + var vlanID = nexthop.Metadata["vlanID"].(uint32) + var vport = _toEgressVsi(nexthop.Metadata["egress_vport"].(int)) + entries = append(entries, p4client.TableEntry{ + Tablename: pushDmacVlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.dmac_vlan_push", + Params: []interface{}{uint16(0), uint16(1), uint16(vlanID), dmac}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_dmac_vlan", + Params: []interface{}{modPtr, uint32(vport)}, + }, + }) + // } else if nexthop.NhType == netlink_polling.SVI { + case netlink_polling.SVI: + var smac, _ = net.ParseMAC(nexthop.Metadata["smac"].(string)) + var dmac, _ = net.ParseMAC(nexthop.Metadata["dmac"].(string)) + var vlanID = nexthop.Metadata["vlanID"] + var vport = _toEgressVsi(nexthop.Metadata["egress_vport"].(int)) + var Type = nexthop.Metadata["portType"] + switch Type { + case ipuDB.TRUNK: + + entries = append(entries, p4client.TableEntry{ + Tablename: pushMacVlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.update_smac_dmac_vlan", + Params: []interface{}{smac, dmac, 0, 1, vlanID.(uint16)}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_mac_vlan", + Params: []interface{}{modPtr, uint32(vport)}, + }, + }) + // } else if Type == ipuDB.ACCESS { + case ipuDB.ACCESS: + entries = append(entries, p4client.TableEntry{ + Tablename: macMod, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.update_smac_dmac", + Params: []interface{}{smac, dmac}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_mac", + Params: []interface{}{modPtr, uint32(vport)}, + }, + }) + // } else { + default: + return entries + } + // } else { + default: + return entries + } + + return entries +} + +/*// translateChangedNexthop translate the changed nexthop to p4 entries +func (l L3Decoder) translateChangedNexthop(nexthop netlink_polling.NexthopStruct) []interface{} { + return l.translateAddedNexthop(nexthop) +}*/ +//nolint:funlen +// translateDeletedNexthop translate the deleted nexthop to p4 entries +func (l L3Decoder) translateDeletedNexthop(nexthop netlink_polling.NexthopStruct) []interface{} { + if nexthop.NhType == netlink_polling.VXLAN { + var entries []interface{} + return entries + } + var key []interface{} + key = append(key, nexthop.Key.VrfName, nexthop.Key.Dst, nexthop.Key.Dev, nexthop.Key.Local) + var modPtr = ptrPool.getID(EntryType.l3NH, key) + nhID := _p4NexthopID(nexthop, Direction.Tx) + var entries = make([]interface{}, 0) + switch nexthop.NhType { + case netlink_polling.PHY: + // if nexthop.NhType == netlink_polling.PHY { + entries = append(entries, p4client.TableEntry{ + Tablename: macMod, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: p2pIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + // } else if nexthop.NhType == netlink_polling.ACC { + case netlink_polling.ACC: + entries = append(entries, p4client.TableEntry{ + Tablename: pushDmacVlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + // } else if nexthop.NhType == netlink_polling.SVI { + case netlink_polling.SVI: + var Type = nexthop.Metadata["portType"] + switch Type { + case ipuDB.TRUNK: + // if Type == ipuDB.TRUNK { + entries = append(entries, p4client.TableEntry{ + Tablename: pushMacVlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + // } else if Type == ipuDB.ACCESS { + case ipuDB.ACCESS: + entries = append(entries, p4client.TableEntry{ + Tablename: macMod, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(nhID), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + // } else { + default: + return entries + } + // } else { + default: + return entries + } + err := ptrPool.putID(EntryType.l3NH, key) + if err != nil { + log.Println(err) + } + return entries +} + +// StaticAdditions do the static additions for p4 tables +// +//nolint:funlen +func (l L3Decoder) StaticAdditions() []interface{} { + var tcamPrefix = TcamPrefix.GRD + var entries = make([]interface{}, 0) + + entries = append(entries, p4client.TableEntry{ + Tablename: podInIPTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {l._muxVsi, "exact"}, + "vid": {Vlan.GRD, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_vlan_set_vrfid", + Params: []interface{}{ModPointer.ignorePtr, uint32(0), tcamPrefix, uint32(0)}, + }, + }, + ) + for _, port := range l._grpcPorts { + var peerVsi, err = strconv.ParseInt(port.peer["vsi"], 10, 64) + if err != nil { + panic(err) + } + var peerDa, _ = net.ParseMAC(port.peer["mac"]) + var portDa, _ = net.ParseMAC(port.mac) + entries = append(entries, p4client.TableEntry{ + Tablename: portInSviAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port.vsi), "exact"}, + "da": {peerDa, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.fwd_to_port", + Params: []interface{}{uint32(_toEgressVsi(int(peerVsi)))}, + }, + }, + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {portDa, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.l2_fwd", + Params: []interface{}{uint32(_toEgressVsi(port.vsi))}, + }, + }) + } + for _, port := range l._phyPorts { + entries = append(entries, p4client.TableEntry{ + Tablename: phyInIP, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "port_id": {uint16(port.id), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_vrf_id", + Params: []interface{}{tcamPrefix, uint32(_toEgressVsi(l._defaultVsi)), uint32(0)}, + }, + }, + p4client.TableEntry{ + Tablename: phyInArp, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "port_id": {uint16(port.id), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.fwd_to_port", + Params: []interface{}{uint32(_toEgressVsi(port.vsi))}, + }, + }, + p4client.TableEntry{ + Tablename: podInIPAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port.vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.fwd_to_port", + Params: []interface{}{uint32(port.id)}, + }, + }, + p4client.TableEntry{ + Tablename: podInArpAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port.vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.fwd_to_port", + Params: []interface{}{uint32(port.id)}, + }, + }) + } + tidx := trieIndexPool.getID(EntryType.trieIn, []interface{}{TcamPrefix.P2P}) + trieIndexPool.refCount(EntryType.trieIn, []interface{}{TcamPrefix.P2P}, RefCountOp.RESET) + entries = append(entries, p4client.TableEntry{ + Tablename: tcamEntries2, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "user_meta.cmeta.tcam_prefix": {TcamPrefix.P2P, "ternary"}, + }, + Priority: int32(tidx), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.ecmp_lpm_root_lut2_action", + Params: []interface{}{tidx}, + }, + }) + return entries +} + +// StaticDeletions do the static deletion for p4 tables +func (l L3Decoder) StaticDeletions() []interface{} { + var entries = make([]interface{}, 0) + for _, port := range l._phyPorts { + entries = append(entries, p4client.TableEntry{ + Tablename: phyInIP, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "port_id": {uint16(port.id), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: phyInArp, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "port_id": {uint16(port.id), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: podInIPAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port.vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: podInArpAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port.vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + } + for _, port := range l._grpcPorts { + var peerDa, _ = net.ParseMAC(port.peer["mac"]) + var portDa, _ = net.ParseMAC(port.mac) + entries = append(entries, p4client.TableEntry{ + Tablename: portInSviAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port.vsi), "exact"}, + "da": {peerDa, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {portDa, "exact"}, + }, + Priority: int32(0), + }, + }) + } + entries = append(entries, p4client.TableEntry{ + Tablename: podInIPTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {l._muxVsi, "exact"}, + "vid": {Vlan.GRD, "exact"}, + }, + Priority: int32(0), + }, + }) + tidx := trieIndexPool.getID(EntryType.trieIn, []interface{}{TcamPrefix.P2P}) + entries = append(entries, p4client.TableEntry{ + Tablename: tcamEntries2, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "user_meta.cmeta.tcam_prefix": {TcamPrefix.P2P, "ternary"}, + }, + Priority: int32(tidx), + }, + }) + return entries +} + +// VxlanDecoder structure +type VxlanDecoder struct { + vxlanUDPPort uint32 + _muxVsi int + _defaultVsi int +} + +// VxlanDecoderInit initialize vxlan decoder +func (v VxlanDecoder) VxlanDecoderInit(representors map[string][2]string) VxlanDecoder { + var muxVsi, err = strconv.ParseInt(representors["vrf_mux"][0], 10, 32) + if err != nil { + panic(err) + } + s := VxlanDecoder{ + vxlanUDPPort: 4789, + _defaultVsi: 0xb, + _muxVsi: int(muxVsi), + } + return s +} + +// _isL2vpnEnabled check s if l2evpn enabled +func _isL2vpnEnabled(lb *infradb.LogicalBridge) bool { + return lb.Spec.Vni != nil +} + +// translateAddedVrf translates the added vrf +func (v VxlanDecoder) translateAddedVrf(vrf *infradb.Vrf) []interface{} { + var entries = make([]interface{}, 0) + if !_isL3vpnEnabled(vrf) { + return entries + } + var tcamPrefix, err = _getTcamPrefix(*vrf.Spec.Vni, Direction.Rx) + if err != nil { + return entries + } + G, _ := infradb.GetVrf(vrf.Name) + var detail map[string]interface{} + var Rmac net.HardwareAddr + for _, com := range G.Status.Components { + if com.Name == "frr" { + err := json.Unmarshal([]byte(com.Details), &detail) + if err != nil { + log.Println("intel-e2000: Error: ", err) + } + rmac, found := detail["rmac"].(string) + if !found { + log.Println("intel-e2000: Key 'rmac' not found") + break + } + Rmac, err = net.ParseMAC(rmac) + if err != nil { + log.Println("intel-e2000: Error parsing MAC address:", err) + } + } + } + if reflect.ValueOf(Rmac).IsZero() { + log.Println("intel-e2000: Rmac not found for Vtep :", vrf.Spec.VtepIP.IP) + + return entries + } + entries = append(entries, p4client.TableEntry{ + Tablename: phyInVxlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "dst_ip": {vrf.Spec.VtepIP.IP, "exact"}, + "vni": {*vrf.Spec.Vni, "exact"}, + "da": {Rmac, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_vxlan_set_vrf_id", + Params: []interface{}{ModPointer.ignorePtr, uint32(tcamPrefix), uint32(_toEgressVsi(v._defaultVsi)), *vrf.Spec.Vni}, + }, + }) + return entries +} + +// translateDeletedVrf translates the deleted vrf +func (v VxlanDecoder) translateDeletedVrf(vrf *infradb.Vrf) []interface{} { + var entries = make([]interface{}, 0) + if !_isL3vpnEnabled(vrf) { + return entries + } + G, _ := infradb.GetVrf(vrf.Name) + var detail map[string]interface{} + var Rmac net.HardwareAddr + for _, com := range G.Status.Components { + if com.Name == "frr" { + err := json.Unmarshal([]byte(com.Details), &detail) + if err != nil { + log.Println("intel-e2000: Error: ", err) + } + rmac, found := detail["rmac"].(string) + if !found { + log.Println("intel-e2000: Key 'rmac' not found") + break + } + Rmac, err = net.ParseMAC(rmac) + if err != nil { + log.Println("intel-e2000: Error parsing MAC address:", err) + } + } + } + if reflect.ValueOf(Rmac).IsZero() { + log.Println("intel-e2000: Rmac not found for Vtep :", vrf.Spec.VtepIP.IP) + + return entries + } + entries = append(entries, p4client.TableEntry{ + Tablename: phyInVxlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "dst_ip": {vrf.Spec.VtepIP.IP, "exact"}, + "vni": {*vrf.Spec.Vni, "exact"}, + "da": {Rmac, "exact"}, + }, + Priority: int32(0), + }, + }) + return entries +} + +// translateAddedLb translates the added lb +func (v VxlanDecoder) translateAddedLb(lb *infradb.LogicalBridge) []interface{} { + var entries = make([]interface{}, 0) + if !(_isL2vpnEnabled(lb)) { + return entries + } + entries = append(entries, p4client.TableEntry{ + Tablename: phyInVxlanL2, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "dst_ip": {lb.Spec.VtepIP.IP, "exact"}, + "vni": {*lb.Spec.Vni, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_vxlan_set_vlan_id", + Params: []interface{}{ModPointer.ignorePtr, uint16(lb.Spec.VlanID), uint32(_toEgressVsi(v._defaultVsi))}, + }, + }) + return entries +} + +// translateDeletedLb translates the deleted lb +func (v VxlanDecoder) translateDeletedLb(lb *infradb.LogicalBridge) []interface{} { + var entries = make([]interface{}, 0) + + if !(_isL2vpnEnabled(lb)) { + return entries + } + entries = append(entries, p4client.TableEntry{ + Tablename: phyInVxlanL2, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "dst_ip": {lb.Spec.VtepIP.IP, "exact"}, + "vni": {*lb.Spec.Vni, "exact"}, + }, + Priority: int32(0), + }, + }) + return entries +} + +// translateAddedNexthop translates the added nexthop +func (v VxlanDecoder) translateAddedNexthop(nexthop netlink_polling.NexthopStruct) []interface{} { + var entries = make([]interface{}, 0) + + if nexthop.NhType != netlink_polling.VXLAN { + return entries + } + var key []interface{} + key = append(key, nexthop.Key.VrfName, nexthop.Key.Dev, nexthop.Key.Dst, nexthop.Key.Dev, nexthop.Key.Local) + + var modPtr = ptrPool.getID(EntryType.l3NH, key) + var vport = nexthop.Metadata["egress_vport"].(int) + var smac, _ = net.ParseMAC(nexthop.Metadata["phy_smac"].(string)) + var dmac, _ = net.ParseMAC(nexthop.Metadata["phy_dmac"].(string)) + var srcAddr = nexthop.Metadata["local_vtep_ip"] + var dstAddr = nexthop.Metadata["remote_vtep_ip"] + var vni = nexthop.Metadata["vni"] + var innerSmacAddr, _ = net.ParseMAC(nexthop.Metadata["inner_smac"].(string)) + var innerDmacAddr, _ = net.ParseMAC(nexthop.Metadata["inner_dmac"].(string)) + entries = append(entries, p4client.TableEntry{ + Tablename: pushVxlanHdr, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.omac_vxlan_imac_push", + Params: []interface{}{smac, dmac, net.IP(srcAddr.(string)), net.IP(dstAddr.(string)), v.vxlanUDPPort, vni.(uint32), innerSmacAddr, innerDmacAddr}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Tx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_outermac_vxlan_innermac", + Params: []interface{}{modPtr, uint32(vport)}, + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.send_p2p_push_outermac_vxlan_innermac", + Params: []interface{}{modPtr, uint32(vport), uint16(_p2pQid(vport))}, + }, + }, + p4client.TableEntry{ + Tablename: p2pIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.send_p2p", + Params: []interface{}{uint32(vport)}, + }, + }) + return entries +} + +/*// translateChangedNexthop translates the changed nexthop +func (v VxlanDecoder) translateChangedNexthop(nexthop netlink_polling.NexthopStruct) []interface{} { + return v.translateAddedNexthop(nexthop) +}*/ + +// translateDeletedNexthop translates the deleted nexthop +func (v VxlanDecoder) translateDeletedNexthop(nexthop netlink_polling.NexthopStruct) []interface{} { + var entries = make([]interface{}, 0) + + if nexthop.NhType != netlink_polling.VXLAN { + return entries + } + var key []interface{} + key = append(key, nexthop.Key.VrfName, nexthop.Key.Dev, nexthop.Key.Dst, nexthop.Key.Dev, nexthop.Key.Local) + var modPtr = ptrPool.getID(EntryType.l3NH, key) + entries = append(entries, p4client.TableEntry{ + Tablename: pushVxlanHdr, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Tx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l3NH, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: p2pIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {uint16(_p4NexthopID(nexthop, Direction.Rx)), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + err := ptrPool.putID(EntryType.l3NH, key) + if err != nil { + log.Println(err) + } + return entries +} + +// translateAddedL2Nexthop translates the added l2 nexthop +func (v VxlanDecoder) translateAddedL2Nexthop(nexthop netlink_polling.L2NexthopStruct) []interface{} { + var entries = make([]interface{}, 0) + + if nexthop.Type != netlink_polling.VXLAN { + return entries + } + var key []interface{} + key = append(key, nexthop.Key.Dev, nexthop.Key.VlanID, nexthop.Key.Dst) + + var modPtr = ptrPool.getID(EntryType.l2Nh, key) + var vport = nexthop.Metadata["egress_vport"].(int) + var srcMac, _ = net.ParseMAC(nexthop.Metadata["phy_smac"].(string)) + var dstMac, _ = net.ParseMAC(nexthop.Metadata["phy_dmac"].(string)) + var srcIP = nexthop.Metadata["local_vtep_ip"] + var dstIP = nexthop.Metadata["remote_vtep_ip"] + var vni = nexthop.Metadata["vni"] + var vsiOut = _toEgressVsi(vport) + var neighbor = nexthop.ID + entries = append(entries, p4client.TableEntry{ + Tablename: pushVxlanOutHdr, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.omac_vxlan_push", + Params: []interface{}{srcMac, dstMac, net.IP(srcIP.(string)), net.ParseIP(dstIP.(string)), v.vxlanUDPPort, vni.(uint32)}, + }, + }, + p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {_bigEndian16(neighbor), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_outermac_vxlan", + Params: []interface{}{modPtr, vsiOut}, + }, + }) + return entries +} + +/*// translateChangedL2Nexthop translates the changed l2 nexthop +func (v VxlanDecoder) translateChangedL2Nexthop(nexthop netlink_polling.L2NexthopStruct) []interface{} { + return v.translateAddedL2Nexthop(nexthop) +}*/ + +// translateDeletedL2Nexthop translates the deleted l2 nexthop +func (v VxlanDecoder) translateDeletedL2Nexthop(nexthop netlink_polling.L2NexthopStruct) []interface{} { + var entries = make([]interface{}, 0) + + if nexthop.Type != netlink_polling.VXLAN { + return entries + } + var key []interface{} + key = append(key, nexthop.Key.Dev, nexthop.Key.VlanID, nexthop.Key.Dst) + + var modPtr = ptrPool.getID(EntryType.l2Nh, key) + var neighbor = nexthop.ID + err := ptrPool.putID(EntryType.l2Nh, key) + if err != nil { + log.Println(err) + } + entries = append(entries, p4client.TableEntry{ + Tablename: pushVxlanOutHdr, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {_bigEndian16(neighbor), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + return entries +} + +// translateAddedFdb translates the added fdb entry +func (v VxlanDecoder) translateAddedFdb(fdb netlink_polling.FdbEntryStruct) []interface{} { + var entries = make([]interface{}, 0) + + if fdb.Type != netlink_polling.VXLAN { + return entries + } + var mac, _ = net.ParseMAC(fdb.Mac) + var directions = _directionsOf(fdb) + + for _, dir := range directions { + entries = append(entries, p4client.TableEntry{ + Tablename: l2Fwd, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vlan_id": {_bigEndian16(fdb.VlanID), "exact"}, + "da": {mac, "exact"}, + "direction": {uint16(dir), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_neighbor", + Params: []interface{}{uint16(fdb.Metadata["nh_id"].(int))}, + }, + }) + } + return entries +} + +/*// translateChangedFdb translates the changed fdb entry +func (v VxlanDecoder) translateChangedFdb(fdb netlink_polling.FdbEntryStruct) []interface{} { + return v.translateAddedFdb(fdb) +}*/ + +// translateDeletedFdb translates the deleted fdb entry +func (v VxlanDecoder) translateDeletedFdb(fdb netlink_polling.FdbEntryStruct) []interface{} { + var entries = make([]interface{}, 0) + + if fdb.Type != netlink_polling.VXLAN { + return entries + } + var mac, _ = net.ParseMAC(fdb.Mac) + var directions = _directionsOf(fdb) + + for _, dir := range directions { + entries = append(entries, p4client.TableEntry{ + Tablename: l2Fwd, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vlan_id": {_bigEndian16(fdb.VlanID), "exact"}, + "da": {mac, "exact"}, + "direction": {uint16(dir), "exact"}, + }, + Priority: int32(0), + }, + }) + } + return entries +} + +// PodDecoder structure for pod decode +type PodDecoder struct { + portMuxIDs [2]string + _portMuxVsi int + _portMuxMac string + vrfMuxIDs [2]string + _vrfMuxVsi int + _vrfMuxMac string + floodModPtr uint32 + floodNhID uint16 +} + +// PodDecoderInit initializes the pod decoder +func (p PodDecoder) PodDecoderInit(representors map[string][2]string) PodDecoder { + p.portMuxIDs = representors["port_mux"] + p.vrfMuxIDs = representors["vrf_mux"] + + portMuxVsi, err := strconv.ParseInt(p.portMuxIDs[0], 10, 32) + if err != nil { + panic(err) + } + vrfMuxVsi, err := strconv.ParseInt(p.vrfMuxIDs[0], 10, 32) + if err != nil { + panic(err) + } + p._portMuxVsi = int(portMuxVsi) + p._portMuxMac = p.portMuxIDs[1] + p._vrfMuxVsi = int(vrfMuxVsi) + p._vrfMuxMac = p.vrfMuxIDs[1] + p.floodModPtr = ModPointer.l2FloodingPtr + p.floodNhID = uint16(0) + return p +} + +// translateAddedBp translate the added bp +// +//nolint:funlen,gocognit +func (p PodDecoder) translateAddedBp(bp *infradb.BridgePort) ([]interface{}, error) { + var entries = make([]interface{}, 0) + + var portMuxVsiOut = _toEgressVsi(p._portMuxVsi) + port, err := strconv.ParseUint(bp.Metadata.VPort, 10, 16) + if err != nil { + return entries, err + } + var vsi = port + var vsiOut = _toEgressVsi(int(vsi)) + var modPtr = ptrPool.getID(EntryType.BP, []interface{}{port}) + var ignorePtr = ModPointer.ignorePtr + var mac = *bp.Spec.MacAddress + if p._portMuxVsi < 0 || p._portMuxVsi > math.MaxUint16 { + panic(err) + } + if bp.Spec.Ptype == infradb.Trunk { + var modPtrD = ptrPool.getID(EntryType.BP, []interface{}{mac}) + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portMuxIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {uint16(vsi), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_stag_vlan", + Params: []interface{}{modPtrD, uint32(vsiOut)}, + }, + }, + // From Rx-to-Tx-recirculate (pass 3) entry + p4client.TableEntry{ + Tablename: popStag, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "mod_blob_ptr": {modPtrD, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.vlan_stag_pop", + Params: []interface{}{mac}, + }, + }, + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {mac, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.l2_fwd", + Params: []interface{}{uint32(vsiOut)}, + }, + }, + p4client.TableEntry{ + Tablename: podOutTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtrD, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.vlan_push_trunk", + Params: []interface{}{0, 0, uint32(vsi)}, + }, + }) + for _, vlan := range bp.Spec.LogicalBridges { + BrObj, err := infradb.GetLB(vlan) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", vlan, err) + return entries, err + } + if BrObj.Spec.VlanID > math.MaxUint16 { + log.Printf("intel-e2000: VlanID %v value passed in Logical Bridge create is greater than 16 bit value\n", BrObj.Spec.VlanID) + return entries, errors.New("VlanID value passed in Logical Bridge create is greater than 16 bit value") + } + + vid := uint16(BrObj.Spec.VlanID) + entries = append(entries, p4client.TableEntry{ + // To MUX PORT + Tablename: podInArpTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "vid": {vid, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.send_to_port_mux_trunk", + Params: []interface{}{modPtr, uint32(portMuxVsiOut)}, + }, + }, + // To L2 FWD + p4client.TableEntry{ + Tablename: podInIPTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "vid": {vid, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_vlan_and_pop_vlan", + Params: []interface{}{ignorePtr, vid, uint32(0)}, + }, + }) + + if BrObj.Svi != "" { + SviObj, err := infradb.GetSvi(BrObj.Svi) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", BrObj.Svi, err) + return entries, err + } + VrfObj, err := infradb.GetVrf(SviObj.Spec.Vrf) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", SviObj.Spec.Vrf, err) + return entries, err + } + tcamPrefix, err := _getTcamPrefix(*VrfObj.Spec.Vni, Direction.Tx) + if err != nil { + return entries, err + } + // To VRF SVI + var sviMac = *SviObj.Spec.MacAddress + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portInSviTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {uint16(vsi), "exact"}, + "da": {sviMac, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_vlan_set_vrf_id", + Params: []interface{}{ignorePtr, uint32(tcamPrefix), uint32(0), uint16(*VrfObj.Spec.Vni)}, + }, + }) + } else { + log.Println("intel-e2000: no associated SVI object created") + } + } + } else if bp.Spec.Ptype == infradb.Access { + BrObj, err := infradb.GetLB(bp.Spec.LogicalBridges[0]) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", bp.Spec.LogicalBridges[0], err) + return entries, err + } + if BrObj.Spec.VlanID > math.MaxUint16 { + log.Printf("intel-e2000: VlanID %v value passed in Logical Bridge create is greater than 16 bit value\n", BrObj.Spec.VlanID) + return entries, errors.New("VlanID value passed in Logical Bridge create is greater than 16 bit value") + } + var vid = uint16(BrObj.Spec.VlanID) + var modPtrD = ptrPool.getID(EntryType.BP, []interface{}{*bp.Spec.MacAddress}) + var dstMacAddr = *bp.Spec.MacAddress + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portMuxIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {uint16(vsi), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_ctag_stag_vlan", + Params: []interface{}{modPtrD, uint32(vsiOut)}, + }, + }, + p4client.TableEntry{ + Tablename: popCtagStag, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtrD, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.vlan_ctag_stag_pop", + Params: []interface{}{dstMacAddr}, + }, + }, + // From Rx-to-Tx-recirculate (pass 3) entry + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {dstMacAddr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.l2_fwd", + Params: []interface{}{uint32(vsiOut)}, + }, + }, + // To MUX PORT + p4client.TableEntry{ + Tablename: podOutAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.vlan_push_access", + Params: []interface{}{uint16(0), uint16(0), vid, uint16(0), uint16(0), uint16(vsi)}, + }, + }, + p4client.TableEntry{ + Tablename: podInArpAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.send_to_port_mux_access", + Params: []interface{}{modPtr, uint32(portMuxVsiOut)}, + }, + }, + // To L2 FWD + p4client.TableEntry{ + Tablename: podInIPAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_vlan", + Params: []interface{}{vid, uint32(0)}, + }, + }) + if BrObj.Svi != "" { + SviObj, err := infradb.GetSvi(BrObj.Svi) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", BrObj.Svi, err) + return entries, err + } + VrfObj, err := infradb.GetVrf(SviObj.Spec.Vrf) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", SviObj.Spec.Vrf, err) + return entries, err + } + tcamPrefix, err := _getTcamPrefix(*VrfObj.Spec.Vni, Direction.Tx) + if err != nil { + return entries, err + } + var sviMac = *SviObj.Spec.MacAddress + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portInSviAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "da": {sviMac, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_vrf_id_tx", + Params: []interface{}{uint32(tcamPrefix), uint32(0), uint16(*VrfObj.Spec.Vni)}, + }, + }) + } else { + log.Printf("no SVI for VLAN {vid} on BP {vsi}, skipping entry for SVI table") + } + } + return entries, nil +} + +// translateDeletedBp translate the deleted bp +// +//nolint:funlen +func (p PodDecoder) translateDeletedBp(bp *infradb.BridgePort) ([]interface{}, error) { + var entries []interface{} + port, err := strconv.ParseUint(bp.Metadata.VPort, 10, 16) + if err != nil { + return entries, err + } + var vsi = port + var modPtr = ptrPool.getID(EntryType.BP, []interface{}{port}) + var mac = *bp.Spec.MacAddress + var modPtrD = ptrPool.getID(EntryType.BP, []interface{}{mac}) + if p._portMuxVsi < 0 || p._portMuxVsi > math.MaxUint16 { + panic(err) + } + if bp.Spec.Ptype == infradb.Trunk { + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portMuxIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {uint16(vsi), "exact"}, + }, + Priority: int32(0), + }, + }, + // From Rx-to-Tx-recirculate (pass 3) entry + p4client.TableEntry{ + Tablename: popStag, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "mod_blob_ptr": {modPtrD, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {mac, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: podOutTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtrD, "exact"}, + }, + Priority: int32(0), + }, + }) + for _, vlan := range bp.Spec.LogicalBridges { + BrObj, err := infradb.GetLB(vlan) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", vlan, err) + return entries, err + } + if BrObj.Spec.VlanID > math.MaxUint16 { + log.Printf("intel-e2000: VlanID %v value passed in Logical Bridge create is greater than 16 bit value\n", BrObj.Spec.VlanID) + return entries, errors.New("VlanID value passed in Logical Bridge create is greater than 16 bit value") + } + vid := uint16(BrObj.Spec.VlanID) + entries = append(entries, p4client.TableEntry{ + // To MUX PORT + Tablename: podInArpTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "vid": {vid, "exact"}, + }, + Priority: int32(0), + }, + }, + // To L2 FWD + p4client.TableEntry{ + Tablename: podInIPTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "vid": {vid, "exact"}, + }, + Priority: int32(0), + }, + }) + + if BrObj.Svi != "" { + SviObj, err := infradb.GetSvi(BrObj.Svi) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", BrObj.Svi, err) + return entries, err + } + // To VRF SVI + var sviMac = *SviObj.Spec.MacAddress + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portInSviTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {uint16(vsi), "exact"}, + "da": {sviMac, "exact"}, + }, + Priority: int32(0), + }, + }) + } else { + log.Printf("no SVI for VLAN {vid} on BP {vsi}, skipping entry for SVI table") + } + } + } else if bp.Spec.Ptype == infradb.Access { + BrObj, err := infradb.GetLB(bp.Spec.LogicalBridges[0]) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", bp.Spec.LogicalBridges[0], err) + return entries, err + } + var dstMacAddr = *bp.Spec.MacAddress + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portMuxIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {uint16(vsi), "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: popCtagStag, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtrD, "exact"}, + }, + Priority: int32(0), + }, + }, + // From Rx-to-Tx-recirculate (pass 3) entry + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {dstMacAddr, "exact"}, + }, + Priority: int32(0), + }, + }, + // To MUX PORT + p4client.TableEntry{ + Tablename: podOutAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: podInArpAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + // To L2 FWD + p4client.TableEntry{ + Tablename: podInIPAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + if BrObj.Svi != "" { + SviObj, err := infradb.GetSvi(BrObj.Svi) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", BrObj.Svi, err) + return entries, err + } + var sviMac = *SviObj.Spec.MacAddress + entries = append(entries, p4client.TableEntry{ + // From MUX + Tablename: portInSviAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(vsi), "exact"}, + "da": {sviMac, "exact"}, + }, + Priority: int32(0), + }, + }) + } else { + log.Printf("no SVI for VLAN {vid} on BP {vsi}, skipping entry for SVI table") + } + } + err = ptrPool.putID(EntryType.BP, []interface{}{port}) + if err != nil { + log.Println(err) + } + err = ptrPool.putID(EntryType.BP, []interface{}{*bp.Spec.MacAddress}) + if err != nil { + log.Println(err) + } + return entries, nil +} + +// translateAddedSvi translate the added svi +func (p PodDecoder) translateAddedSvi(svi *infradb.Svi) ([]interface{}, error) { + var ignorePtr = int(ModPointer.ignorePtr) + var mac = *svi.Spec.MacAddress + var entries = make([]interface{}, 0) + + BrObj, err := infradb.GetLB(svi.Spec.LogicalBridge) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", svi.Spec.LogicalBridge, err) + return entries, err + } + for k, v := range BrObj.BridgePorts { + if !v { + PortObj, err := infradb.GetBP(k) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", k, err) + return entries, err + } + port, err := strconv.ParseUint(PortObj.Metadata.VPort, 10, 16) + if err != nil { + return entries, err + } + VrfObj, err := infradb.GetVrf(svi.Spec.Vrf) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v", svi.Spec.Vrf, err) + return entries, err + } + tcamPrefix, err := _getTcamPrefix(*VrfObj.Spec.Vni, Direction.Tx) + if err != nil { + return entries, err + } + if PortObj.Spec.Ptype == infradb.Access { + entries = append(entries, p4client.TableEntry{ + Tablename: portInSviAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port), "exact"}, + "da": {mac, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_vrf_id_tx", + Params: []interface{}{uint32(tcamPrefix), uint32(0), uint16(*VrfObj.Spec.Vni)}, + }, + }) + } else if PortObj.Spec.Ptype == infradb.Trunk { + entries = append(entries, p4client.TableEntry{ + Tablename: portInSviTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port), "exact"}, + "vid": {uint16(BrObj.Spec.VlanID), "exact"}, + "da": {mac, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.pop_vlan_set_vrf_id", + Params: []interface{}{ignorePtr, uint32(tcamPrefix), uint32(0), uint16(*VrfObj.Spec.Vni)}, + }, + }) + } + } + } + return entries, nil +} + +// translateDeletedSvi translate the deleted svi +func (p PodDecoder) translateDeletedSvi(svi *infradb.Svi) ([]interface{}, error) { + var mac = *svi.Spec.MacAddress + var entries = make([]interface{}, 0) + + BrObj, err := infradb.GetLB(svi.Spec.LogicalBridge) + if err != nil { + log.Printf("intel-e2000: unable to find key %s and error is %v\n", svi.Spec.LogicalBridge, err) + return entries, err + } + + for k, v := range BrObj.BridgePorts { + if !v { + PortObj, err := infradb.GetBP(k) + if err != nil { + log.Printf("unable to find key %s and error is %v", k, err) + return entries, err + } + port, err := strconv.ParseUint(PortObj.Metadata.VPort, 10, 16) + if err != nil { + return entries, err + } + if PortObj.Spec.Ptype == infradb.Access { + entries = append(entries, p4client.TableEntry{ + Tablename: portInSviAccess, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port), "exact"}, + "da": {mac, "exact"}, + }, + Priority: int32(0), + }, + }) + } else if PortObj.Spec.Ptype == infradb.Trunk { + entries = append(entries, p4client.TableEntry{ + Tablename: portInSviTrunk, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(port), "exact"}, + "vid": {uint16(BrObj.Spec.VlanID), "exact"}, + "da": {mac, "exact"}, + }, + Priority: int32(0), + }, + }) + } + } + } + return entries, nil +} + +// translateAddedFdb translate the added fdb entry +func (p PodDecoder) translateAddedFdb(fdb netlink_polling.FdbEntryStruct) []interface{} { + var entries = make([]interface{}, 0) + + var fdbMac, _ = net.ParseMAC(fdb.Mac) + if fdb.Type != netlink_polling.BRIDGEPORT { + return entries + } + for dir := range _directionsOf(fdb) { + entries = append(entries, p4client.TableEntry{ + Tablename: l2Fwd, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vlan_id": {_bigEndian16(fdb.VlanID), "exact"}, + "da": {fdbMac, "exact"}, + "direction": {uint16(dir), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_neighbor", + Params: []interface{}{uint16(fdb.Metadata["nh_id"].(int))}, + }, + }) + } + return entries +} + +/*// translateChangedFdb translate the changed fdb entry +func (p PodDecoder) translateChangedFdb(fdb netlink_polling.FdbEntryStruct) []interface{} { + return p.translateAddedFdb(fdb) +}*/ + +// translateDeletedFdb translate the deleted fdb entry +func (p PodDecoder) translateDeletedFdb(fdb netlink_polling.FdbEntryStruct) []interface{} { + var entries = make([]interface{}, 0) + + var fdbMac, _ = net.ParseMAC(fdb.Mac) + if fdb.Type != netlink_polling.BRIDGEPORT { + return entries + } + for dir := range _directionsOf(fdb) { + entries = append(entries, p4client.TableEntry{ + Tablename: l2Fwd, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vlan_id": {_bigEndian16(fdb.VlanID), "exact"}, + "da": {fdbMac, "exact"}, + "direction": {uint16(dir), "exact"}, + }, + Priority: int32(0), + }, + }) + } + return entries +} + +// translateAddedL2Nexthop translate the added l2 nexthop entry +func (p PodDecoder) translateAddedL2Nexthop(nexthop netlink_polling.L2NexthopStruct) []interface{} { + var entries = make([]interface{}, 0) + + if nexthop.Type != netlink_polling.BRIDGEPORT { + return entries + } + var neighbor = nexthop.ID + var portType = nexthop.Metadata["portType"] + var portID = nexthop.Metadata["vport_id"] + + if portType == ipuDB.ACCESS { + entries = append(entries, p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {_bigEndian16(neighbor), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.fwd_to_port", + Params: []interface{}{uint32(_toEgressVsi(portID.(int)))}, + }, + }) + } else if portType == ipuDB.TRUNK { + var key []interface{} + key = append(key, nexthop.Key.Dev, nexthop.Key.VlanID, nexthop.Key.Dst) + + var modPtr = ptrPool.getID(EntryType.l2Nh, key) + entries = append(entries, p4client.TableEntry{ + Tablename: pushVlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.vlan_push", + Params: []interface{}{uint16(0), uint16(0), uint16(nexthop.VlanID)}, + }, + }, + p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {_bigEndian16(neighbor), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_vlan", + Params: []interface{}{modPtr, uint32(_toEgressVsi(portID.(int)))}, + }, + }) + } + return entries +} + +/*// translateChangedL2Nexthop translate the changed l2 nexthop entry +func (p PodDecoder) translateChangedL2Nexthop(nexthop netlink_polling.L2NexthopStruct) []interface{} { + return p.translateAddedL2Nexthop(nexthop) +}*/ + +// translateDeletedL2Nexthop translate the deleted l2 nexthop entry +func (p PodDecoder) translateDeletedL2Nexthop(nexthop netlink_polling.L2NexthopStruct) []interface{} { + var entries = make([]interface{}, 0) + + var modPtr uint32 + if nexthop.Type != netlink_polling.BRIDGEPORT { + return entries + } + var neighbor = nexthop.ID + var portType = nexthop.Metadata["portType"] + + if portType == ipuDB.ACCESS { + entries = append(entries, p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {_bigEndian16(neighbor), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + } else if portType == ipuDB.TRUNK { + var key []interface{} + key = append(key, nexthop.Key.Dev, nexthop.Key.VlanID, nexthop.Key.Dst) + + modPtr = ptrPool.getID(EntryType.l2Nh, key) + entries = append(entries, p4client.TableEntry{ + Tablename: pushVlan, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {modPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {_bigEndian16(neighbor), "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + } + var key []interface{} + key = append(key, nexthop.Key.Dev, nexthop.Key.VlanID, nexthop.Key.Dst) + + err := ptrPool.putID(EntryType.l2Nh, key) + if err != nil { + log.Println(err) + } + return entries +} + +// StaticAdditions static additions +func (p PodDecoder) StaticAdditions() []interface{} { + var portMuxDa, _ = net.ParseMAC(p._portMuxMac) + var vrfMuxDa, _ = net.ParseMAC(p._vrfMuxMac) + var entries = make([]interface{}, 0) + + entries = append(entries, p4client.TableEntry{ + Tablename: portMuxFwd, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.send_to_port_mux", + Params: []interface{}{uint32(_toEgressVsi(p._portMuxVsi))}, + }, + }, + /*p4client.TableEntry{ + Tablename: portMuxIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {Vlan.PHY0, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.set_def_vsi_loopback", + Params: []interface{}{uint32(0)}, + }, + },*/ + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {portMuxDa, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.l2_fwd", + Params: []interface{}{uint32(_toEgressVsi(p._portMuxVsi))}, + }, + }, + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {vrfMuxDa, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.l2_fwd", + Params: []interface{}{uint32(_toEgressVsi(p._vrfMuxVsi))}, + }, + }, + // NH entry for flooding + p4client.TableEntry{ + Tablename: pushQnQFlood, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {p.floodModPtr, "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.vlan_push_stag_ctag_flood", + Params: []interface{}{uint32(0)}, + }, + }, + p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {p.floodNhID, "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + Action: p4client.Action{ + ActionName: "linux_networking_control.push_stag_ctag", + Params: []interface{}{p.floodModPtr, uint32(_toEgressVsi(p._vrfMuxVsi))}, + }, + }) + return entries +} + +// StaticDeletions static deletions +func (p PodDecoder) StaticDeletions() []interface{} { + var entries = make([]interface{}, 0) + + var portMuxDa, _ = net.ParseMAC(p._portMuxMac) + var vrfMuxDa, _ = net.ParseMAC(p._vrfMuxMac) + entries = append(entries, p4client.TableEntry{ + Tablename: portMuxFwd, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }, + /*p4client.TableEntry{ + Tablename: portMuxIn, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "vsi": {uint16(p._portMuxVsi), "exact"}, + "vid": {Vlan.PHY0, "exact"}, + }, + Priority: int32(0), + }, + },*/ + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {portMuxDa, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l2FwdLoop, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "da": {vrfMuxDa, "exact"}, + }, + Priority: int32(0), + }, + }, + // NH entry for flooding + p4client.TableEntry{ + Tablename: pushQnQFlood, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "meta.common.mod_blob_ptr": {p.floodModPtr, "exact"}, + }, + Priority: int32(0), + }, + }, + p4client.TableEntry{ + Tablename: l2Nh, + TableField: p4client.TableField{ + FieldValue: map[string][2]interface{}{ + "neighbor": {p.floodNhID, "exact"}, + "bit32_zeros": {uint32(0), "exact"}, + }, + Priority: int32(0), + }, + }) + return entries +} diff --git a/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/p4trans.go b/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/p4trans.go new file mode 100644 index 00000000..588dd651 --- /dev/null +++ b/pkg/vendor_plugins/intel-e2000/p4runtime/p4translation/p4trans.go @@ -0,0 +1,1089 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries. +// Copyright (C) 2023 Nordix Foundation. + +// Package p4translation handles the intel e2000 fast path configuration +package p4translation + +import ( + "encoding/json" + "fmt" + "log" + "os/exec" + "path" + "regexp" + "strconv" + "strings" + "time" + + "github.com/opiproject/opi-evpn-bridge/pkg/config" + "github.com/opiproject/opi-evpn-bridge/pkg/infradb" + "github.com/opiproject/opi-evpn-bridge/pkg/infradb/common" + "github.com/opiproject/opi-evpn-bridge/pkg/infradb/subscriberframework/eventbus" + nm "github.com/opiproject/opi-evpn-bridge/pkg/netlink" + eb "github.com/opiproject/opi-evpn-bridge/pkg/netlink/eventbus" + p4client "github.com/opiproject/opi-evpn-bridge/pkg/vendor_plugins/intel-e2000/p4runtime/p4driverapi" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// L3 of type l3 decoder +var L3 L3Decoder + +// Vxlan var vxlan of type vxlan decoder +var Vxlan VxlanDecoder + +// Pod var pod of type pod decoder +var Pod PodDecoder + +// ModuleipuHandler var empty struct of type module handler +type ModuleipuHandler struct{} + +// isValidMAC checks if mac is valid +func isValidMAC(mac string) bool { + macPattern := `^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$` + + match, _ := regexp.MatchString(macPattern, mac) + return match +} + +// getMac get the mac from interface +func getMac(dev string) string { + cmd := exec.Command("ip", "-d", "-j", "link", "show", dev) + out, err := cmd.CombinedOutput() + if err != nil { + log.Printf("intel-e2000: Error running command: %v\n", err) + return "" + } + + var links []struct { + Address string `json:"address"` + } + if err := json.Unmarshal(out, &links); err != nil { + log.Printf("intel-e2000: Error unmarshaling JSON: %v\n", err) + return "" + } + + if len(links) > 0 { + mac := links[0].Address + return mac + } + + return "" +} + +// vportFromMac get the vport from the mac +func vportFromMac(mac string) int { + mbyte := strings.Split(mac, ":") + if len(mbyte) < 5 { + return -1 + } + byte0, _ := strconv.ParseInt(mbyte[0], 16, 64) + byte1, _ := strconv.ParseInt(mbyte[1], 16, 64) + + return int(byte0<<8 + byte1) +} + +// idsOf get the mac vsi from nexthop id +func idsOf(value string) (string, string, error) { + if isValidMAC(value) { + return strconv.Itoa(vportFromMac(value)), value, nil + } + + mac := getMac(value) + vsi := vportFromMac(mac) + if vsi == -1 { + return "", "", fmt.Errorf("failed to get id") + } + return strconv.Itoa(vsi), mac, nil +} + +var ( + // defaultAddr default address + defaultAddr = "127.0.0.1:9559" + + // Conn default grpc connection + Conn *grpc.ClientConn +) + +// startSubscriber set the subscriber handlers +func startSubscriber(eventBus *eb.EventBus, eventType string) { + subscriber := eventBus.Subscribe(eventType) + + go func() { + for { + select { + case event := <-subscriber.Ch: + log.Printf("intel-e2000: Subscriber for %s received event: %s\n", eventType, event) + switch eventType { + case "route_added": + handleRouteAdded(event) + case "route_updated": + handleRouteUpdated(event) + case "route_deleted": + handleRouteDeleted(event) + case "nexthop_added": + handleNexthopAdded(event) + case "nexthop_updated": + handleNexthopUpdated(event) + case "nexthop_deleted": + handleNexthopDeleted(event) + case "fdb_entry_added": + handleFbdEntryAdded(event) + case "fdb_entry_updated": + handleFbdEntryUpdated(event) + case "fdb_entry_deleted": + handleFbdEntryDeleted(event) + case "l2_nexthop_added": + handleL2NexthopAdded(event) + case "l2_nexthop_updated": + handleL2NexthopUpdated(event) + case "l2_nexthop_deleted": + handleL2NexthopDeleted(event) + } + case <-subscriber.Quit: + return + } + } + }() +} + +// handleRouteAdded handles the added route +func handleRouteAdded(route interface{}) { + var entries []interface{} + routeData, _ := route.(nm.RouteStruct) + entries = L3.translateAddedRoute(routeData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Printf("intel-e2000: Entry is not of type p4client.TableEntry:- %v\n", e) + } + } +} + +// handleRouteUpdated handles the updated route +func handleRouteUpdated(route interface{}) { + var entries []interface{} + routeData, _ := route.(nm.RouteStruct) + entries = L3.translateDeletedRoute(routeData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + err := p4client.DelEntry(e) + if err != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, err) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = append(entries, L3.translateAddedRoute(routeData)) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleRouteDeleted handles the deleted route +func handleRouteDeleted(route interface{}) { + var entries []interface{} + routeData, _ := route.(nm.RouteStruct) + entries = L3.translateDeletedRoute(routeData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleNexthopAdded handles the added nexthop +func handleNexthopAdded(nexthop interface{}) { + var entries []interface{} + nexthopData, _ := nexthop.(nm.NexthopStruct) + entries = L3.translateAddedNexthop(nexthopData) + + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Vxlan.translateAddedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleNexthopUpdated handles the updated nexthop +func handleNexthopUpdated(nexthop interface{}) { + var entries []interface{} + nexthopData, _ := nexthop.(nm.NexthopStruct) + entries = L3.translateDeletedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Vxlan.translateDeletedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = L3.translateAddedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Vxlan.translateAddedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleNexthopDeleted handles the deleted nexthop +func handleNexthopDeleted(nexthop interface{}) { + var entries []interface{} + nexthopData, _ := nexthop.(nm.NexthopStruct) + entries = L3.translateDeletedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Vxlan.translateDeletedNexthop(nexthopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleFbdEntryAdded handles the added fdb entry +func handleFbdEntryAdded(fbdEntry interface{}) { + var entries []interface{} + fbdEntryData, _ := fbdEntry.(nm.FdbEntryStruct) + entries = Vxlan.translateAddedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateAddedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleFbdEntryUpdated handles the updated fdb entry +func handleFbdEntryUpdated(fdbEntry interface{}) { + var entries []interface{} + fbdEntryData, _ := fdbEntry.(nm.FdbEntryStruct) + entries = Vxlan.translateDeletedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateDeletedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + + entries = Vxlan.translateAddedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateAddedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleFbdEntryDeleted handles the deleted fdb entry +func handleFbdEntryDeleted(fdbEntry interface{}) { + var entries []interface{} + fbdEntryData, _ := fdbEntry.(nm.FdbEntryStruct) + entries = Vxlan.translateDeletedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateDeletedFdb(fbdEntryData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleL2NexthopAdded handles the added l2 nexthop +func handleL2NexthopAdded(l2NextHop interface{}) { + var entries []interface{} + l2NextHopData, _ := l2NextHop.(nm.L2NexthopStruct) + + entries = Vxlan.translateAddedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateAddedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleL2NexthopUpdated handles the updated l2 nexthop +func handleL2NexthopUpdated(l2NextHop interface{}) { + var entries []interface{} + l2NextHopData, _ := l2NextHop.(nm.L2NexthopStruct) + entries = Vxlan.translateDeletedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateDeletedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Vxlan.translateDeletedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateDeletedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("iintel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// handleL2NexthopDeleted handles the deleted l2 nexthop +func handleL2NexthopDeleted(l2NextHop interface{}) { + var entries []interface{} + l2NextHopData, _ := l2NextHop.(nm.L2NexthopStruct) + entries = Vxlan.translateDeletedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + err := p4client.DelEntry(e) + if err != nil { + log.Printf("intel-e2000: error deleting entry for %v error %v\n", e.Tablename, err) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + entries = Pod.translateDeletedL2Nexthop(l2NextHopData) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// HandleEvent handles the infradb events +func (h *ModuleipuHandler) HandleEvent(eventType string, objectData *eventbus.ObjectData) { + switch eventType { + case "vrf": + log.Printf("intel-e2000: recevied %s %s\n", eventType, objectData.Name) + handlevrf(objectData) + case "logical-bridge": + log.Printf("inyel-e2000: recevied %s %s\n", eventType, objectData.Name) + handlelb(objectData) + case "bridge-port": + log.Printf("intel-e2000: recevied %s %s\n", eventType, objectData.Name) + handlebp(objectData) + case "svi": + log.Printf("intel-e2000: recevied %s %s\n", eventType, objectData.Name) + handlesvi(objectData) + default: + + log.Println("intel-e2000: error: Unknown event type: ", eventType) + } +} + +// handlevrf handles the vrf events +func handlevrf(objectData *eventbus.ObjectData) { + var comp common.Component + vrf, err := infradb.GetVrf(objectData.Name) + if err != nil { + log.Printf("intel-e2000: GetVRF error: %s %s\n", err, objectData.Name) + return + } + + if objectData.ResourceVersion != vrf.ResourceVersion { + log.Printf("intel-e2000: Mismatch in resoruce version %+v\n and vrf resource version %+v\n", objectData.ResourceVersion, vrf.ResourceVersion) + comp.Name = intele2000Str + comp.CompStatus = common.ComponentStatusError + if comp.Timer == 0 { // wait timer is 2 powerof natural numbers ex : 1,2,3... + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + err = infradb.UpdateVrfStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating vrf status: %s\n", err) + } + return + } + + if len(vrf.Status.Components) != 0 { + for i := 0; i < len(vrf.Status.Components); i++ { + if vrf.Status.Components[i].Name == intele2000Str { + comp = vrf.Status.Components[i] + } + } + } + if vrf.Status.VrfOperStatus != infradb.VrfOperStatusToBeDeleted { + status := offloadVrf(vrf) + if status { + comp.CompStatus = common.ComponentStatusSuccess + + comp.Name = intele2000Str + comp.Timer = 0 + } else { + if comp.Timer == 0 { // wait timer is 2 powerof natural numbers ex : 1,2,3... + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 * time.Second + } + + comp.Name = intele2000Str + comp.CompStatus = common.ComponentStatusError + } + log.Printf("intel-e2000: %+v\n", comp) + err = infradb.UpdateVrfStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, vrf.Metadata, comp) + if err != nil { + log.Printf("error in updating vrf status: %s\n", err) + } + } else { + status := tearDownVrf(vrf) + if status { + comp.CompStatus = common.ComponentStatusSuccess + + comp.Name = intele2000Str + comp.Timer = 0 + } else { + comp.CompStatus = common.ComponentStatusError + comp.Name = intele2000Str + if comp.Timer == 0 { // wait timer is 2 powerof natural numbers ex : 1,2,3... + comp.Timer = 2 + } else { + comp.Timer *= 2 + } + } + + log.Printf("intel-e2000: %+v\n", comp) + err = infradb.UpdateVrfStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating vrf status: %s\n", err) + } + } +} + +// handlelb handles the lb events +func handlelb(objectData *eventbus.ObjectData) { + var comp common.Component + lb, err := infradb.GetLB(objectData.Name) + if err != nil { + log.Printf("intel-e2000: GetLB error: %s %s\n", err, objectData.Name) + return + } + + if len(lb.Status.Components) != 0 { + for i := 0; i < len(lb.Status.Components); i++ { + if lb.Status.Components[i].Name == intele2000Str { + comp = lb.Status.Components[i] + } + } + } + if lb.Status.LBOperStatus != infradb.LogicalBridgeOperStatusToBeDeleted { + status := setUpLb(lb) + comp.Name = intele2000Str + if status { + comp.Details = "" + comp.CompStatus = common.ComponentStatusSuccess + comp.Timer = 0 + } else { + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + comp.CompStatus = common.ComponentStatusError + } + + log.Printf("intel-e2000: %+v \n", comp) + err = infradb.UpdateLBStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating lb status: %s\n", err) + } + } else { + status := tearDownLb(lb) + comp.Name = intele2000Str + if status { + comp.CompStatus = common.ComponentStatusSuccess + comp.Timer = 0 + } else { + comp.CompStatus = common.ComponentStatusError + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + } + + log.Printf("intel-e2000: %+v\n", comp) + err = infradb.UpdateLBStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating lb status: %s\n", err) + } + } +} + +// handlebp handles the bp events +func handlebp(objectData *eventbus.ObjectData) { + var comp common.Component + bp, err := infradb.GetBP(objectData.Name) + if err != nil { + log.Printf("intel-e2000: GetBP error: %s\n", err) + return + } + + if len(bp.Status.Components) != 0 { + for i := 0; i < len(bp.Status.Components); i++ { + if bp.Status.Components[i].Name == intele2000Str { + comp = bp.Status.Components[i] + } + } + } + if bp.Status.BPOperStatus != infradb.BridgePortOperStatusToBeDeleted { + status := setUpBp(bp) + comp.Name = intele2000Str + if status { + comp.Details = "" + comp.CompStatus = common.ComponentStatusSuccess + comp.Timer = 0 + } else { + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + comp.CompStatus = common.ComponentStatusError + } + + log.Printf("intel-e2000: %+v \n", comp) + err = infradb.UpdateBPStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating bp status: %s\n", err) + } + } else { + status := tearDownBp(bp) + comp.Name = intele2000Str + if status { + comp.CompStatus = common.ComponentStatusSuccess + comp.Timer = 0 + } else { + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + comp.CompStatus = common.ComponentStatusError + } + + log.Printf("intel-e2000: %+v \n", comp) + err = infradb.UpdateBPStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating bp status: %s\n", err) + } + } +} + +// handlesvi handles the svi events +func handlesvi(objectData *eventbus.ObjectData) { + var comp common.Component + svi, err := infradb.GetSvi(objectData.Name) + if err != nil { + log.Printf("intel-e2000: GetSvi error: %s %s\n", err, objectData.Name) + return + } + + if objectData.ResourceVersion != svi.ResourceVersion { + log.Printf("intel-e2000:: Mismatch in resoruce version %+v\n and svi resource version %+v\n", objectData.ResourceVersion, svi.ResourceVersion) + comp.Name = intele2000Str + comp.CompStatus = common.ComponentStatusError + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + err = infradb.UpdateSviStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating svi status: %s\n", err) + } + return + } + if len(svi.Status.Components) != 0 { + for i := 0; i < len(svi.Status.Components); i++ { + if svi.Status.Components[i].Name == intele2000Str { + comp = svi.Status.Components[i] + } + } + } + if svi.Status.SviOperStatus != infradb.SviOperStatusToBeDeleted { + status := setUpSvi(svi) + comp.Name = intele2000Str + if status { + comp.CompStatus = common.ComponentStatusSuccess + comp.Timer = 0 + } else { + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + comp.CompStatus = common.ComponentStatusError + } + + log.Printf("intel-e2000:: %+v \n", comp) + err = infradb.UpdateSviStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating svi status: %s\n", err) + } + } else { + status := tearDownSvi(svi) + comp.Name = intele2000Str + if status { + comp.CompStatus = common.ComponentStatusSuccess + comp.Timer = 0 + } else { + comp.CompStatus = common.ComponentStatusError + if comp.Timer == 0 { + comp.Timer = 2 * time.Second + } else { + comp.Timer *= 2 + } + } + log.Printf("intel-e2000: %+v \n", comp) + err = infradb.UpdateSviStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp) + if err != nil { + log.Printf("error in updating svi status: %s\n", err) + } + } +} + +// offloadVrf offload the vrf events +func offloadVrf(vrf *infradb.Vrf) bool { + if path.Base(vrf.Name) == grdStr { + return true + } + + entries := Vxlan.translateAddedVrf(vrf) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("ntel-e2000: Entry is not of type p4client.TableEntry:-", e) + return false + } + } + return true +} + +// setUpLb set up the logical bridge +func setUpLb(lb *infradb.LogicalBridge) bool { + entries := Vxlan.translateAddedLb(lb) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry:-", e) + return false + } + } + return true +} + +// setUpBp set up the bridge port +func setUpBp(bp *infradb.BridgePort) bool { + // var entries []interface{} + entries, err := Pod.translateAddedBp(bp) + if err != nil { + return false + } + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry:-", e) + return false + } + } + return true +} + +// setUpSvi set up the svi +func setUpSvi(svi *infradb.Svi) bool { + // var entries []interface{} + entries, err := Pod.translateAddedSvi(svi) + if err != nil { + return false + } + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry:-", e) + return false + } + } + return true +} + +// tearDownVrf tear down the vrf +func tearDownVrf(vrf *infradb.Vrf) bool { + if path.Base(vrf.Name) == grdStr { + return true + } + // var entries []interface{} + entries := Vxlan.translateDeletedVrf(vrf) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + return false + } + } + return true +} + +// tearDownLb tear down the logical bridge +func tearDownLb(lb *infradb.LogicalBridge) bool { + // var entries []interface{} + entries := Vxlan.translateDeletedLb(lb) + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + return false + } + } + return true +} + +// tearDownBp tear down the bridge port +func tearDownBp(bp *infradb.BridgePort) bool { + // var entries []interface{} + entries, err := Pod.translateDeletedBp(bp) + if err != nil { + return false + } + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + return false + } + } + return true +} + +// tearDownSvi tear down the svi +func tearDownSvi(svi *infradb.Svi) bool { + // var entries []interface{} + entries, err := Pod.translateDeletedSvi(svi) + if err != nil { + return false + } + for _, entry := range entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + return false + } + } + return true +} + +// Init function handles init functionality +func Init() { + // Netlink Listener + startSubscriber(nm.EventBus, "route_added") + + startSubscriber(nm.EventBus, "route_updated") + startSubscriber(nm.EventBus, "route_deleted") + startSubscriber(nm.EventBus, "nexthop_added") + startSubscriber(nm.EventBus, "nexthop_updated") + startSubscriber(nm.EventBus, "nexthop_deleted") + startSubscriber(nm.EventBus, "fdb_entry_added") + startSubscriber(nm.EventBus, "fdb_entry_updated") + startSubscriber(nm.EventBus, "fdb_entry_deleted") + startSubscriber(nm.EventBus, "l2_nexthop_added") + startSubscriber(nm.EventBus, "l2_nexthop_updated") + startSubscriber(nm.EventBus, "l2_nexthop_deleted") + + // InfraDB Listener + + eb := eventbus.EBus + for _, subscriberConfig := range config.GlobalConfig.Subscribers { + if subscriberConfig.Name == intele2000Str { + for _, eventType := range subscriberConfig.Events { + eb.StartSubscriber(subscriberConfig.Name, eventType, subscriberConfig.Priority, &ModuleipuHandler{}) + } + } + } + // Setup p4runtime connection + Conn, err := grpc.Dial(defaultAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("intel-e2000: Cannot connect to server: %v\n", err) + } + + err1 := p4client.NewP4RuntimeClient(config.GlobalConfig.P4.Config.BinFile, config.GlobalConfig.P4.Config.P4infoFile, Conn) + if err1 != nil { + log.Fatalf("intel-e2000: Failed to create P4Runtime client: %v\n", err1) + } + // add static rules into the pipeline of representators read from config + representors := make(map[string][2]string) + for k, v := range config.GlobalConfig.P4.Representors { + vsi, mac, err := idsOf(v.(string)) + if err != nil { + log.Println("intel-e2000: Error:", err) + return + } + representors[k] = [2]string{vsi, mac} + } + log.Printf("intel-e2000: REPRESENTORS %+v\n", representors) + + L3 = L3.L3DecoderInit(representors) + Pod = Pod.PodDecoderInit(representors) + // decoders = []interface{}{L3, Vxlan, Pod} + Vxlan = Vxlan.VxlanDecoderInit(representors) + L3entries := L3.StaticAdditions() + for _, entry := range L3entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + Podentries := Pod.StaticAdditions() + for _, entry := range Podentries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.AddEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} + +// Exit function deletes the static additions +func Exit() { + L3entries := L3.StaticDeletions() + for _, entry := range L3entries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } + Podentries := Pod.StaticDeletions() + for _, entry := range Podentries { + if e, ok := entry.(p4client.TableEntry); ok { + er := p4client.DelEntry(e) + if er != nil { + log.Printf("intel-e2000: error adding entry for %v error %v\n", e.Tablename, er) + } + } else { + log.Println("intel-e2000: Entry is not of type p4client.TableEntry") + } + } +} diff --git a/testscript/auto_create_100_vrfs.sh b/testscript/auto_create_100_vrfs.sh deleted file mode 100755 index dd303554..00000000 --- a/testscript/auto_create_100_vrfs.sh +++ /dev/null @@ -1,45 +0,0 @@ -#/bin/bash - -<< com -{ - set -e - while true - do - sleep 1 - # Since this is run as a subshell (instead of an external command), - # the parent pid is $$, not $PPID. - ip -br l | grep br- | wc -l - # kill -9 $(pidof opi-evpn-bridge_12022024) - done -} & - -com - -i=0 -while [ $i -lt 100 ] -do - ./godpu evpn create-vrf --name brown$i --vni `expr $i + 100` --loopback 10.121.11.`expr $i + 10`/32 --vtep 110.1.41.51/32 --addr localhost:50151 - i=`expr $i + 1` -# sleep 0.1 -done -sleep 40 - -j=0 - -while [ $j -lt 100 ] -do - ./godpu evpn get-vrf --name brown$j - #sleep 0.5 - j=`expr $j + 1` -done - -sleep 30 -x=0 - -while [ $x -lt 100 ] -do - ./godpu evpn delete-vrf --name brown$x - #sleep 0.5 - x=`expr $x + 1` -done - diff --git a/testscript/auto_create_delete_vrf_nosleep.sh b/testscript/auto_create_delete_vrf_nosleep.sh deleted file mode 100755 index 44db2a96..00000000 --- a/testscript/auto_create_delete_vrf_nosleep.sh +++ /dev/null @@ -1,31 +0,0 @@ -#/bin/bash - -a=0 -while [ $a -lt 2 ] -do -i=0 -while [ $i -lt 1 ] -do - ./godpu evpn create-vrf --name yellow$a --vni `expr $a + 301` --loopback 10.11.17.`expr $i + $a + 10`/32 --vtep 110.1.41.5/32 --addr localhost:50151 - i=`expr $i + 1` -done -sleep 0.5 - -j=0 - -while [ $j -lt 1 ] -do - ./godpu evpn get-vrf --name yellow$a - j=`expr $j + 1` -done - -x=0 - -while [ $x -lt 1 ] -do - ./godpu evpn delete-vrf --name yellow$a - x=`expr $x + 1` -done -a=`expr $a + 1` - -done